@volter-ai-dev/supercode-ui 0.1.20 → 0.1.21
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 +7 -4
- package/components.mjs +193 -39
- package/composer.mjs +127 -23
- package/controller.mjs +21 -2
- package/conversation.mjs +213 -195
- package/core.mjs +12 -0
- package/embed.mjs +193 -39
- package/icon.mjs +5 -0
- package/index.d.ts +15 -5
- package/messenger.mjs +193 -39
- package/package.json +1 -1
- package/sessions.mjs +45 -36
- package/styles.css +2 -0
package/composer.mjs
CHANGED
|
@@ -98,6 +98,11 @@ var ICONS = {
|
|
|
98
98
|
/* @__PURE__ */ jsx("path", { d: "M9 3.5v10" }),
|
|
99
99
|
/* @__PURE__ */ jsx("path", { d: "m4.75 9.5 4.25 4 4.25-4" })
|
|
100
100
|
] }),
|
|
101
|
+
image: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
102
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3.5", width: "12", height: "11", rx: "1.75" }),
|
|
103
|
+
/* @__PURE__ */ jsx("circle", { cx: "6.5", cy: "7", r: "1.25" }),
|
|
104
|
+
/* @__PURE__ */ jsx("path", { d: "m4.5 13 3.25-3 2.1 1.85 1.65-1.5L14 13" })
|
|
105
|
+
] }),
|
|
101
106
|
menu: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
102
107
|
/* @__PURE__ */ jsx("circle", { cx: "4", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
|
|
103
108
|
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
|
|
@@ -126,6 +131,9 @@ function UiIcon({ name, size = 16, class: className = "" }) {
|
|
|
126
131
|
// src/context.jsx
|
|
127
132
|
import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
128
133
|
var MAX_CONTEXT_ITEMS = 32;
|
|
134
|
+
var MAX_IMAGE_ITEMS = 4;
|
|
135
|
+
var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
136
|
+
var IMAGE_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
|
|
129
137
|
function normalizeContext(value) {
|
|
130
138
|
return (Array.isArray(value) ? value : value ? [value] : []).flatMap((item) => {
|
|
131
139
|
if (!item || typeof item.label !== "string" || typeof item.detail !== "string") return [];
|
|
@@ -152,6 +160,58 @@ function mergeContext(current, picked) {
|
|
|
152
160
|
}
|
|
153
161
|
return next;
|
|
154
162
|
}
|
|
163
|
+
function normalizeImages(value) {
|
|
164
|
+
const seen = /* @__PURE__ */ new Set();
|
|
165
|
+
return (Array.isArray(value) ? value : value ? [value] : []).flatMap((item) => {
|
|
166
|
+
if (!item || typeof item.label !== "string" || typeof item.url !== "string") return [];
|
|
167
|
+
const label = item.label.trim().slice(0, 200);
|
|
168
|
+
const url = item.url;
|
|
169
|
+
if (!label || seen.has(url) || !(url.startsWith("data:image/") || url.startsWith("https://") || url.startsWith("http://"))) return [];
|
|
170
|
+
seen.add(url);
|
|
171
|
+
return [{
|
|
172
|
+
...typeof item.id === "string" && item.id ? { id: item.id.slice(0, 2e3) } : {},
|
|
173
|
+
label,
|
|
174
|
+
url
|
|
175
|
+
}];
|
|
176
|
+
}).slice(0, MAX_IMAGE_ITEMS);
|
|
177
|
+
}
|
|
178
|
+
function mergeImages(current, picked) {
|
|
179
|
+
const next = [...current];
|
|
180
|
+
const seen = new Set(current.map((item) => item.url));
|
|
181
|
+
for (const item of normalizeImages(picked)) {
|
|
182
|
+
if (seen.has(item.url)) continue;
|
|
183
|
+
seen.add(item.url);
|
|
184
|
+
next.push(item);
|
|
185
|
+
if (next.length === MAX_IMAGE_ITEMS) break;
|
|
186
|
+
}
|
|
187
|
+
return next;
|
|
188
|
+
}
|
|
189
|
+
function partitionAttachments(value) {
|
|
190
|
+
const values = Array.isArray(value) ? value : value ? [value] : [];
|
|
191
|
+
const context = [];
|
|
192
|
+
const images = [];
|
|
193
|
+
for (const item of values) {
|
|
194
|
+
if (item && typeof item.label === "string" && item.label.trim() && typeof item.detail === "string" && item.detail) context.push(item);
|
|
195
|
+
else if (item && typeof item.label === "string" && item.label.trim() && typeof item.url === "string" && (item.url.startsWith("data:image/") || item.url.startsWith("https://") || item.url.startsWith("http://"))) images.push(item);
|
|
196
|
+
else throw new Error("The attachment picker returned an invalid item.");
|
|
197
|
+
}
|
|
198
|
+
return { context, images };
|
|
199
|
+
}
|
|
200
|
+
async function imageAttachmentsFromFiles(value) {
|
|
201
|
+
const files = Array.from(value ?? []).filter((file) => file?.type?.startsWith("image/"));
|
|
202
|
+
if (files.length > MAX_IMAGE_ITEMS) throw new Error(`Attach at most ${MAX_IMAGE_ITEMS} images at a time.`);
|
|
203
|
+
return Promise.all(files.map(async (file) => {
|
|
204
|
+
if (!IMAGE_TYPES.has(file.type)) throw new Error(`${file.name || "That image"} is not PNG, JPEG, GIF, or WebP.`);
|
|
205
|
+
if (file.size > MAX_IMAGE_BYTES) throw new Error(`${file.name || "That image"} is larger than 5 MB.`);
|
|
206
|
+
const url = await new Promise((resolve, reject) => {
|
|
207
|
+
const reader = new FileReader();
|
|
208
|
+
reader.onload = () => resolve(reader.result);
|
|
209
|
+
reader.onerror = () => reject(reader.error ?? new Error(`Could not read ${file.name || "image"}.`));
|
|
210
|
+
reader.readAsDataURL(file);
|
|
211
|
+
});
|
|
212
|
+
return { id: `${file.name}:${file.size}:${file.lastModified}`, label: file.name || "Pasted image", url };
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
155
215
|
function ContextTray({ items, onRemove }) {
|
|
156
216
|
if (!items.length) return null;
|
|
157
217
|
return /* @__PURE__ */ jsx2("div", { class: "scui-compose-context", "aria-label": "Attached context", children: items.map((item, index) => /* @__PURE__ */ jsxs2("span", { children: [
|
|
@@ -160,6 +220,14 @@ function ContextTray({ items, onRemove }) {
|
|
|
160
220
|
/* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `Remove context ${item.label}`, onClick: () => onRemove(index), children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 12 }) })
|
|
161
221
|
] }, item.id ?? `${item.label}:${index}`)) });
|
|
162
222
|
}
|
|
223
|
+
function ImageTray({ items, onRemove }) {
|
|
224
|
+
if (!items.length) return null;
|
|
225
|
+
return /* @__PURE__ */ jsx2("div", { class: "scui-compose-images", "aria-label": "Attached images", children: items.map((item, index) => /* @__PURE__ */ jsxs2("span", { children: [
|
|
226
|
+
/* @__PURE__ */ jsx2("img", { src: item.url, alt: "" }),
|
|
227
|
+
/* @__PURE__ */ jsx2("strong", { children: item.label }),
|
|
228
|
+
onRemove ? /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `Remove image ${item.label}`, onClick: () => onRemove(index), children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 12 }) }) : null
|
|
229
|
+
] }, item.id ?? `${item.label}:${index}`)) });
|
|
230
|
+
}
|
|
163
231
|
|
|
164
232
|
// src/textarea.js
|
|
165
233
|
import { useLayoutEffect } from "preact/hooks";
|
|
@@ -199,19 +267,23 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
199
267
|
] });
|
|
200
268
|
}
|
|
201
269
|
function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
|
|
202
|
-
const remembered = composerMemory.get(memoryKey) ?? { draft: state.savedDraft, context: [], queue: [] };
|
|
270
|
+
const remembered = composerMemory.get(memoryKey) ?? { draft: state.savedDraft, context: [], images: [], queue: [] };
|
|
203
271
|
const [draft, setDraft] = useState(remembered.draft);
|
|
204
|
-
const [context, setContext] = useState(remembered.context);
|
|
205
|
-
const [
|
|
272
|
+
const [context, setContext] = useState(remembered.context ?? []);
|
|
273
|
+
const [images, setImages] = useState(remembered.images ?? []);
|
|
274
|
+
const [queue, setQueue] = useState((remembered.queue ?? []).map((item) => ({ ...item, context: item.context ?? [], images: item.images ?? [] })));
|
|
206
275
|
const [dispatching, setDispatching] = useState(false);
|
|
207
276
|
const [picking, setPicking] = useState(false);
|
|
208
277
|
const [pickerError, setPickerError] = useState(null);
|
|
209
278
|
const textarea = useRef(null);
|
|
210
279
|
useAutosizeTextarea(textarea, draft);
|
|
211
|
-
const remember = (nextDraft, nextContext, nextQueue) => boundedSet(composerMemory, memoryKey, { draft: nextDraft, context: nextContext, queue: nextQueue });
|
|
280
|
+
const remember = (nextDraft, nextContext, nextImages, nextQueue) => boundedSet(composerMemory, memoryKey, { draft: nextDraft, context: nextContext, images: nextImages, queue: nextQueue });
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
remember(draft, context, images, queue);
|
|
283
|
+
}, [draft, context, images, memoryKey, queue]);
|
|
212
284
|
const updateQueue = (update) => setQueue((items) => {
|
|
213
285
|
const next = update(items);
|
|
214
|
-
remember(draft, context, next);
|
|
286
|
+
remember(draft, context, images, next);
|
|
215
287
|
return next;
|
|
216
288
|
});
|
|
217
289
|
const queueBlocked = state.busy || pendingStatus !== null || dispatching;
|
|
@@ -221,9 +293,9 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
221
293
|
const [next, ...rest] = queue;
|
|
222
294
|
setDispatching(true);
|
|
223
295
|
setQueue(rest);
|
|
224
|
-
remember(draft, context, rest);
|
|
225
|
-
onPending?.(next.text, next.context);
|
|
226
|
-
adapter.onIntent({ action: "send", text: next.text, ...next.context.length ? { context: next.context } : {} });
|
|
296
|
+
remember(draft, context, images, rest);
|
|
297
|
+
onPending?.(next.text, next.context, next.images);
|
|
298
|
+
adapter.onIntent({ action: "send", text: next.text, ...next.context.length ? { context: next.context } : {}, ...next.images.length ? { images: next.images } : {} });
|
|
227
299
|
}
|
|
228
300
|
}, [adapter, draft, memoryKey, onPending, queue, queueBlocked, state.canSend]);
|
|
229
301
|
useEffect(() => {
|
|
@@ -236,8 +308,10 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
236
308
|
if (!restoreDraft) return;
|
|
237
309
|
setDraft(restoreDraft.text);
|
|
238
310
|
const restoredContext = normalizeContext(restoreDraft.context);
|
|
311
|
+
const restoredImages = normalizeImages(restoreDraft.images);
|
|
239
312
|
setContext(restoredContext);
|
|
240
|
-
|
|
313
|
+
setImages(restoredImages);
|
|
314
|
+
remember(restoreDraft.text, restoredContext, restoredImages, queue);
|
|
241
315
|
textarea.current?.focus({ preventScroll: true });
|
|
242
316
|
onDraftRestored?.(restoreDraft.id);
|
|
243
317
|
}, [onDraftRestored, restoreDraft?.id]);
|
|
@@ -246,30 +320,55 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
246
320
|
return () => clearTimeout(timer);
|
|
247
321
|
}, [adapter, draft]);
|
|
248
322
|
const pickContext = () => {
|
|
249
|
-
if (!adapter.pickContext || state.mode !== "control" || picking || context.length >= MAX_CONTEXT_ITEMS) return;
|
|
323
|
+
if (!adapter.pickContext || state.mode !== "control" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS) return;
|
|
250
324
|
setPicking(true);
|
|
251
325
|
setPickerError(null);
|
|
252
326
|
Promise.resolve().then(() => adapter.pickContext()).then((picked) => {
|
|
327
|
+
const attachments = partitionAttachments(picked);
|
|
328
|
+
if (context.length + attachments.context.length > MAX_CONTEXT_ITEMS) throw new Error(`Attach at most ${MAX_CONTEXT_ITEMS} text items.`);
|
|
329
|
+
if (images.length + attachments.images.length > MAX_IMAGE_ITEMS) throw new Error(`Attach at most ${MAX_IMAGE_ITEMS} images.`);
|
|
253
330
|
setContext((current) => {
|
|
254
|
-
const next = mergeContext(current,
|
|
255
|
-
remember(draft, next, queue);
|
|
331
|
+
const next = mergeContext(current, attachments.context);
|
|
332
|
+
remember(draft, next, images, queue);
|
|
333
|
+
return next;
|
|
334
|
+
});
|
|
335
|
+
setImages((current) => {
|
|
336
|
+
const next = mergeImages(current, attachments.images);
|
|
337
|
+
remember(draft, context, next, queue);
|
|
256
338
|
return next;
|
|
257
339
|
});
|
|
258
|
-
}
|
|
340
|
+
}).catch((error) => setPickerError(error instanceof Error ? error.message : "Could not attach context.")).finally(() => setPicking(false));
|
|
341
|
+
};
|
|
342
|
+
const pasteImages = (event) => {
|
|
343
|
+
const files = Array.from(event.clipboardData?.files ?? []).filter((file) => file.type.startsWith("image/"));
|
|
344
|
+
if (!files.length) return;
|
|
345
|
+
event.preventDefault();
|
|
346
|
+
if (images.length + files.length > MAX_IMAGE_ITEMS) {
|
|
347
|
+
setPickerError(`Attach at most ${MAX_IMAGE_ITEMS} images.`);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
setPicking(true);
|
|
351
|
+
setPickerError(null);
|
|
352
|
+
imageAttachmentsFromFiles(files).then((picked) => setImages((current) => {
|
|
353
|
+
const next = mergeImages(current, picked);
|
|
354
|
+
remember(draft, context, next, queue);
|
|
355
|
+
return next;
|
|
356
|
+
}), (error) => setPickerError(error instanceof Error ? error.message : "Could not paste image.")).finally(() => setPicking(false));
|
|
259
357
|
};
|
|
260
358
|
const send = () => {
|
|
261
359
|
const text = draft.trim();
|
|
262
360
|
if (!text) return;
|
|
263
|
-
const message = { text, context };
|
|
361
|
+
const message = { text, context, images };
|
|
264
362
|
if (queuesNewMessage) updateQueue((items) => [...items, message]);
|
|
265
363
|
else if (state.canSend) {
|
|
266
364
|
if (onPending) setDispatching(true);
|
|
267
|
-
onPending?.(text, context);
|
|
268
|
-
adapter.onIntent({ action: "send", text, ...context.length ? { context } : {} });
|
|
365
|
+
onPending?.(text, context, images);
|
|
366
|
+
adapter.onIntent({ action: "send", text, ...context.length ? { context } : {}, ...images.length ? { images } : {} });
|
|
269
367
|
} else return;
|
|
270
368
|
setDraft("");
|
|
271
369
|
setContext([]);
|
|
272
|
-
|
|
370
|
+
setImages([]);
|
|
371
|
+
remember("", [], [], queuesNewMessage ? [...queue, message] : queue);
|
|
273
372
|
};
|
|
274
373
|
return /* @__PURE__ */ jsxs3("div", { class: "scui-compose", children: [
|
|
275
374
|
queue.length ? /* @__PURE__ */ jsxs3("div", { class: "scui-queue", children: [
|
|
@@ -280,26 +379,31 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
280
379
|
queue.map((item, index) => /* @__PURE__ */ jsxs3("span", { children: [
|
|
281
380
|
/* @__PURE__ */ jsxs3("span", { children: [
|
|
282
381
|
item.text,
|
|
283
|
-
item.context.length ? /* @__PURE__ */ jsxs3("small", { children: [
|
|
284
|
-
item.context.length,
|
|
382
|
+
item.context.length + item.images.length ? /* @__PURE__ */ jsxs3("small", { children: [
|
|
383
|
+
item.context.length + item.images.length,
|
|
285
384
|
" attached"
|
|
286
385
|
] }) : null
|
|
287
386
|
] }),
|
|
288
387
|
/* @__PURE__ */ jsx3("button", { type: "button", "aria-label": `Remove queued message ${index + 1}`, onClick: () => updateQueue((items) => items.filter((_, itemIndex) => itemIndex !== index)), children: /* @__PURE__ */ jsx3(UiIcon, { name: "close", size: 13 }) })
|
|
289
388
|
] }, `${index}:${item.text}`))
|
|
290
389
|
] }) : null,
|
|
390
|
+
/* @__PURE__ */ jsx3(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
|
|
391
|
+
const next = items.filter((_, itemIndex) => itemIndex !== index);
|
|
392
|
+
remember(draft, context, next, queue);
|
|
393
|
+
return next;
|
|
394
|
+
}) }),
|
|
291
395
|
/* @__PURE__ */ jsx3(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
|
|
292
396
|
const next = items.filter((_, itemIndex) => itemIndex !== index);
|
|
293
|
-
remember(draft, next, queue);
|
|
397
|
+
remember(draft, next, images, queue);
|
|
294
398
|
return next;
|
|
295
399
|
}) }),
|
|
296
400
|
pickerError ? /* @__PURE__ */ jsx3("small", { class: "scui-context-error", role: "alert", children: pickerError }) : null,
|
|
297
401
|
/* @__PURE__ */ jsxs3("div", { class: "scui-envelope", children: [
|
|
298
|
-
adapter.pickContext && state.mode === "control" ? /* @__PURE__ */ jsx3("button", { class: "scui-attach", type: "button", "aria-label": "Attach
|
|
299
|
-
/* @__PURE__ */ jsx3("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) => {
|
|
402
|
+
adapter.pickContext && state.mode === "control" ? /* @__PURE__ */ jsx3("button", { class: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS, onClick: pickContext, children: picking ? /* @__PURE__ */ jsx3("i", { class: "scui-control-spinner" }) : /* @__PURE__ */ jsx3(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
403
|
+
/* @__PURE__ */ jsx3("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, onPaste: pasteImages, onInput: (event) => {
|
|
300
404
|
const value = event.currentTarget.value;
|
|
301
405
|
setDraft(value);
|
|
302
|
-
remember(value, context, queue);
|
|
406
|
+
remember(value, context, images, queue);
|
|
303
407
|
}, onKeyDown: (event) => {
|
|
304
408
|
if (isSendKey(event)) {
|
|
305
409
|
event.preventDefault();
|
package/controller.mjs
CHANGED
|
@@ -99,6 +99,23 @@ function projectContext(context) {
|
|
|
99
99
|
return projected.length ? projected : undefined;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
function projectImages(images) {
|
|
103
|
+
if (!Array.isArray(images)) return undefined;
|
|
104
|
+
const projected = images.flatMap((item) => {
|
|
105
|
+
if (!item || typeof item !== 'object' || typeof item.label !== 'string') return [];
|
|
106
|
+
const url = typeof item.url === 'string' && !item.url.endsWith('\n…')
|
|
107
|
+
&& (!item.url.startsWith('data:image/') || item.url.length <= 256_000)
|
|
108
|
+
? item.url
|
|
109
|
+
: null;
|
|
110
|
+
return [{
|
|
111
|
+
...(typeof item.id === 'string' ? { id: item.id } : {}),
|
|
112
|
+
label: item.label,
|
|
113
|
+
...(url ? { url } : {}),
|
|
114
|
+
}];
|
|
115
|
+
}).slice(0, 4);
|
|
116
|
+
return projected.length ? projected : undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
102
119
|
function requestSummary(entry) {
|
|
103
120
|
const options = Array.isArray(entry.options)
|
|
104
121
|
? entry.options.map((option) => option?.name).filter(Boolean).join(' / ')
|
|
@@ -113,6 +130,7 @@ function projectConversationEntry(entry, maxEntryChars) {
|
|
|
113
130
|
if (entry.visibility === 'context') return null;
|
|
114
131
|
const body = truncate(entry.text, maxEntryChars);
|
|
115
132
|
const context = projectContext(entry.context);
|
|
133
|
+
const images = projectImages(entry.images);
|
|
116
134
|
return {
|
|
117
135
|
id: entry.id,
|
|
118
136
|
role: entry.role,
|
|
@@ -121,6 +139,7 @@ function projectConversationEntry(entry, maxEntryChars) {
|
|
|
121
139
|
ts: timestampFromMetadata(entry.metadata),
|
|
122
140
|
truncated: body.truncated,
|
|
123
141
|
...(context ? { context } : {}),
|
|
142
|
+
...(images ? { images } : {}),
|
|
124
143
|
};
|
|
125
144
|
}
|
|
126
145
|
if (entry.kind === 'tool') {
|
|
@@ -376,10 +395,10 @@ async function dispatchStandard(controller, intent, options) {
|
|
|
376
395
|
const active = snapshot.activeSessionKey;
|
|
377
396
|
if (intent.action === 'mounted') return;
|
|
378
397
|
if (intent.action === 'attach') return controller.dispatch({ type: 'observe', sessionKey: intent.key });
|
|
379
|
-
if (intent.action === 'send') return controller.dispatch({ type: 'send', text: intent.text, ...(intent.context?.length ? { context: intent.context } : {}) });
|
|
398
|
+
if (intent.action === 'send') return controller.dispatch({ type: 'send', text: intent.text, ...(intent.context?.length ? { context: intent.context } : {}), ...(intent.images?.length ? { images: intent.images } : {}) });
|
|
380
399
|
if (intent.action === 'new') {
|
|
381
400
|
await controller.dispatch({ type: 'start', harness: intent.harness });
|
|
382
|
-
return controller.dispatch({ type: 'send', text: intent.text, ...(intent.context?.length ? { context: intent.context } : {}) });
|
|
401
|
+
return controller.dispatch({ type: 'send', text: intent.text, ...(intent.context?.length ? { context: intent.context } : {}), ...(intent.images?.length ? { images: intent.images } : {}) });
|
|
383
402
|
}
|
|
384
403
|
if (intent.action === 'resume' && active) return controller.dispatch({ type: 'resume', sessionKey: active });
|
|
385
404
|
if (intent.action === 'join' && active) return controller.dispatch({ type: 'attach', sessionKey: active });
|