# Architecture OmaRest declares `com.mirashif.omarest` with two kinds: `service` and `bar-widget`. The service owns the fullscreen break surfaces; the bar widget owns the anchored app popup. ```text Wayland active toplevel ─┐ Hyprland health check ───┼─> Service.qml ─> GateState ─> GateOverlay × screens Omarchy idle + lock ─────┘ │ │ ├─> StateStore ├─> XDG JSON └─> Widget.qml ─> anchored KeyboardPanel └─> Panel.qml app UI ``` ## Host boundary `Service.qml` is loaded once by Omarchy. The shell injects `shell`, `manifest`, and `omarchyPath`. The service reads the first-party `omarchy.idle` and `omarchy.lock` singletons through `shell.serviceFor`; it does not duplicate idle or lock monitors. The bar host does not inject services, so `Widget.qml` resolves the singleton through `bar.shell.serviceFor(moduleName)` and passes it into the embedded `Panel.qml` app UI. Because the manifest has no standalone `panel`, Omarchy routes `shell summon`, `hide`, and `toggle` to the live bar widget instance. `KeyboardPanel` keeps the popup attached to the widget on any bar edge and provides keyboard focus plus outside-click dismissal. ## Accounting `GateState.js` is deterministic and has no QML dependencies. Each rule owns a continuous session `{usedMs, awayMs, warned}`. Foreground ticks add usage; background ticks add away time. Reaching `resetAfterMinutes` clears that session. Reaching the allowance opens a break, and completion or override clears the session. Elapsed ticks are capped at five seconds. A long process stall, suspend, or wall-clock jump therefore cannot be charged as foreground usage. Lock and idle states pause normal accounting, while an already-visible break continues to elapse. ## Persistence `StateStore.qml` creates XDG directories without shell-string construction and uses Quickshell `FileView` only for atomic writes. Producer-side readers reject configuration above 256 KiB and state above 1 MiB before either file enters QML or `JSON.parse`. Configuration and runtime/activity state are separate. Runtime writes are coalesced to at most once every five seconds; configuration writes use a short debounce. Configuration retains at most 128 rules. Runtime state retains at most 128 sessions, and each of the ledger's 31 local-day summaries retains at most 128 per-rule usage buckets. Rule identifiers and user-visible strings are length bounded during normalization. The ledger never receives application titles or URLs. Removing a rule erases its usage buckets while retaining aggregate intervention counts; **Delete all data** replaces both files with empty/default state. The recurring Hyprland fallback is producer-capped at 16 KiB and retains at most 160 characters of application class. ## Failure behavior - A missing or malformed JSON file falls back to normalized defaults. - A missing Hyprland probe does not stop Wayland event tracking. - A missing idle/lock singleton defaults to active-session accounting. - Removing or disabling a gated rule tears down the gate. - The overlay always exposes a visible override and `Ctrl+Shift+Esc`. - Disabling or crashing the shell removes all layer-shell surfaces, so the plugin cannot permanently trap desktop input.