@volter-ai-dev/supercode-ui 0.1.51 → 0.1.53

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/README.md CHANGED
@@ -43,6 +43,9 @@ const mounted = mountSupercodeMessenger(document.querySelector('#agent'), {
43
43
  async pickContext() {
44
44
  return host.pickFilesAndImages();
45
45
  },
46
+ openKeyboardShortcuts() {
47
+ panel.showKeyboardShortcuts();
48
+ },
46
49
  onClose() {
47
50
  panel.close();
48
51
  },
@@ -135,7 +138,11 @@ Hosts do not need to fork the messenger to connect their own object model:
135
138
  }}
136
139
  contextCandidates={visibleObjects.map(asTranscriptContext)}
137
140
  components={{ ContextCandidate: ObjectPreview }}
138
- slots={{ beforeSessions: ProjectThreads }}
141
+ slots={{
142
+ listHeader: () => null,
143
+ listToolbarActions: NewConversationButton,
144
+ beforeSessions: ProjectThreads,
145
+ }}
139
146
  />
140
147
  ```
141
148
 
@@ -217,6 +224,8 @@ its external agent is working or needs input.
217
224
  replacement. Replacements receive the same typed, capability-filtered props as defaults.
218
225
  - `slots.headerActions` adds compact product controls to the default list, chat, and new-chat
219
226
  headers without replacing their navigation, status, or accessibility behavior.
227
+ - `slots.listHeader` can delegate list chrome to the host, while `slots.listToolbarActions` adds
228
+ controls beside the package-owned search field without duplicating search state.
220
229
  - Stable `scui-*` classes and `data-*` attributes support additive styling; CSS custom properties
221
230
  are the supported theme API.
222
231
  - `styles.css` contains both neutral tokens and default component rules. Teams may load it whole,
@@ -251,6 +260,9 @@ inventing fallback prompt text. The composer bounds images to four supported bro
251
260
  messages, retry, edit, and new-chat recovery. The host still chooses how explicit file selection
252
261
  is exposed and must revalidate every returned item.
253
262
 
263
+ `openKeyboardShortcuts` is also optional and host-owned. When present, the default conversation
264
+ overflow includes a Keyboard shortcuts item without consuming permanent header width.
265
+
254
266
  Transcript images with a browser-safe URL open in a keyboard-accessible, viewport-bounded viewer;
255
267
  remote images expose copy-link and open-original actions, while local data images can be downloaded.
256
268
  `createClientProjection` keeps large historical data URLs in a projection-scoped host registry and
package/components.mjs CHANGED
@@ -952,6 +952,10 @@ var ICONS = {
952
952
  /* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
953
953
  /* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
954
954
  ] }),
955
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
956
+ /* @__PURE__ */ jsx("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
957
+ /* @__PURE__ */ jsx("path", { d: "m10.5 5 2.5 2.5" })
958
+ ] }),
955
959
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
956
960
  /* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
957
961
  /* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
@@ -2348,6 +2352,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2348
2352
  const rows = filterSessions(state.sessions, query);
2349
2353
  const Row = components.SessionRow ?? SessionRow;
2350
2354
  const ListHeader = slots.listHeader;
2355
+ const ListToolbarActions = slots.listToolbarActions;
2351
2356
  const BeforeSessions = slots.beforeSessions;
2352
2357
  const AfterSessions = slots.afterSessions;
2353
2358
  useLayoutEffect3(() => {
@@ -2387,15 +2392,18 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2387
2392
  onClose ? /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }) : null
2388
2393
  ] }),
2389
2394
  state.startup !== "ready" ? /* @__PURE__ */ jsx8(LoadingStatus, { state, compact: rows.length > 0 }) : null,
2390
- state.sessions.length > 4 ? /* @__PURE__ */ jsxs7("label", { className: "scui-search", children: [
2391
- /* @__PURE__ */ jsx8(UiIcon, { name: "search", size: 15 }),
2392
- /* @__PURE__ */ jsx8("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
2393
- const value = event.currentTarget.value;
2394
- setQuery(value);
2395
- boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
2396
- if (rowScroller.current) rowScroller.current.scrollTop = 0;
2397
- } }),
2398
- /* @__PURE__ */ jsx8("small", { children: rows.length })
2395
+ state.sessions.length > 4 || ListToolbarActions ? /* @__PURE__ */ jsxs7("div", { className: "scui-list-toolbar", children: [
2396
+ state.sessions.length > 4 ? /* @__PURE__ */ jsxs7("label", { className: "scui-search", children: [
2397
+ /* @__PURE__ */ jsx8(UiIcon, { name: "search", size: 15 }),
2398
+ /* @__PURE__ */ jsx8("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
2399
+ const value = event.currentTarget.value;
2400
+ setQuery(value);
2401
+ boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
2402
+ if (rowScroller.current) rowScroller.current.scrollTop = 0;
2403
+ } }),
2404
+ query ? /* @__PURE__ */ jsx8("small", { children: rows.length }) : null
2405
+ ] }) : null,
2406
+ ListToolbarActions ? /* @__PURE__ */ jsx8(ListToolbarActions, { state, adapter, value: { query, rows, onNew, canStart: state.harnesses.some((item) => item.startable) } }) : null
2399
2407
  ] }) : null,
2400
2408
  /* @__PURE__ */ jsxs7("div", { className: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
2401
2409
  BeforeSessions ? /* @__PURE__ */ jsx8(BeforeSessions, { state, adapter, value: { query, rows } }) : null,
@@ -2655,7 +2663,8 @@ function ConversationActions({ state, adapter, actionPending, onNew, onSettings
2655
2663
  state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
2656
2664
  state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
2657
2665
  state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null,
2658
- state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null
2666
+ state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null,
2667
+ adapter.openKeyboardShortcuts ? { key: "shortcuts", label: "Keyboard shortcuts", onSelect: adapter.openKeyboardShortcuts } : null
2659
2668
  ].filter(Boolean)
2660
2669
  },
2661
2670
  {
package/composer.mjs CHANGED
@@ -110,6 +110,10 @@ var ICONS = {
110
110
  /* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
111
111
  /* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
112
112
  ] }),
113
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
114
+ /* @__PURE__ */ jsx("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
115
+ /* @__PURE__ */ jsx("path", { d: "m10.5 5 2.5 2.5" })
116
+ ] }),
113
117
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
114
118
  /* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
115
119
  /* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
package/conversation.mjs CHANGED
@@ -603,6 +603,10 @@ var ICONS = {
603
603
  /* @__PURE__ */ jsx2("path", { d: "m4.75 4.75 8.5 8.5" }),
604
604
  /* @__PURE__ */ jsx2("path", { d: "m13.25 4.75-8.5 8.5" })
605
605
  ] }),
606
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
607
+ /* @__PURE__ */ jsx2("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
608
+ /* @__PURE__ */ jsx2("path", { d: "m10.5 5 2.5 2.5" })
609
+ ] }),
606
610
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
607
611
  /* @__PURE__ */ jsx2("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
608
612
  /* @__PURE__ */ jsx2("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
package/embed.mjs CHANGED
@@ -915,6 +915,10 @@ var ICONS = {
915
915
  /* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
916
916
  /* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
917
917
  ] }),
918
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
919
+ /* @__PURE__ */ jsx("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
920
+ /* @__PURE__ */ jsx("path", { d: "m10.5 5 2.5 2.5" })
921
+ ] }),
918
922
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
919
923
  /* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
920
924
  /* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
@@ -2265,6 +2269,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2265
2269
  const rows = filterSessions(state.sessions, query);
2266
2270
  const Row = components.SessionRow ?? SessionRow;
2267
2271
  const ListHeader = slots.listHeader;
2272
+ const ListToolbarActions = slots.listToolbarActions;
2268
2273
  const BeforeSessions = slots.beforeSessions;
2269
2274
  const AfterSessions = slots.afterSessions;
2270
2275
  useLayoutEffect3(() => {
@@ -2304,15 +2309,18 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2304
2309
  onClose ? /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx7(UiIcon, { name: "close", size: 18 }) }) : null
2305
2310
  ] }),
2306
2311
  state.startup !== "ready" ? /* @__PURE__ */ jsx7(LoadingStatus, { state, compact: rows.length > 0 }) : null,
2307
- state.sessions.length > 4 ? /* @__PURE__ */ jsxs6("label", { className: "scui-search", children: [
2308
- /* @__PURE__ */ jsx7(UiIcon, { name: "search", size: 15 }),
2309
- /* @__PURE__ */ jsx7("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
2310
- const value = event.currentTarget.value;
2311
- setQuery(value);
2312
- boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
2313
- if (rowScroller.current) rowScroller.current.scrollTop = 0;
2314
- } }),
2315
- /* @__PURE__ */ jsx7("small", { children: rows.length })
2312
+ state.sessions.length > 4 || ListToolbarActions ? /* @__PURE__ */ jsxs6("div", { className: "scui-list-toolbar", children: [
2313
+ state.sessions.length > 4 ? /* @__PURE__ */ jsxs6("label", { className: "scui-search", children: [
2314
+ /* @__PURE__ */ jsx7(UiIcon, { name: "search", size: 15 }),
2315
+ /* @__PURE__ */ jsx7("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
2316
+ const value = event.currentTarget.value;
2317
+ setQuery(value);
2318
+ boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
2319
+ if (rowScroller.current) rowScroller.current.scrollTop = 0;
2320
+ } }),
2321
+ query ? /* @__PURE__ */ jsx7("small", { children: rows.length }) : null
2322
+ ] }) : null,
2323
+ ListToolbarActions ? /* @__PURE__ */ jsx7(ListToolbarActions, { state, adapter, value: { query, rows, onNew, canStart: state.harnesses.some((item) => item.startable) } }) : null
2316
2324
  ] }) : null,
2317
2325
  /* @__PURE__ */ jsxs6("div", { className: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
2318
2326
  BeforeSessions ? /* @__PURE__ */ jsx7(BeforeSessions, { state, adapter, value: { query, rows } }) : null,
@@ -2572,7 +2580,8 @@ function ConversationActions({ state, adapter, actionPending, onNew, onSettings
2572
2580
  state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
2573
2581
  state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
2574
2582
  state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null,
2575
- state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null
2583
+ state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null,
2584
+ adapter.openKeyboardShortcuts ? { key: "shortcuts", label: "Keyboard shortcuts", onSelect: adapter.openKeyboardShortcuts } : null
2576
2585
  ].filter(Boolean)
2577
2586
  },
2578
2587
  {
package/icon.mjs CHANGED
@@ -9,6 +9,10 @@ var ICONS = {
9
9
  /* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
10
10
  /* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
11
11
  ] }),
12
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
13
+ /* @__PURE__ */ jsx("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
14
+ /* @__PURE__ */ jsx("path", { d: "m10.5 5 2.5 2.5" })
15
+ ] }),
12
16
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
13
17
  /* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
14
18
  /* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
package/index.d.ts CHANGED
@@ -348,6 +348,8 @@ export interface UiAdapter {
348
348
  pickContext?(): TranscriptAttachment | TranscriptAttachment[] | null | Promise<TranscriptAttachment | TranscriptAttachment[] | null>;
349
349
  /** Resolve one projected historical image on demand. The UI owns and revokes its object URL. */
350
350
  resolveImage?(image: TranscriptImage): Blob | Promise<Blob>;
351
+ /** Open the embedding host's keyboard-shortcut reference from the standard overflow menu. */
352
+ openKeyboardShortcuts?(): void;
351
353
  onClose?(): void;
352
354
  onOpen?(): void;
353
355
  copyText?(value: string): void | Promise<void>;
@@ -477,6 +479,8 @@ export interface MessengerSlots {
477
479
  header?: ComponentType<ComponentOverrideProps>;
478
480
  /** Replace the conversation-list header. A component returning `null` delegates all list chrome to its host. */
479
481
  listHeader?: ComponentType<ComponentOverrideProps<{ query: string; rows: SessionRowModel[]; onNew?(): void }>>;
482
+ /** Add a compact host control beside conversation search without replacing search behavior. */
483
+ listToolbarActions?: ComponentType<ComponentOverrideProps<{ query: string; rows: SessionRowModel[]; onNew?(): void; canStart: boolean }>>;
480
484
  /** Add a compact consumer control to every default messenger header. `value` is the current view. */
481
485
  headerActions?: ComponentType<ComponentOverrideProps<'list' | 'chat' | 'new'>>;
482
486
  /** Host-owned rows rendered inside the searchable conversation scroller. */
@@ -543,7 +547,7 @@ export function parseSupercodeUiIntent(value: unknown): SupercodeUiIntent | null
543
547
 
544
548
  export function HarnessLogo(props: { id: HarnessId; activity?: SessionActivity; size?: number; onMissingLogo?(id: string): void }): VNode | null;
545
549
  export function AgentActivityLauncher(props: AgentActivityLauncherProps): VNode;
546
- export type UiIconName = 'attach' | 'back' | 'check' | 'chevron' | 'close' | 'copy' | 'down' | 'menu' | 'plus' | 'search' | 'send' | 'stop';
550
+ export type UiIconName = 'attach' | 'back' | 'check' | 'chevron' | 'close' | 'compose' | 'copy' | 'down' | 'menu' | 'plus' | 'search' | 'send' | 'stop';
547
551
  export function UiIcon(props: { name: UiIconName; size?: number; class?: string }): VNode | null;
548
552
  export function hasHarnessLogo(id: string): boolean;
549
553
  export function harnessLogoDataUrl(id: string): string | null;
package/messenger.mjs CHANGED
@@ -912,6 +912,10 @@ var ICONS = {
912
912
  /* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
913
913
  /* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
914
914
  ] }),
915
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
916
+ /* @__PURE__ */ jsx("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
917
+ /* @__PURE__ */ jsx("path", { d: "m10.5 5 2.5 2.5" })
918
+ ] }),
915
919
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
916
920
  /* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
917
921
  /* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
@@ -2262,6 +2266,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2262
2266
  const rows = filterSessions(state.sessions, query);
2263
2267
  const Row = components.SessionRow ?? SessionRow;
2264
2268
  const ListHeader = slots.listHeader;
2269
+ const ListToolbarActions = slots.listToolbarActions;
2265
2270
  const BeforeSessions = slots.beforeSessions;
2266
2271
  const AfterSessions = slots.afterSessions;
2267
2272
  useLayoutEffect3(() => {
@@ -2301,15 +2306,18 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2301
2306
  onClose ? /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx7(UiIcon, { name: "close", size: 18 }) }) : null
2302
2307
  ] }),
2303
2308
  state.startup !== "ready" ? /* @__PURE__ */ jsx7(LoadingStatus, { state, compact: rows.length > 0 }) : null,
2304
- state.sessions.length > 4 ? /* @__PURE__ */ jsxs6("label", { className: "scui-search", children: [
2305
- /* @__PURE__ */ jsx7(UiIcon, { name: "search", size: 15 }),
2306
- /* @__PURE__ */ jsx7("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
2307
- const value = event.currentTarget.value;
2308
- setQuery(value);
2309
- boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
2310
- if (rowScroller.current) rowScroller.current.scrollTop = 0;
2311
- } }),
2312
- /* @__PURE__ */ jsx7("small", { children: rows.length })
2309
+ state.sessions.length > 4 || ListToolbarActions ? /* @__PURE__ */ jsxs6("div", { className: "scui-list-toolbar", children: [
2310
+ state.sessions.length > 4 ? /* @__PURE__ */ jsxs6("label", { className: "scui-search", children: [
2311
+ /* @__PURE__ */ jsx7(UiIcon, { name: "search", size: 15 }),
2312
+ /* @__PURE__ */ jsx7("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
2313
+ const value = event.currentTarget.value;
2314
+ setQuery(value);
2315
+ boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
2316
+ if (rowScroller.current) rowScroller.current.scrollTop = 0;
2317
+ } }),
2318
+ query ? /* @__PURE__ */ jsx7("small", { children: rows.length }) : null
2319
+ ] }) : null,
2320
+ ListToolbarActions ? /* @__PURE__ */ jsx7(ListToolbarActions, { state, adapter, value: { query, rows, onNew, canStart: state.harnesses.some((item) => item.startable) } }) : null
2313
2321
  ] }) : null,
2314
2322
  /* @__PURE__ */ jsxs6("div", { className: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
2315
2323
  BeforeSessions ? /* @__PURE__ */ jsx7(BeforeSessions, { state, adapter, value: { query, rows } }) : null,
@@ -2569,7 +2577,8 @@ function ConversationActions({ state, adapter, actionPending, onNew, onSettings
2569
2577
  state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
2570
2578
  state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
2571
2579
  state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null,
2572
- state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null
2580
+ state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null,
2581
+ adapter.openKeyboardShortcuts ? { key: "shortcuts", label: "Keyboard shortcuts", onSelect: adapter.openKeyboardShortcuts } : null
2573
2582
  ].filter(Boolean)
2574
2583
  },
2575
2584
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter-ai-dev/supercode-ui",
3
- "version": "0.1.51",
3
+ "version": "0.1.53",
4
4
  "type": "module",
5
5
  "description": "Composable default UI kit for Supercode-powered coding-agent experiences",
6
6
  "exports": {
@@ -952,6 +952,10 @@ var ICONS = {
952
952
  /* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
953
953
  /* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
954
954
  ] }),
955
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
956
+ /* @__PURE__ */ jsx("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
957
+ /* @__PURE__ */ jsx("path", { d: "m10.5 5 2.5 2.5" })
958
+ ] }),
955
959
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
956
960
  /* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
957
961
  /* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
@@ -2348,6 +2352,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2348
2352
  const rows = filterSessions(state.sessions, query);
2349
2353
  const Row = components.SessionRow ?? SessionRow;
2350
2354
  const ListHeader = slots.listHeader;
2355
+ const ListToolbarActions = slots.listToolbarActions;
2351
2356
  const BeforeSessions = slots.beforeSessions;
2352
2357
  const AfterSessions = slots.afterSessions;
2353
2358
  useLayoutEffect3(() => {
@@ -2387,15 +2392,18 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2387
2392
  onClose ? /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }) : null
2388
2393
  ] }),
2389
2394
  state.startup !== "ready" ? /* @__PURE__ */ jsx8(LoadingStatus, { state, compact: rows.length > 0 }) : null,
2390
- state.sessions.length > 4 ? /* @__PURE__ */ jsxs7("label", { className: "scui-search", children: [
2391
- /* @__PURE__ */ jsx8(UiIcon, { name: "search", size: 15 }),
2392
- /* @__PURE__ */ jsx8("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
2393
- const value = event.currentTarget.value;
2394
- setQuery(value);
2395
- boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
2396
- if (rowScroller.current) rowScroller.current.scrollTop = 0;
2397
- } }),
2398
- /* @__PURE__ */ jsx8("small", { children: rows.length })
2395
+ state.sessions.length > 4 || ListToolbarActions ? /* @__PURE__ */ jsxs7("div", { className: "scui-list-toolbar", children: [
2396
+ state.sessions.length > 4 ? /* @__PURE__ */ jsxs7("label", { className: "scui-search", children: [
2397
+ /* @__PURE__ */ jsx8(UiIcon, { name: "search", size: 15 }),
2398
+ /* @__PURE__ */ jsx8("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
2399
+ const value = event.currentTarget.value;
2400
+ setQuery(value);
2401
+ boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
2402
+ if (rowScroller.current) rowScroller.current.scrollTop = 0;
2403
+ } }),
2404
+ query ? /* @__PURE__ */ jsx8("small", { children: rows.length }) : null
2405
+ ] }) : null,
2406
+ ListToolbarActions ? /* @__PURE__ */ jsx8(ListToolbarActions, { state, adapter, value: { query, rows, onNew, canStart: state.harnesses.some((item) => item.startable) } }) : null
2399
2407
  ] }) : null,
2400
2408
  /* @__PURE__ */ jsxs7("div", { className: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
2401
2409
  BeforeSessions ? /* @__PURE__ */ jsx8(BeforeSessions, { state, adapter, value: { query, rows } }) : null,
@@ -2655,7 +2663,8 @@ function ConversationActions({ state, adapter, actionPending, onNew, onSettings
2655
2663
  state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
2656
2664
  state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
2657
2665
  state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null,
2658
- state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null
2666
+ state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null,
2667
+ adapter.openKeyboardShortcuts ? { key: "shortcuts", label: "Keyboard shortcuts", onSelect: adapter.openKeyboardShortcuts } : null
2659
2668
  ].filter(Boolean)
2660
2669
  },
2661
2670
  {
@@ -110,6 +110,10 @@ var ICONS = {
110
110
  /* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
111
111
  /* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
112
112
  ] }),
113
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
114
+ /* @__PURE__ */ jsx("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
115
+ /* @__PURE__ */ jsx("path", { d: "m10.5 5 2.5 2.5" })
116
+ ] }),
113
117
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
114
118
  /* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
115
119
  /* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
@@ -603,6 +603,10 @@ var ICONS = {
603
603
  /* @__PURE__ */ jsx2("path", { d: "m4.75 4.75 8.5 8.5" }),
604
604
  /* @__PURE__ */ jsx2("path", { d: "m13.25 4.75-8.5 8.5" })
605
605
  ] }),
606
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
607
+ /* @__PURE__ */ jsx2("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
608
+ /* @__PURE__ */ jsx2("path", { d: "m10.5 5 2.5 2.5" })
609
+ ] }),
606
610
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
607
611
  /* @__PURE__ */ jsx2("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
608
612
  /* @__PURE__ */ jsx2("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
package/react/icon.mjs CHANGED
@@ -9,6 +9,10 @@ var ICONS = {
9
9
  /* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
10
10
  /* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
11
11
  ] }),
12
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
13
+ /* @__PURE__ */ jsx("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
14
+ /* @__PURE__ */ jsx("path", { d: "m10.5 5 2.5 2.5" })
15
+ ] }),
12
16
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
13
17
  /* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
14
18
  /* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
package/react/index.d.ts CHANGED
@@ -77,6 +77,8 @@ export interface MessengerComponents {
77
77
 
78
78
  export interface MessengerSlots {
79
79
  header?: ComponentType<ComponentOverrideProps>;
80
+ listHeader?: ComponentType<ComponentOverrideProps<{ query: string; rows: SessionRowModel[]; onNew?(): void }>>;
81
+ listToolbarActions?: ComponentType<ComponentOverrideProps<{ query: string; rows: SessionRowModel[]; onNew?(): void; canStart: boolean }>>;
80
82
  headerActions?: ComponentType<ComponentOverrideProps<'list' | 'chat' | 'new'>>;
81
83
  beforeSessions?: ComponentType<ComponentOverrideProps<{ query: string; rows: SessionRowModel[] }>>;
82
84
  afterSessions?: ComponentType<ComponentOverrideProps<{ query: string; rows: SessionRowModel[] }>>;
@@ -912,6 +912,10 @@ var ICONS = {
912
912
  /* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
913
913
  /* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
914
914
  ] }),
915
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
916
+ /* @__PURE__ */ jsx("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
917
+ /* @__PURE__ */ jsx("path", { d: "m10.5 5 2.5 2.5" })
918
+ ] }),
915
919
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
916
920
  /* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
917
921
  /* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
@@ -2262,6 +2266,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2262
2266
  const rows = filterSessions(state.sessions, query);
2263
2267
  const Row = components.SessionRow ?? SessionRow;
2264
2268
  const ListHeader = slots.listHeader;
2269
+ const ListToolbarActions = slots.listToolbarActions;
2265
2270
  const BeforeSessions = slots.beforeSessions;
2266
2271
  const AfterSessions = slots.afterSessions;
2267
2272
  useLayoutEffect3(() => {
@@ -2301,15 +2306,18 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2301
2306
  onClose ? /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx7(UiIcon, { name: "close", size: 18 }) }) : null
2302
2307
  ] }),
2303
2308
  state.startup !== "ready" ? /* @__PURE__ */ jsx7(LoadingStatus, { state, compact: rows.length > 0 }) : null,
2304
- state.sessions.length > 4 ? /* @__PURE__ */ jsxs6("label", { className: "scui-search", children: [
2305
- /* @__PURE__ */ jsx7(UiIcon, { name: "search", size: 15 }),
2306
- /* @__PURE__ */ jsx7("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
2307
- const value = event.currentTarget.value;
2308
- setQuery(value);
2309
- boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
2310
- if (rowScroller.current) rowScroller.current.scrollTop = 0;
2311
- } }),
2312
- /* @__PURE__ */ jsx7("small", { children: rows.length })
2309
+ state.sessions.length > 4 || ListToolbarActions ? /* @__PURE__ */ jsxs6("div", { className: "scui-list-toolbar", children: [
2310
+ state.sessions.length > 4 ? /* @__PURE__ */ jsxs6("label", { className: "scui-search", children: [
2311
+ /* @__PURE__ */ jsx7(UiIcon, { name: "search", size: 15 }),
2312
+ /* @__PURE__ */ jsx7("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
2313
+ const value = event.currentTarget.value;
2314
+ setQuery(value);
2315
+ boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
2316
+ if (rowScroller.current) rowScroller.current.scrollTop = 0;
2317
+ } }),
2318
+ query ? /* @__PURE__ */ jsx7("small", { children: rows.length }) : null
2319
+ ] }) : null,
2320
+ ListToolbarActions ? /* @__PURE__ */ jsx7(ListToolbarActions, { state, adapter, value: { query, rows, onNew, canStart: state.harnesses.some((item) => item.startable) } }) : null
2313
2321
  ] }) : null,
2314
2322
  /* @__PURE__ */ jsxs6("div", { className: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
2315
2323
  BeforeSessions ? /* @__PURE__ */ jsx7(BeforeSessions, { state, adapter, value: { query, rows } }) : null,
@@ -2569,7 +2577,8 @@ function ConversationActions({ state, adapter, actionPending, onNew, onSettings
2569
2577
  state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
2570
2578
  state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
2571
2579
  state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null,
2572
- state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null
2580
+ state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null,
2581
+ adapter.openKeyboardShortcuts ? { key: "shortcuts", label: "Keyboard shortcuts", onSelect: adapter.openKeyboardShortcuts } : null
2573
2582
  ].filter(Boolean)
2574
2583
  },
2575
2584
  {
@@ -184,6 +184,10 @@ var ICONS = {
184
184
  /* @__PURE__ */ jsx2("path", { d: "m4.75 4.75 8.5 8.5" }),
185
185
  /* @__PURE__ */ jsx2("path", { d: "m13.25 4.75-8.5 8.5" })
186
186
  ] }),
187
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
188
+ /* @__PURE__ */ jsx2("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
189
+ /* @__PURE__ */ jsx2("path", { d: "m10.5 5 2.5 2.5" })
190
+ ] }),
187
191
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
188
192
  /* @__PURE__ */ jsx2("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
189
193
  /* @__PURE__ */ jsx2("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
@@ -370,6 +374,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
370
374
  const rows = filterSessions(state.sessions, query);
371
375
  const Row = components.SessionRow ?? SessionRow;
372
376
  const ListHeader = slots.listHeader;
377
+ const ListToolbarActions = slots.listToolbarActions;
373
378
  const BeforeSessions = slots.beforeSessions;
374
379
  const AfterSessions = slots.afterSessions;
375
380
  useLayoutEffect2(() => {
@@ -409,15 +414,18 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
409
414
  onClose ? /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx6(UiIcon, { name: "close", size: 18 }) }) : null
410
415
  ] }),
411
416
  state.startup !== "ready" ? /* @__PURE__ */ jsx6(LoadingStatus, { state, compact: rows.length > 0 }) : null,
412
- state.sessions.length > 4 ? /* @__PURE__ */ jsxs5("label", { className: "scui-search", children: [
413
- /* @__PURE__ */ jsx6(UiIcon, { name: "search", size: 15 }),
414
- /* @__PURE__ */ jsx6("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
415
- const value = event.currentTarget.value;
416
- setQuery(value);
417
- boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
418
- if (rowScroller.current) rowScroller.current.scrollTop = 0;
419
- } }),
420
- /* @__PURE__ */ jsx6("small", { children: rows.length })
417
+ state.sessions.length > 4 || ListToolbarActions ? /* @__PURE__ */ jsxs5("div", { className: "scui-list-toolbar", children: [
418
+ state.sessions.length > 4 ? /* @__PURE__ */ jsxs5("label", { className: "scui-search", children: [
419
+ /* @__PURE__ */ jsx6(UiIcon, { name: "search", size: 15 }),
420
+ /* @__PURE__ */ jsx6("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
421
+ const value = event.currentTarget.value;
422
+ setQuery(value);
423
+ boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
424
+ if (rowScroller.current) rowScroller.current.scrollTop = 0;
425
+ } }),
426
+ query ? /* @__PURE__ */ jsx6("small", { children: rows.length }) : null
427
+ ] }) : null,
428
+ ListToolbarActions ? /* @__PURE__ */ jsx6(ListToolbarActions, { state, adapter, value: { query, rows, onNew, canStart: state.harnesses.some((item) => item.startable) } }) : null
421
429
  ] }) : null,
422
430
  /* @__PURE__ */ jsxs5("div", { className: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
423
431
  BeforeSessions ? /* @__PURE__ */ jsx6(BeforeSessions, { state, adapter, value: { query, rows } }) : null,
@@ -158,6 +158,10 @@ var ICONS = {
158
158
  /* @__PURE__ */ jsx2("path", { d: "m4.75 4.75 8.5 8.5" }),
159
159
  /* @__PURE__ */ jsx2("path", { d: "m13.25 4.75-8.5 8.5" })
160
160
  ] }),
161
+ compose: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
162
+ /* @__PURE__ */ jsx2("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
163
+ /* @__PURE__ */ jsx2("path", { d: "m10.5 5 2.5 2.5" })
164
+ ] }),
161
165
  copy: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
162
166
  /* @__PURE__ */ jsx2("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
163
167
  /* @__PURE__ */ jsx2("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
package/sessions.mjs CHANGED
@@ -184,6 +184,10 @@ var ICONS = {
184
184
  /* @__PURE__ */ jsx2("path", { d: "m4.75 4.75 8.5 8.5" }),
185
185
  /* @__PURE__ */ jsx2("path", { d: "m13.25 4.75-8.5 8.5" })
186
186
  ] }),
187
+ compose: () => /* @__PURE__ */ jsxs(Fragment, { children: [
188
+ /* @__PURE__ */ jsx2("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
189
+ /* @__PURE__ */ jsx2("path", { d: "m10.5 5 2.5 2.5" })
190
+ ] }),
187
191
  copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
188
192
  /* @__PURE__ */ jsx2("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
189
193
  /* @__PURE__ */ jsx2("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
@@ -370,6 +374,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
370
374
  const rows = filterSessions(state.sessions, query);
371
375
  const Row = components.SessionRow ?? SessionRow;
372
376
  const ListHeader = slots.listHeader;
377
+ const ListToolbarActions = slots.listToolbarActions;
373
378
  const BeforeSessions = slots.beforeSessions;
374
379
  const AfterSessions = slots.afterSessions;
375
380
  useLayoutEffect2(() => {
@@ -409,15 +414,18 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
409
414
  onClose ? /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx6(UiIcon, { name: "close", size: 18 }) }) : null
410
415
  ] }),
411
416
  state.startup !== "ready" ? /* @__PURE__ */ jsx6(LoadingStatus, { state, compact: rows.length > 0 }) : null,
412
- state.sessions.length > 4 ? /* @__PURE__ */ jsxs5("label", { className: "scui-search", children: [
413
- /* @__PURE__ */ jsx6(UiIcon, { name: "search", size: 15 }),
414
- /* @__PURE__ */ jsx6("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
415
- const value = event.currentTarget.value;
416
- setQuery(value);
417
- boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
418
- if (rowScroller.current) rowScroller.current.scrollTop = 0;
419
- } }),
420
- /* @__PURE__ */ jsx6("small", { children: rows.length })
417
+ state.sessions.length > 4 || ListToolbarActions ? /* @__PURE__ */ jsxs5("div", { className: "scui-list-toolbar", children: [
418
+ state.sessions.length > 4 ? /* @__PURE__ */ jsxs5("label", { className: "scui-search", children: [
419
+ /* @__PURE__ */ jsx6(UiIcon, { name: "search", size: 15 }),
420
+ /* @__PURE__ */ jsx6("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
421
+ const value = event.currentTarget.value;
422
+ setQuery(value);
423
+ boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
424
+ if (rowScroller.current) rowScroller.current.scrollTop = 0;
425
+ } }),
426
+ query ? /* @__PURE__ */ jsx6("small", { children: rows.length }) : null
427
+ ] }) : null,
428
+ ListToolbarActions ? /* @__PURE__ */ jsx6(ListToolbarActions, { state, adapter, value: { query, rows, onNew, canStart: state.harnesses.some((item) => item.startable) } }) : null
421
429
  ] }) : null,
422
430
  /* @__PURE__ */ jsxs5("div", { className: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
423
431
  BeforeSessions ? /* @__PURE__ */ jsx6(BeforeSessions, { state, adapter, value: { query, rows } }) : null,
package/settings.mjs CHANGED
@@ -158,6 +158,10 @@ var ICONS = {
158
158
  /* @__PURE__ */ jsx2("path", { d: "m4.75 4.75 8.5 8.5" }),
159
159
  /* @__PURE__ */ jsx2("path", { d: "m13.25 4.75-8.5 8.5" })
160
160
  ] }),
161
+ compose: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
162
+ /* @__PURE__ */ jsx2("path", { d: "M11.75 3.75 14.25 6.25 7 13.5l-3.25.75.75-3.25 7.25-7.25Z" }),
163
+ /* @__PURE__ */ jsx2("path", { d: "m10.5 5 2.5 2.5" })
164
+ ] }),
161
165
  copy: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
162
166
  /* @__PURE__ */ jsx2("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
163
167
  /* @__PURE__ */ jsx2("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
package/styles.css CHANGED
@@ -97,7 +97,9 @@
97
97
  .scui-progress i[data-progress="done"] { background:var(--scui-accent) }
98
98
  .scui-progress i[data-progress="current"]::after { content:""; display:block; width:55%; height:100%; background:var(--scui-accent); animation:scui-progress 1s ease-in-out infinite alternate }
99
99
 
100
- .scui-search { display:flex; flex:none; align-items:center; gap:7px; margin:8px; padding:6px 8px; border:1px solid var(--scui-border); border-radius:8px; background:var(--scui-bg-raised); color:var(--scui-muted) }
100
+ .scui-list-toolbar { display:flex; flex:none; align-items:center; gap:6px; padding:8px }
101
+ .scui-list-toolbar > :not(.scui-search) { flex:none }
102
+ .scui-search { display:flex; flex:1; min-width:0; align-items:center; gap:7px; padding:6px 8px; border:1px solid var(--scui-border); border-radius:8px; background:var(--scui-bg-raised); color:var(--scui-muted) }
101
103
  .scui-search input { flex:1; min-width:0; border:0; outline:0; background:transparent; color:var(--scui-fg) }
102
104
  .scui-session-rows { flex:1; min-height:0; overflow:auto; padding:0 6px 8px }
103
105
  .scui-session { display:flex; align-items:flex-start; gap:9px; width:100%; padding:8px; border:0; border-radius:7px; background:transparent; text-align:left; cursor:pointer }