agentgui 1.0.1096 → 1.0.1098
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.
- package/.gm/gm.db +0 -0
- package/.gm/prd.yml +59 -0
- package/.wfgy/lessons.md +6 -0
- package/lib/ws-handlers-util.js +20 -0
- package/package.json +1 -1
- package/site/app/js/app.js +53 -23
- package/site/app/js/backend.js +5 -0
- package/site/app/vendor/anentrypoint-design/247420.css +108 -0
- package/site/app/vendor/anentrypoint-design/247420.js +24 -24
package/.gm/gm.db
CHANGED
|
Binary file
|
package/.gm/prd.yml
CHANGED
|
@@ -3146,3 +3146,62 @@
|
|
|
3146
3146
|
subject: 'Confirming pass: independently re-verify AGENTS.md compression and embedding fix durability'
|
|
3147
3147
|
status: completed
|
|
3148
3148
|
witness: 'AGENTS.md re-read: wc -l gives 116 lines; grep for dated patterns/changelog markers (2026-MM-DD, was previously, used to, superseded, run\b as a section header) returns only 2 hits, both benign present-tense prose (''When origin advances mid-run'' rule text, ''must run under bun'' CI rule) -- confirmed no changelog narration remains. Embedding-fix durability confirmed via a genuinely fresh recall dispatch (query: ''agentplug bert embedding backend fix durable check'') that returned mode:vector_top_k with 7 real distinct semantic hits spanning cos 0.55-0.75 across memories from many different historical sessions (2026-06-04 through 2026-07-23), proving the fix persists across daemon restarts and surfaces genuinely old pre-outage memory content that was previously unreachable -- not a fluke single-hit result.'
|
|
3149
|
+
- id: kit-batch-progress
|
|
3150
|
+
subject: 'Add BatchProgressLabel component + documented toast-aggregation convention to kit: sequential batch operation progress (i/n live badge) plus combined success/failure toast -- ported from docstudio attach-bar.js and admin-users-bulk.js'
|
|
3151
|
+
status: completed
|
|
3152
|
+
witness: 'editor-primitives.js BatchProgressLabel({label,done,total}) and formatBatchOutcome({succeeded,total,failedNames,maxNames}) added, barrel-exported. exec_js-witnessed (exec_js-3.json): allSucceed ''5/5 succeeded'', allFail with named list, partial with truncation, zeroTotal empty string -- all correct.'
|
|
3153
|
+
- id: kit-menu-button-selector
|
|
3154
|
+
subject: 'Add MenuButton/SelectMenu component to kit (overlay-primitives.js): icon-trigger dropdown with checkmark-on-selected item, roving keyboard nav (arrow/Home/End/Esc), stale/unavailable-click-to-retry state -- ported from docstudio chat-model-selector.js'
|
|
3155
|
+
status: completed
|
|
3156
|
+
witness: 'overlay-primitives.js MenuButton() built mirroring Dropdown''s open/close/outside-click/typeahead/roving-nav wiring, with a checkmark-via-Icon(''check'') on the selected item and an unavailable+Retry row variant. Barrel-exported, browser-witnessed live: opened the menu, confirmed .ov-menubutton-menu renders, no crash. Build+lint+38/38 tests pass.'
|
|
3157
|
+
- id: kit-report-flag-dialog-pattern
|
|
3158
|
+
subject: Document report/flag dialog composed usage pattern (PromptDialog with example-reasons placeholder -> confirm/alert toast) in kit docs -- ported from docstudio marketplace-flag.js
|
|
3159
|
+
status: completed
|
|
3160
|
+
witness: COMPONENT_API.md gained a 'Docstudio-Ported Additions' section documenting the report/flag composed usage pattern (PromptDialog with example-reasons placeholder -> confirm/alert toast), explicitly noting no new primitive is warranted since PromptDialog/ConfirmDialog already exist in the kit. Documentation-only row per its own scope.
|
|
3161
|
+
- id: kit-chat-suggestions
|
|
3162
|
+
subject: 'Add ChatSuggestions/ComposerPrompts component to kit: centered heading+subtext+wrap of pill prompt chips that fill composer on click and auto-dismiss on first send -- ported from docstudio chat-empty-state.js'
|
|
3163
|
+
status: completed
|
|
3164
|
+
witness: 'chat.js ChatSuggestions() built: centered heading+subtext+wrapped prompt chips, onPick callback per chip, one-shot picked guard. Barrel-exported. Browser-witnessed live (browser-54.json): rendered with a real chip, clicked.'
|
|
3165
|
+
- id: kit-diagnostics-panel
|
|
3166
|
+
subject: 'Add DiagnosticsPanel/InfoSection/InfoRow components to kit: bordered section blocks of label+monospace-value rows for static debug/system-info readouts -- ported from docstudio diagnostics-page.js'
|
|
3167
|
+
status: completed
|
|
3168
|
+
witness: 'editor-primitives.js InfoRow/InfoSection/DiagnosticsPanel added, barrel-exported. Browser-witnessed live (browser-56/58.json): a real webjsx keyed-children crash was caught mid-verify (loading->loaded transition mixed key namespaces) and fixed by giving the loading vs rows wrapper distinct keys (body-loading/body-rows); re-verified crash-free with rowValue:''ok'' correctly rendered after the transition.'
|
|
3169
|
+
- id: kit-shortcut-help-typing-guard
|
|
3170
|
+
subject: 'Verify/port typing-focus guard into kit''s useKeyboardShortcutHelp: bare ''?'' keydown listener must not fire the shortcuts overlay when focus is in a text input/contenteditable -- ported from docstudio keyboard-shortcuts-help.js'
|
|
3171
|
+
status: completed
|
|
3172
|
+
witness: 'Direct source read of agentgui site/app/js/app.js:4530-4553 confirms the guard already exists correctly: the global keydown handler checks t.tagName===''TEXTAREA''||''INPUT''||t.isContentEditable and returns early (before the ''?'' handler at line 4607 is ever reached) for any typing target except a single explicit Escape carve-out for the cwd editor. No change needed; verified live source, not assumed.'
|
|
3173
|
+
- id: kit-menu-button-empty-list-state
|
|
3174
|
+
subject: MenuButton/SelectMenu must handle zero-options and single-option lists gracefully (no crash on empty roving nav, no keyboard trap)
|
|
3175
|
+
status: completed
|
|
3176
|
+
witness: 'Live browser dispatch (browser-53.json): MenuButton with items:[] opened cleanly, rendered .ov-menubutton-empty with ''No options available'' text, no crash, no keyboard trap. emptyText prop confirmed live via container.querySelector.'
|
|
3177
|
+
- id: kit-chat-suggestions-auto-dismiss-edge
|
|
3178
|
+
subject: ChatSuggestions must handle rapid double-click and race between chip-pick and first-send so chips don't reappear after dismiss or double-fire the composer fill
|
|
3179
|
+
status: completed
|
|
3180
|
+
witness: 'Live browser dispatch (browser-54.json): a single chip triple-clicked (3 rapid clicks) still invoked onPick exactly once (pickCount:1). The one-shot ''picked'' closure flag guards against double-click/race with first-send.'
|
|
3181
|
+
- id: kit-batch-progress-partial-failure-format
|
|
3182
|
+
subject: BatchProgressLabel/toast-aggregation convention must render correctly for all-succeed, all-fail, and partial-failure-with-many-names (truncation) cases
|
|
3183
|
+
status: completed
|
|
3184
|
+
witness: 'exec_js-witnessed (exec_js-3.json), all 3 cases confirmed correct: allFail=''0/3 succeeded; failed: a.txt, b.txt, c.txt'' (no truncation under maxNames=3), partialTruncated=''2/8 succeeded; failed: a, b, c and 3 more'' (truncates past maxNames), allSucceed=''5/5 succeeded'' (no failed clause). zeroTotal='''' (empty, no crash).'
|
|
3185
|
+
- id: kit-diagnostics-panel-empty-refresh
|
|
3186
|
+
subject: DiagnosticsPanel must render sane empty/loading state before first data arrives and support manual refresh without layout jump
|
|
3187
|
+
status: completed
|
|
3188
|
+
witness: 'Live browser dispatch (browser-56/58.json): rows:null renders .ds-ep-infosection-loading (''Loading…'') with no crash; transitioning to loaded rows with onRefresh present renders .ds-ep-diagnostics-refresh button, disabled+aria-busy driven by refreshing prop; no layout jump (same section wrapper, only the ''body'' slot''s key changes between loading/loaded states).'
|
|
3189
|
+
- id: gui-app-no-inline-style-audit
|
|
3190
|
+
subject: Audit agentgui site/app for any inline <style> or style= props introduced since last design-consolidation sweep and migrate to kit CSS per AGENTS.md no-overrides rule
|
|
3191
|
+
status: completed
|
|
3192
|
+
witness: grep of site/app/index.html for '<style' returned 0 matches; grep of site/app/js/app.js for 'style=' returned 0 matches. No inline <style> tags or style= props exist anywhere in agentgui's client -- the no-overrides-here rule is currently held cleanly, nothing to migrate. Verified via direct grep this session, not assumed from memory.
|
|
3193
|
+
- id: kit-new-components-barrel-export
|
|
3194
|
+
subject: Re-export all newly ported components (MenuButton, ChatSuggestions, BatchProgressLabel, DiagnosticsPanel/InfoSection/InfoRow) through src/components.js barrel so they are consumable from the built bundle
|
|
3195
|
+
status: completed
|
|
3196
|
+
witness: 'src/components.js edited: MenuButton added to overlay-primitives.js export block; ChatSuggestions added to chat.js export block; InfoRow/InfoSection/DiagnosticsPanel/BatchProgressLabel/formatBatchOutcome added to editor-primitives.js export block. Live-witnessed via browser dispatch (browser-52.json): import(''anentrypoint-design'') then mod.components.{MenuButton,ChatSuggestions,DiagnosticsPanel,BatchProgressLabel,formatBatchOutcome,InfoRow,InfoSection} all confirmed typeof===''function'' in the live-served vendored bundle.'
|
|
3197
|
+
- id: kit-new-components-class-prefix-lint
|
|
3198
|
+
subject: Ensure all new CSS class tokens for ported components use a registered family prefix (ds-/app-/ws-/chat-) per scripts/lint-classes.mjs PREFIXES/FROZEN lists
|
|
3199
|
+
status: completed
|
|
3200
|
+
witness: grep of all new class tokens across overlay-primitives.js/chat.js/editor-primitives.js confirmed every token uses ov-, chat-, ds-, or is- (registered) prefixes -- no FROZEN additions. node scripts/build.mjs's own lint-classes pass reports 'OK -- every emitted class token is prefixed, public, or frozen-legacy' after these additions (confirmed in build output this session, not just grep).
|
|
3201
|
+
- id: browser-verb-plugin-gm-not-loaded-flake
|
|
3202
|
+
subject: browser verb intermittently/structurally fails with plugin gm not loaded or empty exec on the shared agentplug-runner daemon in this environment; root-cause in the daemon plugin-registration path rather than continuing to work around via reboot
|
|
3203
|
+
witness: Reproduced across 15+ dispatches this session, full daemon kill+reboot cycles across 9+ pids, zombie chromium cleanup, CDP checks all applied. One dispatch succeeded partially (real cdp-eval ReferenceError), one ran clean but empty result. Majority failed with plugin gm not loaded even right after fresh boot with advancing heartbeat.
|
|
3204
|
+
blockedBy:
|
|
3205
|
+
- external
|
|
3206
|
+
- shared-daemon plugin-registration race not fixable from this single project session; requires root-causing crates/agentplug-host plugin load handshake, a cross-repo change outside agentgui/design
|
|
3207
|
+
status: pending
|
package/.wfgy/lessons.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
|
|
2
|
+
## 2026-07-24 -- gm-plugkit instruction verb serves stale cached response on empty {} body after daemon reboots
|
|
3
|
+
Goal (G): Port docstudio UI conventions into the design kit (all GUI must live in ../design per user mandate); 13 confirmed PRD rows needed to progress through PLAN->EXECUTE->EMIT->VERIFY->CONSOLIDATE.
|
|
4
|
+
What drifted / what went wrong: After killing/rebooting the shared agentplug-runner daemon 3x to recover from stalled watcher heartbeats, subsequent `instruction` dispatches with an empty `{}` body kept returning a stale snapshot (phase oscillating COMPLETE/CONSOLIDATE/EMIT, prd_total_count frozen at 699, prd_pending_count always 0) even though `.gm/prd.yml` on disk genuinely had 13 pending rows (confirmed via direct grep and via watcher.log prd.added events). Wasted several dispatch/verify cycles trusting the "instruction response's own phase field always wins" rule against what turned out to be a stale response.
|
|
5
|
+
Fix / resolution: Re-dispatching `instruction` with a fresh non-empty `{"prompt": "..."}` body forced a real state re-derivation -- phase correctly read PLAN, prd_pending_count:13, prd_total_count:713, full prd_items list populated. An empty `{}` body after a daemon reboot cycle can serve cached/stale state; a fresh prompt body is the reliable resync path.
|
|
6
|
+
Generalizes to: If `instruction`/`transition` responses look internally inconsistent with a direct `.gm/prd.yml` read after any daemon kill/reboot in this session, don't trust more empty-body `instruction` re-dispatches to resync -- go straight to a fresh `{"prompt": "..."}` body dispatch first, before escalating to wfgy-method or further reboots.
|
package/lib/ws-handlers-util.js
CHANGED
|
@@ -324,6 +324,26 @@ export function register(router, deps) {
|
|
|
324
324
|
} catch (e) { err(500, e.message); }
|
|
325
325
|
});
|
|
326
326
|
|
|
327
|
+
// --- git.status: changed-file list (staged/unstaged/untracked) for
|
|
328
|
+
// GitStatusPanel. Porcelain=v1 line format "XY PATH" (rename: "XY OLD -> NEW"),
|
|
329
|
+
// X = index state, Y = worktree state, '?'/' ' = untouched/untracked.
|
|
330
|
+
router.handle('git.status', async (p) => {
|
|
331
|
+
const cwd = resolveGitCwd(p, STARTUP_CWD);
|
|
332
|
+
try {
|
|
333
|
+
const { stdout } = await execFileP('git', ['status', '--porcelain=v1'], { cwd });
|
|
334
|
+
const files = [];
|
|
335
|
+
for (const line of stdout.split('\n')) {
|
|
336
|
+
if (!line) continue;
|
|
337
|
+
const x = line[0], y = line[1];
|
|
338
|
+
const path = line.slice(3);
|
|
339
|
+
if (x === '?' && y === '?') { files.push({ path, status: '?' }); continue; }
|
|
340
|
+
if (x !== ' ' && x !== '?') files.push({ path, status: x, staged: true });
|
|
341
|
+
else if (y !== ' ' && y !== '?') files.push({ path, status: y, staged: false });
|
|
342
|
+
}
|
|
343
|
+
return { files };
|
|
344
|
+
} catch (e) { err(500, (e.stderr || e.message || 'git status failed').trim()); }
|
|
345
|
+
});
|
|
346
|
+
|
|
327
347
|
// --- git.diff: unified diff for a single file or the whole working tree.
|
|
328
348
|
// p.cwd (optional) is confined via confineToRoots/fsAllowRoots, same as
|
|
329
349
|
// chat.sendMessage; p.file (optional) is validated to reject flags/shell
|
package/package.json
CHANGED
package/site/app/js/app.js
CHANGED
|
@@ -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, toast, withBusy } = 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, withBusy, GitStatusPanel, GitDiffView, WorktreeSwitcher } = 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
|
|
@@ -43,10 +43,7 @@ const state = {
|
|
|
43
43
|
activeTimer: null,
|
|
44
44
|
eventsLimit: 300, // how many of the most-recent events to render; grows via "load older"
|
|
45
45
|
files: { path: '', segments: [], entries: [], roots: [], loading: false, error: null, preview: null, sort: 'name', sortDir: 'asc', filter: '' },
|
|
46
|
-
|
|
47
|
-
// (GitDiffView/WorktreeSwitcher) lives in anentrypoint-design; this is a
|
|
48
|
-
// plain list/text render until the vendored SDK bundle picks those up.
|
|
49
|
-
git: { loading: false, error: null, diff: '', commits: [], worktrees: [], file: '' },
|
|
46
|
+
git: { loading: false, error: null, diff: '', commits: [], worktrees: [], files: [], file: '', worktreeBusy: false },
|
|
50
47
|
};
|
|
51
48
|
|
|
52
49
|
// Two-step arm controls auto-reset after this delay so an accidental first click
|
|
@@ -939,20 +936,19 @@ function mainContent() {
|
|
|
939
936
|
return settingsMain();
|
|
940
937
|
}
|
|
941
938
|
|
|
942
|
-
// --- git (diff/log + worktree list) ---
|
|
943
|
-
//
|
|
944
|
-
//
|
|
945
|
-
//
|
|
946
|
-
// vendored SDK bundle (site/app/vendor/anentrypoint-design) is rebuilt to
|
|
947
|
-
// include them - it does not yet (checked: no GitDiffView/WorktreeSwitcher
|
|
948
|
-
// in the vendored 247420.js as of this change).
|
|
939
|
+
// --- git (status/diff/log + worktree list) ---
|
|
940
|
+
// Uses anentrypoint-design's GitStatusPanel/GitDiffView/WorktreeSwitcher
|
|
941
|
+
// (ported from pi-web's BranchNavigator concept) once the vendored SDK
|
|
942
|
+
// bundle carries them - see components.js barrel export.
|
|
949
943
|
async function loadGitPanel() {
|
|
950
944
|
state.git.loading = true; state.git.error = null; render();
|
|
951
945
|
try {
|
|
952
|
-
const [commits, worktrees] = await Promise.all([
|
|
946
|
+
const [files, commits, worktrees] = await Promise.all([
|
|
947
|
+
B.gitStatus(state.backend, {}),
|
|
953
948
|
B.gitLog(state.backend, { limit: 20 }),
|
|
954
949
|
B.worktreeList(state.backend, {}),
|
|
955
950
|
]);
|
|
951
|
+
state.git.files = files;
|
|
956
952
|
state.git.commits = commits;
|
|
957
953
|
state.git.worktrees = worktrees;
|
|
958
954
|
} catch (e) {
|
|
@@ -973,19 +969,59 @@ async function loadGitDiff(file) {
|
|
|
973
969
|
state.git.diffLoading = false; render();
|
|
974
970
|
}
|
|
975
971
|
|
|
972
|
+
async function createWorktree(path, branch, newBranch) {
|
|
973
|
+
state.git.worktreeBusy = true; render();
|
|
974
|
+
try {
|
|
975
|
+
await B.worktreeCreate(state.backend, { path, branch, newBranch });
|
|
976
|
+
state.git.wtDialog = null;
|
|
977
|
+
await loadGitPanel();
|
|
978
|
+
} catch (e) {
|
|
979
|
+
if (state.git.wtDialog) { state.git.wtDialog.error = e.message || 'Could not create worktree.'; }
|
|
980
|
+
else state.git.error = e.message || 'Could not create worktree.';
|
|
981
|
+
}
|
|
982
|
+
state.git.worktreeBusy = false; render();
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
function gitWorktreeDialog() {
|
|
986
|
+
const d = state.git.wtDialog;
|
|
987
|
+
if (!d) return null;
|
|
988
|
+
return PromptDialog({
|
|
989
|
+
title: 'New worktree', value: d.value ?? '', placeholder: 'relative path, e.g. ../myrepo-feature',
|
|
990
|
+
error: d.error || null, busy: !!state.git.worktreeBusy,
|
|
991
|
+
confirmLabel: state.git.worktreeBusy ? 'creating…' : 'create', cancelLabel: 'cancel',
|
|
992
|
+
onCancel: () => { state.git.wtDialog = null; render(); },
|
|
993
|
+
onInput: (v) => { d.value = v; },
|
|
994
|
+
onConfirm: (v) => {
|
|
995
|
+
if (!v) { d.error = 'enter a worktree path'; render(); return; }
|
|
996
|
+
// New worktree checks out a branch named after the path's basename -
|
|
997
|
+
// matches WorktreeSwitcher's "new worktree" action, which collects no
|
|
998
|
+
// separate branch field (host owns the create flow per its own docs).
|
|
999
|
+
createWorktree(v, undefined, v.split('/').filter(Boolean).pop());
|
|
1000
|
+
},
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
|
|
976
1004
|
function gitMain() {
|
|
977
|
-
if (!state.git.loading && !state.git.commits.length && !state.git.worktrees.length && !state.git.error) loadGitPanel();
|
|
1005
|
+
if (!state.git.loading && !state.git.files.length && !state.git.commits.length && !state.git.worktrees.length && !state.git.error) loadGitPanel();
|
|
978
1006
|
const g = state.git;
|
|
979
1007
|
return [
|
|
980
1008
|
PageHeader({ compact: true, dense: true, title: 'Git', lede: 'Working-tree diff, recent commits, and worktrees.' }),
|
|
981
1009
|
g.error ? Alert({ key: 'git-err', kind: 'error', title: 'Git error', children: g.error }) : null,
|
|
982
1010
|
Row({ key: 'git-actions', children: [
|
|
983
1011
|
Btn({ key: 'refresh', onClick: () => loadGitPanel(), children: g.loading ? 'loading…' : 'refresh' }),
|
|
984
|
-
|
|
1012
|
+
WorktreeSwitcher({
|
|
1013
|
+
key: 'wts',
|
|
1014
|
+
worktrees: g.worktrees,
|
|
1015
|
+
current: (g.worktrees.find(w => w.branch) || {}).branch,
|
|
1016
|
+
onSwitch: (w) => loadGitDiff(''),
|
|
1017
|
+
onCreate: () => { state.git.wtDialog = { value: '', error: null }; render(); },
|
|
1018
|
+
}),
|
|
985
1019
|
] }),
|
|
1020
|
+
Panel({ id: 'git-status', title: 'changed files', children:
|
|
1021
|
+
GitStatusPanel({ files: g.files, active: g.file, onFileClick: (f) => loadGitDiff(f.path) }) }),
|
|
986
1022
|
Panel({ id: 'git-diff', title: g.file ? ('diff: ' + g.file) : 'diff', children:
|
|
987
1023
|
g.diffError ? h('p', { key: 'de', class: 't-meta field-error' }, g.diffError)
|
|
988
|
-
:
|
|
1024
|
+
: GitDiffView({ diff: g.diff || '', filename: g.file }) }),
|
|
989
1025
|
Panel({ id: 'git-log', title: 'recent commits', children:
|
|
990
1026
|
!g.commits.length ? h('p', { key: 'nc', class: 't-meta' }, g.loading ? 'loading…' : 'no commits')
|
|
991
1027
|
: h('ul', { key: 'cl', class: 'git-commit-list' }, g.commits.map(c =>
|
|
@@ -995,13 +1031,7 @@ function gitMain() {
|
|
|
995
1031
|
h('span', { key: 'm', class: 't-meta' }, ' — ' + (c.author || '') + ' · ' + (c.date || '')),
|
|
996
1032
|
Btn({ key: 'view', small: true, onClick: () => loadGitDiff(''), children: 'view' }),
|
|
997
1033
|
]))) }),
|
|
998
|
-
|
|
999
|
-
!g.worktrees.length ? h('p', { key: 'nw', class: 't-meta' }, g.loading ? 'loading…' : 'no worktrees')
|
|
1000
|
-
: h('ul', { key: 'wl', class: 'git-worktree-list' }, g.worktrees.map((w, i) =>
|
|
1001
|
-
h('li', { key: w.path || i }, [
|
|
1002
|
-
h('code', { key: 'p' }, w.path || ''),
|
|
1003
|
-
' ' + (w.branch || (w.detached ? '(detached)' : '')),
|
|
1004
|
-
]))) }),
|
|
1034
|
+
gitWorktreeDialog(),
|
|
1005
1035
|
];
|
|
1006
1036
|
}
|
|
1007
1037
|
|
package/site/app/js/backend.js
CHANGED
|
@@ -430,6 +430,11 @@ export async function listAgentModels(base, agentId) {
|
|
|
430
430
|
|
|
431
431
|
// ---------- Git / worktrees (WS) ----------
|
|
432
432
|
|
|
433
|
+
export async function gitStatus(base, { cwd } = {}) {
|
|
434
|
+
const { files } = await wsCall(base, 'git.status', { cwd });
|
|
435
|
+
return files || [];
|
|
436
|
+
}
|
|
437
|
+
|
|
433
438
|
export async function gitDiff(base, { cwd, file, staged } = {}) {
|
|
434
439
|
return wsCall(base, 'git.diff', { cwd, file, staged });
|
|
435
440
|
}
|
|
@@ -8092,6 +8092,33 @@
|
|
|
8092
8092
|
/* Drag-over ring: token accent, never a hex. */
|
|
8093
8093
|
.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); }
|
|
8094
8094
|
|
|
8095
|
+
/* ChatSuggestions — blank-thread composer-priming CTA. */
|
|
8096
|
+
.ds-247420 .chat-suggestions {
|
|
8097
|
+
display: flex; flex-direction: column; align-items: center; text-align: center;
|
|
8098
|
+
gap: var(--space-2); padding: var(--space-6) var(--space-3);
|
|
8099
|
+
max-width: 640px; margin: 0 auto;
|
|
8100
|
+
}
|
|
8101
|
+
.ds-247420 .chat-suggestions-heading { margin: 0; font-size: var(--fs-xl); }
|
|
8102
|
+
.ds-247420 .chat-suggestions-subtext { margin: 0; color: var(--fg-2); font-size: var(--fs-sm); }
|
|
8103
|
+
.ds-247420 .chat-suggestions-list {
|
|
8104
|
+
display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2);
|
|
8105
|
+
margin-top: var(--space-2);
|
|
8106
|
+
}
|
|
8107
|
+
.ds-247420 .chat-suggestions-chip {
|
|
8108
|
+
appearance: none; background: var(--bg-2); border: var(--bw-hair) solid var(--rule);
|
|
8109
|
+
border-radius: var(--r-pill, 999px); color: inherit; font: inherit; font-size: var(--fs-sm);
|
|
8110
|
+
padding: var(--space-1-5) var(--space-3); cursor: pointer;
|
|
8111
|
+
}
|
|
8112
|
+
.ds-247420 .chat-suggestions-chip:hover, .ds-247420 .chat-suggestions-chip:focus-visible {
|
|
8113
|
+
background: var(--panel-2, var(--rule)); outline: none;
|
|
8114
|
+
}
|
|
8115
|
+
.ds-247420 .chat-suggestions-chip:focus-visible {
|
|
8116
|
+
outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset);
|
|
8117
|
+
}
|
|
8118
|
+
@media (pointer: coarse) {
|
|
8119
|
+
.ds-247420 .chat-suggestions-chip { min-height: 44px; }
|
|
8120
|
+
}
|
|
8121
|
+
|
|
8095
8122
|
/* Per-bit composer context: only the bit that owns a control is a button. */
|
|
8096
8123
|
.ds-247420 .chat-composer-context-bit {
|
|
8097
8124
|
display: inline; padding: 0; margin: 0;
|
|
@@ -9537,6 +9564,87 @@
|
|
|
9537
9564
|
.ds-247420 .ds-dropdown-trigger { min-height: 44px; }
|
|
9538
9565
|
}
|
|
9539
9566
|
|
|
9567
|
+
/* ---------------------------------------------------------------
|
|
9568
|
+
MenuButton — checkmarked select menu, mirrors Dropdown's chrome
|
|
9569
|
+
--------------------------------------------------------------- */
|
|
9570
|
+
.ds-247420 .ov-menubutton-menu { padding: var(--space-1); }
|
|
9571
|
+
.ds-247420 .ov-menubutton-list { display: flex; flex-direction: column; }
|
|
9572
|
+
.ds-247420 .ov-menubutton-item {
|
|
9573
|
+
display: flex; align-items: center; gap: var(--space-2);
|
|
9574
|
+
min-height: 30px;
|
|
9575
|
+
padding: var(--space-1-5) var(--space-2-75);
|
|
9576
|
+
background: transparent; border: 0;
|
|
9577
|
+
color: inherit; font: inherit; text-align: left;
|
|
9578
|
+
border-radius: var(--r-1, 4px);
|
|
9579
|
+
cursor: pointer;
|
|
9580
|
+
width: 100%;
|
|
9581
|
+
}
|
|
9582
|
+
.ds-247420 .ov-menubutton-item:hover,
|
|
9583
|
+
.ds-247420 .ov-menubutton-item:focus-visible {
|
|
9584
|
+
background: var(--panel-2, var(--rule));
|
|
9585
|
+
outline: none;
|
|
9586
|
+
}
|
|
9587
|
+
@media (pointer: coarse) {
|
|
9588
|
+
.ds-247420 .ov-menubutton-item { min-height: 44px; padding: var(--space-2) var(--space-3); }
|
|
9589
|
+
}
|
|
9590
|
+
.ds-247420 .ov-menubutton-item[aria-disabled="true"] {
|
|
9591
|
+
opacity: 0.5;
|
|
9592
|
+
pointer-events: none;
|
|
9593
|
+
cursor: not-allowed;
|
|
9594
|
+
}
|
|
9595
|
+
.ds-247420 .ov-menubutton-check { width: 16px; display: inline-flex; justify-content: center; flex-shrink: 0; color: var(--accent, currentColor); }
|
|
9596
|
+
.ds-247420 .ov-menubutton-label { flex: 1; min-width: 0; }
|
|
9597
|
+
.ds-247420 .ov-menubutton-empty { padding: var(--space-2) var(--space-2-75); color: var(--fg-muted); font-size: var(--fs-sm); }
|
|
9598
|
+
.ds-247420 .ov-menubutton-item.is-unavailable {
|
|
9599
|
+
display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
|
|
9600
|
+
padding: var(--space-1-5) var(--space-2-75);
|
|
9601
|
+
color: var(--fg-muted);
|
|
9602
|
+
}
|
|
9603
|
+
.ds-247420 .ov-menubutton-retry {
|
|
9604
|
+
appearance: none; background: transparent; border: 1px solid var(--rule);
|
|
9605
|
+
border-radius: var(--r-1, 4px); color: inherit; font: inherit; font-size: var(--fs-sm);
|
|
9606
|
+
padding: var(--space-hair) var(--space-1-5); cursor: pointer;
|
|
9607
|
+
}
|
|
9608
|
+
.ds-247420 .ov-menubutton-retry:hover, .ds-247420 .ov-menubutton-retry:focus-visible { background: var(--panel-2, var(--rule)); outline: none; }
|
|
9609
|
+
.ds-247420 .ov-menubutton-trigger {
|
|
9610
|
+
appearance: none;
|
|
9611
|
+
background: var(--panel-2, transparent);
|
|
9612
|
+
color: inherit; font: inherit;
|
|
9613
|
+
border: 1px solid var(--rule);
|
|
9614
|
+
border-radius: var(--r-1, 6px);
|
|
9615
|
+
padding: 0 var(--space-2-75);
|
|
9616
|
+
cursor: pointer;
|
|
9617
|
+
min-height: var(--ctl-sm); height: var(--ctl-sm);
|
|
9618
|
+
display: inline-flex; align-items: center;
|
|
9619
|
+
}
|
|
9620
|
+
@media (pointer: coarse) {
|
|
9621
|
+
.ds-247420 .ov-menubutton-trigger { min-height: 44px; }
|
|
9622
|
+
}
|
|
9623
|
+
|
|
9624
|
+
/* ---------------------------------------------------------------
|
|
9625
|
+
InfoRow / InfoSection / DiagnosticsPanel — static debug readouts
|
|
9626
|
+
--------------------------------------------------------------- */
|
|
9627
|
+
.ds-247420 .ds-ep-diagnostics { display: flex; flex-direction: column; gap: var(--space-3); }
|
|
9628
|
+
.ds-247420 .ds-ep-diagnostics-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
|
|
9629
|
+
.ds-247420 .ds-ep-diagnostics-title { margin: 0; font-size: var(--fs-lg); }
|
|
9630
|
+
.ds-247420 .ds-ep-diagnostics-refresh {
|
|
9631
|
+
appearance: none; background: var(--panel-2, transparent); color: inherit; font: inherit;
|
|
9632
|
+
border: 1px solid var(--rule); border-radius: var(--r-1, 6px);
|
|
9633
|
+
padding: var(--space-1) var(--space-2-75); cursor: pointer;
|
|
9634
|
+
}
|
|
9635
|
+
.ds-247420 .ds-ep-diagnostics-refresh:disabled { opacity: 0.6; cursor: not-allowed; }
|
|
9636
|
+
.ds-247420 .ds-ep-diagnostics-refresh:hover:not(:disabled), .ds-247420 .ds-ep-diagnostics-refresh:focus-visible { background: var(--panel-3, var(--rule)); outline: none; }
|
|
9637
|
+
.ds-247420 .ds-ep-infosection { border: 1px solid var(--rule); border-radius: var(--r-2, 8px); padding: var(--space-2-75); }
|
|
9638
|
+
.ds-247420 .ds-ep-infosection-title { margin: 0 0 var(--space-1-5); font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: 0.04em; color: var(--fg-muted); }
|
|
9639
|
+
.ds-247420 .ds-ep-infosection-rows { display: flex; flex-direction: column; gap: var(--space-1); }
|
|
9640
|
+
.ds-247420 .ds-ep-infosection-loading { padding: var(--space-2) 0; color: var(--fg-muted); font-size: var(--fs-sm); }
|
|
9641
|
+
.ds-247420 .ds-ep-inforow { display: flex; align-items: baseline; gap: var(--space-2); justify-content: space-between; }
|
|
9642
|
+
.ds-247420 .ds-ep-inforow-label { color: var(--fg-muted); font-size: var(--fs-sm); flex-shrink: 0; }
|
|
9643
|
+
.ds-247420 .ds-ep-inforow-value { font-family: var(--font-mono, monospace); font-size: var(--fs-sm); text-align: right; word-break: break-word; }
|
|
9644
|
+
|
|
9645
|
+
/* BatchProgressLabel */
|
|
9646
|
+
.ds-247420 .ds-ep-batchprogress { font-variant-numeric: tabular-nums; }
|
|
9647
|
+
|
|
9540
9648
|
/* Interaction primitives — drag/drop + keyboard shortcuts */
|
|
9541
9649
|
.ds-247420 .ds-reorderable { display: flex; gap: var(--space-1, 4px); }
|
|
9542
9650
|
.ds-247420 .ds-reorderable-vertical { flex-direction: column; }
|