@volter-ai-dev/supercode-ui 0.1.52 → 0.1.54

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
@@ -138,7 +138,11 @@ Hosts do not need to fork the messenger to connect their own object model:
138
138
  }}
139
139
  contextCandidates={visibleObjects.map(asTranscriptContext)}
140
140
  components={{ ContextCandidate: ObjectPreview }}
141
- slots={{ beforeSessions: ProjectThreads }}
141
+ slots={{
142
+ listHeader: () => null,
143
+ listToolbarActions: NewConversationButton,
144
+ beforeSessions: ProjectThreads,
145
+ }}
142
146
  />
143
147
  ```
144
148
 
@@ -220,6 +224,8 @@ its external agent is working or needs input.
220
224
  replacement. Replacements receive the same typed, capability-filtered props as defaults.
221
225
  - `slots.headerActions` adds compact product controls to the default list, chat, and new-chat
222
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.
223
229
  - Stable `scui-*` classes and `data-*` attributes support additive styling; CSS custom properties
224
230
  are the supported theme API.
225
231
  - `styles.css` contains both neutral tokens and default component rules. Teams may load it whole,
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" })
@@ -2094,13 +2098,13 @@ function ToolRow({ entry, workspace, open = false, adapter }) {
2094
2098
  ] });
2095
2099
  }
2096
2100
  function ActivityGroup({ entries, state, adapter }) {
2097
- if (entries.length === 1) return /* @__PURE__ */ jsx7("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx7(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
2098
2101
  const active = entries.some((entry) => entry.status === "pending");
2099
2102
  const [open, setOpen] = useState3(active);
2100
2103
  const id = useId();
2101
2104
  useEffect5(() => {
2102
2105
  if (active) setOpen(true);
2103
2106
  }, [active]);
2107
+ if (entries.length === 1) return /* @__PURE__ */ jsx7("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx7(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
2104
2108
  return /* @__PURE__ */ jsxs6("section", { className: "scui-activity", children: [
2105
2109
  /* @__PURE__ */ jsxs6("button", { className: "scui-activity-head", type: "button", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
2106
2110
  /* @__PURE__ */ jsx7("span", { className: "scui-fold", "data-open": open, children: /* @__PURE__ */ jsx7(UiIcon, { name: "chevron", size: 14 }) }),
@@ -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,
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" })
@@ -1130,13 +1134,13 @@ function ToolRow({ entry, workspace, open = false, adapter }) {
1130
1134
  ] });
1131
1135
  }
1132
1136
  function ActivityGroup({ entries, state, adapter }) {
1133
- if (entries.length === 1) return /* @__PURE__ */ jsx4("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx4(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
1134
1137
  const active = entries.some((entry) => entry.status === "pending");
1135
1138
  const [open, setOpen] = useState2(active);
1136
1139
  const id = useId();
1137
1140
  useEffect3(() => {
1138
1141
  if (active) setOpen(true);
1139
1142
  }, [active]);
1143
+ if (entries.length === 1) return /* @__PURE__ */ jsx4("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx4(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
1140
1144
  return /* @__PURE__ */ jsxs3("section", { className: "scui-activity", children: [
1141
1145
  /* @__PURE__ */ jsxs3("button", { className: "scui-activity-head", type: "button", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
1142
1146
  /* @__PURE__ */ jsx4("span", { className: "scui-fold", "data-open": open, children: /* @__PURE__ */ jsx4(UiIcon, { name: "chevron", size: 14 }) }),
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" })
@@ -1950,13 +1954,13 @@ function ToolRow({ entry, workspace, open = false, adapter }) {
1950
1954
  ] });
1951
1955
  }
1952
1956
  function ActivityGroup({ entries, state, adapter }) {
1953
- if (entries.length === 1) return /* @__PURE__ */ jsx5("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx5(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
1954
1957
  const active = entries.some((entry) => entry.status === "pending");
1955
1958
  const [open, setOpen] = useState3(active);
1956
1959
  const id = useId();
1957
1960
  useEffect4(() => {
1958
1961
  if (active) setOpen(true);
1959
1962
  }, [active]);
1963
+ if (entries.length === 1) return /* @__PURE__ */ jsx5("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx5(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
1960
1964
  return /* @__PURE__ */ jsxs4("section", { className: "scui-activity", children: [
1961
1965
  /* @__PURE__ */ jsxs4("button", { className: "scui-activity-head", type: "button", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
1962
1966
  /* @__PURE__ */ jsx5("span", { className: "scui-fold", "data-open": open, children: /* @__PURE__ */ jsx5(UiIcon, { name: "chevron", size: 14 }) }),
@@ -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,
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
@@ -479,6 +479,8 @@ export interface MessengerSlots {
479
479
  header?: ComponentType<ComponentOverrideProps>;
480
480
  /** Replace the conversation-list header. A component returning `null` delegates all list chrome to its host. */
481
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 }>>;
482
484
  /** Add a compact consumer control to every default messenger header. `value` is the current view. */
483
485
  headerActions?: ComponentType<ComponentOverrideProps<'list' | 'chat' | 'new'>>;
484
486
  /** Host-owned rows rendered inside the searchable conversation scroller. */
@@ -545,7 +547,7 @@ export function parseSupercodeUiIntent(value: unknown): SupercodeUiIntent | null
545
547
 
546
548
  export function HarnessLogo(props: { id: HarnessId; activity?: SessionActivity; size?: number; onMissingLogo?(id: string): void }): VNode | null;
547
549
  export function AgentActivityLauncher(props: AgentActivityLauncherProps): VNode;
548
- 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';
549
551
  export function UiIcon(props: { name: UiIconName; size?: number; class?: string }): VNode | null;
550
552
  export function hasHarnessLogo(id: string): boolean;
551
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" })
@@ -1947,13 +1951,13 @@ function ToolRow({ entry, workspace, open = false, adapter }) {
1947
1951
  ] });
1948
1952
  }
1949
1953
  function ActivityGroup({ entries, state, adapter }) {
1950
- if (entries.length === 1) return /* @__PURE__ */ jsx5("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx5(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
1951
1954
  const active = entries.some((entry) => entry.status === "pending");
1952
1955
  const [open, setOpen] = useState3(active);
1953
1956
  const id = useId();
1954
1957
  useEffect4(() => {
1955
1958
  if (active) setOpen(true);
1956
1959
  }, [active]);
1960
+ if (entries.length === 1) return /* @__PURE__ */ jsx5("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx5(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
1957
1961
  return /* @__PURE__ */ jsxs4("section", { className: "scui-activity", children: [
1958
1962
  /* @__PURE__ */ jsxs4("button", { className: "scui-activity-head", type: "button", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
1959
1963
  /* @__PURE__ */ jsx5("span", { className: "scui-fold", "data-open": open, children: /* @__PURE__ */ jsx5(UiIcon, { name: "chevron", size: 14 }) }),
@@ -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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter-ai-dev/supercode-ui",
3
- "version": "0.1.52",
3
+ "version": "0.1.54",
4
4
  "type": "module",
5
5
  "description": "Composable default UI kit for Supercode-powered coding-agent experiences",
6
6
  "exports": {
@@ -157,7 +157,6 @@
157
157
  }
158
158
  },
159
159
  "devDependencies": {
160
- "@volter-ai-dev/supercode-client": "0.3.22",
161
160
  "@storybook/addon-a11y": "10.5.9",
162
161
  "@storybook/addon-docs": "10.5.9",
163
162
  "@storybook/addon-themes": "10.5.9",
@@ -165,10 +164,13 @@
165
164
  "@storybook/preact-vite": "10.5.9",
166
165
  "@types/react": "19.2.18",
167
166
  "@vitest/browser-playwright": "4.1.10",
167
+ "@volter-ai-dev/supercode-client": "0.3.22",
168
168
  "esbuild": "^0.25.0",
169
169
  "playwright": "1.62.1",
170
170
  "preact": "^10.19.0",
171
171
  "preact-render-to-string": "^6.5.0",
172
+ "react": "^19.2.8",
173
+ "react-test-renderer": "^19.2.8",
172
174
  "storybook": "10.5.9",
173
175
  "typescript": "5.6.3",
174
176
  "vite": "7.3.6",
@@ -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" })
@@ -2094,13 +2098,13 @@ function ToolRow({ entry, workspace, open = false, adapter }) {
2094
2098
  ] });
2095
2099
  }
2096
2100
  function ActivityGroup({ entries, state, adapter }) {
2097
- if (entries.length === 1) return /* @__PURE__ */ jsx7("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx7(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
2098
2101
  const active = entries.some((entry) => entry.status === "pending");
2099
2102
  const [open, setOpen] = useState3(active);
2100
2103
  const id = useId();
2101
2104
  useEffect5(() => {
2102
2105
  if (active) setOpen(true);
2103
2106
  }, [active]);
2107
+ if (entries.length === 1) return /* @__PURE__ */ jsx7("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx7(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
2104
2108
  return /* @__PURE__ */ jsxs6("section", { className: "scui-activity", children: [
2105
2109
  /* @__PURE__ */ jsxs6("button", { className: "scui-activity-head", type: "button", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
2106
2110
  /* @__PURE__ */ jsx7("span", { className: "scui-fold", "data-open": open, children: /* @__PURE__ */ jsx7(UiIcon, { name: "chevron", size: 14 }) }),
@@ -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,
@@ -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" })
@@ -1130,13 +1134,13 @@ function ToolRow({ entry, workspace, open = false, adapter }) {
1130
1134
  ] });
1131
1135
  }
1132
1136
  function ActivityGroup({ entries, state, adapter }) {
1133
- if (entries.length === 1) return /* @__PURE__ */ jsx4("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx4(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
1134
1137
  const active = entries.some((entry) => entry.status === "pending");
1135
1138
  const [open, setOpen] = useState2(active);
1136
1139
  const id = useId();
1137
1140
  useEffect3(() => {
1138
1141
  if (active) setOpen(true);
1139
1142
  }, [active]);
1143
+ if (entries.length === 1) return /* @__PURE__ */ jsx4("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx4(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
1140
1144
  return /* @__PURE__ */ jsxs3("section", { className: "scui-activity", children: [
1141
1145
  /* @__PURE__ */ jsxs3("button", { className: "scui-activity-head", type: "button", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
1142
1146
  /* @__PURE__ */ jsx4("span", { className: "scui-fold", "data-open": open, children: /* @__PURE__ */ jsx4(UiIcon, { name: "chevron", size: 14 }) }),
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" })
@@ -1947,13 +1951,13 @@ function ToolRow({ entry, workspace, open = false, adapter }) {
1947
1951
  ] });
1948
1952
  }
1949
1953
  function ActivityGroup({ entries, state, adapter }) {
1950
- if (entries.length === 1) return /* @__PURE__ */ jsx5("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx5(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
1951
1954
  const active = entries.some((entry) => entry.status === "pending");
1952
1955
  const [open, setOpen] = useState3(active);
1953
1956
  const id = useId();
1954
1957
  useEffect4(() => {
1955
1958
  if (active) setOpen(true);
1956
1959
  }, [active]);
1960
+ if (entries.length === 1) return /* @__PURE__ */ jsx5("section", { className: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx5(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
1957
1961
  return /* @__PURE__ */ jsxs4("section", { className: "scui-activity", children: [
1958
1962
  /* @__PURE__ */ jsxs4("button", { className: "scui-activity-head", type: "button", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
1959
1963
  /* @__PURE__ */ jsx5("span", { className: "scui-fold", "data-open": open, children: /* @__PURE__ */ jsx5(UiIcon, { name: "chevron", size: 14 }) }),
@@ -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,
@@ -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 }