agentgui 1.0.1079 → 1.0.1081

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-6d49f08bebac54c9-744
3
+ ns: default
4
+ created: 1784300719472
5
+ updated: 1784300719472
6
+ ---
7
+
8
+ agentgui-45th-run-docstudio-full-detail: third docstudio design-cue pass. Fresh Explore-agent survey (told what 43rd/44th runs already shipped) found 5 new gaps, all implemented: ChatComposer/AgentChat streamingSince prop (role=status tabular-nums elapsed counter while streaming); Enter-to-send gated behind matchMedia('(pointer:coarse)'); CountdownDialog primitive in files-modals.js; PermissionMenu roving-tabindex menuitemcheckbox list in overlay-primitives.js; ChatComposer detectAttachment pasted-URL sniff badge. bun test.js 38/38 pass, 0 lint errors. Rebased cleanly onto concurrent v0.0.344/v0.0.345 (RTL support). Kit pushed 5c0571e->1d46202, agentgui re-vendored+pushed, browser-witnessed bodyLen:19739 hasAgentgui:true 0 pageErrors.
@@ -0,0 +1,8 @@
1
+ ---
2
+ key: mem-a9968a186368b022-1150
3
+ ns: default
4
+ created: 1784300690988
5
+ updated: 1784300690988
6
+ ---
7
+
8
+ agentgui 46th run confirming pass (2026-07-17): per gm-continue's mandatory first-confirming-pass discipline, a second independent Explore-agent survey of docstudio (told what runs 42-46 already shipped) found one more narrow gap: kit toast() only supported self-dismissing {message,kind,duration}, unlike docstudio update-check.js:18-30's persistent click-to-reload bar. Added actionLabel/onAction props rendering a non-auto-dismissing button. Real CI catch: docs/component-props.md was stale after the new toast() signature (component-docs lint), fixed by regenerating. Kit rebased twice onto concurrent v0.0.350/v0.0.351 releases (dist-only, rebuilt fresh). Browser-witnessed both toast variants live: action toast renders .ds-ep-toast.has-action with working button firing onAction(dismiss); plain toast unaffected (no has-action class, no button) confirming backward compat. Kit pushed b5530cb->e93f4f7, agentgui pushed ed36a36->3a59c3b, all CI green. A second confirming pass after this one found nothing further - the docstudio design-cue mining task for ../design and ./ is genuinely exhausted after 4 full sweeps + 1 confirming-pass finding.
@@ -0,0 +1,8 @@
1
+ ---
2
+ key: mem-e9bc5f9ed6edbcf7-1580
3
+ ns: default
4
+ created: 1784300703197
5
+ updated: 1784300703197
6
+ ---
7
+
8
+ agentgui-45th-run-docstudio-full-detail: third docstudio design-cue pass, same directive repeated. 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 (chat-input.js:116-125) - coarse-pointer devices get a newline+"Tap Send to send" hint instead; CountdownDialog({title,message,seconds,onExpire,actions}) primitive in files-modals.js (docstudio google-reconnect.js:41-75's live sign-out countdown), composing over the existing Modal()/Backdrop() shell rather than editor-primitives.js's standalone Dialog; PermissionMenu({categories,approved,onToggle,onToggleAll}) in overlay-primitives.js (docstudio tool-approval-panel.js's roving-tabindex menuitemcheckbox list); ChatComposer detectAttachment(text)=>{type,label,id}|null prop rendering a dismissible .chat-composer-detected-badge (chat-input.js:49-62's pasted-URL sniff). Implementation delegated to a background worktree agent; bun test.js 38/38 pass, 0 lint errors across 7 lints. Concurrent upstream v0.0.344/v0.0.345 (RTL support) landed mid-pass, rebased cleanly, dist rebuilt fresh. Kit pushed 5c0571e->1d46202. agentgui re-vendored, browser-witnessed (bodyLen:19739, hasAgentgui:true, 0 pageErrors), grep-confirmed all 5 feature strings present before pushing.
package/.gm/prd.yml CHANGED
@@ -3133,22 +3133,14 @@
3133
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
3134
  status: completed
3135
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: chat-log-aria-live
3137
- subject: Add aria-live=polite aria-relevant=additions to the role=log chat thread containers in chat.js:533 and agent-chat.js:476/581, matching docstudio chat-tabs.js:62-65, so new messages are announced without spamming on every streamed token
3138
- witness: 'Browser-witnessed live on localhost:3009/gm/: .agentchat-thread carries role=log aria-live=polite aria-relevant=additions (confirmed via page.evaluate getAttribute), bodyLen:19429, hasAgentgui:true, 0 console errors. Implemented in kit chat.js:533,581 and agent-chat.js:476; kit tests pass; re-vendored into agentgui and pushed.'
3139
- status: completed
3140
- commit_comment: aria-live/aria-relevant on chat log regions (docstudio cue)
3141
- - id: chat-log-aria-live-streaming-verify
3142
- subject: Verify aria-live=polite does not cause screen-reader spam during token-by-token streaming (aria-relevant=additions should limit announcements to new message additions, not text mutations within an existing message) - live witness via browser dispatch
3143
- witness: 'Verified via source inspection that aria-relevant=additions structurally scopes to sibling-node insertion, not to text-content mutation within an existing node: agent-chat.js:582 renders each message as a distinct keyed VElement (...all.map((m,i)=>ChatMessage({...m, key: m.key != null ? m.key : i}))), so streaming token updates mutate an existing keyed node''s subtree (no announcement) while a genuinely new message is a new keyed sibling (announced) - matches docstudio''s own chat-tabs.js pairing intent. No spam risk by construction.'
3144
- status: completed
3145
- commit_comment: verified aria-relevant=additions does not spam-announce during token streaming
3146
3136
  - id: kit-test-spacing-regex-drift
3147
3137
  subject: test.js lint-tokens.mjs spacing-report regex only matched the old REPORT format; upstream v0.0.349 changed lint-spacing output to PASS/baseline format, silently breaking the test's ability to catch regressions until this run's test execution surfaced it
3148
3138
  witness: bun test.js failed with 'lint-spacing report line not found' after rebasing onto origin/main (8436f0a); fixed by widening the regex to match both formats
3149
3139
  status: completed
3150
- - id: toast-persistent-action-variant
3151
- subject: Add onAction/actionLabel/duration:0-persistent support to the kit's toast() (editor-primitives.js:637-653), matching docstudio's update-check.js:18-30 click-to-reload pattern - currently toast() only supports message/kind/duration and always self-dismisses via setTimeout, with no way to render a non-auto-dismissing actionable CTA toast
3152
- witness: 'Browser-witnessed live on localhost:3009/gm/: imported the vendored kit module directly and called toast({message,kind,duration:0,actionLabel:''Reload'',onAction}) - hasActionEl:true, btnText:''Reload'', clicking the button fired onAction with the dismiss callback (actionFired:true). Verified backward compat: plain toast({message,kind,duration}) with no actionLabel/onAction renders with no has-action class and no button. Kit built+tested (bun test.js all pass, node scripts/generate-component-docs.mjs --check clean, node scripts/build.mjs 0 lint errors). Kit pushed b5530cb->e93f4f7 (one dist-only rebase + one component-docs-lint fix), all 3 kit CI checks green. Re-vendored into agentgui, 0 pageErrors, bodyLen:19466, hasAgentgui:true.'
3153
- status: completed
3154
- commit_comment: persistent action-CTA toast variant (docstudio update-check.js cue, confirming-pass finding)
3140
+ - id: browser-verb-url-dom-prefix-broken
3141
+ subject: 'The gm browser verb''s url=/dom= prefix dispatches evaluate in a Node vm context (ReferenceError: document/window is not defined) despite navigation_requested:true, and the screenshot= prefix mis-parses its own argument as JS syntax (Unexpected token). Only the bare ''capture\n<script>'' dispatch form (no url=/dom=/screenshot= prefix) actually evaluates in real page context this session. Tool defect, not fixable from this repo - recorded per gm''s diagnose-the-tool-not-the-row discipline.'
3142
+ status: pending
3143
+ witness: browser-6/-7/-8/-9 dispatches this session all returned ReferenceError or SyntaxError from a Node vm, not the real page, despite prior AGENTS.md notes claiming url=<target>\n<script> works
3144
+ blockedBy:
3145
+ - external
3146
+ - gm-plugkit/playwriter browser verb prefix-parsing regression, needs upstream fix
package/AGENTS.md CHANGED
@@ -1,12 +1,16 @@
1
1
  # AgentGUI — Agent Notes
2
2
 
3
+ ## Docstudio design-cue follow-up (2026-07-17) — forty-sixth run, confirming pass
4
+
5
+ Per gm-continue's mandatory first-confirming-pass discipline, a second independent Explore-agent survey (told what runs 42-46 already shipped, including the aria-live fix above) checked docstudio's remaining source (`update-check.js`, `debug-panel.js`, `chat-tabs.js`, `chat-model-selector.js`, `webview-detector.js`, `xls-preview.js`, `doc-tab.js`, `chat-approval-prompts.js`) and found one more narrow, real prop-surface gap: the kit's `toast()` (`editor-primitives.js:637`) only supported a self-dismissing `{message,kind,duration}` shape, unlike docstudio's `update-check.js:18-30` persistent click-to-reload notification bar. Added `actionLabel`/`onAction` props rendering a non-auto-dismissing button (`duration:0` already worked; the gap was no action-button support) — `.ds-ep-toast-action` styled with `--space-*` tokens (a first attempt with raw px literals tripped the frozen spacing-lint baseline, caught by `node scripts/build.mjs` before commit). CI caught a real miss on first push: `docs/component-props.md` was stale after the new `toast()` signature (`node scripts/generate-component-docs.mjs --check` lint), fixed by regenerating and re-pushing — a genuine CI-catches-a-real-gap case, not flakiness. Kit rebased twice cleanly onto concurrent `v0.0.350`/`v0.0.351` releases (dist-only, rebuilt fresh both times). Kit pushed `b5530cb`→`e93f4f7`, all 3 kit CI checks green. Browser-witnessed live on `localhost:3009/gm/` by importing the vendored module directly and calling `toast()` both ways: action variant renders `.ds-ep-toast.has-action` with a working `Reload` button whose click fires `onAction(dismiss)`; plain toast (no `actionLabel`/`onAction`) renders with no `has-action` class and no button, confirming backward compatibility. agentgui re-vendored, pushed `ed36a36`→`3a59c3b`, all 4 CI checks green. A second confirming pass after this one found nothing further.
6
+
3
7
  ## Docstudio design-cue follow-up (2026-07-17) — forty-sixth run
4
8
 
5
9
  Fourth docstudio-cue pass, same directive repeated a fourth time. A fresh Explore-agent survey of `/config/docstudio` (told explicitly what runs 42-45 already shipped — toasts/Skeleton/Alert/Badge-tone/dialogs/breadcrumbs/inline-error-retry/Avatar/sidePanel/sortable-Table/streamingSince/coarse-pointer Enter-gating/CountdownDialog/PermissionMenu/detectAttachment/RTL support) came back with the low-hanging fruit exhausted: only 1 genuinely new, verified-missing candidate survived (a second, "self-retry dropdown trigger" candidate had no existing async-populated-dropdown call site in the kit to wire it to, so it was explicitly skipped as speculative unused plumbing rather than implemented). Landed: `chat.js`'s two `role=log` chat-thread containers (`chat.js:533`,`581`) and `agent-chat.js`'s one (`agent-chat.js:476`) gained `aria-live="polite" aria-relevant="additions"`, matching docstudio's `chat-tabs.js:62-65` pairing, so new messages announce to screen readers without spamming on every streamed token — verified by source that `aria-relevant=additions` only fires on new keyed-sibling insertion (`agent-chat.js:582`'s `...all.map((m,i)=>ChatMessage({...m,key:...}))`), not on in-place text mutation within an existing message's subtree during streaming. Also fixed, as a genuinely unrelated hygiene issue surfaced mid-rebase: `test.js`'s `lint-tokens.mjs spacing report` check regexed for the old `[lint-spacing] REPORT — N raw` format, but upstream `v0.0.349`'s "ratcheted spacing lint" consolidation changed the script's own stdout to `PASS — N <= baseline N`, silently breaking the regression check's ability to catch anything post-rebase — widened the regex to match both formats. Kit built+tested (`bun test.js` all pass post-fix), rebased cleanly onto `v0.0.348`/`v0.0.349` (one dist-only conflict, resolved by rebuilding `dist/247420.js` fresh rather than hand-merging generated code — the standing discipline). Kit pushed `8436f0a`→`f81f93d`, all 3 kit CI checks green (ci/Publish/Deploy-GH-Pages). agentgui re-vendored both dist files, browser-witnessed live on `localhost:3009/gm/` (`bodyLen:19429`, `hasAgentgui:true`, 0 console errors, `.agentchat-thread` confirmed via `page.evaluate` to carry `role=log aria-live=polite aria-relevant=additions`), pushed `e84cf26`→`639b78e`, all 4 CI checks green (Test/Publish-and-Release/Auto-Declaudeify/Deploy-GH-Pages) — the `Test` job's `bun install` step took an unusually long ~13 minutes this run (likely npm-registry propagation delay for the just-published kit version) before completing green; not a hang, just slow.
6
10
 
7
11
  ## Docstudio design-cue follow-up (2026-07-17) — forty-fifth run
8
12
 
9
- 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.
13
+ Third docstudio-cue pass: 5 new candidates landed (ChatComposer streamingSince elapsed counter, coarse-pointer Enter-gating, CountdownDialog, PermissionMenu, detectAttachment badge). Full detail in rs-learn (recall "agentgui-45th-run-docstudio-full-detail"). Kit pushed `5c0571e`→`1d46202`, agentgui re-vendored and pushed, browser-witnessed clean.
10
14
 
11
15
  ## Docstudio design-cue follow-up (2026-07-17) — forty-fourth run
12
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.1079",
3
+ "version": "1.0.1081",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
@@ -3,7 +3,7 @@ import * as B from './backend.js';
3
3
 
4
4
  installStyles().catch(() => {});
5
5
 
6
- const { AppShell, WorkspaceShell, WorkspaceRail, Topbar, Crumb, Side, Status, Chat, ChatComposer, AgentChat, ConversationList, SessionDashboard, Row, Panel, PageHeader, TextField, Select, Btn, Icon, IconButton, EventList, Spinner, Alert, FileGrid, FileSkeleton, sortFiles, FileToolbar, RootsPicker, BreadcrumbPath, EmptyState, FileViewer, FilePreviewPane, FilePreviewCode, FilePreviewText, FilePreviewMedia, ThemeToggle, ContextPane, PromptDialog, ConfirmDialog, DropZone, UploadProgress, FilterPills, SessionMeta, BulkBar, Checkbox, ShortcutList, FocusTrap, AgentListSkeleton, flashComposerNote } = C;
6
+ const { AppShell, WorkspaceShell, WorkspaceRail, Topbar, Crumb, Side, Status, Chat, ChatComposer, AgentChat, ConversationList, SessionDashboard, Row, Panel, PageHeader, TextField, Select, Btn, Icon, IconButton, EventList, Spinner, Alert, FileGrid, FileSkeleton, sortFiles, FileToolbar, RootsPicker, BreadcrumbPath, EmptyState, FileViewer, FilePreviewPane, FilePreviewCode, FilePreviewText, FilePreviewMedia, ThemeToggle, ContextPane, PromptDialog, ConfirmDialog, DropZone, UploadProgress, FilterPills, SessionMeta, BulkBar, Checkbox, ShortcutList, FocusTrap, AgentListSkeleton, flashComposerNote, toast } = C;
7
7
 
8
8
  // One duration/bytes vocabulary across every surface: prefer the kit's shared
9
9
  // formatters (exported alongside the components), fall back to the local
@@ -4163,6 +4163,37 @@ async function recheckHealth() {
4163
4163
  render();
4164
4164
  }
4165
4165
 
4166
+ // Build-freshness: a long-open tab keeps running the JS it parsed at load
4167
+ // time even after a new version is deployed. window.__SERVER_VERSION is
4168
+ // baked in at page load; /health's `version` field reflects whatever is
4169
+ // currently running server-side. Poll it at a low frequency (distinct from
4170
+ // the health-outage poll above, which only runs while unhealthy) and, on a
4171
+ // mismatch, surface a persistent action toast rather than silently leaving
4172
+ // the tab on stale code — mirrors docstudio's update-check.js version.json
4173
+ // poll + reload-nudge pattern.
4174
+ const BUILD_FRESHNESS_POLL_MS = 90000;
4175
+ let _buildFreshnessTimer = null;
4176
+ let _buildStaleNoticeShown = false;
4177
+ function startBuildFreshnessPoll() {
4178
+ if (_buildFreshnessTimer || !window.__SERVER_VERSION) return;
4179
+ _buildFreshnessTimer = setInterval(async () => {
4180
+ if (_buildStaleNoticeShown) return;
4181
+ const r = await B.probeBackend(state.backend);
4182
+ if (r.ok && r.info && r.info.version && r.info.version !== window.__SERVER_VERSION) {
4183
+ _buildStaleNoticeShown = true;
4184
+ clearInterval(_buildFreshnessTimer);
4185
+ _buildFreshnessTimer = null;
4186
+ toast({
4187
+ message: 'A new version is available',
4188
+ kind: 'info',
4189
+ duration: 0,
4190
+ actionLabel: 'reload',
4191
+ onAction: (dismiss) => { dismiss(); location.reload(); },
4192
+ });
4193
+ }
4194
+ }, BUILD_FRESHNESS_POLL_MS);
4195
+ }
4196
+
4166
4197
  async function init() {
4167
4198
  state.health = { status: 'unknown' };
4168
4199
  render();
@@ -4174,6 +4205,7 @@ async function init() {
4174
4205
  }
4175
4206
  render();
4176
4207
  if (!(await loadAgents())) retryLoadAgents();
4208
+ startBuildFreshnessPoll();
4177
4209
 
4178
4210
  const hp = readHash();
4179
4211
  const bootTab = hp.tab || (hp.sid ? 'history' : 'chat');
@@ -9842,6 +9842,49 @@
9842
9842
  }
9843
9843
  .ds-247420 .ov-perm-action:hover { background: var(--panel-2, var(--rule)); }
9844
9844
 
9845
+ /* ApprovalPrompt — inline in-thread tool-permission card: name/category
9846
+ head, optional args preview, an optional free-text note, four resolution
9847
+ actions. Distinct from PermissionMenu (a settings dropdown); this renders
9848
+ directly in the chat flow like a message. */
9849
+ .ds-247420 .ov-approval {
9850
+ display: flex; flex-direction: column; gap: var(--space-2, 8px);
9851
+ max-width: var(--measure, 640px);
9852
+ padding: var(--space-4, 16px);
9853
+ border: var(--bw-hair) solid var(--rule);
9854
+ border-radius: var(--r-2, 12px);
9855
+ background: var(--panel, var(--bg-2));
9856
+ box-shadow: var(--shadow-1);
9857
+ }
9858
+ .ds-247420 .ov-approval-head { display: flex; align-items: center; gap: var(--space-2, 8px); color: var(--accent-ink); }
9859
+ .ds-247420 .ov-approval-icon { display: inline-flex; flex: 0 0 auto; }
9860
+ .ds-247420 .ov-approval-tool { color: var(--fg); }
9861
+ .ds-247420 .ov-approval-cat { color: var(--fg-3); font-weight: 400; }
9862
+ .ds-247420 .ov-approval-args {
9863
+ margin: 0; padding: var(--space-2, 8px);
9864
+ background: var(--panel-2, var(--rule)); border-radius: var(--r-1, 10px);
9865
+ font: var(--fs-tiny, 12px)/1.4 var(--ff-mono, monospace);
9866
+ color: var(--fg-3); max-height: 100px; overflow: auto; white-space: pre-wrap; word-break: break-all;
9867
+ }
9868
+ .ds-247420 .ov-approval-note {
9869
+ width: 100%; min-height: 48px; resize: vertical;
9870
+ padding: var(--space-2, 8px) var(--space-2-75, 12px);
9871
+ border: var(--bw-hair) solid var(--rule); border-radius: var(--r-1, 10px);
9872
+ background: var(--bg); color: var(--fg); font: inherit; box-sizing: border-box;
9873
+ }
9874
+ .ds-247420 .ov-approval-note:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
9875
+ .ds-247420 .ov-approval-actions { display: flex; gap: var(--space-2, 8px); flex-wrap: wrap; align-items: center; }
9876
+ .ds-247420 .ov-approval-btn {
9877
+ appearance: none; border: var(--bw-hair) solid var(--rule); background: transparent;
9878
+ color: var(--fg); font: inherit; font-size: var(--fs-tiny, 13px); font-weight: 500;
9879
+ padding: var(--space-1-75, 6px) var(--space-3, 16px); border-radius: var(--r-pill, 999px); cursor: pointer;
9880
+ }
9881
+ .ds-247420 .ov-approval-btn:hover { background: var(--panel-2, var(--rule)); }
9882
+ .ds-247420 .ov-approval-btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
9883
+ .ds-247420 .ov-approval-btn-primary:hover { filter: brightness(1.05); }
9884
+ .ds-247420 .ov-approval-btn-soft { background: var(--panel-2, var(--rule)); border-color: transparent; }
9885
+ .ds-247420 .ov-approval-btn-deny { margin-left: auto; border-color: var(--danger); color: var(--danger-ink, var(--danger)); }
9886
+ .ds-247420 .ov-approval-btn-deny:hover { background: var(--danger); color: var(--bg); }
9887
+
9845
9888
  /* CountdownDialog's ticking status line: tabular-nums so the number swap
9846
9889
  each second doesn't jitter the modal layout. */
9847
9890
  .ds-247420 .ds-countdown-status {