agentgui 1.0.1071 → 1.0.1073

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ ---
2
+ key: mem-5feb252a5244149c-1791
3
+ ns: default
4
+ created: 1784294077108
5
+ updated: 1784294077108
6
+ ---
7
+
8
+ agentgui 45th run (2026-07-17): third docstudio design-cue pass. Fresh Explore-agent survey (told what 43rd/44th runs already shipped) found 5 new gaps, all implemented in the design kit: ChatComposer/AgentChat streamingSince prop -> role=status tabular-nums elapsed counter next to stop control while streaming (docstudio chat-input.js:71-96), self-ticking via interval keyed off passed timestamp; Enter-to-send gated behind module-cached matchMedia('(pointer:coarse)') check (docstudio chat-input.js:116-125), coarse-pointer shows 'Tap Send to send'; CountdownDialog({title,message,seconds,onExpire,actions}) added to files-modals.js (docstudio google-reconnect.js:41-75 sign-out countdown) - composes over the same Modal()/Backdrop() shell ConfirmDialog/PromptDialog already use there, NOT editor-primitives.js's Dialog (deviation from initial spec once actual pattern traced); PermissionMenu({categories,approved,onToggle,onToggleAll}) added to overlay-primitives.js (docstudio tool-approval-panel.js roving-tabindex menuitemcheckbox list), mirrors Dropdown's open/close/outside-click wiring; ChatComposer detectAttachment(text)=>{type,label,id}|null prop renders dismissible .chat-composer-detected-badge above textarea (docstudio chat-input.js:49-62 pasted-URL sniff), imperative DOM matching flashComposerNote's style. Implementation delegated to a background worktree agent. bun test.js 38/38 pass, node scripts/build.mjs 0 lint errors. Concurrent upstream v0.0.344/v0.0.345 (RTL applyDirection/getDirection) landed mid-pass - rebased clean, dist rebuilt fresh. Kit pushed 5c0571e->1d46202. agentgui re-vendored, browser-witnessed live localhost:3009/gm/ (bodyLen:19739, hasAgentgui:true, 0 pageErrors), grep-confirmed all 5 feature strings present in re-vendored bundle before push.
@@ -0,0 +1,8 @@
1
+ ---
2
+ key: mem-b451e5e17e555202-1490
3
+ ns: default
4
+ created: 1784294096167
5
+ updated: 1784294096167
6
+ ---
7
+
8
+ agentgui 39th run (2026-07-12) full detail: gui-logic-predictability workflow wf_88c431e5-931 (26 agents, 202 tool calls) - 6 confirmed findings. App: runningPanel() rows gained runningRowTitle() resolved-identity prefix; stopActiveChat() gained a bounded 9s timeout to clear a stuck 'stopping' state + retry banner; computeLiveSessions() SSE tally now accumulates total_cost_usd alongside tokens; WorkspaceRail footer gained a visible shortcuts-help IconButton (the ?-overlay was 100% keyboard-only tribal knowledge before). Kit: CodeNode.refSink gained the same in-progress-text-selection guard MdNode.refSink already had; Row.sub gained a title= tooltip fallback for string values; added a help icon path to ICON_PATHS/Glyph. Kit rebased on concurrent v0.0.316, pushed 00dae4c; agentgui pushed 64f4466, all CI green. Browser-witnessed live via page.context().setHTTPCredentials()+page.evaluate(): help button in .ws-rail-foot opens shortcuts overlay, 0 pageErrors; Row.sub title attr confirmed live. Tool-defect notes from this run: (1) this env's nginx Basic-Auth password contains a literal comma (123,slam,123,slam) and embedding it directly in page.goto URL throws net::ERR_INVALID_AUTH_CREDENTIALS even percent-encoded - fix is page.context().setHTTPCredentials() before goto with a plain URL; (2) capture-prefix browser scripts run in a bare Node vm with no document/page binding unless the script itself calls page.evaluate(()=>{...}) - wrap every DOM read/write in page.evaluate.
package/.gm/prd.yml CHANGED
@@ -3106,4 +3106,34 @@
3106
3106
  witness: agentgui pushed f08f698 to origin/main (fast-forward from 89121d5, confirmed via git diff origin/main HEAD before push). gh run list confirmed all 4 CI checks (Deploy GH Pages, Publish and Release, Test, Auto-Declaudeify) conclusion:success at f08f698 after a bounded poll loop.
3107
3107
  - id: docstudio-cue-agents-md-44
3108
3108
  subject: Record the run in AGENTS.md (present-tense/staging-ground convention) and dual-write the load-bearing facts to memorize-fire, draining a compensating amount of old detail-heavy AGENTS.md entries per the documentation policy
3109
+ status: completed
3110
+ witness: 'AGENTS.md gained a new 44th-run entry (present-tense/staging-ground convention) and CHANGELOG.md gained a matching one-entry-per-commit-family summary. Compensating drain: the detail-heavy 40th-run entry was compressed from ~2200 words of inline detail to a one-line pointer, with the substance dual-written to memorize-fire (mem-2f4fd06515abf439-2562, the new 44th-run fact) alongside the 40th-run drain reference. Committed 8e54c2e, pushed, all 4 CI checks green.'
3111
+ - id: kit-elapsed-streaming-counter
3112
+ subject: Add elapsed-time streaming counter to ChatComposer/AgentChat (docstudio 45th-run cue)
3113
+ notes: docstudio chat-input.js:71-96 shows a role=status tabular-nums m:ss counter next to stop button while streaming. Kit has none. Add streamingSince prop, .chat-composer-elapsed span, self-ticking interval keyed off start timestamp.
3114
+ status: completed
3115
+ witness: Implemented in design kit (commit 5c0571e), bun test.js all pass, node scripts/build.mjs 0 lint errors, rebased onto v0.0.345 (1d46202), re-vendored into agentgui site/app/vendor/anentrypoint-design/247420.{js,css}, browser-witnessed live on localhost:3009/gm/ (bodyLen:19739, hasAgentgui:true, 0 pageErrors); grep confirms feature-specific string present in vendored bundle.
3116
+ - id: kit-pointer-aware-enter-send
3117
+ subject: Gate Enter-to-send behind pointer:coarse matchMedia check (docstudio 45th-run cue)
3118
+ notes: docstudio chat-input.js:116-125 only sends on Enter for fine-pointer devices; coarse-pointer always inserts newline. Kit chat.js:421 sends on Enter unconditionally. Add matchMedia gate + swap composer hint copy on coarse pointers.
3119
+ status: completed
3120
+ witness: 'chat.js: isCoarsePointer() module-cached matchMedia(pointer:coarse) check gates the Enter-to-send onkeydown branch + swaps composer hint copy; confirmed present in rebuilt bundle. bun test.js 38/38 pass post-implementation; node scripts/build.mjs 0 lint errors. Live witness localhost:3009/gm/?token=...: bodyLen 19739, 0 pageErrors, hasAgentgui true (commit 5c0571e, rebased 1d46202).'
3121
+ - id: kit-countdown-dialog
3122
+ subject: Add CountdownDialog primitive (docstudio 45th-run cue)
3123
+ notes: docstudio google-reconnect.js:41-75 shows a live countdown-to-signout dialog. Kit's Dialog/ConfirmDialog/PromptDialog have no live-countdown variant. Add CountdownDialog({title,message,seconds,onExpire,actions}) to editor-primitives.js reusing existing focus-trap/backdrop-dismiss plumbing.
3124
+ status: completed
3125
+ witness: 'files-modals.js: CountdownDialog composes over the existing Modal()/Backdrop() shell shared with ConfirmDialog/PromptDialog, role=status ticking line + onExpire at zero; exported from src/components.js barrel. grep ''CountdownDialog'' site/app/vendor/anentrypoint-design/247420.js confirms symbol present in rebuilt+re-vendored bundle. bun test.js 38/38 pass, build 0 lint errors, commit 5c0571e rebased onto 1d46202.'
3126
+ - id: kit-permission-menu
3127
+ subject: Add PermissionMenu primitive (docstudio 45th-run cue)
3128
+ notes: docstudio tool-approval-panel.js is a roving-tabindex role=menu of menuitemcheckbox rows with approve-all/revoke-all, arrow/home/end nav, esc-restore-focus. Kit has only a static FileRow read/write badge. Add PermissionMenu({categories,approved,onToggle,onToggleAll}) to overlay-primitives.js mirroring Dropdown wiring.
3129
+ status: completed
3130
+ witness: 'overlay-primitives.js: PermissionMenu mirrors Dropdown''s open/close/outside-click wiring, role=menu of role=menuitemcheckbox rows with roving tabindex + Approve-all/Revoke-all; exported from barrel. grep ''PermissionMenu'' site/app/vendor/anentrypoint-design/247420.js confirms symbol present in rebuilt+re-vendored bundle. bun test.js 38/38 pass, build 0 lint errors, commit 5c0571e rebased onto 1d46202.'
3131
+ - id: kit-detect-attachment-badge
3132
+ subject: Add detectAttachment prop + dismissible badge to ChatComposer (docstudio 45th-run cue)
3133
+ notes: docstudio chat-input.js:49-62 sniffs pasted Google Doc/Sheet/Drive URLs and shows a dismissable inline confirm badge before send. Kit has no live parse-and-confirm affordance. Add optional detectAttachment(text)=>{type,label,id}|null prop, render .chat-composer-detected-badge above textarea, dismissible, reuse flashComposerNote treatment.
3134
+ status: completed
3135
+ witness: 'chat.js: updateDetectedBadge() closure invoked from autoGrow/taRef on every input change, renders .chat-composer-detected-badge with dismiss control matching flashComposerNote''s imperative-DOM style. grep ''chat-composer-detected-badge'' in both site/app/vendor/anentrypoint-design/247420.js and .css confirms markup+CSS present in rebuilt+re-vendored bundle. bun test.js 38/38 pass, build 0 lint errors, commit 5c0571e rebased onto 1d46202.'
3136
+ - id: kit-45th-run-build-test-vendor
3137
+ subject: Build+test kit after 45th-run additions, re-vendor into agentgui, browser-witness live
3138
+ notes: 'Standard closure: bun test.js all pass, node scripts/build.mjs 0 lint errors, copy dist into agentgui site/app/vendor, browser-witness on buildesk with 0 pageErrors, push both repos, update AGENTS.md with a 45th-run entry per Documentation Policy (drain an old entry too per migration back-pressure rule).'
3109
3139
  status: pending
package/AGENTS.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # AgentGUI — Agent Notes
2
2
 
3
+ ## Docstudio design-cue follow-up (2026-07-17) — forty-fifth run
4
+
5
+ Third docstudio-cue pass, same directive repeated. A fresh Explore-agent survey of `/config/docstudio` (told explicitly what the 43rd/44th runs already shipped — toasts/Skeleton/Alert/Badge-tone/dialogs/breadcrumbs/inline-error-retry/Avatar/sidePanel/sortable-Table) returned 5 new candidates, all implemented in the kit: `ChatComposer`/`AgentChat` gained a `streamingSince` prop rendering a `role=status` tabular-nums elapsed-time counter next to the stop control while streaming (docstudio `chat-input.js:71-96`), self-ticking via an interval keyed off the passed timestamp so re-renders don't reset it; Enter-to-send is now gated behind a module-cached `matchMedia('(pointer:coarse)')` check (docstudio `chat-input.js:116-125`) — coarse-pointer devices insert a newline and see "Tap Send to send" instead of the Enter hint; a `CountdownDialog({title,message,seconds,onExpire,actions})` primitive landed in `files-modals.js` (docstudio `google-reconnect.js:41-75`'s live sign-out countdown), composing over the same `Modal()`/`Backdrop()` shell `ConfirmDialog`/`PromptDialog` already use in that file rather than `editor-primitives.js`'s standalone `Dialog` — a deviation from the initial spec once the actual established pattern was traced; a `PermissionMenu({categories,approved,onToggle,onToggleAll})` primitive landed in `overlay-primitives.js` (docstudio `tool-approval-panel.js`'s roving-tabindex `menuitemcheckbox` list), mirroring `Dropdown`'s existing open/close/outside-click wiring; `ChatComposer` gained an optional `detectAttachment(text)=>{type,label,id}|null` prop rendering a dismissible `.chat-composer-detected-badge` above the textarea (docstudio `chat-input.js:49-62`'s pasted-URL sniff), built as imperative DOM matching `flashComposerNote`'s own style rather than a VElement tree. Implementation delegated to a background worktree agent; `bun test.js` 38/38 pass, `node scripts/build.mjs` 0 lint errors across all 7 lints. A concurrent upstream release (`v0.0.344`/`v0.0.345`, RTL `applyDirection`/`getDirection` support) landed mid-pass — rebased cleanly, `dist/247420.js` rebuilt fresh post-rebase (dist is gitignored in the kit repo but tracked via a separate publish step), re-tested. Kit pushed `5c0571e`→`1d46202`. agentgui re-vendored both dist files, browser-witnessed live on `localhost:3009/gm/` (`bodyLen:19739`, `hasAgentgui:true`, 0 pageErrors), grep-confirmed all 5 feature-specific strings (`CountdownDialog`, `PermissionMenu`, `chat-composer-elapsed`, `chat-composer-detected-badge`) present in the re-vendored bundle before pushing.
6
+
3
7
  ## Docstudio design-cue follow-up (2026-07-17) — forty-fourth run
4
8
 
5
9
  Re-stated user directive ("if there's any further ideas we can bring across from that project, do it now") following the 43rd run's sidePanel/sortable-Table pass. A fresh research agent surveyed `/config/docstudio`'s source (app-shell.js, ds-chat-messages.css, utils/dialog.js, utils/state-views.js) against `../design`'s current components (explicitly told what the kit already had, to avoid re-suggesting solved gaps) and returned 5 concrete, file:line-cited candidates, confirming the kit still leads on toasts/Skeleton/Alert/Badge-tone/dialogs/breadcrumbs. Implemented: `ChatMessage` (`design/src/components/chat.js`) gained `error`/`onRetry` — an inline, destructive-toned per-turn failure notice (docstudio pins a failed message's error to that specific turn rather than a global toast), rendered alongside the existing neutral `stopped`/`incomplete` out-of-band notices but visually distinct (border-color `--danger`, a `.chat-msg-retry-btn`). `content.js` gained a generic `Avatar({name,src,fallback,size})` primitive (barrel-exported) consolidating the letter-fallback-disc logic previously duplicated ad hoc in `chat.js`'s local avatar span and `community.js`'s one-off. `FileSkeleton` (`files.js`) was `aria-hidden` with no busy signal, unlike every sibling skeleton (`Skeleton`, `AgentListSkeleton`) — added `role=status`/`aria-busy`/`aria-label`. Verified (no change needed): every existing backdrop-dismiss dialog (`editor-primitives.js`, `files-modals.js`) already does docstudio's strict `e.target === e.currentTarget` check. Skipped as too fragile/low-value per the survey's own low-priority flag: Badge icon-only collapse at narrow width (a CSS-only breakpoint hack risked clipping any Badge that's genuinely just a color dot with no label). **Real bug caught by the mandatory browser witness, not by any lens**: wiring `AgentChat`'s message-mapper (`design/src/components/agent-chat.js`) to forward `error`/`onRetry` was first gated on `!isAssistant`, but agentgui's `sendChat()` always pushes an eager assistant placeholder and attaches a send/stream error onto *that* message, never a dangling user turn — the gate was corrected to key only on trailing-message-position (`i === lastIdx`), independent of role. Separately, removing agentgui's now-redundant top-of-thread `chaterr` `Alert` banner (duplicate of the new inline signal) left a stray closing brace, producing a real `SyntaxError: Illegal return statement` at `app.js:2343` that fully blanked the live page (`bodyLen:149`, `hasAgentgui:false`) — caught on the FIRST post-deploy witness dispatch, fixed (`node --check app.js` confirmed locally), re-pushed, re-witnessed clean (`pageErrors:[]`, `hasAgentgui:true`, `bodyLen:18805`). **Lesson: a same-session "verify no change needed" pass (like the backdrop-dismiss check) is not a substitute for actually re-running the mandatory live browser witness on code that WAS changed — the crash here was caught only because VERIFY dispatched a real page load post-push, not from static reasoning about the diff.** Kit built+tested 3 times across the pass's 3 commits (`bun test.js` all pass each time, including the pre-existing byte-identical contract tests), 2 clean rebases + 1 dist-only rebuild-fresh rebase (concurrent upstream `v0.0.340`-`v0.0.343` releases landed mid-pass), pushed `3417592`→`f362528`→`03c9028`→`17ee86c`, all CI green every push. agentgui re-vendored + pushed `f08f698` (after the syntax-crash fix), all 4 CI checks green.
@@ -22,7 +26,7 @@ User-driven ask ("setting the cwd is not nearly practical enough... go over ever
22
26
 
23
27
  ## GUI logic+predictability sweep (2026-07-12) — thirty-ninth run
24
28
 
25
- `gui-logic-predictability` workflow wf_88c431e5-931 (26 agents, 202 tool calls, 0 errors) — 6 confirmed findings after hunt+adversarial-verify, all implemented; a much thinner yield than the 37th/38th runs, consistent with the same lens converging after two straight passes. App (`site/app/js/app.js`): `runningPanel()`'s inline running-chat rows previously showed only agent+model+elapsed+cwd with no session title, reading nameless while the identical session shows a resolved title on the sessions rail/history rail/Live SessionCard — a new `runningRowTitle()` helper (wrapping the existing `resumeSidLabel()`) prepends the same resolved identity string; `stopActiveChat()` could wedge a stop button permanently disabled if the backend accepted the cancel but the underlying process outlived the immediate post-cancel `refreshActive()` call (the next 3s poll would silently re-add the sid to `state.active` while `stopping` never cleared) — a bounded 9s timeout now clears `stopping` and surfaces the same "conversation did not stop - try again" retry banner used on outright rejection; `computeLiveSessions()`'s per-sid SSE tally accumulated `tokens` but never `cost`, so every live/external session showed a blank cost field except the one open in-page in Chat — the tally now accumulates `total_cost_usd` from `result` events too, with the same tally-fallback pattern tokens already had; WorkspaceRail's footer gained a visible shortcuts-help `IconButton` (previously the `?`-overlay was 100% keyboard-only tribal knowledge with zero discovery path outside Settings). Kit (`design/src/components/{chat,content,shell}.js`): `CodeNode.refSink`'s syntax-highlight swap gained the same in-progress-text-selection guard `MdNode.refSink` already had (the two paths had drifted — code blocks still wiped an active selection mid-stream after the markdown path was fixed for the same bug); `Row.sub` gained a `title=` tooltip fallback for string values, matching every other long-text field in the app (agent-chat cwd, sessions rail title/sub/cwd/agent) — `Row` is used everywhere (history sessions rail, search results, context-pane cwd fact, agents panel) so this closes the gap at every call site at once; added a `help` icon path to `ICON_PATHS`/`Glyph` for the new rail affordance. Kit built+tested (`bun test.js` all pass), rebased cleanly on a concurrent `v0.0.316` release-bump commit (no source conflicts), dist rebuilt fresh post-rebase, pushed `00dae4c`. agentgui re-vendored, pushed `64f4466`, all 3 CI checks (Auto-Declaudeify/Publish and Release/Deploy GH Pages) green. Browser-witnessed live on buildesk via `page.context().setHTTPCredentials()` + `page.evaluate()`: help button found in `.ws-rail-foot`, clicking it opened the shortcuts overlay (`shortcutsTextPresent:true`), zero pageErrors; history rail `Row.sub` confirmed rendering `title="server default"` live. **New tool-defect note this run: this env's nginx Basic-Auth password contains a literal comma (`123,slam,123,slam` — see the 32nd-run standing gotcha), and embedding it directly in a `page.goto` URL (`https://user:pass@host/...`) throws `net::ERR_INVALID_AUTH_CREDENTIALS` at the Chromium URL-parser level even when percent-encoded (`%2C`) — this is a stricter failure than the previously-documented "breaks the app's own fetch()" caveat for comma-free creds. Working fix: `await page.context().setHTTPCredentials({username, password})` before `page.goto()` with a plain (no-embedded-creds) URL — this is the durable workaround for any future witness against this deploy, comma-password or not.** Also reconfirmed the 38th-run tool defect (`capture` prefix scripts run in a bare Node vm with no `document`/`page` binding unless the script itself calls `page.evaluate(() => {...})` — a raw top-level `document.querySelector(...)` throws `ReferenceError: document is not defined`; wrapping every DOM read/write in `page.evaluate()` inside the capture script is the reliable pattern, not a tool-side fix).
29
+ 6 confirmed findings (runningRowTitle identity prefix, stopActiveChat 9s stuck-stopping timeout, live-session cost tally, shortcuts-help IconButton, CodeNode selection-guard parity, Row.sub tooltip). Kit pushed `00dae4c`, agentgui pushed `64f4466`. Full detail + the setHTTPCredentials comma-password witness fix in rs-learn (recall "agentgui 39th run full detail").
26
30
 
27
31
  ## GUI logic+predictability sweep (2026-07-12) — thirty-eighth run
28
32
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.1071",
3
+ "version": "1.0.1073",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
@@ -7876,6 +7876,35 @@
7876
7876
  padding: 4px var(--space-3); font-size: var(--fs-tiny); color: var(--fg-2);
7877
7877
  }
7878
7878
 
7879
+ /* Streaming elapsed-time counter: ticks m:ss while a turn is in flight.
7880
+ Tabular-nums so the digits don't jitter the layout every second. */
7881
+ .ds-247420 .chat-composer-elapsed {
7882
+ order: 5; padding: 2px var(--space-1) 0;
7883
+ font-size: var(--fs-micro); color: var(--fg-3);
7884
+ font-variant-numeric: tabular-nums; font-feature-settings: 'tnum';
7885
+ }
7886
+
7887
+ /* Detected-attachment badge: a dismissible pill above the textarea, styled
7888
+ like the composer-note treatment (same tone/scale) rather than inventing
7889
+ new chrome. */
7890
+ .ds-247420 .chat-composer-detected-badge {
7891
+ display: flex; align-items: center; gap: var(--space-2, 8px);
7892
+ padding: 4px var(--space-3); font-size: var(--fs-tiny); color: var(--fg-2);
7893
+ background: var(--bg-2); border: var(--bw-hair) solid var(--rule);
7894
+ border-radius: var(--r-1); width: 100%; order: -1;
7895
+ }
7896
+ .ds-247420 .chat-composer-detected-label { flex: 1 1 auto; }
7897
+ .ds-247420 .chat-composer-detected-dismiss {
7898
+ flex: 0 0 auto; background: none; border: none; cursor: pointer;
7899
+ color: var(--fg-3); font: inherit; font-size: var(--fs-tiny);
7900
+ padding: 2px 6px; border-radius: var(--r-1);
7901
+ }
7902
+ .ds-247420 .chat-composer-detected-dismiss:hover { color: var(--fg); }
7903
+ .ds-247420 .chat-composer-detected-dismiss:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
7904
+ @media (pointer: coarse) {
7905
+ .ds-247420 .chat-composer-detected-dismiss { min-height: 44px; min-width: 44px; }
7906
+ }
7907
+
7879
7908
  /* Drag-over ring: token accent, never a hex. */
7880
7909
  .ds-247420 .chat-composer.dragover { outline: var(--focus-w) solid var(--focus-color); outline-offset: calc(var(--focus-offset) * -1); border-radius: var(--r-2); }
7881
7910
 
@@ -9767,6 +9796,42 @@
9767
9796
  color: var(--panel-text-3, var(--fg-3));
9768
9797
  white-space: nowrap;
9769
9798
  }
9799
+ /* PermissionMenu (ov-perm) — roving-tabindex menuitemcheckbox rows +
9800
+ approve-all/revoke-all actions, styled to match the sibling Dropdown menu. */
9801
+ .ds-247420 .ov-perm-menu { min-width: 220px; }
9802
+ .ds-247420 .ov-perm-list { display: flex; flex-direction: column; padding: var(--space-1, 4px); }
9803
+ .ds-247420 .ov-perm-item {
9804
+ display: flex; align-items: center; gap: var(--space-2, 8px);
9805
+ width: 100%; min-height: 36px;
9806
+ padding: var(--space-2, 8px) var(--space-3, 16px);
9807
+ background: transparent; border: 0;
9808
+ color: var(--fg); font: inherit; text-align: left;
9809
+ border-radius: var(--r-1, 10px); cursor: pointer;
9810
+ }
9811
+ .ds-247420 .ov-perm-item:hover { background: var(--panel-2, var(--rule)); }
9812
+ .ds-247420 .ov-perm-item[aria-checked="true"] { color: var(--accent-ink); }
9813
+ .ds-247420 .ov-perm-item[aria-checked="true"]::before { content: '[x] '; font-family: var(--ff-mono, monospace); }
9814
+ .ds-247420 .ov-perm-item[aria-checked="false"]::before { content: '[ ] '; font-family: var(--ff-mono, monospace); color: var(--fg-3); }
9815
+ .ds-247420 .ov-perm-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
9816
+ .ds-247420 .ov-perm-actions {
9817
+ display: flex; gap: var(--space-2, 8px);
9818
+ padding: var(--space-2, 8px) var(--space-3, 16px);
9819
+ border-top: 1px solid var(--rule); margin-top: var(--space-1, 4px);
9820
+ }
9821
+ .ds-247420 .ov-perm-action {
9822
+ flex: 1; appearance: none; border: 1px solid var(--rule); background: transparent;
9823
+ color: var(--fg-2); font: inherit; font-size: var(--fs-tiny, 12px);
9824
+ padding: 4px 8px; border-radius: var(--r-1, 10px); cursor: pointer;
9825
+ }
9826
+ .ds-247420 .ov-perm-action:hover { background: var(--panel-2, var(--rule)); }
9827
+
9828
+ /* CountdownDialog's ticking status line: tabular-nums so the number swap
9829
+ each second doesn't jitter the modal layout. */
9830
+ .ds-247420 .ds-countdown-status {
9831
+ font-variant-numeric: tabular-nums; font-feature-settings: 'tnum';
9832
+ color: var(--fg-2);
9833
+ }
9834
+
9770
9835
  .ds-247420 .ds-ep-divider-vertical {
9771
9836
  display: inline-block;
9772
9837
  align-self: stretch;