@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.cjs
CHANGED
|
@@ -127,12 +127,12 @@ function askResponse(id, response) {
|
|
|
127
127
|
//#endregion
|
|
128
128
|
//#region src/hooks/ui-intent-state.ts
|
|
129
129
|
/**
|
|
130
|
-
*
|
|
130
|
+
* How the GUI surface answers a `ui_intent`.
|
|
131
131
|
*
|
|
132
132
|
* The server requester-routes `ui_intent`, so everything that arrives here was asked for by THIS
|
|
133
133
|
* surface and must be answered visibly. An intent with a GUI screen opens it (the session picker is
|
|
134
|
-
* the session sidebar
|
|
135
|
-
*
|
|
134
|
+
* the session sidebar; settings and the plugin manager both open the Settings modal; the agent
|
|
135
|
+
* switcher is a sheet). An intent whose
|
|
136
136
|
* command is on the GUI's exclusion list never reaches here at all (`excluded-commands.ts` catches it
|
|
137
137
|
* before the session is asked) — this module only answers a screen this HOST cannot back even though
|
|
138
138
|
* the GUI knows how to render it (the session picker, when the host cannot list sessions), or a kind
|
|
@@ -162,7 +162,7 @@ function guiScreenForUiIntent(intent) {
|
|
|
162
162
|
* Describe one intent for this surface: a plain, honest sentence for a screen this HOST cannot back
|
|
163
163
|
* right now (a session picker with no session list), the `/theme` exclusion this intent defensively
|
|
164
164
|
* reuses, or an unrecognized future kind. Never called for `show-settings`, `show-plugin-manager` or
|
|
165
|
-
* `show-agent-switcher` — all three always have a screen now
|
|
165
|
+
* `show-agent-switcher` — all three always have a screen now —
|
|
166
166
|
* but their cases are left out of this switch rather than kept as dead branches that would claim
|
|
167
167
|
* otherwise; an unknown wire-level kind (a newer host) still yields an explicit notice naming the raw
|
|
168
168
|
* kind, so the "never a silent no-op" floor holds for future intents too.
|
|
@@ -266,7 +266,7 @@ function createWsSessionClient(url, callbacks) {
|
|
|
266
266
|
//#endregion
|
|
267
267
|
//#region src/hooks/server-message-handling.ts
|
|
268
268
|
/**
|
|
269
|
-
*
|
|
269
|
+
* Exhaustive ownership for every decoded server variant on the shared GUI surface.
|
|
270
270
|
* Adding a wire variant without deciding its GUI disposition is a compile error instead of a silent
|
|
271
271
|
* reducer omission. `intentionally-not-rendered` is an explicit disposition for server-side query
|
|
272
272
|
* snapshots and domain events that this session/usage surface does not currently visualize.
|
|
@@ -345,10 +345,10 @@ function nextRequestId$7(prefix) {
|
|
|
345
345
|
return `${prefix}_${requestCounter$7}_${Date.now()}`;
|
|
346
346
|
}
|
|
347
347
|
/**
|
|
348
|
-
*
|
|
348
|
+
* The agent switcher sheet's own correlated request/response state, the same
|
|
349
349
|
* shape `useSettingsState` already uses for `get-settings`/`update-settings` — independent from the
|
|
350
350
|
* chat conversation, because choosing an agent is answered by the sheet's own confirmation line
|
|
351
|
-
* ("Default agent: Explore"), never a conversation card
|
|
351
|
+
* ("Default agent: Explore"), never a conversation card.
|
|
352
352
|
*
|
|
353
353
|
* `openAgentSwitcher` re-fetches every time (`/agent` bare, or reopening after a close), so a change
|
|
354
354
|
* made elsewhere reaches an open-then-reopened sheet, matching `useSettingsState`'s own reasoning.
|
|
@@ -710,7 +710,7 @@ function nextRequestId$4() {
|
|
|
710
710
|
return `model_list_${requestCounter$4}_${Date.now()}`;
|
|
711
711
|
}
|
|
712
712
|
/**
|
|
713
|
-
*
|
|
713
|
+
* The model list the model control's pop-up menu is built from, requested on
|
|
714
714
|
* demand (`list-models`) and correlated by `requestId` (mirrors `usePersonalUsageState`'s pattern) so
|
|
715
715
|
* a reply to an older request — one already superseded by a newer one, e.g. across a reconnect — is
|
|
716
716
|
* never applied over the list a still-open menu is currently showing.
|
|
@@ -835,7 +835,7 @@ function nextRequestId$2(prefix) {
|
|
|
835
835
|
return `${prefix}_${requestCounter$2}_${Date.now()}`;
|
|
836
836
|
}
|
|
837
837
|
/**
|
|
838
|
-
*
|
|
838
|
+
* The Project panel's correlated request/response state (status, one file's diff, project
|
|
839
839
|
* memory), independent from chat state. Mirrors `usePersonalUsageState`'s shape: a status per read, a
|
|
840
840
|
* `requestId` ref that drops a stale reply (a diff request superseded by clicking another file before
|
|
841
841
|
* the first answered), and a request function per read.
|
|
@@ -921,15 +921,15 @@ function useProjectPanelState(send) {
|
|
|
921
921
|
//#endregion
|
|
922
922
|
//#region src/hooks/use-schedules-state.ts
|
|
923
923
|
/**
|
|
924
|
-
*
|
|
924
|
+
* The Agents panel's "Scheduled" group. No `requestId` correlates
|
|
925
925
|
* `get-background-tasks`/`background_tasks` (the message carries none — see `wire-messages.ts`), so
|
|
926
926
|
* this owns that whole message type for the GUI: every reply is a full refresh of the roster,
|
|
927
927
|
* filtered server-side to `kind: 'scheduled'` (the same tasks `/schedule list` shows).
|
|
928
928
|
*
|
|
929
929
|
* Pause/resume send the SAME `command` text `/schedule pause <id>` / `/schedule resume <id>` runs.
|
|
930
930
|
* Delete sends the existing `cancel-background-task` write — the same one Stop already uses for any
|
|
931
|
-
* other background-task kind
|
|
932
|
-
* one is permanent (distinct from the reversible pause)
|
|
931
|
+
* other background-task kind — since a schedule IS a background task, and cancelling
|
|
932
|
+
* one is permanent (distinct from the reversible pause).
|
|
933
933
|
*/
|
|
934
934
|
function useSchedulesState(send) {
|
|
935
935
|
const [scheduledTasks, setScheduledTasks] = (0, react.useState)([]);
|
|
@@ -1007,7 +1007,7 @@ function keepsSessionsLive(listing) {
|
|
|
1007
1007
|
return listing.sessions.some((row) => typeof row.live === "boolean");
|
|
1008
1008
|
}
|
|
1009
1009
|
/**
|
|
1010
|
-
*
|
|
1010
|
+
* A desktop Reconnect reloads the page, which loses every bit of JS state — so the
|
|
1011
1011
|
* session to go back to cannot live in a ref like `lastSwitchedIdRef` below. `sessionStorage` survives
|
|
1012
1012
|
* the reload (and only that tab), so it carries the id across it. Namespaced so a page hosting other
|
|
1013
1013
|
* state under the same origin does not collide.
|
|
@@ -1044,7 +1044,7 @@ function initialSidebarOpen() {
|
|
|
1044
1044
|
return window.matchMedia("(min-width: 768px)").matches;
|
|
1045
1045
|
}
|
|
1046
1046
|
/**
|
|
1047
|
-
*
|
|
1047
|
+
* The host's session list beside the conversation: requests correlated by id (the latest
|
|
1048
1048
|
* wins), the start/switch commands, and whether the sidebar is shown.
|
|
1049
1049
|
*
|
|
1050
1050
|
* A host that keeps sessions live binds each connection to its own session and puts a new connection
|
|
@@ -1163,12 +1163,12 @@ function nextRequestId(prefix) {
|
|
|
1163
1163
|
return `${prefix}_${requestCounter}_${Date.now()}`;
|
|
1164
1164
|
}
|
|
1165
1165
|
/**
|
|
1166
|
-
*
|
|
1166
|
+
* The Settings screen's own correlated request/response state, independent from chat
|
|
1167
1167
|
* state — the same shape `usePersonalUsageState` already uses for `get-personal-usage-report`.
|
|
1168
1168
|
*
|
|
1169
1169
|
* `openSettings` re-fetches every time (opening from the gear, `/settings`, or reopening after a
|
|
1170
1170
|
* close): the simplest correct way for a change made elsewhere — another typed command, another
|
|
1171
|
-
* client — to reach an open-then-reopened screen
|
|
1171
|
+
* client — to reach an open-then-reopened screen.
|
|
1172
1172
|
*
|
|
1173
1173
|
* A failed UPDATE never touches `settingsSnapshot`, so a control bound to it shows the value it had
|
|
1174
1174
|
* before the write — "a failed write leaves the control at its previous value" — while
|
|
@@ -1236,7 +1236,7 @@ function useSettingsState(send) {
|
|
|
1236
1236
|
//#endregion
|
|
1237
1237
|
//#region src/hooks/useSessionClient.ts
|
|
1238
1238
|
/**
|
|
1239
|
-
*
|
|
1239
|
+
* The transport-neutral session reducer for the GUI presentation layer. Reconstructs conversation
|
|
1240
1240
|
* state from the wire `TServerMessage` stream, independent of HOW the bytes arrive (a `TMakeSessionClient`
|
|
1241
1241
|
* factory over `{onMessage, onStatusChange}`). The core is **generic over its status type** (default
|
|
1242
1242
|
* `TConnectionStatus`) so a surface with extra connection states (e.g. the browser WebRTC surface) can widen it
|
|
@@ -1276,7 +1276,7 @@ function pushToolStartSegment(segments, tool) {
|
|
|
1276
1276
|
}];
|
|
1277
1277
|
}
|
|
1278
1278
|
/**
|
|
1279
|
-
*
|
|
1279
|
+
* Attribute a `tool_end` to its call by executionId first — two same-named parallel calls
|
|
1280
1280
|
* can finish out of start order, and matching by name+running alone would close whichever running
|
|
1281
1281
|
* call of that name is found first, not the one that actually finished. Falls back to name+running
|
|
1282
1282
|
* only when the event carries no executionId (legacy fixtures / hosts that predate it).
|
|
@@ -1333,7 +1333,7 @@ function applyToolEndToSegments(segments, state) {
|
|
|
1333
1333
|
});
|
|
1334
1334
|
}
|
|
1335
1335
|
/**
|
|
1336
|
-
*
|
|
1336
|
+
* The files an Edit/Write call touched during the turn, aggregated by path — the "Changed
|
|
1337
1337
|
* files" row's data. `diffLines` keeps the LATEST edit's diff (what a click on the file opens); the
|
|
1338
1338
|
* +/- counts sum every edit to that path within the turn.
|
|
1339
1339
|
*/
|
|
@@ -1366,7 +1366,7 @@ function activeToolFromHistoricalState(state) {
|
|
|
1366
1366
|
};
|
|
1367
1367
|
}
|
|
1368
1368
|
/**
|
|
1369
|
-
*
|
|
1369
|
+
* Turns the server's chronological `IHistoryDisplaySegment[]` (a reload/reconnect/resume
|
|
1370
1370
|
* replay) into the SAME `TConversationEntry[]` shape a live turn's `finishTurn` produces — reusing its
|
|
1371
1371
|
* exact segment-accumulation and "Changed files" logic, so the two paths cannot draw the turn/grouping
|
|
1372
1372
|
* boundary differently. A `'text', role: 'user'` segment is both its own entry AND the turn boundary:
|
|
@@ -1443,7 +1443,7 @@ function useSessionClient(makeClient) {
|
|
|
1443
1443
|
}, []);
|
|
1444
1444
|
/**
|
|
1445
1445
|
* End the running turn: its text and tool calls stay in the conversation as SEPARATE entries, in
|
|
1446
|
-
* the order they happened
|
|
1446
|
+
* the order they happened — not one tools-block grouped ahead of a single merged reply.
|
|
1447
1447
|
*/
|
|
1448
1448
|
const finishTurn = (0, react.useCallback)((toolStatus) => {
|
|
1449
1449
|
const segments = turnSegmentsRef.current;
|
|
@@ -1520,7 +1520,7 @@ function useSessionClient(makeClient) {
|
|
|
1520
1520
|
if (requestId !== void 0) sessionChangeRequestIdsRef.current.delete(requestId);
|
|
1521
1521
|
}, []);
|
|
1522
1522
|
/**
|
|
1523
|
-
*
|
|
1523
|
+
* Run a command the same way `send` does, but flag it as controls-triggered —
|
|
1524
1524
|
* the model/mode/effort pop-up menus use this instead of `send` directly, so their change is
|
|
1525
1525
|
* confirmed by the control's own label (via the `get-status` refresh `command_result` already
|
|
1526
1526
|
* triggers) rather than a conversation card. A typed `/command` still goes through plain `send`
|
|
@@ -1529,7 +1529,7 @@ function useSessionClient(makeClient) {
|
|
|
1529
1529
|
* `TClientMessage` — giving `command` a `requestId` there would let ANY caller of `send` opt a typed
|
|
1530
1530
|
* command into silence, which is exactly what this must not allow.
|
|
1531
1531
|
*
|
|
1532
|
-
*
|
|
1532
|
+
* `send` also intercepts `help` and an excluded command before either reaches the wire —
|
|
1533
1533
|
* for `help` that opens the sheet regardless of how it was asked for, but an excluded command's
|
|
1534
1534
|
* interception adds a VISIBLE info card, defeating the silence this function promises. Every current
|
|
1535
1535
|
* caller (model/mode/effort) names neither, so this is dormant, not exercised — a future caller must
|
|
@@ -2156,7 +2156,7 @@ function resolveClientRuntimeHost(environment) {
|
|
|
2156
2156
|
//#endregion
|
|
2157
2157
|
//#region src/clipboard.ts
|
|
2158
2158
|
/**
|
|
2159
|
-
* Copies `text` to the clipboard for the "Copy code" / "Copy message" buttons
|
|
2159
|
+
* Copies `text` to the clipboard for the "Copy code" / "Copy message" buttons: the async
|
|
2160
2160
|
* Clipboard API first, and — for a page that denies it (no permission granted yet, a browser that
|
|
2161
2161
|
* never implements it, or a context that disallows it outright) — a same-tick `execCommand('copy')`
|
|
2162
2162
|
* fallback through a throwaway, invisible textarea. Rejects only once both have failed, so a caller
|
|
@@ -2203,7 +2203,7 @@ const FEEDBACK_MS = 2e3;
|
|
|
2203
2203
|
/**
|
|
2204
2204
|
* The "Copy code" / "Copy message" buttons' shared feedback state: `copied` or `error` for a couple of
|
|
2205
2205
|
* seconds, then back to `idle` — drives both the button's own label swap and an `aria-live` region a
|
|
2206
|
-
* screen reader announces without moving focus
|
|
2206
|
+
* screen reader announces without moving focus.
|
|
2207
2207
|
*/
|
|
2208
2208
|
function useCopyFeedback() {
|
|
2209
2209
|
const [status, setStatus] = (0, react.useState)("idle");
|
|
@@ -2223,7 +2223,7 @@ function useCopyFeedback() {
|
|
|
2223
2223
|
//#endregion
|
|
2224
2224
|
//#region src/driver-labels.ts
|
|
2225
2225
|
/**
|
|
2226
|
-
*
|
|
2226
|
+
* Plain, human labels for a driver id, and whether one names the SAME KIND
|
|
2227
2227
|
* of surface as this connection's own. Shared by every place the GUI shows who drove a turn (a
|
|
2228
2228
|
* message, a permission/ask prompt): the person's own input never carries a label, and a turn from a
|
|
2229
2229
|
* different kind of surface does — in words, never a raw id.
|
|
@@ -2277,7 +2277,7 @@ function humanDriverLabel(driverId) {
|
|
|
2277
2277
|
}
|
|
2278
2278
|
/**
|
|
2279
2279
|
* The ready-to-render attribution phrase: "from <label>" for a co-driver, or "Automatic — loop"
|
|
2280
|
-
* for the agent's own wake-ups — "from automatic" reads as a typo, not a source.
|
|
2280
|
+
* for the agent's own wake-ups — "from automatic" reads as a typo, not a source. Every
|
|
2281
2281
|
* `AGENT_DRIVER_ID` turn is, today, a loop's own injected iteration (fixed or self-paced —
|
|
2282
2282
|
* `requestWakeup` in `interactive-session.ts` is the one place that stamps this id); if a
|
|
2283
2283
|
* differently-sourced agent-wakeup turn is ever added, this label needs to name that source
|
|
@@ -2622,7 +2622,7 @@ function AppViewFrame({ tool, host, sandboxUrl, viewKey, sourceId }) {
|
|
|
2622
2622
|
//#endregion
|
|
2623
2623
|
//#region src/components/DiffLines.tsx
|
|
2624
2624
|
/**
|
|
2625
|
-
*
|
|
2625
|
+
* Renders a unified diff already built server-side (the ONE diff builder, in
|
|
2626
2626
|
* `interactive-session-streaming.ts`) — a tool row's expanded result and the Edit/Write permission
|
|
2627
2627
|
* prompt both show the SAME diff through this, so approving a change and reviewing it afterwards
|
|
2628
2628
|
* look identical. This component never computes a diff itself, only displays one.
|
|
@@ -2687,7 +2687,7 @@ function nodeText(node) {
|
|
|
2687
2687
|
return "";
|
|
2688
2688
|
}
|
|
2689
2689
|
/**
|
|
2690
|
-
* "Copy code" / "Copy message"
|
|
2690
|
+
* "Copy code" / "Copy message": shows on hover and keyboard focus, always on touch (no
|
|
2691
2691
|
* `hover` capability to gate on there). The transient "Copied" / "Couldn't copy" feedback lives in a
|
|
2692
2692
|
* visually hidden `aria-live` region that is a SIBLING of the button, not a descendant of it — ARIA
|
|
2693
2693
|
* name computation only ever looks at an element's own subtree, so a live region placed anywhere
|
|
@@ -2731,7 +2731,7 @@ function CopyButton({ label, getText, className = "" }) {
|
|
|
2731
2731
|
/**
|
|
2732
2732
|
* A fenced code block: unwrapped and horizontally scrollable (a visible scrollbar via
|
|
2733
2733
|
* `.gui-code-scroll`, `tabIndex=0` and a name so a keyboard user without a mouse wheel can still reach
|
|
2734
|
-
* the rest of a long line), with its own "Copy code" button revealed on hover/focus
|
|
2734
|
+
* the rest of a long line), with its own "Copy code" button revealed on hover/focus.
|
|
2735
2735
|
*/
|
|
2736
2736
|
function CodeBlockRenderer({ children }) {
|
|
2737
2737
|
const text = nodeText(children).replace(/\n$/, "");
|
|
@@ -2875,7 +2875,7 @@ function ToolIcon({ name, className }) {
|
|
|
2875
2875
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Wrench, { ...props });
|
|
2876
2876
|
}
|
|
2877
2877
|
/**
|
|
2878
|
-
*
|
|
2878
|
+
* A long path's directory is shortened FROM THE LEFT so the filename — the part someone
|
|
2879
2879
|
* actually needs to recognise — is never the part that gets cut.
|
|
2880
2880
|
*/
|
|
2881
2881
|
const PATH_DISPLAY_MAX_CHARS = 52;
|
|
@@ -3071,7 +3071,7 @@ const COMMAND_TONE = {
|
|
|
3071
3071
|
}
|
|
3072
3072
|
};
|
|
3073
3073
|
/**
|
|
3074
|
-
* A slash command's outcome, where it was typed: line breaks kept, long output folded.
|
|
3074
|
+
* A slash command's outcome, where it was typed: line breaks kept, long output folded. The
|
|
3075
3075
|
* card itself still marks the command (the `/name` header stays monospace, a fixed-width label rather
|
|
3076
3076
|
* than prose), but its BODY reads as normal text — proportional, normal size — unless it is actually a
|
|
3077
3077
|
* long listing (the same measure that decides whether to fold it: many raw lines, or tall once
|
|
@@ -3178,7 +3178,7 @@ function ToolGroup({ tools }) {
|
|
|
3178
3178
|
]
|
|
3179
3179
|
});
|
|
3180
3180
|
}
|
|
3181
|
-
/**
|
|
3181
|
+
/** A turn that changed files ends with this compact row — a click opens that file's diff. */
|
|
3182
3182
|
function ChangedFilesRow({ files }) {
|
|
3183
3183
|
const [openPath, setOpenPath] = (0, react.useState)(null);
|
|
3184
3184
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3233,7 +3233,7 @@ function ConversationView({ messages, activeTools, streamingText, isThinking, ow
|
|
|
3233
3233
|
setIsAtBottomState(value);
|
|
3234
3234
|
};
|
|
3235
3235
|
/**
|
|
3236
|
-
*
|
|
3236
|
+
* Follows new content only while the person is already at the bottom: streaming text, a
|
|
3237
3237
|
* finished message, a tool starting or finishing. Expanding something already on screen (a tool row,
|
|
3238
3238
|
* a folded command card) touches none of these, so it never causes a jump — the effect simply does
|
|
3239
3239
|
* not run for it. An instant jump rather than a smooth one, so it can never itself be mistaken by
|
|
@@ -3419,7 +3419,7 @@ const SECONDARY_BUTTON$2 = `${BUTTON$2} bg-raised text-foreground hover:bg-hover
|
|
|
3419
3419
|
const DESTRUCTIVE_BUTTON$2 = `${BUTTON$2} bg-destructive text-destructive-foreground hover:opacity-90`;
|
|
3420
3420
|
/**
|
|
3421
3421
|
* A title, a body, Cancel focused by default, and a confirm button that can be styled destructive.
|
|
3422
|
-
*
|
|
3422
|
+
* `destructive` also picks the dialog's ARIA role — `"alertdialog"` when it is (an action
|
|
3423
3423
|
* that demands a decision before anything else, e.g. deleting a session), `"dialog"` otherwise.
|
|
3424
3424
|
*/
|
|
3425
3425
|
function ConfirmDialog({ open, title, body, confirmLabel, cancelLabel = "Cancel", destructive = false, onConfirm, onCancel, restoreFocusTo }) {
|
|
@@ -3715,7 +3715,7 @@ function AgentCard({ entry, selected, onOpen, onStop }) {
|
|
|
3715
3715
|
]
|
|
3716
3716
|
});
|
|
3717
3717
|
}
|
|
3718
|
-
/**
|
|
3718
|
+
/** One row of the "Scheduled" group — what it runs, when next, status, controls. */
|
|
3719
3719
|
function ScheduleCard({ task, onPause, onResume, onRequestDelete }) {
|
|
3720
3720
|
const isPaused = task.status === "paused";
|
|
3721
3721
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3763,7 +3763,7 @@ function ScheduleCard({ task, onPause, onResume, onRequestDelete }) {
|
|
|
3763
3763
|
]
|
|
3764
3764
|
});
|
|
3765
3765
|
}
|
|
3766
|
-
/**
|
|
3766
|
+
/** The current goal — objective, plain-word status, and Cancel. */
|
|
3767
3767
|
function GoalCard({ goal, onCancel }) {
|
|
3768
3768
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3769
3769
|
className: "rounded-xl bg-card px-3.5 py-3",
|
|
@@ -3850,12 +3850,12 @@ function AttentionTag({ attention, status, deniedToolCalls }) {
|
|
|
3850
3850
|
//#endregion
|
|
3851
3851
|
//#region src/components/AgentSwitcherSheet.tsx
|
|
3852
3852
|
/**
|
|
3853
|
-
*
|
|
3853
|
+
* The agent switcher: `show-agent-switcher` (`/agent` with no args) opens this
|
|
3854
3854
|
* sheet instead of the surface's old "not available" line. It lists every agent this session can
|
|
3855
3855
|
* run — name, one-line description, and where each is defined in plain words — with the current one
|
|
3856
3856
|
* checked. Choosing a row runs the SAME path as typing `/agent <name>` (a plain `command`, wired in
|
|
3857
3857
|
* `useAgentSwitcherState`); the reply is this sheet's own confirmation line, never a conversation
|
|
3858
|
-
* card
|
|
3858
|
+
* card.
|
|
3859
3859
|
*/
|
|
3860
3860
|
function AgentSwitcherSheet({ state }) {
|
|
3861
3861
|
if (!state.agentSwitcherOpen) return null;
|
|
@@ -3937,7 +3937,7 @@ function isEditableElement(element) {
|
|
|
3937
3937
|
return element !== null && element.matches("input, textarea, select, [contenteditable]");
|
|
3938
3938
|
}
|
|
3939
3939
|
/**
|
|
3940
|
-
*
|
|
3940
|
+
* The one destructive option in the provider profile action menu (the ask built
|
|
3941
3941
|
* by `askProviderProfileAction` in `agent-command`'s `provider-command-profile.ts`, `request.id`
|
|
3942
3942
|
* `'provider-profile-action'`): "Delete" answers to no digit and renders apart from Switch/Edit/Test/
|
|
3943
3943
|
* Duplicate, styled destructive — the exact audited bug ("Delete looks the same as Switch, answers to
|
|
@@ -4352,7 +4352,7 @@ const BUTTON$1 = "inline-flex items-center gap-2 rounded-lg px-3.5 py-1.5 text-[
|
|
|
4352
4352
|
const PRIMARY_BUTTON$1 = `${BUTTON$1} bg-primary text-primary-foreground hover:opacity-90`;
|
|
4353
4353
|
const SECONDARY_BUTTON$1 = `${BUTTON$1} bg-raised text-foreground hover:bg-hover`;
|
|
4354
4354
|
const GHOST_BUTTON = `${BUTTON$1} text-muted-foreground hover:bg-hover hover:text-foreground`;
|
|
4355
|
-
/**
|
|
4355
|
+
/** A destructive ask option (Delete) — visually distinct from Switch/Edit/Test/Duplicate. */
|
|
4356
4356
|
const DESTRUCTIVE_BUTTON$1 = `${BUTTON$1} bg-destructive/12 text-destructive hover:bg-destructive/20`;
|
|
4357
4357
|
/**
|
|
4358
4358
|
* The free-text answer to one ask prompt. Its value lives ONLY here, in a component the parent
|
|
@@ -4378,7 +4378,7 @@ const FreeTextField = (0, react.forwardRef)(function FreeTextField({ masked, pla
|
|
|
4378
4378
|
onCancel();
|
|
4379
4379
|
};
|
|
4380
4380
|
/**
|
|
4381
|
-
* A key that reaches the field types into it — the prompt's own shortcuts never see it
|
|
4381
|
+
* A key that reaches the field types into it — the prompt's own shortcuts never see it.
|
|
4382
4382
|
* The Enter that only finishes an IME composition (Korean/Japanese/Chinese) must not submit the
|
|
4383
4383
|
* still-uncommitted text — `isComposing` (or `keyCode` 229, on browsers that predate it) marks that
|
|
4384
4384
|
* Enter; the keystroke is left alone so the browser can commit the composition normally.
|
|
@@ -4463,7 +4463,7 @@ function ToolArgs({ args }) {
|
|
|
4463
4463
|
});
|
|
4464
4464
|
}
|
|
4465
4465
|
/**
|
|
4466
|
-
*
|
|
4466
|
+
* A Shell request shows just its command — and the working directory only when the server
|
|
4467
4467
|
* says it differs from the workspace — instead of every raw argument.
|
|
4468
4468
|
*/
|
|
4469
4469
|
function ShellCommandPreview({ command, cwd }) {
|
|
@@ -4498,7 +4498,7 @@ function formatUpdatedAt(updatedAt, now = Date.now()) {
|
|
|
4498
4498
|
return new Date(at).toISOString().slice(0, 10);
|
|
4499
4499
|
}
|
|
4500
4500
|
/**
|
|
4501
|
-
* A session's title
|
|
4501
|
+
* A session's title: its name, else its stable first-message title, else "New session".
|
|
4502
4502
|
* Never `preview` — that field is the raw latest reply, which is what changes every turn and is what
|
|
4503
4503
|
* this title exists to stop showing.
|
|
4504
4504
|
*/
|
|
@@ -4529,12 +4529,12 @@ function lockedSessionsLine(count) {
|
|
|
4529
4529
|
return count === 1 ? "1 saved session is sealed and not listed here." : `${count} saved sessions are sealed and not listed here.`;
|
|
4530
4530
|
}
|
|
4531
4531
|
/**
|
|
4532
|
-
*
|
|
4532
|
+
* This workspace's sessions on the left of the conversation, as in Claude Code Desktop:
|
|
4533
4533
|
* start a new one, or click another to make it current. Rows the host could not read are listed,
|
|
4534
4534
|
* disabled, so a damaged session never looks deleted. A refused switch comes back as a notice.
|
|
4535
4535
|
* A host that keeps several sessions live marks the rows running now and counts who else is on them.
|
|
4536
4536
|
*
|
|
4537
|
-
*
|
|
4537
|
+
* Each row also carries a "More" menu (or right-click) with Rename (inline) and Delete
|
|
4538
4538
|
* (confirmed). Rename on the current row reuses the `/rename` command, which also updates the live
|
|
4539
4539
|
* session's own name and title bar; rename on any other row writes the stored record directly.
|
|
4540
4540
|
*/
|
|
@@ -5418,10 +5418,10 @@ function ProductWordmark({ surface }) {
|
|
|
5418
5418
|
//#endregion
|
|
5419
5419
|
//#region src/hooks/command-menu.ts
|
|
5420
5420
|
/**
|
|
5421
|
-
*
|
|
5421
|
+
* What the composer's `/` menu offers for a draft. Pure, so the matching is testable without
|
|
5422
5422
|
* rendering: the TUI's autocomplete offers commands and skills the same way.
|
|
5423
5423
|
*
|
|
5424
|
-
*
|
|
5424
|
+
* A command the GUI cannot run at all (`isExcludedCommand`) never appears here — there is
|
|
5425
5425
|
* nothing useful for choosing it to do, and typing it by hand is answered separately (see
|
|
5426
5426
|
* `excluded-commands.ts`, `useSessionClient.ts`). Commands are listed before skills, each in its own
|
|
5427
5427
|
* group, so the menu can head them "Commands" and "Skills".
|
|
@@ -5461,7 +5461,7 @@ function byName(a, b) {
|
|
|
5461
5461
|
//#endregion
|
|
5462
5462
|
//#region src/components/composer-attachments.ts
|
|
5463
5463
|
/**
|
|
5464
|
-
*
|
|
5464
|
+
* Pure logic behind the composer's attach button and drag-and-drop, kept apart from
|
|
5465
5465
|
* `Composer.tsx` so it is testable without React or a DOM.
|
|
5466
5466
|
*
|
|
5467
5467
|
* The runtime resolves `@relative/path` references already (`prompt-file-reference-resolver.ts` in
|
|
@@ -5477,7 +5477,7 @@ function byName(a, b) {
|
|
|
5477
5477
|
const MAX_ATTACHMENT_FILE_BYTES = 64 * 1024;
|
|
5478
5478
|
/** Mirrors `DEFAULT_MAX_TOTAL_BYTES` in the same resolver. */
|
|
5479
5479
|
const MAX_ATTACHMENT_TOTAL_BYTES = 256 * 1024;
|
|
5480
|
-
/** Shown whenever a file cannot be resolved to a path inside the workspace — rule 3
|
|
5480
|
+
/** Shown whenever a file cannot be resolved to a path inside the workspace — rule 3. */
|
|
5481
5481
|
const ATTACHMENT_NO_PATH_NOTICE = "Only files inside this project folder can be attached.";
|
|
5482
5482
|
/** Shown once the running count of attachments would pass `MAX_ATTACHMENT_COUNT`. */
|
|
5483
5483
|
const ATTACHMENT_COUNT_LIMIT_NOTICE = `You can attach up to 8 files to one message.`;
|
|
@@ -5660,11 +5660,11 @@ function evaluateCandidateFile(file, workspacePath, existingTotalBytes, existing
|
|
|
5660
5660
|
};
|
|
5661
5661
|
}
|
|
5662
5662
|
/**
|
|
5663
|
-
* Inserts each chip as the runtime's existing `@relative/path` reference syntax — rule 2
|
|
5663
|
+
* Inserts each chip as the runtime's existing `@relative/path` reference syntax — rule 2.
|
|
5664
5664
|
* Always prefixed with `./`: the parser's `isPathLikeReference` only treats a token as a file
|
|
5665
5665
|
* reference when it has a `.` somewhere or an explicit relative-path prefix, so a dotless name like
|
|
5666
5666
|
* `Makefile`, `Dockerfile` or `LICENSE` — common, useful, and never refused for lacking one — would
|
|
5667
|
-
* otherwise be sent as plain text with no file and no diagnostic
|
|
5667
|
+
* otherwise be sent as plain text with no file and no diagnostic. `./` makes
|
|
5668
5668
|
* every path path-like regardless of its name, and the resolver collapses it back to the plain
|
|
5669
5669
|
* relative path before use.
|
|
5670
5670
|
*/
|
|
@@ -5676,7 +5676,7 @@ function buildPromptWithAttachments(prompt, attachments) {
|
|
|
5676
5676
|
//#endregion
|
|
5677
5677
|
//#region src/components/PopupMenu.tsx
|
|
5678
5678
|
/**
|
|
5679
|
-
* A HIG pop-up menu
|
|
5679
|
+
* A HIG pop-up menu: `role="menu"`, its choices as `menuitemradio` with `aria-checked`
|
|
5680
5680
|
* (or `menuitem` for a plain action, e.g. "Manage providers…"), grouped into optional labelled
|
|
5681
5681
|
* sections. Arrow keys move a roving focus, Enter/Space activates the focused item, and Esc — or a
|
|
5682
5682
|
* click outside — closes the menu and returns focus to the trigger that opened it, so the person
|
|
@@ -5792,7 +5792,7 @@ function PopupMenu({ label, sections, onClose, triggerRef }) {
|
|
|
5792
5792
|
//#endregion
|
|
5793
5793
|
//#region src/components/StatusControls.tsx
|
|
5794
5794
|
/**
|
|
5795
|
-
*
|
|
5795
|
+
* The plain label for each effort selection. Kept in step with
|
|
5796
5796
|
* `EFFORT_LEVEL_LABELS` in `packages/agent-framework/src/effort/effort-resolution.ts` (which
|
|
5797
5797
|
* `agent-command`'s `/effort` re-exports and reports as its own outcome text) — this package cannot
|
|
5798
5798
|
* import that one directly (it is a Node-side package this browser bundle does not depend on), so the
|
|
@@ -5809,7 +5809,7 @@ const EFFORT_LEVEL_LABELS = {
|
|
|
5809
5809
|
xhigh: "Extra high",
|
|
5810
5810
|
max: "Maximum"
|
|
5811
5811
|
};
|
|
5812
|
-
/** The primary row
|
|
5812
|
+
/** The primary row; the rest sit under "More". */
|
|
5813
5813
|
const EFFORT_PRIMARY = [
|
|
5814
5814
|
"auto",
|
|
5815
5815
|
"low",
|
|
@@ -5832,7 +5832,7 @@ function modeLabel(catalog, mode) {
|
|
|
5832
5832
|
}
|
|
5833
5833
|
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";
|
|
5834
5834
|
const CHIP_WARNING_CLASS = "text-warning hover:text-warning";
|
|
5835
|
-
/** One status chip that opens a `PopupMenu` HIG pop-up beneath it
|
|
5835
|
+
/** One status chip that opens a `PopupMenu` HIG pop-up beneath it. */
|
|
5836
5836
|
function StatusChip({ chipLabel, value, icon, connected, warning = false, menuLabel, sections, onOpen }) {
|
|
5837
5837
|
const [open, setOpen] = (0, react.useState)(false);
|
|
5838
5838
|
const triggerRef = (0, react.useRef)(null);
|
|
@@ -5869,7 +5869,7 @@ const ICON_PROPS = {
|
|
|
5869
5869
|
"aria-hidden": true
|
|
5870
5870
|
};
|
|
5871
5871
|
/**
|
|
5872
|
-
* Model · mode · effort, each a pop-up menu
|
|
5872
|
+
* Model · mode · effort, each a pop-up menu, and the context the conversation fills.
|
|
5873
5873
|
* Every menu applies its choice through the same command wire path the equivalent typed slash
|
|
5874
5874
|
* command uses — `onSilentCommand` — so `/model <id>`, `/mode <name>` and `/effort <value>` and a
|
|
5875
5875
|
* click in these menus can never drift apart; the control's own chip label (fed by the session's next
|
|
@@ -6002,7 +6002,7 @@ const EMPTY_DRAFT = {
|
|
|
6002
6002
|
attachments: []
|
|
6003
6003
|
};
|
|
6004
6004
|
/**
|
|
6005
|
-
*
|
|
6005
|
+
* A command whose whole job is opening a GUI screen — choosing it from the `/` menu runs
|
|
6006
6006
|
* it at once (its `ui_intent` opens the screen the normal way) instead of filling the draft and
|
|
6007
6007
|
* waiting for Enter, since there is nothing useful to type after it. `help` never reaches the
|
|
6008
6008
|
* session at all (`useSessionClient.ts`'s `send` opens the Help sheet locally). Every other command
|
|
@@ -6021,14 +6021,14 @@ function menuGroupLabel(item) {
|
|
|
6021
6021
|
return item.kind === "skill" ? "Skills" : "Commands";
|
|
6022
6022
|
}
|
|
6023
6023
|
/**
|
|
6024
|
-
*
|
|
6024
|
+
* The unsent draft survives a Chat → Usage → Chat switch (the composer unmounts), a page
|
|
6025
6025
|
* reload, and a desktop relaunch — `localStorage`, not `sessionStorage`, since only `localStorage`
|
|
6026
6026
|
* survives a closed window/tab being reopened. Namespaced (the configured draft prefix, matching
|
|
6027
6027
|
* the configured restore key in `use-session-directory.ts`) so a page hosting other state under the
|
|
6028
6028
|
* same origin does not collide. Per session id when one is known; a single fallback key before the
|
|
6029
6029
|
* first status arrives (the gap is brief and is reconciled once it does — see the effect below).
|
|
6030
6030
|
*
|
|
6031
|
-
*
|
|
6031
|
+
* The stored value is now JSON (`IStoredDraft`), not the bare text string it used to be —
|
|
6032
6032
|
* `parseStoredDraft` treats anything that does not parse as that shape (including a draft saved
|
|
6033
6033
|
* before this change shipped) as plain text with no attachments, so an old stored draft still loads.
|
|
6034
6034
|
*/
|
|
@@ -6126,7 +6126,7 @@ const ComposerInstance = (0, react.forwardRef)(function Composer({ draftMode = "
|
|
|
6126
6126
|
(0, react.useEffect)(() => {
|
|
6127
6127
|
menuOptionRefs.current[selected]?.scrollIntoView?.({ block: "nearest" });
|
|
6128
6128
|
}, [selected, menu]);
|
|
6129
|
-
/**
|
|
6129
|
+
/** Every draft change is persisted at once, so a reload or relaunch loses nothing. */
|
|
6130
6130
|
const setDraft = (value) => {
|
|
6131
6131
|
stateRef.current = {
|
|
6132
6132
|
...stateRef.current,
|
|
@@ -6135,7 +6135,7 @@ const ComposerInstance = (0, react.forwardRef)(function Composer({ draftMode = "
|
|
|
6135
6135
|
setDraftState(value);
|
|
6136
6136
|
if (draftMode === "persistent") writeDraft(browserNamespace, sessionIdRef.current, stateRef.current);
|
|
6137
6137
|
};
|
|
6138
|
-
/**
|
|
6138
|
+
/** Attachment chips persist alongside the text, under the same per-session draft key. */
|
|
6139
6139
|
const setAttachments = (updater) => {
|
|
6140
6140
|
const next = typeof updater === "function" ? updater(stateRef.current.attachments) : updater;
|
|
6141
6141
|
stateRef.current = {
|
|
@@ -6293,7 +6293,7 @@ const ComposerInstance = (0, react.forwardRef)(function Composer({ draftMode = "
|
|
|
6293
6293
|
chooseMenuItem(item);
|
|
6294
6294
|
return true;
|
|
6295
6295
|
};
|
|
6296
|
-
/**
|
|
6296
|
+
/** Edit puts the queued text back in the draft and cancels the queue behind it (the wire
|
|
6297
6297
|
* has no per-message cancel — only a whole-queue clear). Offered only when exactly one prompt is
|
|
6298
6298
|
* queued: with more than one, `cancel-queue` would still drop every one of them, but only the
|
|
6299
6299
|
* shown prompt's text is known here, so "Edit" would silently lose the rest — the row offers only
|
|
@@ -6616,7 +6616,7 @@ const RECORD_KIND_LABEL = {
|
|
|
6616
6616
|
group_summary: "Summary"
|
|
6617
6617
|
};
|
|
6618
6618
|
/**
|
|
6619
|
-
*
|
|
6619
|
+
* Opening an Agents panel entry — status, what it was asked (one line, from its
|
|
6620
6620
|
* headline), its transcript so far (paginated via `onLoadMore`) and, once it has one, its result.
|
|
6621
6621
|
* `entry === null` renders nothing; the caller owns showing/hiding this over the panel.
|
|
6622
6622
|
*/
|
|
@@ -6836,7 +6836,7 @@ const SHORTCUTS = [
|
|
|
6836
6836
|
}
|
|
6837
6837
|
];
|
|
6838
6838
|
/**
|
|
6839
|
-
* The GUI's Help sheet
|
|
6839
|
+
* The GUI's Help sheet, a `Dialog` opened by `/help` — client-only: `useSessionClient.ts`'s
|
|
6840
6840
|
* `send` catches `/help` before it ever reaches the session and opens this instead, so there is no
|
|
6841
6841
|
* round trip and nothing terminal-style to replace it with. It lists the same commands and skills the
|
|
6842
6842
|
* `/` menu offers (`commandMenuFor`, unfiltered by any query — an excluded command has no row here
|
|
@@ -7160,7 +7160,7 @@ const VIEW_LABEL = {
|
|
|
7160
7160
|
project: "Project"
|
|
7161
7161
|
};
|
|
7162
7162
|
/**
|
|
7163
|
-
*
|
|
7163
|
+
* The folder this session works in, wherever `document.title` is settable: a page (the
|
|
7164
7164
|
* browser tab) and the desktop window that loads it (Electron follows the page's title unless told
|
|
7165
7165
|
* otherwise, and nothing in `apps/agent-app/electron/main.ts` overrides it — see its own SPEC note).
|
|
7166
7166
|
* Absent while the host has not said, so a not-yet-connected surface keeps whatever title it loaded
|
|
@@ -7235,7 +7235,7 @@ function SessionTitleBar({ status, surface, title, workspace, showBrand, view, o
|
|
|
7235
7235
|
/** The column every part of the conversation shares (matches `SessionSurface`'s `COLUMN`). */
|
|
7236
7236
|
const BANNER_COLUMN = "mx-auto w-full max-w-[760px] px-6";
|
|
7237
7237
|
/**
|
|
7238
|
-
* A connection that drops after a working session
|
|
7238
|
+
* A connection that drops after a working session: a banner directly above the
|
|
7239
7239
|
* conversation — never a full-screen replacement — so the conversation and its history stay visible
|
|
7240
7240
|
* and scrollable throughout. Shown as soon as the drop happens; says nothing on the very first
|
|
7241
7241
|
* connect, because nothing has been lost yet.
|
|
@@ -7324,7 +7324,7 @@ function displayProvider(provider) {
|
|
|
7324
7324
|
return PROVIDER_DISPLAY_NAMES[provider] ?? provider[0].toUpperCase() + provider.slice(1);
|
|
7325
7325
|
}
|
|
7326
7326
|
/**
|
|
7327
|
-
* A provider/session error classified at the wire boundary
|
|
7327
|
+
* A provider/session error classified at the wire boundary as one plain sentence naming
|
|
7328
7328
|
* what happened and, where there is one, the next step — `null` for a notice this classification does
|
|
7329
7329
|
* not cover, which keeps showing its own `message` instead.
|
|
7330
7330
|
*
|
|
@@ -7514,7 +7514,7 @@ function SettingsLanguageField({ label, current, recommended, note, onChange, di
|
|
|
7514
7514
|
//#region src/components/SettingsGeneralSection.tsx
|
|
7515
7515
|
/**
|
|
7516
7516
|
* Language, output style and preset — reusing the exact choices and descriptions the runtime
|
|
7517
|
-
* already builds for `/language`, `/output-style` and `/preset
|
|
7517
|
+
* already builds for `/language`, `/output-style` and `/preset`. Never internal ids:
|
|
7518
7518
|
* every pop-up shows the runtime's human labels.
|
|
7519
7519
|
*/
|
|
7520
7520
|
function SettingsGeneralSection({ snapshot, onUpdate, onRequestPresetChange }) {
|
|
@@ -7632,7 +7632,7 @@ function ServerRow({ server, expanded, onToggleExpanded, onToggleEnabled }) {
|
|
|
7632
7632
|
});
|
|
7633
7633
|
}
|
|
7634
7634
|
/**
|
|
7635
|
-
* The MCP Servers section
|
|
7635
|
+
* The MCP Servers section: every configured server's name, where it is
|
|
7636
7636
|
* configured, its live connection status and tool count, an enable/disable switch (approve/reject),
|
|
7637
7637
|
* an expandable list of its tool names, and a section-level Reload. Adding or editing a server is
|
|
7638
7638
|
* left out — the runtime has no command path that writes MCP config (recorded in
|
|
@@ -7727,7 +7727,7 @@ function PermissionRuleRow({ rule, onRequestRemove }) {
|
|
|
7727
7727
|
});
|
|
7728
7728
|
}
|
|
7729
7729
|
/**
|
|
7730
|
-
* Default permission mode, the rules `/permissions` shows, and the sandbox switch
|
|
7730
|
+
* Default permission mode, the rules `/permissions` shows, and the sandbox switch.
|
|
7731
7731
|
* "Skip all checks" as the mode and a preset that would set it both go through
|
|
7732
7732
|
* `onRequestModeChange`'s parent-owned confirmation before anything is sent.
|
|
7733
7733
|
*/
|
|
@@ -7838,7 +7838,7 @@ function PluginRow({ plugin, canInstall, onToggleEnabled, onRequestUninstall })
|
|
|
7838
7838
|
});
|
|
7839
7839
|
}
|
|
7840
7840
|
/**
|
|
7841
|
-
* The Plugins section
|
|
7841
|
+
* The Plugins section: every installed plugin's name, description and enabled
|
|
7842
7842
|
* state, an enable/disable switch, a section-level Reload, and — local surfaces only — install and
|
|
7843
7843
|
* uninstall, since installing runs third-party code. `canInstall` (from the snapshot) mirrors the
|
|
7844
7844
|
* refusal `/plugin install|uninstall` itself gives a remote device or an observer.
|
|
@@ -7929,7 +7929,7 @@ const BUTTON = "rounded-lg px-2.5 py-1.5 text-[12.5px] font-medium transition-co
|
|
|
7929
7929
|
const PRIMARY_BUTTON = `${BUTTON} bg-primary text-primary-foreground hover:opacity-90`;
|
|
7930
7930
|
const SECONDARY_BUTTON = `${BUTTON} bg-raised text-foreground hover:bg-hover`;
|
|
7931
7931
|
const DESTRUCTIVE_BUTTON = `${BUTTON} text-destructive hover:bg-destructive/10`;
|
|
7932
|
-
/** The "Model" action: a `PopupMenu` of this ONE profile's catalog models
|
|
7932
|
+
/** The "Model" action: a `PopupMenu` of this ONE profile's catalog models. */
|
|
7933
7933
|
function ProviderModelButton({ profile, modelList, onSelect }) {
|
|
7934
7934
|
const [open, setOpen] = (0, react.useState)(false);
|
|
7935
7935
|
const triggerRef = (0, react.useRef)(null);
|
|
@@ -8035,7 +8035,7 @@ function ProviderProfileRow({ profile, modelList, onUse, onModelChange, onComman
|
|
|
8035
8035
|
});
|
|
8036
8036
|
}
|
|
8037
8037
|
/**
|
|
8038
|
-
* The configured provider profiles
|
|
8038
|
+
* The configured provider profiles, each row a plain provider name, its model's label,
|
|
8039
8039
|
* a checkmark on the one in use, and a plain connection state when one is known. Every action goes
|
|
8040
8040
|
* through the same path the equivalent command does: Use/Model apply through `state.updateSettings`
|
|
8041
8041
|
* (the same `field` patterns Preset/Mode/Sandbox use); Edit/Test/Duplicate/Add dispatch the raw
|
|
@@ -8083,7 +8083,7 @@ function SettingsProvidersSection({ snapshot, modelList, onRequestModelList, onU
|
|
|
8083
8083
|
//#endregion
|
|
8084
8084
|
//#region src/components/SettingsScreen.tsx
|
|
8085
8085
|
/**
|
|
8086
|
-
* The Settings screen's sections
|
|
8086
|
+
* The Settings screen's sections. Adding a later section (Advisor) is one entry here plus
|
|
8087
8087
|
* one component in the switch below — nothing else in this file changes.
|
|
8088
8088
|
*/
|
|
8089
8089
|
const SECTIONS = [
|
|
@@ -8109,7 +8109,7 @@ const SECTIONS = [
|
|
|
8109
8109
|
}
|
|
8110
8110
|
];
|
|
8111
8111
|
/**
|
|
8112
|
-
* A large modal sheet with a section sidebar on the left and content on the right
|
|
8112
|
+
* A large modal sheet with a section sidebar on the left and content on the right — the
|
|
8113
8113
|
* GUI's Settings screen, opened by the sidebar's gear button, the desktop app's ⌘, menu item, or
|
|
8114
8114
|
* `/settings` (its `show-settings` `ui_intent` calls `state.openSettings()`, wired in
|
|
8115
8115
|
* `useSessionClient`). Every change applies at once; there is no Save button. Esc and the Close
|
|
@@ -8384,7 +8384,7 @@ function describePluginAction(action) {
|
|
|
8384
8384
|
//#endregion
|
|
8385
8385
|
//#region src/hooks/use-media-query.ts
|
|
8386
8386
|
/**
|
|
8387
|
-
* Tracks whether `query` currently matches, reactively (
|
|
8387
|
+
* Tracks whether `query` currently matches, reactively (the narrow-window session sheet
|
|
8388
8388
|
* below `md`). A host without `window.matchMedia` (an older embedding, or a non-browser test) reports
|
|
8389
8389
|
* `false` and never subscribes, rather than throwing.
|
|
8390
8390
|
*/
|
|
@@ -8420,7 +8420,7 @@ const COLUMN = "mx-auto w-full max-w-[760px] px-6";
|
|
|
8420
8420
|
const APP_VIEW_HOST_VERSION = "1.0.0";
|
|
8421
8421
|
/**
|
|
8422
8422
|
* Designed empty state shown before the first turn. Renders in the same slot as `ConversationView`
|
|
8423
|
-
* (never together), so it carries the same `main` landmark and label
|
|
8423
|
+
* (never together), so it carries the same `main` landmark and label — nothing
|
|
8424
8424
|
* above this slot supplies one.
|
|
8425
8425
|
*/
|
|
8426
8426
|
function EmptyState() {
|
|
@@ -8444,14 +8444,14 @@ function EmptyState() {
|
|
|
8444
8444
|
});
|
|
8445
8445
|
}
|
|
8446
8446
|
/**
|
|
8447
|
-
*
|
|
8447
|
+
* Shown in place of the conversation while `sessionStatus.setupRequired` holds: the
|
|
8448
8448
|
* session is running (the GUI itself connected fine), but has no provider to reply with yet. "Set up
|
|
8449
8449
|
* provider" runs `/provider add` as a command; its questions dock above where the composer would be
|
|
8450
8450
|
* (the same `PermissionPrompt` any ask uses), and the panel clears itself once the session reports a
|
|
8451
8451
|
* real provider, live — no restart, no reload.
|
|
8452
8452
|
*
|
|
8453
8453
|
* Renders in the same slot as `ConversationView`/`EmptyState` (never together), so it carries the
|
|
8454
|
-
* page's `main` landmark here too
|
|
8454
|
+
* page's `main` landmark here too — nothing above this slot supplies one.
|
|
8455
8455
|
*/
|
|
8456
8456
|
function SetupPanel({ onSetUp, disabled }) {
|
|
8457
8457
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("main", {
|
|
@@ -8774,7 +8774,7 @@ function SessionSurface({ state, surface, personalUsageEnabled: personalUsageReq
|
|
|
8774
8774
|
}
|
|
8775
8775
|
/**
|
|
8776
8776
|
* A centered chrome frame for the pre-session (loading) and fatal states — reused by app shells. Its
|
|
8777
|
-
* content area is this page's `main` landmark
|
|
8777
|
+
* content area is this page's `main` landmark: nothing above `CenteredChrome`'s
|
|
8778
8778
|
* three call sites (starting, fatal, the trust question) supplies one, and none of the three ever
|
|
8779
8779
|
* render alongside `SessionSurface`'s own. `header` stays a sibling of `main`, not inside it, so it
|
|
8780
8780
|
* keeps its own implicit `banner` role.
|