@volter-ai-dev/supercode-ui 0.1.0
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 +116 -0
- package/components.mjs +963 -0
- package/composer.mjs +168 -0
- package/controller.d.ts +53 -0
- package/controller.mjs +262 -0
- package/conversation.mjs +375 -0
- package/core.mjs +349 -0
- package/embed.mjs +972 -0
- package/index.d.ts +313 -0
- package/index.mjs +3 -0
- package/logo.mjs +114 -0
- package/messenger.mjs +947 -0
- package/package.json +106 -0
- package/sessions.mjs +196 -0
- package/styles.css +154 -0
package/conversation.mjs
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
// src/components.jsx
|
|
2
|
+
import MarkdownIt from "markdown-it";
|
|
3
|
+
import { useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
4
|
+
|
|
5
|
+
// core.mjs
|
|
6
|
+
var HARNESS_NAMES = Object.freeze({
|
|
7
|
+
"claude-code": "Claude Code",
|
|
8
|
+
codex: "Codex",
|
|
9
|
+
opencode: "OpenCode",
|
|
10
|
+
pi: "Pi",
|
|
11
|
+
grok: "Grok"
|
|
12
|
+
});
|
|
13
|
+
var DEFAULT_LABELS = Object.freeze({
|
|
14
|
+
chats: "Chats",
|
|
15
|
+
newChat: "New chat",
|
|
16
|
+
searchChats: "Search chats",
|
|
17
|
+
askAgent: "Ask your agent\u2026",
|
|
18
|
+
continueHere: "Continue here",
|
|
19
|
+
joinLive: "Join live",
|
|
20
|
+
forkHere: "Fork here"
|
|
21
|
+
});
|
|
22
|
+
var EMPTY_UI_STATE = Object.freeze({
|
|
23
|
+
pill: Object.freeze({ tone: "off", label: "connecting\u2026" }),
|
|
24
|
+
startup: "connecting",
|
|
25
|
+
transcript: Object.freeze([]),
|
|
26
|
+
busy: false,
|
|
27
|
+
operation: null,
|
|
28
|
+
needsInput: false,
|
|
29
|
+
harness: "",
|
|
30
|
+
mode: "none",
|
|
31
|
+
strategy: null,
|
|
32
|
+
canSend: false,
|
|
33
|
+
canResume: false,
|
|
34
|
+
canBranch: false,
|
|
35
|
+
canAttach: false,
|
|
36
|
+
canDetach: false,
|
|
37
|
+
canOpenTerminal: false,
|
|
38
|
+
canExport: false,
|
|
39
|
+
canReduce: false,
|
|
40
|
+
canInterrupt: false,
|
|
41
|
+
canRespond: false,
|
|
42
|
+
messaging: null,
|
|
43
|
+
workspace: "",
|
|
44
|
+
taskPlan: Object.freeze({ source: "none", items: Object.freeze([]), residueCount: 0, observedAt: null }),
|
|
45
|
+
semantics: Object.freeze({ fidelity: null, residue: Object.freeze([]), residueCount: 0, parseErrors: 0, rawRecords: 0, subagents: Object.freeze([]) }),
|
|
46
|
+
terminalHandoff: null,
|
|
47
|
+
exportBackTarget: null,
|
|
48
|
+
exportReceipt: null,
|
|
49
|
+
reductionReceipt: null,
|
|
50
|
+
error: null,
|
|
51
|
+
recoverable: false,
|
|
52
|
+
harnesses: Object.freeze([]),
|
|
53
|
+
history: Object.freeze({ sessionLimit: 0, hasMoreSessions: false, transcriptLimit: 120, hasEarlier: false }),
|
|
54
|
+
savedDraft: "",
|
|
55
|
+
attention: Object.freeze([]),
|
|
56
|
+
sessions: Object.freeze([]),
|
|
57
|
+
attached: null,
|
|
58
|
+
owned: null,
|
|
59
|
+
attachError: null
|
|
60
|
+
});
|
|
61
|
+
function harnessDisplayName(id) {
|
|
62
|
+
return HARNESS_NAMES[id] ?? id;
|
|
63
|
+
}
|
|
64
|
+
function groupConversation(entries) {
|
|
65
|
+
const blocks = [];
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
if (entry.role !== "tool") {
|
|
68
|
+
blocks.push({ kind: "entry", id: entry.id, entry });
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const previous = blocks.at(-1);
|
|
72
|
+
if (previous?.kind === "activity") previous.entries.push(entry);
|
|
73
|
+
else blocks.push({ kind: "activity", id: `activity:${entry.id}`, entries: [entry] });
|
|
74
|
+
}
|
|
75
|
+
return blocks;
|
|
76
|
+
}
|
|
77
|
+
function toolCategory(entry) {
|
|
78
|
+
const name = entry.label?.toLocaleLowerCase() ?? "";
|
|
79
|
+
if (/read|view|open_file|list_dir/.test(name)) return "read";
|
|
80
|
+
if (/search|find|grep|glob/.test(name)) return "search";
|
|
81
|
+
if (/edit|write|patch|replace|create_file/.test(name)) return "edit";
|
|
82
|
+
if (/test|typecheck|lint|build/.test(name)) return "test";
|
|
83
|
+
if (/terminal|bash|shell|command|exec/.test(name)) return /\b(test|typecheck|lint|build)\b/i.test(entry.arguments ?? "") ? "test" : "command";
|
|
84
|
+
if (/browser|web|fetch|url/.test(name)) return "web";
|
|
85
|
+
if (/subagent|spawn|task/.test(name)) return "agent";
|
|
86
|
+
return "other";
|
|
87
|
+
}
|
|
88
|
+
function toolTarget(argumentsText) {
|
|
89
|
+
if (!argumentsText) return "";
|
|
90
|
+
try {
|
|
91
|
+
const args = JSON.parse(argumentsText);
|
|
92
|
+
for (const key of ["file_path", "target_file", "path", "command", "cmd", "query", "pattern", "url"]) {
|
|
93
|
+
if (typeof args?.[key] === "string") return args[key];
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
return argumentsText.length > 120 ? `${argumentsText.slice(0, 117)}\u2026` : argumentsText;
|
|
97
|
+
}
|
|
98
|
+
return "";
|
|
99
|
+
}
|
|
100
|
+
function compactToolTarget(target, workspace) {
|
|
101
|
+
const prefix = workspace && !workspace.endsWith("/") ? `${workspace}/` : workspace;
|
|
102
|
+
return prefix && target.startsWith(prefix) ? target.slice(prefix.length) : target;
|
|
103
|
+
}
|
|
104
|
+
function activitySummary(entries) {
|
|
105
|
+
const counts = /* @__PURE__ */ new Map();
|
|
106
|
+
for (const entry of entries) counts.set(toolCategory(entry), (counts.get(toolCategory(entry)) ?? 0) + 1);
|
|
107
|
+
if ([...counts.keys()].every((key) => key === "read" || key === "search")) return `Explored ${entries.length} ${entries.length === 1 ? "item" : "items"}`;
|
|
108
|
+
const labels = { edit: "changed", test: "tests/builds", command: "commands", read: "reads", search: "searches", web: "web", agent: "agents", other: "other" };
|
|
109
|
+
return `Activity \xB7 ${Object.entries(labels).flatMap(([key, label]) => counts.has(key) ? [`${counts.get(key)} ${label}`] : []).join(" \xB7 ")}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/components.jsx
|
|
113
|
+
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
114
|
+
var markdown = new MarkdownIt({ html: false, linkify: true, breaks: false });
|
|
115
|
+
var defaultLinkOpen = markdown.renderer.rules.link_open;
|
|
116
|
+
markdown.renderer.rules.link_open = (tokens, index, options, env, self) => {
|
|
117
|
+
tokens[index]?.attrSet("target", "_blank");
|
|
118
|
+
tokens[index]?.attrSet("rel", "noreferrer noopener");
|
|
119
|
+
return defaultLinkOpen ? defaultLinkOpen(tokens, index, options, env, self) : self.renderToken(tokens, index, options);
|
|
120
|
+
};
|
|
121
|
+
function Markdown({ value }) {
|
|
122
|
+
const html = useMemo(() => markdown.render(value), [value]);
|
|
123
|
+
return /* @__PURE__ */ jsx("div", { class: "scui-markdown", dangerouslySetInnerHTML: { __html: html } });
|
|
124
|
+
}
|
|
125
|
+
function LoadingStatus({ state, compact = false }) {
|
|
126
|
+
const copy = {
|
|
127
|
+
connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
|
|
128
|
+
starting: [`Starting ${harnessDisplayName(state.harness) || "coding agent"}`, "Opening a controlled session in this workspace.", 1],
|
|
129
|
+
discovering: ["Loading recent sessions", "Scanning native session stores without loading full transcripts.", 2],
|
|
130
|
+
ready: ["Ready", "Coding sessions are up to date.", 3]
|
|
131
|
+
}[state.startup];
|
|
132
|
+
return /* @__PURE__ */ jsxs("div", { class: `scui-loading${compact ? " scui-loading-compact" : ""}`, role: "status", "aria-busy": state.startup !== "ready", children: [
|
|
133
|
+
/* @__PURE__ */ jsx("span", { class: "scui-orbit", "aria-hidden": "true", children: /* @__PURE__ */ jsx("i", {}) }),
|
|
134
|
+
/* @__PURE__ */ jsxs("span", { class: "scui-loading-copy", children: [
|
|
135
|
+
/* @__PURE__ */ jsx("strong", { children: copy[0] }),
|
|
136
|
+
/* @__PURE__ */ jsx("small", { children: copy[1] })
|
|
137
|
+
] }),
|
|
138
|
+
/* @__PURE__ */ jsx("span", { class: "scui-progress", "aria-hidden": "true", children: [1, 2, 3].map((step) => /* @__PURE__ */ jsx("i", { "data-progress": step <= copy[2] ? "done" : step === copy[2] + 1 ? "current" : "waiting" }, step)) })
|
|
139
|
+
] });
|
|
140
|
+
}
|
|
141
|
+
function RequestCard({ entry, adapter, canRespond }) {
|
|
142
|
+
const request = entry.request;
|
|
143
|
+
if (!request) return null;
|
|
144
|
+
if (request.status === "responded") {
|
|
145
|
+
return /* @__PURE__ */ jsxs("div", { class: "scui-request-done", children: [
|
|
146
|
+
"\u2713 Request answered \xB7 ",
|
|
147
|
+
request.resolution?.name ?? request.requestKind
|
|
148
|
+
] });
|
|
149
|
+
}
|
|
150
|
+
return /* @__PURE__ */ jsxs("section", { class: "scui-request", "aria-label": `${request.requestKind} needs input`, children: [
|
|
151
|
+
/* @__PURE__ */ jsx("strong", { children: "Agent needs input" }),
|
|
152
|
+
/* @__PURE__ */ jsx(Markdown, { value: request.payloadText || entry.text }),
|
|
153
|
+
/* @__PURE__ */ jsxs("div", { class: "scui-request-actions", children: [
|
|
154
|
+
request.options.map((option) => /* @__PURE__ */ jsx("button", { type: "button", disabled: !canRespond, onClick: () => adapter.onIntent({ action: "respond", requestId: request.requestId, optionId: option.optionId }), children: option.name }, option.optionId)),
|
|
155
|
+
request.cancellable ? /* @__PURE__ */ jsx("button", { type: "button", disabled: !canRespond, onClick: () => adapter.onIntent({ action: "respond", requestId: request.requestId, optionId: null }), children: "Cancel" }) : null
|
|
156
|
+
] })
|
|
157
|
+
] });
|
|
158
|
+
}
|
|
159
|
+
function ContextDisclosure({ context }) {
|
|
160
|
+
if (!context?.length) return null;
|
|
161
|
+
return /* @__PURE__ */ jsxs("details", { class: "scui-context", children: [
|
|
162
|
+
/* @__PURE__ */ jsxs("summary", { children: [
|
|
163
|
+
"Context \xB7 ",
|
|
164
|
+
context.length
|
|
165
|
+
] }),
|
|
166
|
+
/* @__PURE__ */ jsx("div", { children: context.map((item, index) => /* @__PURE__ */ jsxs("p", { children: [
|
|
167
|
+
/* @__PURE__ */ jsx("strong", { children: item.label }),
|
|
168
|
+
/* @__PURE__ */ jsx("span", { children: item.detail })
|
|
169
|
+
] }, item.id ?? index)) })
|
|
170
|
+
] });
|
|
171
|
+
}
|
|
172
|
+
function TranscriptEntry({ entry, state, adapter }) {
|
|
173
|
+
if (entry.role === "request") return /* @__PURE__ */ jsx(RequestCard, { entry, adapter, canRespond: state.canRespond });
|
|
174
|
+
if (entry.role === "reasoning") {
|
|
175
|
+
return /* @__PURE__ */ jsxs("details", { class: "scui-reasoning", open: entry.streaming, children: [
|
|
176
|
+
/* @__PURE__ */ jsx("summary", { children: entry.streaming ? "Reasoning\u2026" : "Reasoning" }),
|
|
177
|
+
/* @__PURE__ */ jsx(Markdown, { value: entry.text })
|
|
178
|
+
] });
|
|
179
|
+
}
|
|
180
|
+
if (entry.role === "notice" || entry.role === "system") return /* @__PURE__ */ jsx("div", { class: "scui-notice", "data-code": entry.code, children: entry.text });
|
|
181
|
+
return /* @__PURE__ */ jsxs("article", { class: "scui-message", "data-role": entry.role, children: [
|
|
182
|
+
/* @__PURE__ */ jsx(Markdown, { value: entry.text }),
|
|
183
|
+
/* @__PURE__ */ jsx(ContextDisclosure, { context: entry.context }),
|
|
184
|
+
entry.truncated ? /* @__PURE__ */ jsx("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null
|
|
185
|
+
] });
|
|
186
|
+
}
|
|
187
|
+
function ToolRow({ entry, workspace }) {
|
|
188
|
+
const [open, setOpen] = useState(entry.status === "pending");
|
|
189
|
+
const detailId = useId();
|
|
190
|
+
const target = compactToolTarget(toolTarget(entry.arguments), workspace);
|
|
191
|
+
const hasDetail = Boolean(entry.arguments || entry.resultText);
|
|
192
|
+
const category = toolCategory(entry);
|
|
193
|
+
return /* @__PURE__ */ jsxs("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: [
|
|
194
|
+
/* @__PURE__ */ jsxs("button", { type: "button", class: "scui-tool-head", disabled: !hasDetail, "aria-expanded": hasDetail ? open : void 0, "aria-controls": hasDetail ? detailId : void 0, onClick: () => hasDetail && setOpen((value) => !value), children: [
|
|
195
|
+
/* @__PURE__ */ jsx("span", { class: "scui-tool-glyph", "aria-hidden": "true", children: { read: "\u25A4", search: "\u2315", edit: "\u270E", command: ">_", test: "\u25C7", web: "\u25CE", agent: "\u2659", other: "\u25C6" }[category] }),
|
|
196
|
+
/* @__PURE__ */ jsx("strong", { children: entry.label?.split(/__|\//).at(-1)?.replaceAll("_", " ") || "Tool" }),
|
|
197
|
+
target ? /* @__PURE__ */ jsx("span", { class: "scui-tool-target", title: toolTarget(entry.arguments), children: target }) : null,
|
|
198
|
+
/* @__PURE__ */ jsx("span", { class: "scui-spacer" }),
|
|
199
|
+
/* @__PURE__ */ jsx("span", { "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? "\u25CC" : entry.status === "error" ? "\xD7" : "\u2713" })
|
|
200
|
+
] }),
|
|
201
|
+
hasDetail && open ? /* @__PURE__ */ jsxs("div", { id: detailId, class: "scui-tool-detail", children: [
|
|
202
|
+
entry.arguments ? /* @__PURE__ */ jsxs("pre", { children: [
|
|
203
|
+
/* @__PURE__ */ jsx("b", { children: "Input" }),
|
|
204
|
+
"\n",
|
|
205
|
+
entry.arguments
|
|
206
|
+
] }) : null,
|
|
207
|
+
entry.resultText ? /* @__PURE__ */ jsxs("pre", { "data-error": entry.status === "error", children: [
|
|
208
|
+
/* @__PURE__ */ jsx("b", { children: "Output" }),
|
|
209
|
+
"\n",
|
|
210
|
+
entry.resultText,
|
|
211
|
+
entry.truncated ? "\n[truncated]" : ""
|
|
212
|
+
] }) : null
|
|
213
|
+
] }) : null
|
|
214
|
+
] });
|
|
215
|
+
}
|
|
216
|
+
function ActivityGroup({ entries, state }) {
|
|
217
|
+
const active = entries.some((entry) => entry.status === "pending");
|
|
218
|
+
const [open, setOpen] = useState(active);
|
|
219
|
+
const id = useId();
|
|
220
|
+
useEffect(() => {
|
|
221
|
+
if (active) setOpen(true);
|
|
222
|
+
}, [active]);
|
|
223
|
+
return /* @__PURE__ */ jsxs("section", { class: "scui-activity", children: [
|
|
224
|
+
/* @__PURE__ */ jsxs("button", { class: "scui-activity-head", type: "button", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
|
|
225
|
+
/* @__PURE__ */ jsx("span", { class: "scui-fold", "data-open": open, children: "\u203A" }),
|
|
226
|
+
/* @__PURE__ */ jsx("strong", { children: activitySummary(entries) }),
|
|
227
|
+
/* @__PURE__ */ jsx("span", { class: "scui-spacer" }),
|
|
228
|
+
/* @__PURE__ */ jsxs("small", { children: [
|
|
229
|
+
entries.filter((entry) => entry.status === "completed").length,
|
|
230
|
+
"/",
|
|
231
|
+
entries.length
|
|
232
|
+
] })
|
|
233
|
+
] }),
|
|
234
|
+
open ? /* @__PURE__ */ jsx("div", { id, children: entries.map((entry) => /* @__PURE__ */ jsx(ToolRow, { entry, workspace: state.workspace }, entry.id)) }) : null
|
|
235
|
+
] });
|
|
236
|
+
}
|
|
237
|
+
function TaskPlan({ plan }) {
|
|
238
|
+
if (!plan.items.length) return null;
|
|
239
|
+
const complete = plan.items.filter((item) => item.status === "completed" || item.status === "cancelled").length;
|
|
240
|
+
return /* @__PURE__ */ jsxs("details", { class: "scui-plan", children: [
|
|
241
|
+
/* @__PURE__ */ jsxs("summary", { children: [
|
|
242
|
+
/* @__PURE__ */ jsx("span", { children: "Plan" }),
|
|
243
|
+
/* @__PURE__ */ jsxs("small", { children: [
|
|
244
|
+
complete,
|
|
245
|
+
"/",
|
|
246
|
+
plan.items.length
|
|
247
|
+
] })
|
|
248
|
+
] }),
|
|
249
|
+
/* @__PURE__ */ jsx("ol", { children: plan.items.map((item) => /* @__PURE__ */ jsxs("li", { "data-status": item.status, children: [
|
|
250
|
+
/* @__PURE__ */ jsx("i", { "aria-hidden": "true" }),
|
|
251
|
+
" ",
|
|
252
|
+
/* @__PURE__ */ jsx("span", { children: item.title })
|
|
253
|
+
] }, item.id)) })
|
|
254
|
+
] });
|
|
255
|
+
}
|
|
256
|
+
function SessionDetails({ semantics }) {
|
|
257
|
+
if (!semantics.fidelity && !semantics.residueCount && !semantics.parseErrors && !semantics.subagents.length) return null;
|
|
258
|
+
return /* @__PURE__ */ jsxs("details", { class: "scui-details", children: [
|
|
259
|
+
/* @__PURE__ */ jsx("summary", { children: "Session details" }),
|
|
260
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
261
|
+
semantics.fidelity ? /* @__PURE__ */ jsxs("p", { children: [
|
|
262
|
+
/* @__PURE__ */ jsx("strong", { children: "Fidelity" }),
|
|
263
|
+
/* @__PURE__ */ jsx("span", { children: semantics.fidelity.replaceAll("_", " ") })
|
|
264
|
+
] }) : null,
|
|
265
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
266
|
+
/* @__PURE__ */ jsx("strong", { children: "Native records" }),
|
|
267
|
+
/* @__PURE__ */ jsx("span", { children: semantics.rawRecords })
|
|
268
|
+
] }),
|
|
269
|
+
semantics.residueCount ? /* @__PURE__ */ jsxs("p", { children: [
|
|
270
|
+
/* @__PURE__ */ jsx("strong", { children: "Residue" }),
|
|
271
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
272
|
+
semantics.residueCount,
|
|
273
|
+
" retained"
|
|
274
|
+
] })
|
|
275
|
+
] }) : null,
|
|
276
|
+
semantics.parseErrors ? /* @__PURE__ */ jsxs("p", { children: [
|
|
277
|
+
/* @__PURE__ */ jsx("strong", { children: "Parse diagnostics" }),
|
|
278
|
+
/* @__PURE__ */ jsx("span", { children: semantics.parseErrors })
|
|
279
|
+
] }) : null,
|
|
280
|
+
semantics.subagents.map((agent) => /* @__PURE__ */ jsxs("p", { children: [
|
|
281
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
282
|
+
agent.source,
|
|
283
|
+
" subagent"
|
|
284
|
+
] }),
|
|
285
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
286
|
+
agent.messages,
|
|
287
|
+
" messages \xB7 ",
|
|
288
|
+
agent.fidelity.replaceAll("_", " ")
|
|
289
|
+
] })
|
|
290
|
+
] }, agent.id))
|
|
291
|
+
] })
|
|
292
|
+
] });
|
|
293
|
+
}
|
|
294
|
+
var conversationMemory = /* @__PURE__ */ new Map();
|
|
295
|
+
function Conversation({ state, adapter, components = {}, slots = {}, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pending = null }) {
|
|
296
|
+
const scroller = useRef(null);
|
|
297
|
+
const remembered = conversationMemory.get(memoryKey) ?? { top: null, atBottom: true };
|
|
298
|
+
const [atBottom, setAtBottom] = useState(remembered.atBottom);
|
|
299
|
+
const earlierAnchor = useRef(null);
|
|
300
|
+
const restored = useRef(false);
|
|
301
|
+
const blocks = groupConversation(state.transcript);
|
|
302
|
+
const Entry = components.TranscriptEntry ?? TranscriptEntry;
|
|
303
|
+
const Group = components.ActivityGroup ?? ActivityGroup;
|
|
304
|
+
const Before = slots.beforeConversation;
|
|
305
|
+
const After = slots.afterConversation;
|
|
306
|
+
const Empty = slots.emptyConversation;
|
|
307
|
+
const pin = () => {
|
|
308
|
+
if (!scroller.current) return;
|
|
309
|
+
scroller.current.scrollTop = scroller.current.scrollHeight;
|
|
310
|
+
setAtBottom(true);
|
|
311
|
+
conversationMemory.set(memoryKey, { top: scroller.current.scrollTop, atBottom: true });
|
|
312
|
+
};
|
|
313
|
+
useLayoutEffect(() => {
|
|
314
|
+
const element = scroller.current;
|
|
315
|
+
if (!element) return;
|
|
316
|
+
const anchor = earlierAnchor.current;
|
|
317
|
+
if (anchor && state.transcript.length > anchor.entries) {
|
|
318
|
+
element.scrollTop = anchor.top + (element.scrollHeight - anchor.height);
|
|
319
|
+
earlierAnchor.current = null;
|
|
320
|
+
conversationMemory.set(memoryKey, { top: element.scrollTop, atBottom: false });
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (!restored.current) {
|
|
324
|
+
restored.current = true;
|
|
325
|
+
if (remembered.top !== null && !remembered.atBottom) element.scrollTop = remembered.top;
|
|
326
|
+
else pin();
|
|
327
|
+
} else if (atBottom) pin();
|
|
328
|
+
}, [memoryKey, state.transcript.length, state.busy]);
|
|
329
|
+
return /* @__PURE__ */ jsxs("div", { class: "scui-conversation-wrap", children: [
|
|
330
|
+
/* @__PURE__ */ jsx("div", { class: "scui-conversation", ref: scroller, tabIndex: 0, "aria-label": "Conversation", onScroll: (event) => {
|
|
331
|
+
const element = event.currentTarget;
|
|
332
|
+
const bottom = element.scrollHeight - element.scrollTop - element.clientHeight <= 64;
|
|
333
|
+
setAtBottom(bottom);
|
|
334
|
+
conversationMemory.set(memoryKey, { top: element.scrollTop, atBottom: bottom });
|
|
335
|
+
}, children: /* @__PURE__ */ jsxs("div", { children: [
|
|
336
|
+
Before ? /* @__PURE__ */ jsx(Before, { state, adapter, value: null }) : null,
|
|
337
|
+
/* @__PURE__ */ jsx(TaskPlan, { plan: state.taskPlan }),
|
|
338
|
+
/* @__PURE__ */ jsx(SessionDetails, { semantics: state.semantics }),
|
|
339
|
+
state.history.hasEarlier ? /* @__PURE__ */ jsx("button", { class: "scui-load", type: "button", disabled: Boolean(state.operation), onClick: () => {
|
|
340
|
+
const element = scroller.current;
|
|
341
|
+
if (element) earlierAnchor.current = { height: element.scrollHeight, top: element.scrollTop, entries: state.transcript.length };
|
|
342
|
+
setAtBottom(false);
|
|
343
|
+
adapter.onIntent({ action: "loadEarlier" });
|
|
344
|
+
}, children: "Load earlier messages" }) : null,
|
|
345
|
+
!blocks.length && state.startup !== "ready" ? /* @__PURE__ */ jsx(LoadingStatus, { state }) : null,
|
|
346
|
+
!blocks.length && state.startup === "ready" ? Empty ? /* @__PURE__ */ jsx(Empty, { state, adapter, value: null }) : /* @__PURE__ */ jsx("div", { class: "scui-empty", children: state.error ?? (state.harness ? `${harnessDisplayName(state.harness)} is listening. Say something.` : "No transcript yet.") }) : null,
|
|
347
|
+
blocks.map((block) => block.kind === "activity" ? /* @__PURE__ */ jsx(Group, { value: block.entries, entries: block.entries, state, adapter }, block.id) : /* @__PURE__ */ jsx(Entry, { value: block.entry, entry: block.entry, state, adapter }, block.id)),
|
|
348
|
+
pending ? /* @__PURE__ */ jsxs("article", { class: "scui-message scui-pending", "data-role": "user", children: [
|
|
349
|
+
/* @__PURE__ */ jsx(Markdown, { value: pending }),
|
|
350
|
+
/* @__PURE__ */ jsx("small", { children: state.error && !state.busy ? "Not sent" : "Sending\u2026" })
|
|
351
|
+
] }) : null,
|
|
352
|
+
state.busy ? /* @__PURE__ */ jsxs("div", { class: "scui-working", role: "status", children: [
|
|
353
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2726" }),
|
|
354
|
+
/* @__PURE__ */ jsx("i", {}),
|
|
355
|
+
/* @__PURE__ */ jsx("i", {}),
|
|
356
|
+
/* @__PURE__ */ jsx("i", {}),
|
|
357
|
+
/* @__PURE__ */ jsxs("small", { children: [
|
|
358
|
+
harnessDisplayName(state.harness),
|
|
359
|
+
" is working"
|
|
360
|
+
] })
|
|
361
|
+
] }) : null,
|
|
362
|
+
After ? /* @__PURE__ */ jsx(After, { state, adapter, value: null }) : null
|
|
363
|
+
] }) }),
|
|
364
|
+
!atBottom ? /* @__PURE__ */ jsx("button", { class: "scui-latest", type: "button", onClick: pin, children: "\u2193 Latest" }) : null
|
|
365
|
+
] });
|
|
366
|
+
}
|
|
367
|
+
export {
|
|
368
|
+
ActivityGroup,
|
|
369
|
+
Conversation,
|
|
370
|
+
LoadingStatus,
|
|
371
|
+
RequestCard,
|
|
372
|
+
SessionDetails,
|
|
373
|
+
TaskPlan,
|
|
374
|
+
TranscriptEntry
|
|
375
|
+
};
|