init
This commit is contained in:
121 files changed
+17641
No files matched your search
@@ -0,0 +1,129 @@
|
||||
# Kadence — the native desktop app
|
||||
|
||||
Not Electron. **PyQt6 + QML with Qt Quick Controls' Material style**: the window
|
||||
is a native Wayland/X11 surface drawn by Qt, using the session's own fonts,
|
||||
scaling and colour scheme. The only dependency is PyQt6, which on Arch is the
|
||||
system package `python-pyqt6`; everything else — HTTP, config, process scanning —
|
||||
is the Python standard library.
|
||||
|
||||
It is also the piece that *enforces* things, because it is the only part running
|
||||
on your machine.
|
||||
|
||||
```bash
|
||||
sudo pacman -S --needed python-pyqt6 qt6-declarative # Arch
|
||||
./packaging/install-desktop.sh # menu entry, icon, launcher
|
||||
sudo ./helper/install.sh # privileged helper (website blocking)
|
||||
kadence-desktop
|
||||
```
|
||||
|
||||
Run it straight from the checkout instead: `python3 -m kadence_desktop`.
|
||||
|
||||
---
|
||||
|
||||
## What "native" buys you
|
||||
|
||||
| Feature | How |
|
||||
| --- | --- |
|
||||
| **Tray icon that *is* the timer** | `QSystemTrayIcon` (a StatusNotifierItem on KDE). The icon is painted at runtime: a ring that drains as the interval runs, tinted by focus / break / idle, with a pause glyph when held. Middle-click toggles the timer. |
|
||||
| **Real notifications with buttons** | `org.freedesktop.Notifications` over D-Bus, with actions: *Start break* · *Another pomodoro* · *+5 min*. Replaced in place, so a day of pomodoros never stacks a column of toasts. |
|
||||
| **Scriptable over D-Bus** | `org.kadence.Desktop` exports `StartWork`, `Toggle`, `Pause`, `Complete`, `Extend`, `Interrupt`, `Status`, `SendReport`, `Raise`. Bind any of them to a global shortcut in System Settings — no key-grabbing hacks. |
|
||||
| **Screen stays awake** | `org.freedesktop.ScreenSaver.Inhibit` while you focus, released the moment the interval ends. |
|
||||
| **Do Not Disturb** | Other apps' banners are suppressed during focus (Plasma's `Notifications.Inhibit`; on GNOME the `show-banners` gsetting, put back afterwards). |
|
||||
| **Your music pauses** | Any MPRIS player (Spotify, VLC, mpv, browsers) is paused when focus starts, and *only the ones we paused* resume on the break. |
|
||||
| **Taskbar progress** | `com.canonical.Unity.LauncherEntry` — a progress bar under the dock icon and a badge with today's count. |
|
||||
| **Knows when you walked away** | logind `Lock`/`Unlock` and `PrepareForSleep` pause and resume the timer; optional idle-pause uses KDE's or Mutter's idle monitor. |
|
||||
| **Starts with the session** | XDG autostart entry, or a **systemd user unit** that restarts on failure and logs to the journal. |
|
||||
| **Single instance** | Owning the D-Bus name is the lock; a second launch raises the first window and exits. |
|
||||
| **Launcher actions** | Right-click the icon: *Start a pomodoro*, *Start a break*, *Start / pause*, *Send today's report*. |
|
||||
|
||||
Every one of these degrades to a no-op where the desktop does not provide it, so
|
||||
the same build runs on KDE, GNOME or a bare window manager.
|
||||
|
||||
---
|
||||
|
||||
## Blocking
|
||||
|
||||
The rules (which sites, which apps, when) live on the backend so the web app can
|
||||
edit them and a second machine obeys the same list. This app enforces them:
|
||||
|
||||
* **Websites** — the managed region of `/etc/hosts` is kept in sync with the
|
||||
policy. Writing it needs root, which a desktop app must not have, so it calls
|
||||
`kadence-helper` through **pkexec**.
|
||||
* **Applications** — `/proc` is scanned every few seconds for your own processes
|
||||
matching the list; they are terminated (SIGTERM, then SIGKILL) or merely
|
||||
reported, and every attempt is sent to the backend for the evening report.
|
||||
* Everything is released when the interval ends — and on exit, including a crash,
|
||||
because `shutdown()` clears the block and the inhibits.
|
||||
|
||||
### The privileged helper
|
||||
|
||||
`helper/kadence-helper` is the entire privileged surface, and it is deliberately
|
||||
dull:
|
||||
|
||||
* every hostname must match a strict pattern, the redirect must parse as an IP;
|
||||
* only the region between its own markers is rewritten — your own entries above
|
||||
and below survive byte for byte;
|
||||
* the input list is opened `O_NOFOLLOW` and must be a regular file;
|
||||
* the write is atomic (temp file + `rename`), and the first run backs up
|
||||
`/etc/hosts` to `/etc/hosts.kadence-backup`;
|
||||
* `localhost` can never be blocked.
|
||||
|
||||
```bash
|
||||
sudo ./helper/install.sh # polkit asks once per session
|
||||
sudo ./helper/install.sh --passwordless # or never, for your user only
|
||||
sudo ./helper/install.sh --uninstall # unblocks first, then removes itself
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Global shortcuts
|
||||
|
||||
System Settings → Shortcuts → Custom Shortcuts → New → D-Bus command:
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| Service | `org.kadence.Desktop` |
|
||||
| Path | `/Timer` |
|
||||
| Method | `org.kadence.Timer.Toggle` |
|
||||
|
||||
Or bind a command: `kadence-desktop --toggle`. The verbs talk to the running
|
||||
instance instead of starting a second one:
|
||||
|
||||
```bash
|
||||
kadence-desktop --toggle # start → pause → resume
|
||||
kadence-desktop --start # start a focus interval
|
||||
kadence-desktop --break
|
||||
kadence-desktop --complete
|
||||
kadence-desktop --status # "Focus 18:42 · Write the docs · 3/8 today"
|
||||
kadence-desktop --report # send today's report to Telegram
|
||||
```
|
||||
|
||||
In-window shortcuts: **Space** start/pause · **Ctrl+Enter** finish ·
|
||||
**Ctrl+E** +5 min · **Ctrl+R** refresh sources · **Ctrl+1…5** pages ·
|
||||
**Ctrl+W** hide to tray.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
`~/.config/kadence/desktop.json` holds only what is about *this machine* — the
|
||||
backend URL and token, and which integrations this box may use. Timer lengths,
|
||||
task sources, block lists and Telegram all live on the server, so they are the
|
||||
same everywhere. The **Settings** page edits both, clearly separated.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
kadence_desktop/
|
||||
app.py # QApplication, QML engine, tray, D-Bus, single instance
|
||||
controller.py # the object QML binds to; wires transitions to integrations
|
||||
worker.py # the network thread (every HTTP call happens here)
|
||||
api.py # stdlib HTTP client for the backend
|
||||
config.py # ~/.config/kadence/desktop.json
|
||||
tray.py # the painted countdown icon and its menu
|
||||
integrations/ # notifications, inhibit/DND, MPRIS, launcher, logind, D-Bus, autostart
|
||||
blocking/ # hosts file, /proc scanning, and the manager that syncs policy
|
||||
qml/ # Material UI: Timer, Tasks, Blocker, Report, Settings
|
||||
helper/ # the privileged helper + polkit action + installer
|
||||
packaging/ # .desktop entry, icon, user installer
|
||||
```
|
||||
Reference in new issue
Block a user