An Omarchy (Quattro shell) plugin for time tracking: a live bar widget, a quick-start popup, and a full management dashboard (entries, clients, projects, reports, invoices, settings).
- Plugin id:
io.github.sh1d0w.omatrack(third-party ids must not start withomarchy.). - Developed in this repo. The installed copy is what runs: the plugin
registry forbids symlinks, so a one-line local install (Dev loop below)
copies the runtime files into
~/.config/omarchy/plugins/io.github.sh1d0w.omatrack/.
manifest.json plugin manifest (bar-widget + service + panel kinds)
Service.qml headless in-process engine (state, helper channel, IPC)
BarWidget.qml right-section bar label + popup host
Popup.qml anchored quick-start popup
Dashboard.qml toplevel dashboard window (FloatingWindow)
omatrack.py state engine + CLI (python3 stdlib only; single writer)
components/ shared UI: TaskForm, EntryForm, DateRangeBar, EntryRow,
ClientRow, ProjectRow, CardOverlay, PaginationBar
views/ dashboard tabs: Timer, Entries, Clients, Projects,
Reports, Invoices, Settings
tests/helper_test.sh shell tests for omatrack.py (throwaway XDG_STATE_HOME)
docs/ one file per feature (see rule below)
- The shell is one long-running Quickshell process. Plugins run in-process, unsandboxed. Never start a second Quickshell.
Service.qmlis aservicekind: a headless singleton, accessed from the bar widget viabar.shell.serviceFor("io.github.sh1d0w.omatrack")and injected into the dashboard (the root declaresproperty var service).- All state mutations go through
omatrack.py— the single writer of~/.local/state/omarchy/omatrack/state.json(atomic tmp +os.replace,fcntl.flock). QML never writes the file directly. - The dashboard is a
panelkind rendered by the shell's panel loader as aFloatingWindow. Summon:omarchy-shell shell toggle io.github.sh1d0w.omatrack '{"tab":"entries"}'. The bar popup opens only on bar click (the shell routes summon/toggle to the panel when both kinds are present).
Single command, run from the repo root. Wipes the stale copy and re-copies
only the runtime files (.git, docs/, tests/ stay in the repo):
DEST=~/.config/omarchy/plugins/io.github.sh1d0w.omatrack \
&& rm -rf "$DEST" \
&& mkdir -p "$DEST" \
&& cp -R --parents manifest.json Service.qml BarWidget.qml Popup.qml \
Dashboard.qml omatrack.py components views "$DEST" \
&& chmod +x "$DEST/omatrack.py" \
&& (omarchy-shell shell rescanPlugins >/dev/null 2>&1 || true)Re-run it after any change — the shell hot-reloads saved plugin files.
bash tests/helper_test.sh # data engine (must PASS)
# <run the local-install command above to copy into the plugin dir>
omarchy plugin validate ~/.config/omarchy/plugins/io.github.sh1d0w.omatrack
qmllint -I /usr/share/omarchy/shell <file.qml> # every QML fileAfter editing: saving a file under ~/.config/omarchy/plugins/ hot-reloads it.
Caveat: a changed Service.qml may not reload a running service — run
omarchy restart shell when service behavior looks stale.
Visual verification: omarchy capture screenshot, then read the PNG.
Every feature must be documented in docs/. When you add or change a
feature, add or update its doc in the same change.
- No code duplication.
components/(TaskForm,EntryForm,DateRangeBar,EntryRow,ClientRow,ProjectRow,CardOverlay,PaginationBar) and the shell'sqs.Uikit are the shared pieces; views compose them instead of re-implementing. omatrack.py: python3 stdlib only, one JSON line per run ({"ok": true, ...}/{"ok": false, "error": "..."}), timestamps as UTC ISO-8601 seconds.- No polling in QML. One 1s C++ tick (
SystemClock) and only while a timer runs; the helper process exists only during an action. - No symlinks anywhere under the plugin folder. No
omarchy.*plugin id. - Entries are paginated in the UI (limit 15); the full entries array never enters QML (mutation responses carry a compact "view" instead).
- This runtime has no QtSql, QtGraphicalEffects, or QtWebEngine — exports
and invoices are HTML/CSV files opened with
xdg-open.