Sync config from arch

- hypr/apps.lua
- hypr/autostart.lua
- hypr/envs.lua
- hypr/hyprland.lua
- hypr/hyprsunset.conf
- hypr/input.lua
- hypr/looknfeel.lua
- hypr/omasettings.lua
- hypr/xdph.conf
- omarchy/branding/about.txt
- omarchy/branding/screensaver.txt
- omarchy/extensions/omarchy-menu.jsonc
- omarchy/hooks/battery-low.d/play-warning-sound.sample
- omarchy/hooks/font-set.d/show-font-notification.sample
- omarchy/hooks/post-boot.d/weather.sample
- omarchy/hooks/post-update.d/install-voxtype.hook
- omarchy/hooks/post-update.d/setup-agent.hook
- omarchy/hooks/post-update.d/setup-fingerprint.hook
- omarchy/hooks/post-update.d/show-update-notification.sample
- omarchy/hooks/pre-refresh-pacman.d/add-custom-repo.sample
- omarchy/hooks/theme-set.d/show-theme-notification.sample
- omarchy/shell.json
- omarchy/shell.toml
- omarchy/theme.name
- omarchy/themes/azure-glow/README.md
- omarchy/themes/azure-glow/alacritty.toml
- omarchy/themes/azure-glow/btop.theme
- omarchy/themes/azure-glow/hyprland.conf
- omarchy/themes/azure-glow/hyprlock.conf
- omarchy/themes/azure-glow/icons.theme
- … 269 more
This commit is contained in:
asepharyana
2026-09-23 15:19:12 +07:00
commit 1cdb82a76f
300 changed files with 78143 additions and 0 deletions
@@ -0,0 +1,131 @@
# Known Defects
Found outside a task's own verification, during the Task 20 manual matrix.
Each entry records what was observed, what the code actually does, and how it
was resolved.
## 1. README described `sshAgentUnlockOnDemand` as gating signing
**Status:** fixed in the same commit. Found 2026-08-31 on `feature/ssh-agent`.
**Observed:** With **Unlock on demand** switched off and the vault locked,
`ssh-add -T` against a projected public key still opened the panel's unlock
prompt. Dismissing it returned "agent refused operation" for both the Ed25519
and the RSA key.
**Verdict: the code is correct and the documentation was wrong.** The setting
governs the identity listing on a cold cache, not signing. A locked signing
request for a key the helper already holds in its public cache always raises
the unlock prompt and parks the request; dismissing the prompt is what refuses
it, and it refuses immediately rather than at the deadline.
**Where that is settled:**
- `docs/ideas/ssh-agent.md:178` -- the state table row for "Locked, cache
available" reads "List public identities; signing asks panel to unlock",
with no condition attached.
- `docs/ideas/ssh-agent.md:310` -- "When it is on, unlock-on-demand must begin
at `SSH_AGENTC_REQUEST_IDENTITIES` rather than at the sign request: with no
cache there are no identities to offer, so no sign request will ever
arrive."
- `agent/src/main.rs:475` consults `unlock_on_demand` in the `Identities` arm;
the `Sign` arm at `main.rs:511` deliberately does not.
- `agent/tests/lifecycle.rs:320` and `:444` both assert the locked-sign
behavior without ever setting the option, so the default already covers it.
**Fixed by** rewriting README "While the vault is locked" to separate the two
moments, and rewriting the `sshAgentUnlockOnDemand` row in the configuration
reference. No code change; the helper binary is unaffected and needs no
rebuild.
**Consequence for the Task 20 matrix:** the "off -> refused, no panel" row as
originally written cannot pass, because it describes behavior the design does
not have. Signing refusal while locked is tested by dismissing the prompt, and
by asking for a key the public cache does not know.
## 2. A grant's remaining time never counted down
**Status:** fixed. Found 2026-08-31 on `feature/ssh-agent`, during the Task 20
manual matrix.
**Observed:** With one live grant, the ACTIVE APPROVALS row on the SSH agent
settings screen read "1m 59s left" for the whole two minutes and then the row
disappeared, having never counted down. `sshAgentStatus` reported
`"grants":1` throughout.
**Cause:** `sshAgentGrantViews` computed `remainingLabel` from the companion's
`expiresInSec` at the instant of the announcement, and the companion announces
a grant once and says nothing further until the set changes. The view was a
snapshot rendered for the life of the grant; nothing re-derived it, and
nothing dropped a lapsed grant until the next announcement arrived.
**Fixed by** stamping each announced grant with `expiresAtMs` -- when it runs
out rather than how long it had left -- and adding `sshAgentGrantsAt(views,
nowMs)`, which re-derives the remaining time for a given moment and drops what
has lapsed. `Panel.sshGrants` became a derived property over
`sshGrantsAnnounced` and a `sshGrantTick` driven by a 1s timer that runs only
while grants exist, matching the cooldown countdown. Both fallbacks are
deliberate: an unstamped view, or any view before the first tick, is shown as
announced rather than dropped, because a grant must never disappear merely
because a timer has not run yet.
**Also worth knowing:** the same staleness would have hidden a grant that had
genuinely expired, since `sshAgentStatus` reported the announced count rather
than the live one. It now reports the live one.
## 3. The helper leaks its runtime files when the plugin is removed
**Status:** open, low severity. Found 2026-08-31 during the Task 20 manual
matrix.
**Observed:** After `omarchy plugin remove` with the SSH agent still enabled,
`/run/user/1000/qs-bitwarden-cli/` still held `ssh-agent.sock`,
`ssh-agent.lock` and `ssh-keys.fifo`, with no helper process running.
**Cause:** `ServiceRuntime` and `Runtime` in `agent/src/runtime.rs:264` and
`:320` unlink those files from `Drop`, which runs on a graceful shutdown --
which is why turning the agent off leaves nothing behind. Tearing the plugin
down kills the helper rather than shutting it down, so `Drop` never runs.
**Impact:** Small. The files are on a tmpfs and go with the login session, and
`omarchy plugin remove` has no uninstall hook, so nothing of ours can run at
that moment anyway. Until the next login, a client routed at that path finds a
socket that answers nothing rather than no socket at all.
**Possible fix:** A SIGTERM handler in the helper that runs the same cleanup,
if the shell terminates rather than kills the process. `Drop` cannot help
against SIGKILL and nothing can. Worth checking which signal the shell
actually sends before writing the handler.
**Documented meanwhile** in the README's Uninstall section: turn the agent off
before removing the plugin, and what the three files are if you did not.
## 4. The uninstall instructions destroyed a stow-managed shell.json
**Status:** fixed. Found 2026-08-31 during the Task 20 manual matrix, on the
maintainer's own machine.
**Observed:** Following the Uninstall section, the step "delete the
`io.github.elevate08.qs-bitwarden-cli` key under `plugins`" was carried out by
removing `~/.config/omarchy/shell.json`. Every configured plugin disappeared
from the bar, not only this one, and the shell came up on its built-in
defaults.
**Cause:** That path was a `stow` symlink into `~/projects/dotfiles`. Deleting
it removed the link, not the configuration; the shell then found no user
config at all. The real file was intact in the repository throughout, and
restoring the symlink restored everything.
The instruction was also redundant: `omarchy plugin remove` already calls
`omarchy-shell shell setPluginEnabled <id> false`, which removes the bar entry
and its settings before the directory goes.
**Fixed by** deleting the hand-edit step, naming `omarchy plugin disable` for
the stale-entry case, and warning plainly that `shell.json` is often a symlink
and must be changed through the shell rather than edited. Nothing in the
Uninstall section now tells a user to touch a file the shell owns.
**Worth remembering:** every other path in that section is under this plugin's
own directories, where a mistake costs the user only this plugin's data. This
one step reached into a file shared by every plugin on the system, and that is
what made a documentation error destructive.
@@ -0,0 +1,521 @@
# Implementation Plan: Opt-in Bitwarden SSH Agent
## Overview
Build the SSH-agent design in `docs/ideas/ssh-agent.md` as an optional,
disabled-by-default feature. The existing Quickshell panel remains the only
component that invokes `bw` and owns `BW_SESSION`; a supervised Rust companion
implements the local SSH-agent protocol, holds private SSH keys only while the
vault is unlocked, and makes signing contingent on a live approval or bounded
process grant. Before any agent work, the ordinary vault read is sanitized
outside QML so unsupported cipher types and `sshKey.privateKey` cannot enter
the long-lived panel process.
Detailed tasks and checkpoints are tracked in `tasks/todo.md`. No feature code
should be written until this plan has human approval.
## Development Worktree and Live Checkpoints
- Develop on branch `feature/ssh-agent` in `.worktrees/ssh-agent`, based on the
latest fetched `origin/master`. Keep the primary `master` checkout free of
feature changes.
- The enabled Omarchy plugin path
`~/.config/omarchy/plugins/io.github.elevate08.qs-bitwarden-cli` must resolve
to this worktree before any live test.
- At the end of every task slice, run its focused tests plus the applicable
regression/QML/lint/manifest gates, verify the plugin symlink target, run
`omarchy restart shell`, confirm `omarchy-shell shell ping`, summon the
Bitwarden panel, and call its non-secret `status` IPC method.
- Pause after that live reload so the user can exercise the plugin manually.
Do not start the next task until the user confirms the checkpoint or reports
issues to fix.
## Scope
The first release includes:
- a public-only, read-only SSH-key item experience in the panel;
- Ed25519 and RSA SHA-2 authentication/signing through a stable Unix socket;
- explicit per-signature approval and short per-process grants;
- opt-in unlock-on-demand, safe UWSM client routing, and advisory diagnostics;
- public-key file projection for normal Git SSH-signing configuration;
- an x86_64 GNU helper bundled with the plugin and verified by reproducible CI.
It does not include key creation/import, SSH item editing/cloning, master-
password re-prompt keys, forwarding, persistent grants, GPG-agent support,
item types 6–8, aarch64, or a Bitwarden SDK/direct service integration.
## Architecture Decisions
- Sanitize `bw list items` with static `jq` programs before QML parses it. QML
receives intact types 1–4 and a public-only type-5 projection; all other
types fail closed out of the display model.
- Keep one panel-owned `bw list items` read per unlock/sync. When the agent is
enabled, a bounded `tee` branch sends only eligible type-5 private material
to a nonce-framed FIFO. Failure of that optional branch must never prevent
the panel list from loading.
- Use one Rust companion and one stable socket under `$XDG_RUNTIME_DIR`. The
companion spawns no child processes, receives neither `BW_SESSION` nor
unrelated vault data, and exits when its control channel closes.
- Make approval, epoch checks, request limits, and final allow/deny decisions
authoritative in the companion. Process information is prompt context, not
an authentication boundary.
- Treat public-key file export as v1 scope because normal Git SSH signing needs
file paths. The planning assumption is that the companion owns this
projection from its validated public keystore; Task 4 records or corrects
that ownership before implementation proceeds.
- Ship only `x86_64-unknown-linux-gnu` initially. Commit the source, lockfile,
pinned toolchain, release bytes, and checksum; compare clean CI output byte
for byte before anything reaches `master` or a release.
## Dependency Graph
```text
Task 1 sanitized read contract
└── Task 2 panel SSH-key slice
└── Task 3 prerequisites and diagnostics
Task 4 Rust dependency/security decision
└── Task 5 protocol and signing
└── Task 6 epoch keystore
├── Task 7 nonce FIFO loading
└── Task 8 approvals and grants (also depends on Task 5)
└── Task 9 companion lifecycle (also depends on Task 7)
Tasks 3 + 9
└── Task 10 QML supervision
├── Task 11 opt-in setup
└── Task 12 shared vault fan-out (also depends on Tasks 1 + 7)
└── Task 13 vault lifecycle (also depends on Tasks 8 + 9)
├── Task 14 approval UI
└── Task 15 public-key projection
Task 9
└── Task 16 reproducible build
└── Task 17 pull-request gates
Tasks 10 + 16
└── Task 18 bundled-helper validation
└── Task 19 protected release (also depends on Task 17)
Tasks 3 + 11 + 14 + 15 + 19
└── Task 20 documentation and release validation
```
## Task List
### Phase 1: Remove the Existing Exposure
- [x] Task 1: Introduce the sanitized vault-read contract
- [x] Task 2: Deliver the public SSH-key panel slice
- [x] Task 3: Enforce SSH support prerequisites
### Checkpoint: Safe Panel Baseline
- [ ] QML-facing fixture output contains no SSH private-key marker or unknown
cipher-type marker.
- [ ] SSH keys are public-only and cannot reach generic detail/edit paths.
- [ ] All current JavaScript and QML tests pass; the plugin validates and lints
at its existing baseline.
- [ ] Human review approves the security boundary before Rust work continues.
### Phase 2: Prove the Headless Agent Core
- [x] Task 4: Pin the Rust security foundation
- [x] Task 5: Implement bounded SSH protocol signing
- [x] Task 6: Implement the vault-epoch keystore
### Checkpoint: Rust Primitives
- [x] Ed25519 and both RSA SHA-2 modes pass protocol-vector tests.
- [x] Lock, malformed input, mismatch, and limit paths fail closed.
- [x] The dependency/zeroization decision is recorded and reviewed.
- [x] Task 7: Implement nonce-framed FIFO loading
- [x] Task 8: Authorize signatures with bounded grants
- [x] Task 9: Complete the supervised companion lifecycle
### Checkpoint: Headless Companion
- [ ] Disposable-key tests cover identities, signing, multiple clients,
approval, grants, lock races, and FIFO rejection.
- [ ] The helper serves only its private stable socket, has no vault credential,
spawns no process, and exits on control EOF.
- [ ] Manual headless authentication and Git SSH-signing smoke tests pass.
- [ ] Human review approves proceeding to panel integration.
### Phase 3: Integrate the Optional Feature
- [x] Task 10: Establish companion supervision
- [x] Task 11: Deliver opt-in session setup
- [x] Task 12: Feed the companion from the shared vault read
### Checkpoint: Optional Data Plane
- [x] Disabled mode starts no helper and creates no socket, FIFO, or private-key
branch.
- [x] Enabled mode loads keys from the same vault read without delaying or
breaking the ordinary list on helper failure.
- [x] QML remains responsive during blocked SSH clients and helper events.
- [x] Task 13: Enforce vault lifecycle transitions
- [x] Task 14: Deliver signing authorization UX
- [x] Task 15: Project validated public-key files
### Checkpoint: End-to-End Feature
- [x] Lock, logout, account change, suspend, screen lock, disable, crash, and
Quickshell reload all satisfy the state machine.
- [x] Authentication, commit signing, and multi-commit signing work with no
private key on disk.
- [x] Public projections survive lock but clear on logout/account change/disable.
- [ ] Human security and usability review approves packaging.
### Phase 4: Establish the Artifact Trust Path
- [x] Task 16: Make the release build reproducible
- [x] Task 17: Add read-only pull-request gates
### Checkpoint: Candidate Artifact
- [x] Two clean pinned builds produce identical stripped bytes.
- [x] Existing, Rust, audit, license, and end-to-end gates pass with no PR
secrets or write token.
- [x] Task 18: Validate the bundled helper at launch
- [~] Task 19: Protect release provenance -- workflow, environment and tests
are in place; the first protected tag run is still outstanding
### Checkpoint: Shippable Artifact
- [ ] The tracked helper is executable, native-tested, checksum-consistent,
protocol-compatible, and byte-identical to the protected build.
- [ ] A release produces provenance attestation, SBOM, and dependency/license
report with narrowly scoped permissions.
### Phase 5: Document and Release
- [~] Task 20: Complete release documentation -- README, CHANGELOG, manifest
and the design draft are done; the manual release matrix is not
### Checkpoint: Complete
- [ ] Every task's acceptance criteria and the project Definition of Done pass.
- [ ] Setup, conflict, logout/login, upgrade, verification, and uninstall paths
are documented and manually exercised.
- [ ] No private key, session token, signed payload, or signature appears in
QML state, argv, logs, files, CI artifacts, or test diagnostics.
- [ ] Human review approves merge and release.
## Verification Strategy
Use focused tests after each task and these full gates at checkpoints:
```bash
for test_file in tests/*.test.js; do node "$test_file" || exit 1; done
QT_QPA_PLATFORM=offscreen /usr/lib/qt6/bin/qmltestrunner -input tests/qml
mkdir -p /tmp/qs-imports
ln -sfn /usr/share/omarchy/shell /tmp/qs-imports/qs
/usr/lib/qt6/bin/qmllint -I /tmp/qs-imports Panel.qml FormPickerRow.qml
omarchy plugin validate .
cargo fmt --manifest-path agent/Cargo.toml --check
cargo clippy --manifest-path agent/Cargo.toml --locked --all-targets -- -D warnings
cargo test --manifest-path agent/Cargo.toml --locked --all-targets
cargo deny --manifest-path agent/Cargo.toml check
```
The final runtime matrix must include a disposable fixture vault, a fake panel
controller, real OpenSSH clients, Git authentication, Git SSH signing, lock
during every sensitive transition, helper crash/reload, and both unlock-on-
demand modes. CI must never use a real vault or credential.
## Parallelization Opportunities
- After Task 3, the panel baseline can remain stable while Tasks 4–9 build the
headless Rust core; do not parallelize work that changes the sanitized read
contract until Task 1 is merged.
- After Task 9, Task 16's reproducible-build work can run alongside Tasks
10–11 because it consumes the frozen helper interface rather than QML state.
- After Task 13, Tasks 14 and 15 can proceed independently if Task 4 has fixed
the public-export owner and the control protocol is frozen.
- Tasks 17 and 18 may proceed in parallel after Task 16, but Task 19 waits for
both so release policy matches launch-time validation.
## Risks and Mitigations
| Risk | Impact | Mitigation |
|---|---|---|
| QML receives private SSH material before sanitization | High | Make Tasks 1–3 a standalone prerequisite gate with marker-based real-pipeline tests. |
| Selected Rust key types retain secret clones after lock | High | Fail the Task 4/6 spike if parsed private components cannot be bounded and best-effort-zeroized. |
| A lock races a load, approval, grant, or signature | High | Use vault epochs, an atomic deny linearization point, candidate keystores, bounded acknowledgments, and kill-on-timeout. |
| Same-UID FIFO injection replaces the key set | High | Require a fresh 128-bit load nonce delivered only over the private control pipe and reject duplicate/stale payloads. |
| A slow or broken agent branch stalls the core vault list | High | Bound input/output/time, drain eagerly, supervise one process group, and retry the list once without the optional branch. |
| Process attribution creates false security confidence | Medium | Enforce peer UID; present PID/path/start-time as context only; scope grants narrowly and revalidate at sign time. |
| UWSM setup overwrites another agent configuration | High | Use one plugin-owned atomic file, refuse symlinks/unexpected contents, show conflicts, and require confirmation plus re-login. |
| Bundled binary drifts from source | High | Pin every build input and compare clean release bytes byte-for-byte before merge/release. |
| Fork PR artifact policy becomes impossible to satisfy | Medium | Upload read-only candidates for source-only fork PRs; require the matching bytes before merge to `master`, not inside the untrusted fork job. |
| Older/self-hosted servers expose partial SSH support | Medium | Enforce the CLI floor, report capability as unconfirmed when appropriate, and document the 2026.8.0 malformed-item fix. |
| Feature complexity degrades the ordinary vault | High | Keep disabled mode inert, isolate helper failure, checkpoint every 2–3 tasks, and run the full existing regression suite throughout. |
## Open Questions for Human Review
- **Public export scope:** Revision 2 says both that public-key export is
required in v1 and that it remains later work. This plan follows the stronger
end-to-end requirement and includes it in v1. Confirm that choice.
- **Public export owner:** This plan recommends the companion write the
projection from its validated public keystore, using an absolute data path
supplied at launch. Task 4 must record the final owner and its path/cleanup
contract before implementation.
- **Dependency outcome:** The Rust crate set, Bitwarden v2 agent status, RFC
status, zeroization behavior, and license/audit surface must be re-verified
from current primary sources during Task 4; the design draft's review date is
not sufficient evidence.
- **Release governance:** *Resolved during Task 19.* The protected environment
is `release`; its required reviewer is `@Elevate08`, with self-review
permitted because this repository has one maintainer -- the gate exists so
that write, OIDC and attestation credentials never come into existence
without a deliberate human click, not to simulate a second pair of eyes that
does not exist. Deployments are restricted to `v*` tags. CODEOWNERS names
`@Elevate08` throughout, and calls out `/.github/workflows/release.yml`
separately as the only workflow that can write, mint a token, or sign.
## Follow-up Plan: Centered SSH Approval Popup
The approved follow-up specification is
`docs/ideas/ssh-approval-popup.md`. Add a disabled-by-default presentation
choice without changing the companion protocol or authorization rules.
### Dependency order
```text
setting contract
-> centered approval surface
-> locked-vault unlock surface
-> documentation and full verification
```
### Architecture decisions
- Keep request and credential state in `Panel.qml`; popup components are views
that call the same approve, deny, PIN, fingerprint, and password functions.
- Use a full-output transparent `PanelWindow`, `ExclusionMode.Ignore`, the
Wayland overlay layer, and a brief Exclusive-to-OnDemand focus prime,
matching Omarchy's centered transient surfaces.
- Reuse `SshApprovalScreen.qml` between both presentation modes so the key,
process, grant, loading, and deadline semantics cannot drift.
- Keep account login in the full panel. The centered prompt handles only the
signed-in locked/unlocked states involved in SSH requests.
### Risks and mitigations
| Risk | Mitigation |
|---|---|
| Popup accidentally becomes a second authorization authority | It only calls existing `Panel.qml` request functions; helper checks remain unchanged. |
| PIN/fingerprint results are discarded because the anchored panel is closed | Define one transient-auth-surface predicate used by all unlock acceptance checks. |
| The invisible experience leaves a password or pending process behind | Dismissal denies the request and runs popup-specific credential/prewarm cleanup. |
| A request label injects markup | Every request-derived `Text` remains `Text.PlainText`. |
| Overlay traps input after the request ends | Visibility and Wayland keyboard focus bind directly to the live pending request. |
---
# Implementation Plan: Colorized Menu-Bar Icon
## Overview
Add an opt-in `colorizeIcon` boolean setting to the Bitwarden panel. When
enabled, the primary menu-bar shield follows Omarchy's live `Color.accent`
theme color; when disabled, it keeps the current bar foreground color. The
locked padlock, missing-dependency badge, and urgent/error indicators remain
unchanged so color personalization does not erase status meaning.
The existing settings renderer already turns boolean schema entries into
keyboard-operable toggle rows. No custom color picker, palette model, new
dependency, or arbitrary color persistence is required.
## Architecture Decisions
- Store only `colorizeIcon: boolean`; derive the actual color from
`Color.accent` at render time so theme changes are inherited automatically.
- Default the setting to `false` so existing installations retain their current
appearance.
- Apply the setting only to the primary shield glyph in `shieldIconComp`.
Leave the padlock and setup/error badges on their existing bindings.
- Put the row in the General settings group, using the existing schema-driven
toggle and `omarchy bar set` persistence path.
- Treat malformed external values as `false`, using the existing strict
boolean-setting behavior.
## Dependency Graph
```text
manifest default/schema + model schema
│
├── settings persistence/value lookup tests
│
└── Panel color binding + schema-driven General toggle
│
└── QML/static checks + runtime/theme verification
```
## Task List
### Phase 1: Settings Contract
- [x] Task 1: Add the colorization setting contract
### Checkpoint: Settings Contract
- [x] `colorizeIcon` exists in both `manifest.json` and `BitwardenModel.js`
with boolean type and a `false` default.
- [x] Valid booleans round-trip through the existing writer, while malformed
values resolve to `false`.
- [x] Focused settings/model tests pass before UI wiring begins.
### Phase 2: Vertical UI Slice
- [x] Task 2: Wire the theme-accent shield and General toggle
### Checkpoint: Colorized Icon Behavior
- [x] With the setting off, the primary shield still uses the bar foreground.
- [x] With the setting on, the primary shield uses `Color.accent`.
- [x] Lock/setup/error badges retain their existing foreground or urgent colors.
- [x] The settings row is keyboard-operable and persists through the existing
shell reload path.
### Phase 3: Verification and Documentation
- [~] Task 3: Add regression coverage and document the setting
### Checkpoint: Complete
- [x] Focused and full JavaScript tests pass.
- [x] QML tests/lint and plugin validation pass where the Omarchy environment is
available.
- [ ] Manual verification covers both toggle states, a theme accent change,
locked state, setup-required state, and an error/urgent state.
- [x] README or user-facing feature documentation explains the toggle and its
theme-derived behavior.
- [ ] Human review approves the implementation before merge; no commit or push
is performed automatically.
## Task Details
### Task 1: Add the colorization setting contract
**Description:** Register `colorizeIcon` as a General boolean setting in the
manifest and model schema, expose its default through the widget defaults, and
ensure the existing settings value/read/write paths recognize it without
special-case persistence code.
**Acceptance criteria:**
- [x] The manifest and model declare the same `colorizeIcon` key, boolean type,
label, description, and `false` default.
- [x] `boolSetting("colorizeIcon", true)` returns true; malformed values such
as strings and numbers fall back to false.
- [x] The setting is grouped under General and included in visible settings
without changing existing group ordering or setting semantics.
**Verification:**
- [x] Tests pass: `node tests/setup-settings.test.js`
- [x] Tests pass: `node tests/lock-state.test.js`
- [x] Static check confirms manifest/model schema parity.
**Dependencies:** None
**Files likely touched:**
- `manifest.json`
- `BitwardenModel.js`
- `tests/setup-settings.test.js`
- `tests/lock-state.test.js`
**Estimated scope:** Medium: 3–4 files
### Task 2: Wire the theme-accent shield and General toggle
**Description:** Add the root property that reads `colorizeIcon`, use it only
for the primary shield glyph in the menu-bar icon component, and rely on the
existing schema-driven settings delegate to render and persist the toggle.
Add focused static assertions for the binding and for the unchanged status
badge color paths.
**Acceptance criteria:**
- [x] `colorizeIcon` is read from the live setting with a false-safe default.
- [x] The primary shield resolves to `Color.accent` when enabled and the
existing bar foreground when disabled.
- [x] The padlock, missing-tool badge, and urgent/error color bindings are not
redirected through the new preference.
**Verification:**
- [x] Tests pass: `node tests/settings-screen.test.js`
- [x] Focused source assertions pass for shield color precedence and badge
independence.
- [x] QML lint passes for `Panel.qml` with the repository's Omarchy import path.
**Dependencies:** Task 1
**Files likely touched:**
- `Panel.qml`
- `tests/settings-screen.test.js`
**Estimated scope:** Small: 1–2 files
### Task 3: Add regression coverage and document the setting
**Description:** Complete the feature's regression matrix and user-facing
documentation. Verify that persistence survives the shell reload path and that
the selected accent is inherited from the active theme while status indicators
remain recognizable.
**Acceptance criteria:**
- [x] Tests cover default-off behavior, enabling/disabling through the settings
path, malformed persisted values, and preservation of badge colors.
- [x] User-facing documentation says the toggle follows the active Omarchy
theme accent and does not offer arbitrary color selection.
- [x] No unrelated panel colors or status semantics change.
**Verification:**
- [x] Full JavaScript suite passes:
`for test_file in tests/*.test.js; do node "$test_file" || exit 1; done`
- [x] QML suite passes:
`env -u DISPLAY -u WAYLAND_DISPLAY -u QT_QPA_PLATFORMTHEME QML_XHR_ALLOW_FILE_READ=1 QT_QPA_PLATFORM=offscreen /usr/lib/qt6/bin/qmltestrunner -input tests/qml`
- [x] Plugin validation passes: `omarchy plugin validate .`
- [ ] Manual runtime check confirms both toggle states and theme-derived color
behavior when the desktop environment is available.
**Dependencies:** Task 2
**Files likely touched:**
- `README.md` or the relevant feature documentation
- `tests/setup-settings.test.js`
- `tests/settings-screen.test.js`
- `tests/lock-state.test.js`
**Estimated scope:** Medium: 3–4 files
## Risks and Mitigations
| Risk | Impact | Mitigation |
|---|---|---|
| Accent color has poor contrast on a supported theme | Medium | Check light/dark themes manually; retain the existing bar foreground as the safe default and do not alter urgent badges. |
| The setting is added to one schema but not the other | Medium | Keep manifest/model parity assertions in the focused settings tests. |
| The setting accidentally recolors status badges | High | Limit the binding change to the primary shield `Text` and assert badge bindings remain independent. |
| Shell reload does not refresh the bar icon immediately | Low | Verify the existing `omarchy bar set` hot-reload behavior; do not add a second persistence mechanism. |
## Open Questions
- Confirm final user-facing label: **Colorize menu-bar icon** versus **Use
theme accent for icon**. The plan assumes the former.
- Confirm which user-facing documentation file should receive the short setting
note; `README.md` is the default unless project conventions prefer
`docs/features.md`.
File diff suppressed because it is too large Load Diff