@robota-sdk/agent-ui-web 3.3.0 → 3.3.1
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/CHANGELOG.md +11 -0
- package/dist/index.cjs +81 -81
- package/dist/index.d.cts +61 -61
- package/dist/index.d.ts +61 -61
- package/dist/index.js +81 -81
- package/dist/index.js.map +1 -1
- package/docs/SPEC.md +1 -1
- package/package.json +6 -6
- package/src/client/ws-session-client.ts +1 -1
- package/src/clipboard.ts +2 -2
- package/src/components/AgentActivityPanel.tsx +8 -8
- package/src/components/AgentSwitcherSheet.tsx +2 -2
- package/src/components/Composer.tsx +27 -27
- package/src/components/ConversationView.tsx +14 -14
- package/src/components/Dialog.tsx +6 -6
- package/src/components/DiffLines.tsx +2 -2
- package/src/components/ExecutionDetailSheet.tsx +1 -1
- package/src/components/HelpSheet.tsx +2 -2
- package/src/components/PermissionPrompt.tsx +22 -22
- package/src/components/PopupMenu.tsx +1 -1
- package/src/components/ProjectPanel.tsx +2 -2
- package/src/components/SessionMonitor.tsx +1 -1
- package/src/components/SessionSidebar.tsx +8 -8
- package/src/components/SessionSurface.tsx +18 -18
- package/src/components/SessionSurfaceChrome.tsx +8 -8
- package/src/components/SettingsGeneralSection.tsx +1 -1
- package/src/components/SettingsMcpSection.tsx +1 -1
- package/src/components/SettingsPermissionsSection.tsx +1 -1
- package/src/components/SettingsPluginsSection.tsx +1 -1
- package/src/components/SettingsProvidersSection.tsx +3 -3
- package/src/components/SettingsScreen.tsx +3 -3
- package/src/components/StatusControls.tsx +12 -12
- package/src/components/composer-attachments.ts +5 -5
- package/src/driver-labels.ts +3 -3
- package/src/execution-entry-status.ts +1 -1
- package/src/goal-view.ts +1 -1
- package/src/hooks/command-menu.ts +2 -2
- package/src/hooks/excluded-commands.ts +2 -2
- package/src/hooks/prompt-state.ts +4 -4
- package/src/hooks/server-message-handling.ts +11 -11
- package/src/hooks/session-client-types.ts +29 -29
- package/src/hooks/ui-intent-state.ts +6 -6
- package/src/hooks/use-agent-switcher-state.ts +2 -2
- package/src/hooks/use-media-query.ts +1 -1
- package/src/hooks/use-model-list.ts +1 -1
- package/src/hooks/use-project-panel.ts +1 -1
- package/src/hooks/use-schedules-state.ts +4 -4
- package/src/hooks/use-session-directory.ts +4 -4
- package/src/hooks/use-settings-state.ts +4 -4
- package/src/hooks/useSessionClient.ts +44 -44
- package/src/index.ts +5 -5
- package/src/schedule-view.ts +1 -1
- package/src/styles/surface.css +1 -1
package/dist/index.js
CHANGED
|
@@ -101,12 +101,12 @@ function askResponse(id, response) {
|
|
|
101
101
|
//#endregion
|
|
102
102
|
//#region src/hooks/ui-intent-state.ts
|
|
103
103
|
/**
|
|
104
|
-
*
|
|
104
|
+
* How the GUI surface answers a `ui_intent`.
|
|
105
105
|
*
|
|
106
106
|
* The server requester-routes `ui_intent`, so everything that arrives here was asked for by THIS
|
|
107
107
|
* surface and must be answered visibly. An intent with a GUI screen opens it (the session picker is
|
|
108
|
-
* the session sidebar
|
|
109
|
-
*
|
|
108
|
+
* the session sidebar; settings and the plugin manager both open the Settings modal; the agent
|
|
109
|
+
* switcher is a sheet). An intent whose
|
|
110
110
|
* command is on the GUI's exclusion list never reaches here at all (`excluded-commands.ts` catches it
|
|
111
111
|
* before the session is asked) — this module only answers a screen this HOST cannot back even though
|
|
112
112
|
* the GUI knows how to render it (the session picker, when the host cannot list sessions), or a kind
|
|
@@ -136,7 +136,7 @@ function guiScreenForUiIntent(intent) {
|
|
|
136
136
|
* Describe one intent for this surface: a plain, honest sentence for a screen this HOST cannot back
|
|
137
137
|
* right now (a session picker with no session list), the `/theme` exclusion this intent defensively
|
|
138
138
|
* reuses, or an unrecognized future kind. Never called for `show-settings`, `show-plugin-manager` or
|
|
139
|
-
* `show-agent-switcher` — all three always have a screen now
|
|
139
|
+
* `show-agent-switcher` — all three always have a screen now —
|
|
140
140
|
* but their cases are left out of this switch rather than kept as dead branches that would claim
|
|
141
141
|
* otherwise; an unknown wire-level kind (a newer host) still yields an explicit notice naming the raw
|
|
142
142
|
* kind, so the "never a silent no-op" floor holds for future intents too.
|
|
@@ -240,7 +240,7 @@ function createWsSessionClient(url, callbacks) {
|
|
|
240
240
|
//#endregion
|
|
241
241
|
//#region src/hooks/server-message-handling.ts
|
|
242
242
|
/**
|
|
243
|
-
*
|
|
243
|
+
* Exhaustive ownership for every decoded server variant on the shared GUI surface.
|
|
244
244
|
* Adding a wire variant without deciding its GUI disposition is a compile error instead of a silent
|
|
245
245
|
* reducer omission. `intentionally-not-rendered` is an explicit disposition for server-side query
|
|
246
246
|
* snapshots and domain events that this session/usage surface does not currently visualize.
|
|
@@ -319,10 +319,10 @@ function nextRequestId$7(prefix) {
|
|
|
319
319
|
return `${prefix}_${requestCounter$7}_${Date.now()}`;
|
|
320
320
|
}
|
|
321
321
|
/**
|
|
322
|
-
*
|
|
322
|
+
* The agent switcher sheet's own correlated request/response state, the same
|
|
323
323
|
* shape `useSettingsState` already uses for `get-settings`/`update-settings` — independent from the
|
|
324
324
|
* chat conversation, because choosing an agent is answered by the sheet's own confirmation line
|
|
325
|
-
* ("Default agent: Explore"), never a conversation card
|
|
325
|
+
* ("Default agent: Explore"), never a conversation card.
|
|
326
326
|
*
|
|
327
327
|
* `openAgentSwitcher` re-fetches every time (`/agent` bare, or reopening after a close), so a change
|
|
328
328
|
* made elsewhere reaches an open-then-reopened sheet, matching `useSettingsState`'s own reasoning.
|
|
@@ -684,7 +684,7 @@ function nextRequestId$4() {
|
|
|
684
684
|
return `model_list_${requestCounter$4}_${Date.now()}`;
|
|
685
685
|
}
|
|
686
686
|
/**
|
|
687
|
-
*
|
|
687
|
+
* The model list the model control's pop-up menu is built from, requested on
|
|
688
688
|
* demand (`list-models`) and correlated by `requestId` (mirrors `usePersonalUsageState`'s pattern) so
|
|
689
689
|
* a reply to an older request — one already superseded by a newer one, e.g. across a reconnect — is
|
|
690
690
|
* never applied over the list a still-open menu is currently showing.
|
|
@@ -809,7 +809,7 @@ function nextRequestId$2(prefix) {
|
|
|
809
809
|
return `${prefix}_${requestCounter$2}_${Date.now()}`;
|
|
810
810
|
}
|
|
811
811
|
/**
|
|
812
|
-
*
|
|
812
|
+
* The Project panel's correlated request/response state (status, one file's diff, project
|
|
813
813
|
* memory), independent from chat state. Mirrors `usePersonalUsageState`'s shape: a status per read, a
|
|
814
814
|
* `requestId` ref that drops a stale reply (a diff request superseded by clicking another file before
|
|
815
815
|
* the first answered), and a request function per read.
|
|
@@ -895,15 +895,15 @@ function useProjectPanelState(send) {
|
|
|
895
895
|
//#endregion
|
|
896
896
|
//#region src/hooks/use-schedules-state.ts
|
|
897
897
|
/**
|
|
898
|
-
*
|
|
898
|
+
* The Agents panel's "Scheduled" group. No `requestId` correlates
|
|
899
899
|
* `get-background-tasks`/`background_tasks` (the message carries none — see `wire-messages.ts`), so
|
|
900
900
|
* this owns that whole message type for the GUI: every reply is a full refresh of the roster,
|
|
901
901
|
* filtered server-side to `kind: 'scheduled'` (the same tasks `/schedule list` shows).
|
|
902
902
|
*
|
|
903
903
|
* Pause/resume send the SAME `command` text `/schedule pause <id>` / `/schedule resume <id>` runs.
|
|
904
904
|
* Delete sends the existing `cancel-background-task` write — the same one Stop already uses for any
|
|
905
|
-
* other background-task kind
|
|
906
|
-
* one is permanent (distinct from the reversible pause)
|
|
905
|
+
* other background-task kind — since a schedule IS a background task, and cancelling
|
|
906
|
+
* one is permanent (distinct from the reversible pause).
|
|
907
907
|
*/
|
|
908
908
|
function useSchedulesState(send) {
|
|
909
909
|
const [scheduledTasks, setScheduledTasks] = useState([]);
|
|
@@ -981,7 +981,7 @@ function keepsSessionsLive(listing) {
|
|
|
981
981
|
return listing.sessions.some((row) => typeof row.live === "boolean");
|
|
982
982
|
}
|
|
983
983
|
/**
|
|
984
|
-
*
|
|
984
|
+
* A desktop Reconnect reloads the page, which loses every bit of JS state — so the
|
|
985
985
|
* session to go back to cannot live in a ref like `lastSwitchedIdRef` below. `sessionStorage` survives
|
|
986
986
|
* the reload (and only that tab), so it carries the id across it. Namespaced so a page hosting other
|
|
987
987
|
* state under the same origin does not collide.
|
|
@@ -1018,7 +1018,7 @@ function initialSidebarOpen() {
|
|
|
1018
1018
|
return window.matchMedia("(min-width: 768px)").matches;
|
|
1019
1019
|
}
|
|
1020
1020
|
/**
|
|
1021
|
-
*
|
|
1021
|
+
* The host's session list beside the conversation: requests correlated by id (the latest
|
|
1022
1022
|
* wins), the start/switch commands, and whether the sidebar is shown.
|
|
1023
1023
|
*
|
|
1024
1024
|
* A host that keeps sessions live binds each connection to its own session and puts a new connection
|
|
@@ -1137,12 +1137,12 @@ function nextRequestId(prefix) {
|
|
|
1137
1137
|
return `${prefix}_${requestCounter}_${Date.now()}`;
|
|
1138
1138
|
}
|
|
1139
1139
|
/**
|
|
1140
|
-
*
|
|
1140
|
+
* The Settings screen's own correlated request/response state, independent from chat
|
|
1141
1141
|
* state — the same shape `usePersonalUsageState` already uses for `get-personal-usage-report`.
|
|
1142
1142
|
*
|
|
1143
1143
|
* `openSettings` re-fetches every time (opening from the gear, `/settings`, or reopening after a
|
|
1144
1144
|
* close): the simplest correct way for a change made elsewhere — another typed command, another
|
|
1145
|
-
* client — to reach an open-then-reopened screen
|
|
1145
|
+
* client — to reach an open-then-reopened screen.
|
|
1146
1146
|
*
|
|
1147
1147
|
* A failed UPDATE never touches `settingsSnapshot`, so a control bound to it shows the value it had
|
|
1148
1148
|
* before the write — "a failed write leaves the control at its previous value" — while
|
|
@@ -1210,7 +1210,7 @@ function useSettingsState(send) {
|
|
|
1210
1210
|
//#endregion
|
|
1211
1211
|
//#region src/hooks/useSessionClient.ts
|
|
1212
1212
|
/**
|
|
1213
|
-
*
|
|
1213
|
+
* The transport-neutral session reducer for the GUI presentation layer. Reconstructs conversation
|
|
1214
1214
|
* state from the wire `TServerMessage` stream, independent of HOW the bytes arrive (a `TMakeSessionClient`
|
|
1215
1215
|
* factory over `{onMessage, onStatusChange}`). The core is **generic over its status type** (default
|
|
1216
1216
|
* `TConnectionStatus`) so a surface with extra connection states (e.g. the browser WebRTC surface) can widen it
|
|
@@ -1250,7 +1250,7 @@ function pushToolStartSegment(segments, tool) {
|
|
|
1250
1250
|
}];
|
|
1251
1251
|
}
|
|
1252
1252
|
/**
|
|
1253
|
-
*
|
|
1253
|
+
* Attribute a `tool_end` to its call by executionId first — two same-named parallel calls
|
|
1254
1254
|
* can finish out of start order, and matching by name+running alone would close whichever running
|
|
1255
1255
|
* call of that name is found first, not the one that actually finished. Falls back to name+running
|
|
1256
1256
|
* only when the event carries no executionId (legacy fixtures / hosts that predate it).
|
|
@@ -1307,7 +1307,7 @@ function applyToolEndToSegments(segments, state) {
|
|
|
1307
1307
|
});
|
|
1308
1308
|
}
|
|
1309
1309
|
/**
|
|
1310
|
-
*
|
|
1310
|
+
* The files an Edit/Write call touched during the turn, aggregated by path — the "Changed
|
|
1311
1311
|
* files" row's data. `diffLines` keeps the LATEST edit's diff (what a click on the file opens); the
|
|
1312
1312
|
* +/- counts sum every edit to that path within the turn.
|
|
1313
1313
|
*/
|
|
@@ -1340,7 +1340,7 @@ function activeToolFromHistoricalState(state) {
|
|
|
1340
1340
|
};
|
|
1341
1341
|
}
|
|
1342
1342
|
/**
|
|
1343
|
-
*
|
|
1343
|
+
* Turns the server's chronological `IHistoryDisplaySegment[]` (a reload/reconnect/resume
|
|
1344
1344
|
* replay) into the SAME `TConversationEntry[]` shape a live turn's `finishTurn` produces — reusing its
|
|
1345
1345
|
* exact segment-accumulation and "Changed files" logic, so the two paths cannot draw the turn/grouping
|
|
1346
1346
|
* boundary differently. A `'text', role: 'user'` segment is both its own entry AND the turn boundary:
|
|
@@ -1417,7 +1417,7 @@ function useSessionClient(makeClient) {
|
|
|
1417
1417
|
}, []);
|
|
1418
1418
|
/**
|
|
1419
1419
|
* End the running turn: its text and tool calls stay in the conversation as SEPARATE entries, in
|
|
1420
|
-
* the order they happened
|
|
1420
|
+
* the order they happened — not one tools-block grouped ahead of a single merged reply.
|
|
1421
1421
|
*/
|
|
1422
1422
|
const finishTurn = useCallback((toolStatus) => {
|
|
1423
1423
|
const segments = turnSegmentsRef.current;
|
|
@@ -1494,7 +1494,7 @@ function useSessionClient(makeClient) {
|
|
|
1494
1494
|
if (requestId !== void 0) sessionChangeRequestIdsRef.current.delete(requestId);
|
|
1495
1495
|
}, []);
|
|
1496
1496
|
/**
|
|
1497
|
-
*
|
|
1497
|
+
* Run a command the same way `send` does, but flag it as controls-triggered —
|
|
1498
1498
|
* the model/mode/effort pop-up menus use this instead of `send` directly, so their change is
|
|
1499
1499
|
* confirmed by the control's own label (via the `get-status` refresh `command_result` already
|
|
1500
1500
|
* triggers) rather than a conversation card. A typed `/command` still goes through plain `send`
|
|
@@ -1503,7 +1503,7 @@ function useSessionClient(makeClient) {
|
|
|
1503
1503
|
* `TClientMessage` — giving `command` a `requestId` there would let ANY caller of `send` opt a typed
|
|
1504
1504
|
* command into silence, which is exactly what this must not allow.
|
|
1505
1505
|
*
|
|
1506
|
-
*
|
|
1506
|
+
* `send` also intercepts `help` and an excluded command before either reaches the wire —
|
|
1507
1507
|
* for `help` that opens the sheet regardless of how it was asked for, but an excluded command's
|
|
1508
1508
|
* interception adds a VISIBLE info card, defeating the silence this function promises. Every current
|
|
1509
1509
|
* caller (model/mode/effort) names neither, so this is dormant, not exercised — a future caller must
|
|
@@ -2130,7 +2130,7 @@ function resolveClientRuntimeHost(environment) {
|
|
|
2130
2130
|
//#endregion
|
|
2131
2131
|
//#region src/clipboard.ts
|
|
2132
2132
|
/**
|
|
2133
|
-
* Copies `text` to the clipboard for the "Copy code" / "Copy message" buttons
|
|
2133
|
+
* Copies `text` to the clipboard for the "Copy code" / "Copy message" buttons: the async
|
|
2134
2134
|
* Clipboard API first, and — for a page that denies it (no permission granted yet, a browser that
|
|
2135
2135
|
* never implements it, or a context that disallows it outright) — a same-tick `execCommand('copy')`
|
|
2136
2136
|
* fallback through a throwaway, invisible textarea. Rejects only once both have failed, so a caller
|
|
@@ -2177,7 +2177,7 @@ const FEEDBACK_MS = 2e3;
|
|
|
2177
2177
|
/**
|
|
2178
2178
|
* The "Copy code" / "Copy message" buttons' shared feedback state: `copied` or `error` for a couple of
|
|
2179
2179
|
* seconds, then back to `idle` — drives both the button's own label swap and an `aria-live` region a
|
|
2180
|
-
* screen reader announces without moving focus
|
|
2180
|
+
* screen reader announces without moving focus.
|
|
2181
2181
|
*/
|
|
2182
2182
|
function useCopyFeedback() {
|
|
2183
2183
|
const [status, setStatus] = useState("idle");
|
|
@@ -2197,7 +2197,7 @@ function useCopyFeedback() {
|
|
|
2197
2197
|
//#endregion
|
|
2198
2198
|
//#region src/driver-labels.ts
|
|
2199
2199
|
/**
|
|
2200
|
-
*
|
|
2200
|
+
* Plain, human labels for a driver id, and whether one names the SAME KIND
|
|
2201
2201
|
* of surface as this connection's own. Shared by every place the GUI shows who drove a turn (a
|
|
2202
2202
|
* message, a permission/ask prompt): the person's own input never carries a label, and a turn from a
|
|
2203
2203
|
* different kind of surface does — in words, never a raw id.
|
|
@@ -2251,7 +2251,7 @@ function humanDriverLabel(driverId) {
|
|
|
2251
2251
|
}
|
|
2252
2252
|
/**
|
|
2253
2253
|
* The ready-to-render attribution phrase: "from <label>" for a co-driver, or "Automatic — loop"
|
|
2254
|
-
* for the agent's own wake-ups — "from automatic" reads as a typo, not a source.
|
|
2254
|
+
* for the agent's own wake-ups — "from automatic" reads as a typo, not a source. Every
|
|
2255
2255
|
* `AGENT_DRIVER_ID` turn is, today, a loop's own injected iteration (fixed or self-paced —
|
|
2256
2256
|
* `requestWakeup` in `interactive-session.ts` is the one place that stamps this id); if a
|
|
2257
2257
|
* differently-sourced agent-wakeup turn is ever added, this label needs to name that source
|
|
@@ -2596,7 +2596,7 @@ function AppViewFrame({ tool, host, sandboxUrl, viewKey, sourceId }) {
|
|
|
2596
2596
|
//#endregion
|
|
2597
2597
|
//#region src/components/DiffLines.tsx
|
|
2598
2598
|
/**
|
|
2599
|
-
*
|
|
2599
|
+
* Renders a unified diff already built server-side (the ONE diff builder, in
|
|
2600
2600
|
* `interactive-session-streaming.ts`) — a tool row's expanded result and the Edit/Write permission
|
|
2601
2601
|
* prompt both show the SAME diff through this, so approving a change and reviewing it afterwards
|
|
2602
2602
|
* look identical. This component never computes a diff itself, only displays one.
|
|
@@ -2661,7 +2661,7 @@ function nodeText(node) {
|
|
|
2661
2661
|
return "";
|
|
2662
2662
|
}
|
|
2663
2663
|
/**
|
|
2664
|
-
* "Copy code" / "Copy message"
|
|
2664
|
+
* "Copy code" / "Copy message": shows on hover and keyboard focus, always on touch (no
|
|
2665
2665
|
* `hover` capability to gate on there). The transient "Copied" / "Couldn't copy" feedback lives in a
|
|
2666
2666
|
* visually hidden `aria-live` region that is a SIBLING of the button, not a descendant of it — ARIA
|
|
2667
2667
|
* name computation only ever looks at an element's own subtree, so a live region placed anywhere
|
|
@@ -2705,7 +2705,7 @@ function CopyButton({ label, getText, className = "" }) {
|
|
|
2705
2705
|
/**
|
|
2706
2706
|
* A fenced code block: unwrapped and horizontally scrollable (a visible scrollbar via
|
|
2707
2707
|
* `.gui-code-scroll`, `tabIndex=0` and a name so a keyboard user without a mouse wheel can still reach
|
|
2708
|
-
* the rest of a long line), with its own "Copy code" button revealed on hover/focus
|
|
2708
|
+
* the rest of a long line), with its own "Copy code" button revealed on hover/focus.
|
|
2709
2709
|
*/
|
|
2710
2710
|
function CodeBlockRenderer({ children }) {
|
|
2711
2711
|
const text = nodeText(children).replace(/\n$/, "");
|
|
@@ -2849,7 +2849,7 @@ function ToolIcon({ name, className }) {
|
|
|
2849
2849
|
return /* @__PURE__ */ jsx(Wrench, { ...props });
|
|
2850
2850
|
}
|
|
2851
2851
|
/**
|
|
2852
|
-
*
|
|
2852
|
+
* A long path's directory is shortened FROM THE LEFT so the filename — the part someone
|
|
2853
2853
|
* actually needs to recognise — is never the part that gets cut.
|
|
2854
2854
|
*/
|
|
2855
2855
|
const PATH_DISPLAY_MAX_CHARS = 52;
|
|
@@ -3045,7 +3045,7 @@ const COMMAND_TONE = {
|
|
|
3045
3045
|
}
|
|
3046
3046
|
};
|
|
3047
3047
|
/**
|
|
3048
|
-
* A slash command's outcome, where it was typed: line breaks kept, long output folded.
|
|
3048
|
+
* A slash command's outcome, where it was typed: line breaks kept, long output folded. The
|
|
3049
3049
|
* card itself still marks the command (the `/name` header stays monospace, a fixed-width label rather
|
|
3050
3050
|
* than prose), but its BODY reads as normal text — proportional, normal size — unless it is actually a
|
|
3051
3051
|
* long listing (the same measure that decides whether to fold it: many raw lines, or tall once
|
|
@@ -3152,7 +3152,7 @@ function ToolGroup({ tools }) {
|
|
|
3152
3152
|
]
|
|
3153
3153
|
});
|
|
3154
3154
|
}
|
|
3155
|
-
/**
|
|
3155
|
+
/** A turn that changed files ends with this compact row — a click opens that file's diff. */
|
|
3156
3156
|
function ChangedFilesRow({ files }) {
|
|
3157
3157
|
const [openPath, setOpenPath] = useState(null);
|
|
3158
3158
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -3207,7 +3207,7 @@ function ConversationView({ messages, activeTools, streamingText, isThinking, ow
|
|
|
3207
3207
|
setIsAtBottomState(value);
|
|
3208
3208
|
};
|
|
3209
3209
|
/**
|
|
3210
|
-
*
|
|
3210
|
+
* Follows new content only while the person is already at the bottom: streaming text, a
|
|
3211
3211
|
* finished message, a tool starting or finishing. Expanding something already on screen (a tool row,
|
|
3212
3212
|
* a folded command card) touches none of these, so it never causes a jump — the effect simply does
|
|
3213
3213
|
* not run for it. An instant jump rather than a smooth one, so it can never itself be mistaken by
|
|
@@ -3393,7 +3393,7 @@ const SECONDARY_BUTTON$2 = `${BUTTON$2} bg-raised text-foreground hover:bg-hover
|
|
|
3393
3393
|
const DESTRUCTIVE_BUTTON$2 = `${BUTTON$2} bg-destructive text-destructive-foreground hover:opacity-90`;
|
|
3394
3394
|
/**
|
|
3395
3395
|
* A title, a body, Cancel focused by default, and a confirm button that can be styled destructive.
|
|
3396
|
-
*
|
|
3396
|
+
* `destructive` also picks the dialog's ARIA role — `"alertdialog"` when it is (an action
|
|
3397
3397
|
* that demands a decision before anything else, e.g. deleting a session), `"dialog"` otherwise.
|
|
3398
3398
|
*/
|
|
3399
3399
|
function ConfirmDialog({ open, title, body, confirmLabel, cancelLabel = "Cancel", destructive = false, onConfirm, onCancel, restoreFocusTo }) {
|
|
@@ -3689,7 +3689,7 @@ function AgentCard({ entry, selected, onOpen, onStop }) {
|
|
|
3689
3689
|
]
|
|
3690
3690
|
});
|
|
3691
3691
|
}
|
|
3692
|
-
/**
|
|
3692
|
+
/** One row of the "Scheduled" group — what it runs, when next, status, controls. */
|
|
3693
3693
|
function ScheduleCard({ task, onPause, onResume, onRequestDelete }) {
|
|
3694
3694
|
const isPaused = task.status === "paused";
|
|
3695
3695
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -3737,7 +3737,7 @@ function ScheduleCard({ task, onPause, onResume, onRequestDelete }) {
|
|
|
3737
3737
|
]
|
|
3738
3738
|
});
|
|
3739
3739
|
}
|
|
3740
|
-
/**
|
|
3740
|
+
/** The current goal — objective, plain-word status, and Cancel. */
|
|
3741
3741
|
function GoalCard({ goal, onCancel }) {
|
|
3742
3742
|
return /* @__PURE__ */ jsxs("div", {
|
|
3743
3743
|
className: "rounded-xl bg-card px-3.5 py-3",
|
|
@@ -3824,12 +3824,12 @@ function AttentionTag({ attention, status, deniedToolCalls }) {
|
|
|
3824
3824
|
//#endregion
|
|
3825
3825
|
//#region src/components/AgentSwitcherSheet.tsx
|
|
3826
3826
|
/**
|
|
3827
|
-
*
|
|
3827
|
+
* The agent switcher: `show-agent-switcher` (`/agent` with no args) opens this
|
|
3828
3828
|
* sheet instead of the surface's old "not available" line. It lists every agent this session can
|
|
3829
3829
|
* run — name, one-line description, and where each is defined in plain words — with the current one
|
|
3830
3830
|
* checked. Choosing a row runs the SAME path as typing `/agent <name>` (a plain `command`, wired in
|
|
3831
3831
|
* `useAgentSwitcherState`); the reply is this sheet's own confirmation line, never a conversation
|
|
3832
|
-
* card
|
|
3832
|
+
* card.
|
|
3833
3833
|
*/
|
|
3834
3834
|
function AgentSwitcherSheet({ state }) {
|
|
3835
3835
|
if (!state.agentSwitcherOpen) return null;
|
|
@@ -3911,7 +3911,7 @@ function isEditableElement(element) {
|
|
|
3911
3911
|
return element !== null && element.matches("input, textarea, select, [contenteditable]");
|
|
3912
3912
|
}
|
|
3913
3913
|
/**
|
|
3914
|
-
*
|
|
3914
|
+
* The one destructive option in the provider profile action menu (the ask built
|
|
3915
3915
|
* by `askProviderProfileAction` in `agent-command`'s `provider-command-profile.ts`, `request.id`
|
|
3916
3916
|
* `'provider-profile-action'`): "Delete" answers to no digit and renders apart from Switch/Edit/Test/
|
|
3917
3917
|
* Duplicate, styled destructive — the exact audited bug ("Delete looks the same as Switch, answers to
|
|
@@ -4326,7 +4326,7 @@ const BUTTON$1 = "inline-flex items-center gap-2 rounded-lg px-3.5 py-1.5 text-[
|
|
|
4326
4326
|
const PRIMARY_BUTTON$1 = `${BUTTON$1} bg-primary text-primary-foreground hover:opacity-90`;
|
|
4327
4327
|
const SECONDARY_BUTTON$1 = `${BUTTON$1} bg-raised text-foreground hover:bg-hover`;
|
|
4328
4328
|
const GHOST_BUTTON = `${BUTTON$1} text-muted-foreground hover:bg-hover hover:text-foreground`;
|
|
4329
|
-
/**
|
|
4329
|
+
/** A destructive ask option (Delete) — visually distinct from Switch/Edit/Test/Duplicate. */
|
|
4330
4330
|
const DESTRUCTIVE_BUTTON$1 = `${BUTTON$1} bg-destructive/12 text-destructive hover:bg-destructive/20`;
|
|
4331
4331
|
/**
|
|
4332
4332
|
* The free-text answer to one ask prompt. Its value lives ONLY here, in a component the parent
|
|
@@ -4352,7 +4352,7 @@ const FreeTextField = forwardRef(function FreeTextField({ masked, placeholder, a
|
|
|
4352
4352
|
onCancel();
|
|
4353
4353
|
};
|
|
4354
4354
|
/**
|
|
4355
|
-
* A key that reaches the field types into it — the prompt's own shortcuts never see it
|
|
4355
|
+
* A key that reaches the field types into it — the prompt's own shortcuts never see it.
|
|
4356
4356
|
* The Enter that only finishes an IME composition (Korean/Japanese/Chinese) must not submit the
|
|
4357
4357
|
* still-uncommitted text — `isComposing` (or `keyCode` 229, on browsers that predate it) marks that
|
|
4358
4358
|
* Enter; the keystroke is left alone so the browser can commit the composition normally.
|
|
@@ -4437,7 +4437,7 @@ function ToolArgs({ args }) {
|
|
|
4437
4437
|
});
|
|
4438
4438
|
}
|
|
4439
4439
|
/**
|
|
4440
|
-
*
|
|
4440
|
+
* A Shell request shows just its command — and the working directory only when the server
|
|
4441
4441
|
* says it differs from the workspace — instead of every raw argument.
|
|
4442
4442
|
*/
|
|
4443
4443
|
function ShellCommandPreview({ command, cwd }) {
|
|
@@ -4472,7 +4472,7 @@ function formatUpdatedAt(updatedAt, now = Date.now()) {
|
|
|
4472
4472
|
return new Date(at).toISOString().slice(0, 10);
|
|
4473
4473
|
}
|
|
4474
4474
|
/**
|
|
4475
|
-
* A session's title
|
|
4475
|
+
* A session's title: its name, else its stable first-message title, else "New session".
|
|
4476
4476
|
* Never `preview` — that field is the raw latest reply, which is what changes every turn and is what
|
|
4477
4477
|
* this title exists to stop showing.
|
|
4478
4478
|
*/
|
|
@@ -4503,12 +4503,12 @@ function lockedSessionsLine(count) {
|
|
|
4503
4503
|
return count === 1 ? "1 saved session is sealed and not listed here." : `${count} saved sessions are sealed and not listed here.`;
|
|
4504
4504
|
}
|
|
4505
4505
|
/**
|
|
4506
|
-
*
|
|
4506
|
+
* This workspace's sessions on the left of the conversation, as in Claude Code Desktop:
|
|
4507
4507
|
* start a new one, or click another to make it current. Rows the host could not read are listed,
|
|
4508
4508
|
* disabled, so a damaged session never looks deleted. A refused switch comes back as a notice.
|
|
4509
4509
|
* A host that keeps several sessions live marks the rows running now and counts who else is on them.
|
|
4510
4510
|
*
|
|
4511
|
-
*
|
|
4511
|
+
* Each row also carries a "More" menu (or right-click) with Rename (inline) and Delete
|
|
4512
4512
|
* (confirmed). Rename on the current row reuses the `/rename` command, which also updates the live
|
|
4513
4513
|
* session's own name and title bar; rename on any other row writes the stored record directly.
|
|
4514
4514
|
*/
|
|
@@ -5392,10 +5392,10 @@ function ProductWordmark({ surface }) {
|
|
|
5392
5392
|
//#endregion
|
|
5393
5393
|
//#region src/hooks/command-menu.ts
|
|
5394
5394
|
/**
|
|
5395
|
-
*
|
|
5395
|
+
* What the composer's `/` menu offers for a draft. Pure, so the matching is testable without
|
|
5396
5396
|
* rendering: the TUI's autocomplete offers commands and skills the same way.
|
|
5397
5397
|
*
|
|
5398
|
-
*
|
|
5398
|
+
* A command the GUI cannot run at all (`isExcludedCommand`) never appears here — there is
|
|
5399
5399
|
* nothing useful for choosing it to do, and typing it by hand is answered separately (see
|
|
5400
5400
|
* `excluded-commands.ts`, `useSessionClient.ts`). Commands are listed before skills, each in its own
|
|
5401
5401
|
* group, so the menu can head them "Commands" and "Skills".
|
|
@@ -5435,7 +5435,7 @@ function byName(a, b) {
|
|
|
5435
5435
|
//#endregion
|
|
5436
5436
|
//#region src/components/composer-attachments.ts
|
|
5437
5437
|
/**
|
|
5438
|
-
*
|
|
5438
|
+
* Pure logic behind the composer's attach button and drag-and-drop, kept apart from
|
|
5439
5439
|
* `Composer.tsx` so it is testable without React or a DOM.
|
|
5440
5440
|
*
|
|
5441
5441
|
* The runtime resolves `@relative/path` references already (`prompt-file-reference-resolver.ts` in
|
|
@@ -5451,7 +5451,7 @@ function byName(a, b) {
|
|
|
5451
5451
|
const MAX_ATTACHMENT_FILE_BYTES = 64 * 1024;
|
|
5452
5452
|
/** Mirrors `DEFAULT_MAX_TOTAL_BYTES` in the same resolver. */
|
|
5453
5453
|
const MAX_ATTACHMENT_TOTAL_BYTES = 256 * 1024;
|
|
5454
|
-
/** Shown whenever a file cannot be resolved to a path inside the workspace — rule 3
|
|
5454
|
+
/** Shown whenever a file cannot be resolved to a path inside the workspace — rule 3. */
|
|
5455
5455
|
const ATTACHMENT_NO_PATH_NOTICE = "Only files inside this project folder can be attached.";
|
|
5456
5456
|
/** Shown once the running count of attachments would pass `MAX_ATTACHMENT_COUNT`. */
|
|
5457
5457
|
const ATTACHMENT_COUNT_LIMIT_NOTICE = `You can attach up to 8 files to one message.`;
|
|
@@ -5634,11 +5634,11 @@ function evaluateCandidateFile(file, workspacePath, existingTotalBytes, existing
|
|
|
5634
5634
|
};
|
|
5635
5635
|
}
|
|
5636
5636
|
/**
|
|
5637
|
-
* Inserts each chip as the runtime's existing `@relative/path` reference syntax — rule 2
|
|
5637
|
+
* Inserts each chip as the runtime's existing `@relative/path` reference syntax — rule 2.
|
|
5638
5638
|
* Always prefixed with `./`: the parser's `isPathLikeReference` only treats a token as a file
|
|
5639
5639
|
* reference when it has a `.` somewhere or an explicit relative-path prefix, so a dotless name like
|
|
5640
5640
|
* `Makefile`, `Dockerfile` or `LICENSE` — common, useful, and never refused for lacking one — would
|
|
5641
|
-
* otherwise be sent as plain text with no file and no diagnostic
|
|
5641
|
+
* otherwise be sent as plain text with no file and no diagnostic. `./` makes
|
|
5642
5642
|
* every path path-like regardless of its name, and the resolver collapses it back to the plain
|
|
5643
5643
|
* relative path before use.
|
|
5644
5644
|
*/
|
|
@@ -5650,7 +5650,7 @@ function buildPromptWithAttachments(prompt, attachments) {
|
|
|
5650
5650
|
//#endregion
|
|
5651
5651
|
//#region src/components/PopupMenu.tsx
|
|
5652
5652
|
/**
|
|
5653
|
-
* A HIG pop-up menu
|
|
5653
|
+
* A HIG pop-up menu: `role="menu"`, its choices as `menuitemradio` with `aria-checked`
|
|
5654
5654
|
* (or `menuitem` for a plain action, e.g. "Manage providers…"), grouped into optional labelled
|
|
5655
5655
|
* sections. Arrow keys move a roving focus, Enter/Space activates the focused item, and Esc — or a
|
|
5656
5656
|
* click outside — closes the menu and returns focus to the trigger that opened it, so the person
|
|
@@ -5766,7 +5766,7 @@ function PopupMenu({ label, sections, onClose, triggerRef }) {
|
|
|
5766
5766
|
//#endregion
|
|
5767
5767
|
//#region src/components/StatusControls.tsx
|
|
5768
5768
|
/**
|
|
5769
|
-
*
|
|
5769
|
+
* The plain label for each effort selection. Kept in step with
|
|
5770
5770
|
* `EFFORT_LEVEL_LABELS` in `packages/agent-framework/src/effort/effort-resolution.ts` (which
|
|
5771
5771
|
* `agent-command`'s `/effort` re-exports and reports as its own outcome text) — this package cannot
|
|
5772
5772
|
* import that one directly (it is a Node-side package this browser bundle does not depend on), so the
|
|
@@ -5783,7 +5783,7 @@ const EFFORT_LEVEL_LABELS = {
|
|
|
5783
5783
|
xhigh: "Extra high",
|
|
5784
5784
|
max: "Maximum"
|
|
5785
5785
|
};
|
|
5786
|
-
/** The primary row
|
|
5786
|
+
/** The primary row; the rest sit under "More". */
|
|
5787
5787
|
const EFFORT_PRIMARY = [
|
|
5788
5788
|
"auto",
|
|
5789
5789
|
"low",
|
|
@@ -5806,7 +5806,7 @@ function modeLabel(catalog, mode) {
|
|
|
5806
5806
|
}
|
|
5807
5807
|
const CHIP_CLASS = "flex min-w-0 items-center gap-1.5 rounded-lg px-2 py-1 text-[13px] text-muted-foreground hover:bg-hover hover:text-foreground disabled:cursor-not-allowed disabled:opacity-60 disabled:hover:bg-transparent";
|
|
5808
5808
|
const CHIP_WARNING_CLASS = "text-warning hover:text-warning";
|
|
5809
|
-
/** One status chip that opens a `PopupMenu` HIG pop-up beneath it
|
|
5809
|
+
/** One status chip that opens a `PopupMenu` HIG pop-up beneath it. */
|
|
5810
5810
|
function StatusChip({ chipLabel, value, icon, connected, warning = false, menuLabel, sections, onOpen }) {
|
|
5811
5811
|
const [open, setOpen] = useState(false);
|
|
5812
5812
|
const triggerRef = useRef(null);
|
|
@@ -5843,7 +5843,7 @@ const ICON_PROPS = {
|
|
|
5843
5843
|
"aria-hidden": true
|
|
5844
5844
|
};
|
|
5845
5845
|
/**
|
|
5846
|
-
* Model · mode · effort, each a pop-up menu
|
|
5846
|
+
* Model · mode · effort, each a pop-up menu, and the context the conversation fills.
|
|
5847
5847
|
* Every menu applies its choice through the same command wire path the equivalent typed slash
|
|
5848
5848
|
* command uses — `onSilentCommand` — so `/model <id>`, `/mode <name>` and `/effort <value>` and a
|
|
5849
5849
|
* click in these menus can never drift apart; the control's own chip label (fed by the session's next
|
|
@@ -5976,7 +5976,7 @@ const EMPTY_DRAFT = {
|
|
|
5976
5976
|
attachments: []
|
|
5977
5977
|
};
|
|
5978
5978
|
/**
|
|
5979
|
-
*
|
|
5979
|
+
* A command whose whole job is opening a GUI screen — choosing it from the `/` menu runs
|
|
5980
5980
|
* it at once (its `ui_intent` opens the screen the normal way) instead of filling the draft and
|
|
5981
5981
|
* waiting for Enter, since there is nothing useful to type after it. `help` never reaches the
|
|
5982
5982
|
* session at all (`useSessionClient.ts`'s `send` opens the Help sheet locally). Every other command
|
|
@@ -5995,14 +5995,14 @@ function menuGroupLabel(item) {
|
|
|
5995
5995
|
return item.kind === "skill" ? "Skills" : "Commands";
|
|
5996
5996
|
}
|
|
5997
5997
|
/**
|
|
5998
|
-
*
|
|
5998
|
+
* The unsent draft survives a Chat → Usage → Chat switch (the composer unmounts), a page
|
|
5999
5999
|
* reload, and a desktop relaunch — `localStorage`, not `sessionStorage`, since only `localStorage`
|
|
6000
6000
|
* survives a closed window/tab being reopened. Namespaced (the configured draft prefix, matching
|
|
6001
6001
|
* the configured restore key in `use-session-directory.ts`) so a page hosting other state under the
|
|
6002
6002
|
* same origin does not collide. Per session id when one is known; a single fallback key before the
|
|
6003
6003
|
* first status arrives (the gap is brief and is reconciled once it does — see the effect below).
|
|
6004
6004
|
*
|
|
6005
|
-
*
|
|
6005
|
+
* The stored value is now JSON (`IStoredDraft`), not the bare text string it used to be —
|
|
6006
6006
|
* `parseStoredDraft` treats anything that does not parse as that shape (including a draft saved
|
|
6007
6007
|
* before this change shipped) as plain text with no attachments, so an old stored draft still loads.
|
|
6008
6008
|
*/
|
|
@@ -6100,7 +6100,7 @@ const ComposerInstance = forwardRef(function Composer({ draftMode = "persistent"
|
|
|
6100
6100
|
useEffect(() => {
|
|
6101
6101
|
menuOptionRefs.current[selected]?.scrollIntoView?.({ block: "nearest" });
|
|
6102
6102
|
}, [selected, menu]);
|
|
6103
|
-
/**
|
|
6103
|
+
/** Every draft change is persisted at once, so a reload or relaunch loses nothing. */
|
|
6104
6104
|
const setDraft = (value) => {
|
|
6105
6105
|
stateRef.current = {
|
|
6106
6106
|
...stateRef.current,
|
|
@@ -6109,7 +6109,7 @@ const ComposerInstance = forwardRef(function Composer({ draftMode = "persistent"
|
|
|
6109
6109
|
setDraftState(value);
|
|
6110
6110
|
if (draftMode === "persistent") writeDraft(browserNamespace, sessionIdRef.current, stateRef.current);
|
|
6111
6111
|
};
|
|
6112
|
-
/**
|
|
6112
|
+
/** Attachment chips persist alongside the text, under the same per-session draft key. */
|
|
6113
6113
|
const setAttachments = (updater) => {
|
|
6114
6114
|
const next = typeof updater === "function" ? updater(stateRef.current.attachments) : updater;
|
|
6115
6115
|
stateRef.current = {
|
|
@@ -6267,7 +6267,7 @@ const ComposerInstance = forwardRef(function Composer({ draftMode = "persistent"
|
|
|
6267
6267
|
chooseMenuItem(item);
|
|
6268
6268
|
return true;
|
|
6269
6269
|
};
|
|
6270
|
-
/**
|
|
6270
|
+
/** Edit puts the queued text back in the draft and cancels the queue behind it (the wire
|
|
6271
6271
|
* has no per-message cancel — only a whole-queue clear). Offered only when exactly one prompt is
|
|
6272
6272
|
* queued: with more than one, `cancel-queue` would still drop every one of them, but only the
|
|
6273
6273
|
* shown prompt's text is known here, so "Edit" would silently lose the rest — the row offers only
|
|
@@ -6590,7 +6590,7 @@ const RECORD_KIND_LABEL = {
|
|
|
6590
6590
|
group_summary: "Summary"
|
|
6591
6591
|
};
|
|
6592
6592
|
/**
|
|
6593
|
-
*
|
|
6593
|
+
* Opening an Agents panel entry — status, what it was asked (one line, from its
|
|
6594
6594
|
* headline), its transcript so far (paginated via `onLoadMore`) and, once it has one, its result.
|
|
6595
6595
|
* `entry === null` renders nothing; the caller owns showing/hiding this over the panel.
|
|
6596
6596
|
*/
|
|
@@ -6810,7 +6810,7 @@ const SHORTCUTS = [
|
|
|
6810
6810
|
}
|
|
6811
6811
|
];
|
|
6812
6812
|
/**
|
|
6813
|
-
* The GUI's Help sheet
|
|
6813
|
+
* The GUI's Help sheet, a `Dialog` opened by `/help` — client-only: `useSessionClient.ts`'s
|
|
6814
6814
|
* `send` catches `/help` before it ever reaches the session and opens this instead, so there is no
|
|
6815
6815
|
* round trip and nothing terminal-style to replace it with. It lists the same commands and skills the
|
|
6816
6816
|
* `/` menu offers (`commandMenuFor`, unfiltered by any query — an excluded command has no row here
|
|
@@ -7134,7 +7134,7 @@ const VIEW_LABEL = {
|
|
|
7134
7134
|
project: "Project"
|
|
7135
7135
|
};
|
|
7136
7136
|
/**
|
|
7137
|
-
*
|
|
7137
|
+
* The folder this session works in, wherever `document.title` is settable: a page (the
|
|
7138
7138
|
* browser tab) and the desktop window that loads it (Electron follows the page's title unless told
|
|
7139
7139
|
* otherwise, and nothing in `apps/agent-app/electron/main.ts` overrides it — see its own SPEC note).
|
|
7140
7140
|
* Absent while the host has not said, so a not-yet-connected surface keeps whatever title it loaded
|
|
@@ -7209,7 +7209,7 @@ function SessionTitleBar({ status, surface, title, workspace, showBrand, view, o
|
|
|
7209
7209
|
/** The column every part of the conversation shares (matches `SessionSurface`'s `COLUMN`). */
|
|
7210
7210
|
const BANNER_COLUMN = "mx-auto w-full max-w-[760px] px-6";
|
|
7211
7211
|
/**
|
|
7212
|
-
* A connection that drops after a working session
|
|
7212
|
+
* A connection that drops after a working session: a banner directly above the
|
|
7213
7213
|
* conversation — never a full-screen replacement — so the conversation and its history stay visible
|
|
7214
7214
|
* and scrollable throughout. Shown as soon as the drop happens; says nothing on the very first
|
|
7215
7215
|
* connect, because nothing has been lost yet.
|
|
@@ -7298,7 +7298,7 @@ function displayProvider(provider) {
|
|
|
7298
7298
|
return PROVIDER_DISPLAY_NAMES[provider] ?? provider[0].toUpperCase() + provider.slice(1);
|
|
7299
7299
|
}
|
|
7300
7300
|
/**
|
|
7301
|
-
* A provider/session error classified at the wire boundary
|
|
7301
|
+
* A provider/session error classified at the wire boundary as one plain sentence naming
|
|
7302
7302
|
* what happened and, where there is one, the next step — `null` for a notice this classification does
|
|
7303
7303
|
* not cover, which keeps showing its own `message` instead.
|
|
7304
7304
|
*
|
|
@@ -7488,7 +7488,7 @@ function SettingsLanguageField({ label, current, recommended, note, onChange, di
|
|
|
7488
7488
|
//#region src/components/SettingsGeneralSection.tsx
|
|
7489
7489
|
/**
|
|
7490
7490
|
* Language, output style and preset — reusing the exact choices and descriptions the runtime
|
|
7491
|
-
* already builds for `/language`, `/output-style` and `/preset
|
|
7491
|
+
* already builds for `/language`, `/output-style` and `/preset`. Never internal ids:
|
|
7492
7492
|
* every pop-up shows the runtime's human labels.
|
|
7493
7493
|
*/
|
|
7494
7494
|
function SettingsGeneralSection({ snapshot, onUpdate, onRequestPresetChange }) {
|
|
@@ -7606,7 +7606,7 @@ function ServerRow({ server, expanded, onToggleExpanded, onToggleEnabled }) {
|
|
|
7606
7606
|
});
|
|
7607
7607
|
}
|
|
7608
7608
|
/**
|
|
7609
|
-
* The MCP Servers section
|
|
7609
|
+
* The MCP Servers section: every configured server's name, where it is
|
|
7610
7610
|
* configured, its live connection status and tool count, an enable/disable switch (approve/reject),
|
|
7611
7611
|
* an expandable list of its tool names, and a section-level Reload. Adding or editing a server is
|
|
7612
7612
|
* left out — the runtime has no command path that writes MCP config (recorded in
|
|
@@ -7701,7 +7701,7 @@ function PermissionRuleRow({ rule, onRequestRemove }) {
|
|
|
7701
7701
|
});
|
|
7702
7702
|
}
|
|
7703
7703
|
/**
|
|
7704
|
-
* Default permission mode, the rules `/permissions` shows, and the sandbox switch
|
|
7704
|
+
* Default permission mode, the rules `/permissions` shows, and the sandbox switch.
|
|
7705
7705
|
* "Skip all checks" as the mode and a preset that would set it both go through
|
|
7706
7706
|
* `onRequestModeChange`'s parent-owned confirmation before anything is sent.
|
|
7707
7707
|
*/
|
|
@@ -7812,7 +7812,7 @@ function PluginRow({ plugin, canInstall, onToggleEnabled, onRequestUninstall })
|
|
|
7812
7812
|
});
|
|
7813
7813
|
}
|
|
7814
7814
|
/**
|
|
7815
|
-
* The Plugins section
|
|
7815
|
+
* The Plugins section: every installed plugin's name, description and enabled
|
|
7816
7816
|
* state, an enable/disable switch, a section-level Reload, and — local surfaces only — install and
|
|
7817
7817
|
* uninstall, since installing runs third-party code. `canInstall` (from the snapshot) mirrors the
|
|
7818
7818
|
* refusal `/plugin install|uninstall` itself gives a remote device or an observer.
|
|
@@ -7903,7 +7903,7 @@ const BUTTON = "rounded-lg px-2.5 py-1.5 text-[12.5px] font-medium transition-co
|
|
|
7903
7903
|
const PRIMARY_BUTTON = `${BUTTON} bg-primary text-primary-foreground hover:opacity-90`;
|
|
7904
7904
|
const SECONDARY_BUTTON = `${BUTTON} bg-raised text-foreground hover:bg-hover`;
|
|
7905
7905
|
const DESTRUCTIVE_BUTTON = `${BUTTON} text-destructive hover:bg-destructive/10`;
|
|
7906
|
-
/** The "Model" action: a `PopupMenu` of this ONE profile's catalog models
|
|
7906
|
+
/** The "Model" action: a `PopupMenu` of this ONE profile's catalog models. */
|
|
7907
7907
|
function ProviderModelButton({ profile, modelList, onSelect }) {
|
|
7908
7908
|
const [open, setOpen] = useState(false);
|
|
7909
7909
|
const triggerRef = useRef(null);
|
|
@@ -8009,7 +8009,7 @@ function ProviderProfileRow({ profile, modelList, onUse, onModelChange, onComman
|
|
|
8009
8009
|
});
|
|
8010
8010
|
}
|
|
8011
8011
|
/**
|
|
8012
|
-
* The configured provider profiles
|
|
8012
|
+
* The configured provider profiles, each row a plain provider name, its model's label,
|
|
8013
8013
|
* a checkmark on the one in use, and a plain connection state when one is known. Every action goes
|
|
8014
8014
|
* through the same path the equivalent command does: Use/Model apply through `state.updateSettings`
|
|
8015
8015
|
* (the same `field` patterns Preset/Mode/Sandbox use); Edit/Test/Duplicate/Add dispatch the raw
|
|
@@ -8057,7 +8057,7 @@ function SettingsProvidersSection({ snapshot, modelList, onRequestModelList, onU
|
|
|
8057
8057
|
//#endregion
|
|
8058
8058
|
//#region src/components/SettingsScreen.tsx
|
|
8059
8059
|
/**
|
|
8060
|
-
* The Settings screen's sections
|
|
8060
|
+
* The Settings screen's sections. Adding a later section (Advisor) is one entry here plus
|
|
8061
8061
|
* one component in the switch below — nothing else in this file changes.
|
|
8062
8062
|
*/
|
|
8063
8063
|
const SECTIONS = [
|
|
@@ -8083,7 +8083,7 @@ const SECTIONS = [
|
|
|
8083
8083
|
}
|
|
8084
8084
|
];
|
|
8085
8085
|
/**
|
|
8086
|
-
* A large modal sheet with a section sidebar on the left and content on the right
|
|
8086
|
+
* A large modal sheet with a section sidebar on the left and content on the right — the
|
|
8087
8087
|
* GUI's Settings screen, opened by the sidebar's gear button, the desktop app's ⌘, menu item, or
|
|
8088
8088
|
* `/settings` (its `show-settings` `ui_intent` calls `state.openSettings()`, wired in
|
|
8089
8089
|
* `useSessionClient`). Every change applies at once; there is no Save button. Esc and the Close
|
|
@@ -8358,7 +8358,7 @@ function describePluginAction(action) {
|
|
|
8358
8358
|
//#endregion
|
|
8359
8359
|
//#region src/hooks/use-media-query.ts
|
|
8360
8360
|
/**
|
|
8361
|
-
* Tracks whether `query` currently matches, reactively (
|
|
8361
|
+
* Tracks whether `query` currently matches, reactively (the narrow-window session sheet
|
|
8362
8362
|
* below `md`). A host without `window.matchMedia` (an older embedding, or a non-browser test) reports
|
|
8363
8363
|
* `false` and never subscribes, rather than throwing.
|
|
8364
8364
|
*/
|
|
@@ -8394,7 +8394,7 @@ const COLUMN = "mx-auto w-full max-w-[760px] px-6";
|
|
|
8394
8394
|
const APP_VIEW_HOST_VERSION = "1.0.0";
|
|
8395
8395
|
/**
|
|
8396
8396
|
* Designed empty state shown before the first turn. Renders in the same slot as `ConversationView`
|
|
8397
|
-
* (never together), so it carries the same `main` landmark and label
|
|
8397
|
+
* (never together), so it carries the same `main` landmark and label — nothing
|
|
8398
8398
|
* above this slot supplies one.
|
|
8399
8399
|
*/
|
|
8400
8400
|
function EmptyState() {
|
|
@@ -8418,14 +8418,14 @@ function EmptyState() {
|
|
|
8418
8418
|
});
|
|
8419
8419
|
}
|
|
8420
8420
|
/**
|
|
8421
|
-
*
|
|
8421
|
+
* Shown in place of the conversation while `sessionStatus.setupRequired` holds: the
|
|
8422
8422
|
* session is running (the GUI itself connected fine), but has no provider to reply with yet. "Set up
|
|
8423
8423
|
* provider" runs `/provider add` as a command; its questions dock above where the composer would be
|
|
8424
8424
|
* (the same `PermissionPrompt` any ask uses), and the panel clears itself once the session reports a
|
|
8425
8425
|
* real provider, live — no restart, no reload.
|
|
8426
8426
|
*
|
|
8427
8427
|
* Renders in the same slot as `ConversationView`/`EmptyState` (never together), so it carries the
|
|
8428
|
-
* page's `main` landmark here too
|
|
8428
|
+
* page's `main` landmark here too — nothing above this slot supplies one.
|
|
8429
8429
|
*/
|
|
8430
8430
|
function SetupPanel({ onSetUp, disabled }) {
|
|
8431
8431
|
return /* @__PURE__ */ jsx("main", {
|
|
@@ -8748,7 +8748,7 @@ function SessionSurface({ state, surface, personalUsageEnabled: personalUsageReq
|
|
|
8748
8748
|
}
|
|
8749
8749
|
/**
|
|
8750
8750
|
* A centered chrome frame for the pre-session (loading) and fatal states — reused by app shells. Its
|
|
8751
|
-
* content area is this page's `main` landmark
|
|
8751
|
+
* content area is this page's `main` landmark: nothing above `CenteredChrome`'s
|
|
8752
8752
|
* three call sites (starting, fatal, the trust question) supplies one, and none of the three ever
|
|
8753
8753
|
* render alongside `SessionSurface`'s own. `header` stays a sibling of `main`, not inside it, so it
|
|
8754
8754
|
* keeps its own implicit `banner` role.
|