@volter-ai-dev/supercode-ui 0.1.13 → 0.1.14

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/composer.mjs CHANGED
@@ -79,8 +79,65 @@ function boundedSet(map, key, value) {
79
79
  while (map.size > MEMORY_LIMIT) map.delete(map.keys().next().value);
80
80
  }
81
81
 
82
+ // src/icon.jsx
83
+ import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
84
+ var ICONS = {
85
+ back: () => /* @__PURE__ */ jsx("path", { d: "m11.5 4.5-4.5 4.5 4.5 4.5" }),
86
+ check: () => /* @__PURE__ */ jsx("path", { d: "m4 9 3.25 3.25L14 5.5" }),
87
+ chevron: () => /* @__PURE__ */ jsx("path", { d: "m7 4.5 4.5 4.5L7 13.5" }),
88
+ close: () => /* @__PURE__ */ jsxs(Fragment, { children: [
89
+ /* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
90
+ /* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
91
+ ] }),
92
+ copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
93
+ /* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
94
+ /* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
95
+ ] }),
96
+ down: () => /* @__PURE__ */ jsxs(Fragment, { children: [
97
+ /* @__PURE__ */ jsx("path", { d: "M9 3.5v10" }),
98
+ /* @__PURE__ */ jsx("path", { d: "m4.75 9.5 4.25 4 4.25-4" })
99
+ ] }),
100
+ menu: () => /* @__PURE__ */ jsxs(Fragment, { children: [
101
+ /* @__PURE__ */ jsx("circle", { cx: "4", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
102
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
103
+ /* @__PURE__ */ jsx("circle", { cx: "14", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" })
104
+ ] }),
105
+ plus: () => /* @__PURE__ */ jsxs(Fragment, { children: [
106
+ /* @__PURE__ */ jsx("path", { d: "M9 3.5v11" }),
107
+ /* @__PURE__ */ jsx("path", { d: "M3.5 9h11" })
108
+ ] }),
109
+ search: () => /* @__PURE__ */ jsxs(Fragment, { children: [
110
+ /* @__PURE__ */ jsx("circle", { cx: "7.75", cy: "7.75", r: "4.25" }),
111
+ /* @__PURE__ */ jsx("path", { d: "m11 11 3.5 3.5" })
112
+ ] }),
113
+ send: () => /* @__PURE__ */ jsxs(Fragment, { children: [
114
+ /* @__PURE__ */ jsx("path", { d: "M9 14.5v-11" }),
115
+ /* @__PURE__ */ jsx("path", { d: "m4.75 7.75 4.25-4.25 4.25 4.25" })
116
+ ] }),
117
+ stop: () => /* @__PURE__ */ jsx("rect", { x: "4.5", y: "4.5", width: "9", height: "9", rx: "1.5", fill: "currentColor", stroke: "none" })
118
+ };
119
+ function UiIcon({ name, size = 16, class: className = "" }) {
120
+ const Glyph = ICONS[name];
121
+ if (!Glyph) return null;
122
+ return /* @__PURE__ */ jsx("svg", { class: `scui-icon ${className}`, style: { "--scui-icon-size": `${size}px` }, viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Glyph, {}) });
123
+ }
124
+
125
+ // src/textarea.js
126
+ import { useLayoutEffect } from "preact/hooks";
127
+ function useAutosizeTextarea(ref, value) {
128
+ useLayoutEffect(() => {
129
+ const element = ref.current;
130
+ if (!element) return;
131
+ element.style.height = "auto";
132
+ const maxHeight = Number.parseFloat(getComputedStyle(element).maxHeight) || 150;
133
+ const height = Math.min(element.scrollHeight, maxHeight);
134
+ element.style.height = `${height}px`;
135
+ element.style.overflowY = element.scrollHeight > maxHeight ? "auto" : "hidden";
136
+ }, [ref, value]);
137
+ }
138
+
82
139
  // src/composer.jsx
83
- import { jsx, jsxs } from "preact/jsx-runtime";
140
+ import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
84
141
  var composerMemory = /* @__PURE__ */ new Map();
85
142
  function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
86
143
  if (state.mode !== "mirror" || state.canSend) return null;
@@ -90,16 +147,16 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
90
147
  const resume = canContinueHere(state);
91
148
  const join = state.canAttach;
92
149
  const branch = state.canBranch;
93
- if (!resume && !join && !branch) return /* @__PURE__ */ jsx("div", { class: "scui-continuation", children: /* @__PURE__ */ jsxs("span", { children: [
94
- /* @__PURE__ */ jsx("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
95
- /* @__PURE__ */ jsx("small", { children: "This session cannot be continued by an available harness." })
150
+ if (!resume && !join && !branch) return /* @__PURE__ */ jsx2("div", { class: "scui-continuation", children: /* @__PURE__ */ jsxs2("span", { children: [
151
+ /* @__PURE__ */ jsx2("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
152
+ /* @__PURE__ */ jsx2("small", { children: "This session cannot be continued by an available harness." })
96
153
  ] }) });
97
- return /* @__PURE__ */ jsxs("div", { class: "scui-continuation", children: [
98
- /* @__PURE__ */ jsxs("span", { children: [
99
- /* @__PURE__ */ jsx("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
100
- /* @__PURE__ */ jsx("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
154
+ return /* @__PURE__ */ jsxs2("div", { class: "scui-continuation", children: [
155
+ /* @__PURE__ */ jsxs2("span", { children: [
156
+ /* @__PURE__ */ jsx2("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
157
+ /* @__PURE__ */ jsx2("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
101
158
  ] }),
102
- /* @__PURE__ */ jsx("button", { type: "button", disabled: Boolean(state.operation), onClick: () => adapter.onIntent(join ? { action: "join" } : resume ? { action: "resume" } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere })
159
+ /* @__PURE__ */ jsx2("button", { type: "button", disabled: Boolean(state.operation), onClick: () => adapter.onIntent(join ? { action: "join" } : resume ? { action: "resume" } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere })
103
160
  ] });
104
161
  }
105
162
  function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
@@ -108,6 +165,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
108
165
  const [queue, setQueue] = useState(remembered.queue);
109
166
  const [dispatching, setDispatching] = useState(false);
110
167
  const textarea = useRef(null);
168
+ useAutosizeTextarea(textarea, draft);
111
169
  const remember = (nextDraft, nextQueue) => boundedSet(composerMemory, memoryKey, { draft: nextDraft, queue: nextQueue });
112
170
  const updateQueue = (update) => setQueue((items) => {
113
171
  const next = update(items);
@@ -155,19 +213,19 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
155
213
  setDraft("");
156
214
  remember("", queuesNewMessage ? [...queue, text] : queue);
157
215
  };
158
- return /* @__PURE__ */ jsxs("div", { class: "scui-compose", children: [
159
- queue.length ? /* @__PURE__ */ jsxs("div", { class: "scui-queue", children: [
160
- /* @__PURE__ */ jsxs("strong", { children: [
216
+ return /* @__PURE__ */ jsxs2("div", { class: "scui-compose", children: [
217
+ queue.length ? /* @__PURE__ */ jsxs2("div", { class: "scui-queue", children: [
218
+ /* @__PURE__ */ jsxs2("strong", { children: [
161
219
  queue.length,
162
220
  " queued"
163
221
  ] }),
164
- queue.map((item, index) => /* @__PURE__ */ jsxs("span", { children: [
222
+ queue.map((item, index) => /* @__PURE__ */ jsxs2("span", { children: [
165
223
  item,
166
- /* @__PURE__ */ jsx("button", { type: "button", "aria-label": `Remove queued message ${index + 1}`, onClick: () => updateQueue((items) => items.filter((_, itemIndex) => itemIndex !== index)), children: "\xD7" })
224
+ /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `Remove queued message ${index + 1}`, onClick: () => updateQueue((items) => items.filter((_, itemIndex) => itemIndex !== index)), children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 13 }) })
167
225
  ] }, `${index}:${item}`))
168
226
  ] }) : null,
169
- /* @__PURE__ */ jsxs("div", { class: "scui-envelope", children: [
170
- /* @__PURE__ */ jsx("textarea", { ref: textarea, rows: 1, "aria-label": `Message ${harnessDisplayName(state.harness) || "agent"}`, placeholder: state.startup !== "ready" ? "Connecting\u2026" : pendingStatus === "failed" ? "Retry or edit the unsent message\u2026" : pendingStatus === "editing" ? "Edit and resend\u2026" : queueBlocked ? "Queue a follow-up\u2026" : labels.askAgent, value: draft, disabled: state.mode !== "control" && !state.canSend, onInput: (event) => {
227
+ /* @__PURE__ */ jsxs2("div", { class: "scui-envelope", children: [
228
+ /* @__PURE__ */ jsx2("textarea", { ref: textarea, rows: 1, "aria-label": `Message ${harnessDisplayName(state.harness) || "agent"}`, placeholder: state.startup !== "ready" ? "Connecting\u2026" : pendingStatus === "failed" ? "Retry or edit the unsent message\u2026" : pendingStatus === "editing" ? "Edit and resend\u2026" : queueBlocked ? "Queue a follow-up\u2026" : labels.askAgent, value: draft, disabled: state.mode !== "control" && !state.canSend, onInput: (event) => {
171
229
  const value = event.currentTarget.value;
172
230
  setDraft(value);
173
231
  remember(value, queue);
@@ -177,9 +235,9 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
177
235
  send();
178
236
  }
179
237
  } }),
180
- /* @__PURE__ */ jsxs("span", { children: [
181
- state.busy ? /* @__PURE__ */ jsx("button", { class: "scui-stop", type: "button", "aria-label": "Stop agent", disabled: !state.canInterrupt, onClick: () => adapter.onIntent({ action: "interrupt" }), children: "\u25A0" }) : null,
182
- /* @__PURE__ */ jsx("button", { class: "scui-send", type: "button", "aria-label": queuesNewMessage ? "Queue message" : "Send message", disabled: !draft.trim() || !queuesNewMessage && !state.canSend, onClick: send, children: queuesNewMessage ? "+" : "\u2191" })
238
+ /* @__PURE__ */ jsxs2("span", { children: [
239
+ state.busy ? /* @__PURE__ */ jsx2("button", { class: "scui-stop", type: "button", "aria-label": "Stop agent", disabled: !state.canInterrupt, onClick: () => adapter.onIntent({ action: "interrupt" }), children: /* @__PURE__ */ jsx2(UiIcon, { name: "stop", size: 15 }) }) : null,
240
+ /* @__PURE__ */ jsx2("button", { class: "scui-send", type: "button", "aria-label": queuesNewMessage ? "Queue message" : "Send message", disabled: !draft.trim() || !queuesNewMessage && !state.canSend, onClick: send, children: /* @__PURE__ */ jsx2(UiIcon, { name: queuesNewMessage ? "plus" : "send", size: 17 }) })
183
241
  ] })
184
242
  ] })
185
243
  ] });
package/controller.mjs CHANGED
@@ -116,6 +116,7 @@ function projectConversationEntry(entry, maxEntryChars) {
116
116
  return {
117
117
  id: entry.id,
118
118
  role: entry.role,
119
+ ...(Number.isSafeInteger(entry.messageIndex) && entry.messageIndex > 0 ? { messageIndex: entry.messageIndex } : {}),
119
120
  text: body.text,
120
121
  ts: timestampFromMetadata(entry.metadata),
121
122
  truncated: body.truncated,
@@ -129,6 +130,7 @@ function projectConversationEntry(entry, maxEntryChars) {
129
130
  const projected = {
130
131
  id: entry.id,
131
132
  role: 'tool',
133
+ ...(Number.isSafeInteger(entry.messageIndex) && entry.messageIndex > 0 ? { messageIndex: entry.messageIndex } : {}),
132
134
  text: primary.text,
133
135
  ts: timestampFromMetadata(entry.metadata),
134
136
  truncated: argumentsText.truncated || result.truncated,
@@ -401,9 +403,10 @@ export function createControllerBinding(controller, options = {}) {
401
403
  const getState = () => projectClientSnapshot(controller.getSnapshot(), options.projection?.() ?? {});
402
404
  const adapter = {
403
405
  onIntent(intent) {
404
- void Promise.resolve(options.onIntent?.(intent))
406
+ return Promise.resolve(options.onIntent?.(intent))
405
407
  .then(() => dispatchStandard(controller, intent, options))
406
- .catch((error) => options.onError?.(error, intent));
408
+ .catch((error) => options.onError?.(error, intent))
409
+ .then(() => undefined);
407
410
  },
408
411
  ...(options.copyText ? { copyText: options.copyText } : {}),
409
412
  };