diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-30 17:34:33 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-30 17:34:33 +0100 |
| commit | 412dec827b5d87c4138491faf49a0babb229f7e2 (patch) | |
| tree | 3916ccf499c09853e16d2a2c6973fea3a453b877 /desktop | |
| parent | 9b9271c304cc7654de0320760f875b666294f591 (diff) | |
| download | quickshell-412dec827b5d87c4138491faf49a0babb229f7e2.tar.gz quickshell-412dec827b5d87c4138491faf49a0babb229f7e2.zip | |
Drop breaktimer and the archived AI apps
breaktimer is parked and imggen and fanfictioner are archived and
uninstalled, so the shell was reading files nothing writes and offering
buttons that call commands that no longer exist.
Status: remove the bt* properties, the file watchers behind them, the
verb runner and the presentation-mode pause/resume. The status page loses
BreakRow and the tile falls back to "All clear".
AI: remove the imggen and fanfictioner rows from ai-state.sh, their
labels and controls, and the pid-based fanfictioner stop. The sd-cli
engine row stays, since the wallpaper upscaler uses sd-cli too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop')
| -rw-r--r-- | desktop/modules/ai/AiModule.qml | 16 | ||||
| -rw-r--r-- | desktop/modules/ai/AiPage.qml | 2 | ||||
| -rw-r--r-- | desktop/modules/ai/README.md | 10 | ||||
| -rwxr-xr-x | desktop/modules/ai/ai-state.sh | 27 | ||||
| -rwxr-xr-x | desktop/modules/ai/test-ai-state.sh | 22 | ||||
| -rw-r--r-- | desktop/modules/notes/NotesModule.qml | 2 | ||||
| -rw-r--r-- | desktop/modules/status/BreakRow.qml | 84 | ||||
| -rw-r--r-- | desktop/modules/status/README.md | 46 | ||||
| -rw-r--r-- | desktop/modules/status/StatusPage.qml | 12 | ||||
| -rw-r--r-- | desktop/modules/status/StatusTile.qml | 9 |
10 files changed, 21 insertions, 209 deletions
diff --git a/desktop/modules/ai/AiModule.qml b/desktop/modules/ai/AiModule.qml index d58ebfe..4c50224 100644 --- a/desktop/modules/ai/AiModule.qml +++ b/desktop/modules/ai/AiModule.qml @@ -41,16 +41,12 @@ Module { sd: "stable-diffusion.cpp", assistant: "Desktop assistant", chat: "Local chat", - imggen: "imggen", - fanfic: "Fanfictioner", }) readonly property string assistantSh: Quickshell.env("HOME") + "/Programming/GIT/desktop-assistant/assistant.sh" - // Each project's own control. Fanfictioner has no start, a run needs a - // story file, and its stop needs the pid, see stop(). llamachat has no - // restart, and --quit answers before the app is gone, so a --daemon + // Each project's own control. llamachat has no restart, and --quit answers before the app is gone, so a --daemon // straight after would find it still running and do nothing: wait for // --ping to fail first, up to 5s. readonly property var controls: ({ @@ -59,7 +55,6 @@ Module { chat: { start: ["llamachat", "--daemon"], stop: ["llamachat", "--quit"], restart: ["sh", "-c", "llamachat --quit; for _ in $(seq 50); do " + "llamachat --ping || break; sleep 0.1; done; exec llamachat --daemon"] }, - imggen: { start: ["imggen", "start"], stop: ["imggen", "stop"] }, }) function canStart(app) { return controls[app.name] !== undefined; } @@ -75,14 +70,7 @@ Module { } function stop(app) { - if (app.name !== "fanfic") { - exec(controls[app.name].stop); - return; - } - // The sd-cli children first, then the script, by pid only: a name - // match would take unrelated sd-cli and python processes with it. - if (!/^[0-9]+$/.test(app.detail)) return; - exec(["sh", "-c", 'pkill -TERM -P "$1"; kill -TERM "$1"', "sh", app.detail]); + exec(controls[app.name].stop); } // The router frees the model's VRAM; the next request for it loads it diff --git a/desktop/modules/ai/AiPage.qml b/desktop/modules/ai/AiPage.qml index 369c1b5..c1cb07c 100644 --- a/desktop/modules/ai/AiPage.qml +++ b/desktop/modules/ai/AiPage.qml @@ -68,7 +68,7 @@ Column { detail: { if (!modelData.running) return "Stopped"; if (modelData.detail === "") return "Running"; - return "Running · " + (modelData.name === "fanfic" ? "pid " : "") + modelData.detail; + return "Running · " + modelData.detail; } action: modelData.running ? "Stop" : page.ai.canStart(modelData) ? "Start" : "" danger: modelData.running diff --git a/desktop/modules/ai/README.md b/desktop/modules/ai/README.md index e71159b..d87087a 100644 --- a/desktop/modules/ai/README.md +++ b/desktop/modules/ai/README.md @@ -18,10 +18,10 @@ llama's state comes from its API: `/models` for the loaded model, then `autoload=false` matters: a plain `/slots?model=` on an unloaded model loads it into VRAM, which is how this was found. -The sd row covers two processes. A running `sd-cli`, the one-shot runs -fanfictioner makes, reads up and generating with its model marked `(sd-cli)`, -whatever the web UI is doing: it holds VRAM for as long as it lives. The first -version watched only `sd-server` and showed the engine down mid-run. +The sd row covers two processes. A running one-shot `sd-cli` reads up and +generating with its model marked `(sd-cli)`, whatever the web UI is doing: it +holds VRAM for as long as it lives. The first version watched only +`sd-server` and showed the engine down mid-run. sd-server has no progress endpoint. Its log, `~/.cache/sd-server.log`, streams progress bars while it generates, so the row reads busy while the log was @@ -33,8 +33,6 @@ written in the last 5s. A step slower than that reads idle. | --- | --- | --- | --- | --- | | desktop-assistant | `assistant.sh status` | `assistant.sh start` | `assistant.sh stop` | `assistant.sh restart` | | local chat | `llamachat --ping` | `llamachat --daemon` | `llamachat --quit` | quit, wait for `--ping` to fail (5s at most), `--daemon` | -| imggen | `imggen status` | `imggen start` (default model) | `imggen stop` | none | -| fanfictioner | `pgrep` on `python3 .../fanfictioner` | none | TERM its children, then it, by pid | none | Restart shows only while an app is running. llamachat's `--quit` answers before the app has exited, so a `--daemon` sent straight after finds it still diff --git a/desktop/modules/ai/ai-state.sh b/desktop/modules/ai/ai-state.sh index 8b10368..a4406ce 100755 --- a/desktop/modules/ai/ai-state.sh +++ b/desktop/modules/ai/ai-state.sh @@ -12,19 +12,17 @@ # GNU General Public License for more details. # # One sweep of the local AI stack for the drawer's AI page. Always prints -# these six lines, tab separated, and exits 0: +# these four lines, tab separated, and exits 0: # # engine<TAB>llama<TAB>up|down<TAB>idle|busy<TAB>model # engine<TAB>sd<TAB>up|down<TAB>idle|busy<TAB>model, "(sd-cli)" for a CLI run # app<TAB>assistant<TAB>running|stopped<TAB>- # app<TAB>chat<TAB>running|stopped<TAB>- -# app<TAB>imggen<TAB>running|stopped<TAB>model -# app<TAB>fanfic<TAB>running|stopped<TAB>pid # # An empty value is "-". A probe that fails reads as down or stopped, not as # an error: every row is something that may simply not be running. # -# The test stubs curl, pgrep, llamachat and imggen on PATH and points +# The test stubs curl, pgrep and llamachat on PATH and points # AI_ASSISTANT and AI_SD_LOG at fixtures; see test-ai-state.sh. set -u @@ -65,7 +63,7 @@ model_of() { sd() { local line busy=idle mtime - # A one-shot sd-cli (fanfictioner runs them) holds VRAM and generates for + # A one-shot sd-cli holds VRAM and generates for # as long as it lives, so it outranks whatever the web UI is doing. if line="$(pgrep -axo sd-cli)"; then row engine sd up busy "$(model_of "$line") (sd-cli)" @@ -81,8 +79,6 @@ sd() { } apps() { - local out pid - if "$ASSISTANT" status >/dev/null 2>&1; then row app assistant running - else @@ -94,23 +90,6 @@ apps() { else row app chat stopped - fi - - # imggen status exits 0 either way: the daemon's JSON when it is up, - # "daemon down" when it is not. - out="$(imggen status 2>/dev/null)" - if [[ "$out" == "{"* ]]; then - row app imggen running "$(jq -r '.model // "-"' <<<"$out" 2>/dev/null || echo -)" - else - row app imggen stopped - - fi - - # It runs through env, so the command line is python3 then the script, - # by path from ~/bin or by ./fanfictioner from the repo. - if pid="$(pgrep -u "$USER" -fo '^[^ ]*python3[^ ]* [^ ]*fanfictioner( |$)')"; then - row app fanfic running "$pid" - else - row app fanfic stopped - - fi } llama diff --git a/desktop/modules/ai/test-ai-state.sh b/desktop/modules/ai/test-ai-state.sh index e3e8ba0..aac83fb 100755 --- a/desktop/modules/ai/test-ai-state.sh +++ b/desktop/modules/ai/test-ai-state.sh @@ -11,8 +11,8 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # -# The one runnable check for ai-state.sh. It puts stub curl, pgrep, -# llamachat and imggen on PATH and a stub assistant.sh beside them, then runs +# The one runnable check for ai-state.sh. It puts stub curl, pgrep +# and llamachat on PATH and a stub assistant.sh beside them, then runs # the real script, so nothing here touches the live stack. T_* variables pick # the state each stub reports. # @@ -57,9 +57,6 @@ case "$*" in *sd-server*) [[ -n "${T_SD:-}" ]] || exit 1 echo "4242 sd-server --listen-port 7860 --diffusion-model /data/SD/z_image_turbo-Q8_0.gguf --vae /data/SD/vae/flux1-ae.safetensors" ;; - *fanfictioner*) - [[ -n "${T_FANFIC:-}" ]] || exit 1 - echo 31337 ;; *) exit 1 ;; esac STUB @@ -69,13 +66,6 @@ cat > "$stub/llamachat" <<'STUB' [[ "$1" == --ping && -n "${T_CHAT:-}" ]] STUB -# The real imggen exits 0 either way and says which on stdout. -cat > "$stub/imggen" <<'STUB' -#!/bin/bash -[[ "$1" == status ]] || exit 1 -if [[ -n "${T_IMG:-}" ]]; then echo '{"model": "realvis", "ready": true}'; else echo "daemon down"; fi -STUB - cat > "$stub/assistant.sh" <<'STUB' #!/bin/bash [[ "$1" == status && -n "${T_ASSIST:-}" ]] @@ -106,7 +96,7 @@ run() { bash "$here/ai-state.sh" } -apps_down="$(l app assistant stopped -; l app chat stopped -; l app imggen stopped -; l app fanfic stopped -)" +apps_down="$(l app assistant stopped -; l app chat stopped -)" want="$(l engine llama down idle -; l engine sd down idle -) $apps_down" @@ -131,7 +121,7 @@ touch -d '1 minute ago' "$log" got="$(run T_SD=1 | sed -n 2p)" check "sd running, log quiet" "$(l engine sd up idle z_image_turbo-Q8_0.gguf)" "$got" -# A one-shot sd-cli (fanfictioner's) holds VRAM and is generating for as +# A one-shot sd-cli holds VRAM and is generating for as # long as it runs, whatever the web UI is doing. Its prompt is free text, so # a "-m" inside it must not be read as the model. got="$(run T_CLI=1 | sed -n 2p)" @@ -140,8 +130,8 @@ check "sd-cli running" "$(l engine sd up busy "Krea-2-Turbo-Q6_K.gguf (sd-cli)") got="$(run T_CLI=1 T_SD=1 | sed -n 2p)" check "sd-cli wins over an idle web UI" "$(l engine sd up busy "Krea-2-Turbo-Q6_K.gguf (sd-cli)")" "$got" -want="$(l app assistant running -; l app chat running -; l app imggen running realvis; l app fanfic running 31337)" -got="$(run T_ASSIST=1 T_CHAT=1 T_IMG=1 T_FANFIC=1 | tail -n4)" +want="$(l app assistant running -; l app chat running -)" +got="$(run T_ASSIST=1 T_CHAT=1 | tail -n2)" check "every app running" "$want" "$got" printf '\n%d passed, %d failed\n' "$pass" "$fail" diff --git a/desktop/modules/notes/NotesModule.qml b/desktop/modules/notes/NotesModule.qml index 3066c8a..33e1cdf 100644 --- a/desktop/modules/notes/NotesModule.qml +++ b/desktop/modules/notes/NotesModule.qml @@ -238,7 +238,7 @@ Module { mod.lastError = ""; mod.gitPull.command = ["sh", "-c", 'git -C "$1" pull --rebase --autostash 2>&1', "sh", mod.baseDir]; // Reassigning running on a running Process does nothing, so clear it - // first (Status.qml does the same for its breaktimer). + // first (Status.qml does the same for its game poll). mod.gitPull.running = false; mod.gitPull.running = true; } diff --git a/desktop/modules/status/BreakRow.qml b/desktop/modules/status/BreakRow.qml deleted file mode 100644 index 8b732b1..0000000 --- a/desktop/modules/status/BreakRow.qml +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 2026 Danilo M. <danix@danix.xyz> -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -import QtQuick -import "../.." - -// Breaktimer: the daemon's phase and countdown, a pause switch and a -// start/stop button. Every control is a verb; nothing here writes the -// daemon's files. -Item { - id: row - - implicitHeight: Math.max(texts.implicitHeight, controls.implicitHeight) + 16 - - readonly property string phaseLabel: { - if (!Status.btRunning) return "Stopped"; - if (Status.btPaused) return "Paused"; - if (Status.btPhase === "breaking") return "Micro-pause"; - if (Status.btPhase === "longbreak") return "Long pause"; - return "Working"; - } - - // A paused countdown is frozen, and a frozen number reads as a bug, so - // the paused state says what it is instead of showing one. - readonly property string detail: { - if (!Status.btRunning) return "Break reminders are not running."; - if (Status.btPaused) return "Countdown frozen until resumed."; - if (Status.btPhase === "breaking" || Status.btPhase === "longbreak") - return "Back to work in " + Status.btCountdown() + "."; - return "Next break in " + Status.btCountdown() + "."; - } - - Column { - id: texts - anchors { - left: parent.left - right: controls.left - rightMargin: 12 - verticalCenter: parent.verticalCenter - } - spacing: 2 - - Text { - text: "Breaktimer · " + row.phaseLabel - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2; bold: true } - color: Theme.text - } - - Text { - width: parent.width - wrapMode: Text.WordWrap - text: row.detail - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } - color: Theme.subtext - } - } - - Row { - id: controls - anchors { right: parent.right; verticalCenter: parent.verticalCenter } - spacing: 8 - - Button { - text: Status.btRunning ? "Stop" : "Start" - danger: Status.btRunning - onClicked: Status.runBreaktimer(Status.btRunning ? "stop" : "start") - } - - Switch { - id: sw - enabled: Status.btRunning - checked: Status.btRunning && !Status.btPaused - onToggled: Status.runBreaktimer(Status.btPaused ? "resume" : "pause") - } - } -} diff --git a/desktop/modules/status/README.md b/desktop/modules/status/README.md index d3d0cce..ea069d6 100644 --- a/desktop/modules/status/README.md +++ b/desktop/modules/status/README.md @@ -45,7 +45,7 @@ and reasserted when it returns. `dnd` has none of its own. It is state the notification daemon reads. `presentation` sets `dnd`, `nolock` and `quiet`, asserts a Wayland idle -inhibitor, and pauses breaktimer. Turning it off restores the three to the +inhibitor. Turning it off restores the three to the values they had before rather than clearing them, so hand-set DND or a hand-set disable survives a presentation. @@ -71,52 +71,10 @@ The opencode notifier runs its command for every event, `session_started` and are off in `opencode-notifier.json`, and it rereads that file on every event, so neither change needs an opencode restart. -breaktimer is paused and resumed by verb, never by writing its state file. See -Breaktimer below, which is also the read direction. - -## Breaktimer - -The traffic runs both ways, and only one way writes. - -**Reading.** The daemon publishes three files the singleton watches: - - $XDG_RUNTIME_DIR/breaktimer.state running | paused | stopped - $XDG_RUNTIME_DIR/breaktimer.phase working | breaking | longbreak | stopped - $XDG_RUNTIME_DIR/breaktimer.remain seconds left in the phase - -exposed as `Status.btState`, `btPhase` and `btRemain`, with `btRunning` and -`btPaused` derived from the first. The old bar script read the same three -files; the two consumers never knew about each other. - -**Writing: never.** The daemon owns those files and rewrites state and phase on -every transition, so a second writer would race its loop. Every control calls a -verb through `runBreaktimer()`, which is why presentation mode has always -called `pause` rather than writing `breaktimer.state`. - -**A stopped daemon** is read from the state file, not probed. Both paths that -end the daemon write `stopped` there: `stop_daemon`, and the `cleanup` trap on -`TERM`. A daemon lost to `KILL` leaves a stale `running` and the drawer shows a -frozen countdown, a visible wrong answer the Start button resolves, which is -cheaper than a liveness probe on every repaint. QML cannot send a signal, so -the `kill -0` check the old bar module used is not available here anyway. - -**The countdown counts in five second steps**, because that is the daemon's -tick and the shell does not interpolate between its writes. A local one second -timer would be a second clock drifting against the first, correcting itself -with a visible jump every five seconds, and it would keep counting while the -daemon is frozen outside the work window or paused. - -**The tile** shows breaktimer below every mode, so an active mode still owns -the line and breaktimer replaces only the idle `All clear`. It does not count -toward `activeCount`: a running daemon is not a mode the user switched on. - -The daemon's own configuration lives in `~/.config/breaktimer.conf` and is not -edited from here; `breaktimer.sh config` prints what is in effect. - ## Game detection Presentation is set automatically while a game runs, so a fullscreen game does -not get locked or interrupted by the idle timer, DND or breaktimer. The shell +not get locked or interrupted by the idle timer or DND. The shell polls every five seconds for a `gamescope` process, a running Steam binary, or the DuckStation or PCSX2 emulators, and any one is enough: diff --git a/desktop/modules/status/StatusPage.qml b/desktop/modules/status/StatusPage.qml index 8a21ad5..4741bcc 100644 --- a/desktop/modules/status/StatusPage.qml +++ b/desktop/modules/status/StatusPage.qml @@ -37,7 +37,7 @@ Column { width: page.width mode: "presentation" label: "Presentation" - description: "Do not disturb, quiet, no screen lock, breaktimer paused." + description: "Do not disturb, quiet, no screen lock." value: Status.presentation } @@ -74,14 +74,4 @@ Column { SnoozeRow { width: page.width } - - Rectangle { - width: page.width - height: 1 - color: Qt.alpha(Theme.text, 0.08) - } - - BreakRow { - width: page.width - } } diff --git a/desktop/modules/status/StatusTile.qml b/desktop/modules/status/StatusTile.qml index cec18b6..ab3a7af 100644 --- a/desktop/modules/status/StatusTile.qml +++ b/desktop/modules/status/StatusTile.qml @@ -17,15 +17,8 @@ Text { elide: Text.ElideRight font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } color: Status.activeCount > 0 ? Theme.text : Theme.subtext - // Breaktimer sits below every mode, so an active mode still owns the line - // and breaktimer replaces only the idle "All clear". A stopped daemon - // falls through to it, which is true: nothing is being tracked. text: Status.presentation ? "Presenting" : Status.dnd ? "Do not disturb" : Status.nolock ? "No lock" - : !Status.btRunning ? "All clear" - : Status.btPaused ? "Paused" - : (Status.btPhase === "breaking" || Status.btPhase === "longbreak") - ? "Back in " + Status.btCountdown() - : "Break in " + Status.btCountdown() + : "All clear" } |
