@volter-ai-dev/supercode-ui 0.1.13 → 0.1.15
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 +1 -1
- package/components.d.ts +1 -0
- package/components.mjs +713 -362
- package/composer.mjs +77 -19
- package/controller.mjs +5 -2
- package/conversation.mjs +364 -196
- package/core.mjs +4 -2
- package/embed.mjs +714 -364
- package/icon.d.ts +2 -0
- package/icon.mjs +45 -0
- package/index.d.ts +11 -3
- package/messenger.mjs +712 -362
- package/package.json +8 -2
- package/sessions.mjs +161 -49
- package/styles.css +35 -18
package/embed.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { render } from "preact";
|
|
3
3
|
|
|
4
4
|
// src/messenger.jsx
|
|
5
|
-
import { useEffect as
|
|
5
|
+
import { useEffect as useEffect6, useId as useId2, useMemo as useMemo2, useRef as useRef5, useState as useState4 } from "preact/hooks";
|
|
6
6
|
|
|
7
7
|
// core.mjs
|
|
8
8
|
var HARNESS_NAMES = Object.freeze({
|
|
@@ -295,7 +295,8 @@ function readTranscript(value) {
|
|
|
295
295
|
role: item.role,
|
|
296
296
|
text: item.text,
|
|
297
297
|
ts: nullableNumber(item.ts),
|
|
298
|
-
truncated: item.truncated === true
|
|
298
|
+
truncated: item.truncated === true,
|
|
299
|
+
...Number.isSafeInteger(item.messageIndex) && item.messageIndex > 0 ? { messageIndex: item.messageIndex } : {}
|
|
299
300
|
};
|
|
300
301
|
for (const key of ["label", "arguments", "resultText", "code"]) {
|
|
301
302
|
if (typeof item[key] === "string") entry[key] = item[key];
|
|
@@ -344,6 +345,7 @@ function readSessions(value) {
|
|
|
344
345
|
name: string(row.name),
|
|
345
346
|
cwd: string(row.cwd),
|
|
346
347
|
title: string(row.title),
|
|
348
|
+
preview: string(row.preview),
|
|
347
349
|
age: string(row.age),
|
|
348
350
|
updatedAt: nullableNumber(row.updatedAt),
|
|
349
351
|
messages: nullableNumber(row.messages),
|
|
@@ -466,7 +468,7 @@ function normalizeUiState(value) {
|
|
|
466
468
|
savedDraft: string(raw.savedDraft),
|
|
467
469
|
attention: Array.isArray(raw.attention) ? raw.attention.flatMap((candidate) => {
|
|
468
470
|
const item = record(candidate);
|
|
469
|
-
return item && typeof item.key === "string" && ["unseen", "finished", "failed"].includes(item.kind) ? [{ key: item.key, kind: item.kind, ...typeof item.preview === "string" ? { preview: item.preview } : {} }] : [];
|
|
471
|
+
return item && typeof item.key === "string" && ["unseen", "finished", "failed"].includes(item.kind) ? [{ key: item.key, kind: item.kind, ...typeof item.preview === "string" ? { preview: item.preview } : {}, ...Number.isSafeInteger(item.afterMessages) && item.afterMessages >= 0 ? { afterMessages: item.afterMessages } : {} }] : [];
|
|
470
472
|
}) : [],
|
|
471
473
|
sessions: readSessions(raw.sessions),
|
|
472
474
|
attached: readAttached(raw.attached),
|
|
@@ -548,7 +550,7 @@ function canContinueHere(state) {
|
|
|
548
550
|
}
|
|
549
551
|
function operationLabel(operation) {
|
|
550
552
|
if (!operation) return "";
|
|
551
|
-
const labels = { discover: "Refreshing chats\u2026", observe: "Loading recent messages\u2026", attach: "Opening chat\u2026", resume: "Continuing here\u2026", branch: "Starting continuation\u2026", reduce: "Reducing context and verifying reversibility\u2026", terminal: "Preparing terminal handoff\u2026", export: "Exporting losslessly\u2026", refresh: "Retrying\u2026" };
|
|
553
|
+
const labels = { discover: "Refreshing chats\u2026", observe: "Loading recent messages\u2026", attach: "Opening chat\u2026", start: "Starting new chat\u2026", resume: "Continuing here\u2026", join: "Joining live session\u2026", detach: "Detaching to read-only\u2026", branch: "Starting continuation\u2026", reduce: "Reducing context and verifying reversibility\u2026", terminal: "Preparing terminal handoff\u2026", export: "Exporting losslessly\u2026", interrupt: "Stopping agent\u2026", respond: "Sending response\u2026", loadEarlier: "Loading earlier messages\u2026", loadSessions: "Loading more chats\u2026", refresh: "Retrying\u2026" };
|
|
552
554
|
return labels[operation] ?? `${operation.replaceAll("_", " ")}\u2026`;
|
|
553
555
|
}
|
|
554
556
|
function terminalCommand(handoff) {
|
|
@@ -570,8 +572,65 @@ function boundedSet(map, key, value) {
|
|
|
570
572
|
while (map.size > MEMORY_LIMIT) map.delete(map.keys().next().value);
|
|
571
573
|
}
|
|
572
574
|
|
|
575
|
+
// src/icon.jsx
|
|
576
|
+
import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
|
|
577
|
+
var ICONS = {
|
|
578
|
+
back: () => /* @__PURE__ */ jsx("path", { d: "m11.5 4.5-4.5 4.5 4.5 4.5" }),
|
|
579
|
+
check: () => /* @__PURE__ */ jsx("path", { d: "m4 9 3.25 3.25L14 5.5" }),
|
|
580
|
+
chevron: () => /* @__PURE__ */ jsx("path", { d: "m7 4.5 4.5 4.5L7 13.5" }),
|
|
581
|
+
close: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
582
|
+
/* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
|
|
583
|
+
/* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
|
|
584
|
+
] }),
|
|
585
|
+
copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
586
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
|
|
587
|
+
/* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
|
|
588
|
+
] }),
|
|
589
|
+
down: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
590
|
+
/* @__PURE__ */ jsx("path", { d: "M9 3.5v10" }),
|
|
591
|
+
/* @__PURE__ */ jsx("path", { d: "m4.75 9.5 4.25 4 4.25-4" })
|
|
592
|
+
] }),
|
|
593
|
+
menu: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
594
|
+
/* @__PURE__ */ jsx("circle", { cx: "4", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
|
|
595
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
|
|
596
|
+
/* @__PURE__ */ jsx("circle", { cx: "14", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" })
|
|
597
|
+
] }),
|
|
598
|
+
plus: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
599
|
+
/* @__PURE__ */ jsx("path", { d: "M9 3.5v11" }),
|
|
600
|
+
/* @__PURE__ */ jsx("path", { d: "M3.5 9h11" })
|
|
601
|
+
] }),
|
|
602
|
+
search: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
603
|
+
/* @__PURE__ */ jsx("circle", { cx: "7.75", cy: "7.75", r: "4.25" }),
|
|
604
|
+
/* @__PURE__ */ jsx("path", { d: "m11 11 3.5 3.5" })
|
|
605
|
+
] }),
|
|
606
|
+
send: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
607
|
+
/* @__PURE__ */ jsx("path", { d: "M9 14.5v-11" }),
|
|
608
|
+
/* @__PURE__ */ jsx("path", { d: "m4.75 7.75 4.25-4.25 4.25 4.25" })
|
|
609
|
+
] }),
|
|
610
|
+
stop: () => /* @__PURE__ */ jsx("rect", { x: "4.5", y: "4.5", width: "9", height: "9", rx: "1.5", fill: "currentColor", stroke: "none" })
|
|
611
|
+
};
|
|
612
|
+
function UiIcon({ name, size = 16, class: className = "" }) {
|
|
613
|
+
const Glyph = ICONS[name];
|
|
614
|
+
if (!Glyph) return null;
|
|
615
|
+
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, {}) });
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// src/textarea.js
|
|
619
|
+
import { useLayoutEffect } from "preact/hooks";
|
|
620
|
+
function useAutosizeTextarea(ref, value) {
|
|
621
|
+
useLayoutEffect(() => {
|
|
622
|
+
const element = ref.current;
|
|
623
|
+
if (!element) return;
|
|
624
|
+
element.style.height = "auto";
|
|
625
|
+
const maxHeight = Number.parseFloat(getComputedStyle(element).maxHeight) || 150;
|
|
626
|
+
const height = Math.min(element.scrollHeight, maxHeight);
|
|
627
|
+
element.style.height = `${height}px`;
|
|
628
|
+
element.style.overflowY = element.scrollHeight > maxHeight ? "auto" : "hidden";
|
|
629
|
+
}, [ref, value]);
|
|
630
|
+
}
|
|
631
|
+
|
|
573
632
|
// src/composer.jsx
|
|
574
|
-
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
633
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
575
634
|
var composerMemory = /* @__PURE__ */ new Map();
|
|
576
635
|
function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
577
636
|
if (state.mode !== "mirror" || state.canSend) return null;
|
|
@@ -581,16 +640,16 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
581
640
|
const resume = canContinueHere(state);
|
|
582
641
|
const join = state.canAttach;
|
|
583
642
|
const branch = state.canBranch;
|
|
584
|
-
if (!resume && !join && !branch) return /* @__PURE__ */
|
|
585
|
-
/* @__PURE__ */
|
|
586
|
-
/* @__PURE__ */
|
|
643
|
+
if (!resume && !join && !branch) return /* @__PURE__ */ jsx2("div", { class: "scui-continuation", children: /* @__PURE__ */ jsxs2("span", { children: [
|
|
644
|
+
/* @__PURE__ */ jsx2("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
|
|
645
|
+
/* @__PURE__ */ jsx2("small", { children: "This session cannot be continued by an available harness." })
|
|
587
646
|
] }) });
|
|
588
|
-
return /* @__PURE__ */
|
|
589
|
-
/* @__PURE__ */
|
|
590
|
-
/* @__PURE__ */
|
|
591
|
-
/* @__PURE__ */
|
|
647
|
+
return /* @__PURE__ */ jsxs2("div", { class: "scui-continuation", children: [
|
|
648
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
649
|
+
/* @__PURE__ */ jsx2("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
|
|
650
|
+
/* @__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." })
|
|
592
651
|
] }),
|
|
593
|
-
/* @__PURE__ */
|
|
652
|
+
/* @__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 })
|
|
594
653
|
] });
|
|
595
654
|
}
|
|
596
655
|
function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
|
|
@@ -599,6 +658,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
599
658
|
const [queue, setQueue] = useState(remembered.queue);
|
|
600
659
|
const [dispatching, setDispatching] = useState(false);
|
|
601
660
|
const textarea = useRef(null);
|
|
661
|
+
useAutosizeTextarea(textarea, draft);
|
|
602
662
|
const remember = (nextDraft, nextQueue) => boundedSet(composerMemory, memoryKey, { draft: nextDraft, queue: nextQueue });
|
|
603
663
|
const updateQueue = (update) => setQueue((items) => {
|
|
604
664
|
const next = update(items);
|
|
@@ -646,19 +706,19 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
646
706
|
setDraft("");
|
|
647
707
|
remember("", queuesNewMessage ? [...queue, text] : queue);
|
|
648
708
|
};
|
|
649
|
-
return /* @__PURE__ */
|
|
650
|
-
queue.length ? /* @__PURE__ */
|
|
651
|
-
/* @__PURE__ */
|
|
709
|
+
return /* @__PURE__ */ jsxs2("div", { class: "scui-compose", children: [
|
|
710
|
+
queue.length ? /* @__PURE__ */ jsxs2("div", { class: "scui-queue", children: [
|
|
711
|
+
/* @__PURE__ */ jsxs2("strong", { children: [
|
|
652
712
|
queue.length,
|
|
653
713
|
" queued"
|
|
654
714
|
] }),
|
|
655
|
-
queue.map((item, index) => /* @__PURE__ */
|
|
715
|
+
queue.map((item, index) => /* @__PURE__ */ jsxs2("span", { children: [
|
|
656
716
|
item,
|
|
657
|
-
/* @__PURE__ */
|
|
717
|
+
/* @__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 }) })
|
|
658
718
|
] }, `${index}:${item}`))
|
|
659
719
|
] }) : null,
|
|
660
|
-
/* @__PURE__ */
|
|
661
|
-
/* @__PURE__ */
|
|
720
|
+
/* @__PURE__ */ jsxs2("div", { class: "scui-envelope", children: [
|
|
721
|
+
/* @__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) => {
|
|
662
722
|
const value = event.currentTarget.value;
|
|
663
723
|
setDraft(value);
|
|
664
724
|
remember(value, queue);
|
|
@@ -668,35 +728,113 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
668
728
|
send();
|
|
669
729
|
}
|
|
670
730
|
} }),
|
|
671
|
-
/* @__PURE__ */
|
|
672
|
-
state.busy ? /* @__PURE__ */
|
|
673
|
-
/* @__PURE__ */
|
|
731
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
732
|
+
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,
|
|
733
|
+
/* @__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 }) })
|
|
674
734
|
] })
|
|
675
735
|
] })
|
|
676
736
|
] });
|
|
677
737
|
}
|
|
678
738
|
|
|
679
739
|
// src/conversation.jsx
|
|
680
|
-
import {
|
|
740
|
+
import { Fragment as Fragment2 } from "preact";
|
|
741
|
+
import { useEffect as useEffect3, useId, useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState2 } from "preact/hooks";
|
|
681
742
|
|
|
682
743
|
// src/markdown.jsx
|
|
683
744
|
import MarkdownIt from "markdown-it";
|
|
684
|
-
import { useMemo } from "preact/hooks";
|
|
685
|
-
import { jsx as
|
|
745
|
+
import { useEffect as useEffect2, useMemo, useRef as useRef2 } from "preact/hooks";
|
|
746
|
+
import { jsx as jsx3 } from "preact/jsx-runtime";
|
|
686
747
|
var markdown = new MarkdownIt({ html: false, linkify: true, breaks: false });
|
|
748
|
+
var LANGUAGE_LABELS = {
|
|
749
|
+
bash: "Shell",
|
|
750
|
+
css: "CSS",
|
|
751
|
+
html: "HTML",
|
|
752
|
+
js: "JavaScript",
|
|
753
|
+
javascript: "JavaScript",
|
|
754
|
+
jsx: "JSX",
|
|
755
|
+
md: "Markdown",
|
|
756
|
+
markdown: "Markdown",
|
|
757
|
+
py: "Python",
|
|
758
|
+
python: "Python",
|
|
759
|
+
rs: "Rust",
|
|
760
|
+
rust: "Rust",
|
|
761
|
+
sh: "Shell",
|
|
762
|
+
shell: "Shell",
|
|
763
|
+
ts: "TypeScript",
|
|
764
|
+
tsx: "TSX",
|
|
765
|
+
yaml: "YAML",
|
|
766
|
+
yml: "YAML",
|
|
767
|
+
json: "JSON",
|
|
768
|
+
jsonc: "JSONC",
|
|
769
|
+
sql: "SQL",
|
|
770
|
+
xml: "XML"
|
|
771
|
+
};
|
|
772
|
+
var COPY_ICON = '<svg viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5" y="5" width="8" height="8" rx="1.5"></rect><path d="M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25"></path></svg>';
|
|
773
|
+
function languageLabel(info) {
|
|
774
|
+
const language = info.trim().split(/\s+/, 1)[0]?.toLocaleLowerCase() ?? "";
|
|
775
|
+
if (!language) return "Code";
|
|
776
|
+
return LANGUAGE_LABELS[language] ?? language.toLocaleUpperCase();
|
|
777
|
+
}
|
|
778
|
+
function frameCode(render2, tokens, index, options, env, self) {
|
|
779
|
+
const label = markdown.utils.escapeHtml(languageLabel(tokens[index]?.info ?? ""));
|
|
780
|
+
return `<div class="scui-code-block"><div class="scui-code-head"><span>${label}</span><button class="scui-code-copy" type="button" aria-label="Copy code" title="Copy code">${COPY_ICON}<span>Copy</span></button></div>${render2(tokens, index, options, env, self)}</div>`;
|
|
781
|
+
}
|
|
782
|
+
for (const kind of ["fence", "code_block"]) {
|
|
783
|
+
const render2 = markdown.renderer.rules[kind];
|
|
784
|
+
markdown.renderer.rules[kind] = (tokens, index, options, env, self) => frameCode(render2, tokens, index, options, env, self);
|
|
785
|
+
}
|
|
687
786
|
var defaultLinkOpen = markdown.renderer.rules.link_open;
|
|
688
787
|
markdown.renderer.rules.link_open = (tokens, index, options, env, self) => {
|
|
689
788
|
tokens[index]?.attrSet("target", "_blank");
|
|
690
789
|
tokens[index]?.attrSet("rel", "noreferrer noopener");
|
|
691
790
|
return defaultLinkOpen ? defaultLinkOpen(tokens, index, options, env, self) : self.renderToken(tokens, index, options);
|
|
692
791
|
};
|
|
693
|
-
function
|
|
792
|
+
function setCopyState(button, status) {
|
|
793
|
+
const labels = {
|
|
794
|
+
idle: ["Copy code", "Copy"],
|
|
795
|
+
copied: ["Code copied", "Copied"],
|
|
796
|
+
failed: ["Copy failed \xB7 retry", "Retry"]
|
|
797
|
+
};
|
|
798
|
+
const [label, visible] = labels[status];
|
|
799
|
+
button.dataset.status = status;
|
|
800
|
+
button.setAttribute("aria-label", label);
|
|
801
|
+
button.setAttribute("title", label);
|
|
802
|
+
const text = button.querySelector("span");
|
|
803
|
+
if (text) text.textContent = visible;
|
|
804
|
+
}
|
|
805
|
+
function Markdown({ value, copyText }) {
|
|
694
806
|
const html = useMemo(() => markdown.render(value), [value]);
|
|
695
|
-
|
|
807
|
+
const resets = useRef2(/* @__PURE__ */ new Map());
|
|
808
|
+
useEffect2(() => () => {
|
|
809
|
+
for (const timer of resets.current.values()) clearTimeout(timer);
|
|
810
|
+
resets.current.clear();
|
|
811
|
+
}, []);
|
|
812
|
+
const copyCode = async (event) => {
|
|
813
|
+
const button = event.target.closest?.(".scui-code-copy");
|
|
814
|
+
if (!button || !event.currentTarget.contains(button) || !copyText) return;
|
|
815
|
+
const code = button.closest(".scui-code-block")?.querySelector("pre code");
|
|
816
|
+
if (!code) return;
|
|
817
|
+
button.disabled = true;
|
|
818
|
+
try {
|
|
819
|
+
await copyText(code.textContent ?? "");
|
|
820
|
+
setCopyState(button, "copied");
|
|
821
|
+
} catch {
|
|
822
|
+
setCopyState(button, "failed");
|
|
823
|
+
} finally {
|
|
824
|
+
button.disabled = false;
|
|
825
|
+
}
|
|
826
|
+
clearTimeout(resets.current.get(button));
|
|
827
|
+
const timer = setTimeout(() => {
|
|
828
|
+
if (button.isConnected) setCopyState(button, "idle");
|
|
829
|
+
resets.current.delete(button);
|
|
830
|
+
}, 1500);
|
|
831
|
+
resets.current.set(button, timer);
|
|
832
|
+
};
|
|
833
|
+
return /* @__PURE__ */ jsx3("div", { class: "scui-markdown", "data-copyable": Boolean(copyText), onClick: copyCode, dangerouslySetInnerHTML: { __html: html } });
|
|
696
834
|
}
|
|
697
835
|
|
|
698
836
|
// src/conversation.jsx
|
|
699
|
-
import { Fragment, jsx as
|
|
837
|
+
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs3 } from "preact/jsx-runtime";
|
|
700
838
|
function LoadingStatus({ state, compact = false }) {
|
|
701
839
|
const copy = {
|
|
702
840
|
connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
|
|
@@ -704,81 +842,104 @@ function LoadingStatus({ state, compact = false }) {
|
|
|
704
842
|
discovering: ["Loading recent sessions", "Scanning native session stores without loading full transcripts.", 2],
|
|
705
843
|
ready: ["Ready", "Coding sessions are up to date.", 3]
|
|
706
844
|
}[state.startup];
|
|
707
|
-
return /* @__PURE__ */
|
|
708
|
-
/* @__PURE__ */
|
|
709
|
-
/* @__PURE__ */
|
|
710
|
-
/* @__PURE__ */
|
|
711
|
-
/* @__PURE__ */
|
|
845
|
+
return /* @__PURE__ */ jsxs3("div", { class: `scui-loading${compact ? " scui-loading-compact" : ""}`, role: "status", "aria-busy": state.startup !== "ready", children: [
|
|
846
|
+
/* @__PURE__ */ jsx4("span", { class: "scui-orbit", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("i", {}) }),
|
|
847
|
+
/* @__PURE__ */ jsxs3("span", { class: "scui-loading-copy", children: [
|
|
848
|
+
/* @__PURE__ */ jsx4("strong", { children: copy[0] }),
|
|
849
|
+
/* @__PURE__ */ jsx4("small", { children: copy[1] })
|
|
712
850
|
] }),
|
|
713
|
-
/* @__PURE__ */
|
|
851
|
+
/* @__PURE__ */ jsx4("span", { class: "scui-progress", "aria-hidden": "true", children: [1, 2, 3].map((step) => /* @__PURE__ */ jsx4("i", { "data-progress": step <= copy[2] ? "done" : step === copy[2] + 1 ? "current" : "waiting" }, step)) })
|
|
714
852
|
] });
|
|
715
853
|
}
|
|
716
854
|
function RequestCard({ entry, adapter, canRespond }) {
|
|
717
855
|
const request = entry.request;
|
|
718
856
|
if (!request) return null;
|
|
719
857
|
if (request.status === "responded") {
|
|
720
|
-
return /* @__PURE__ */
|
|
858
|
+
return /* @__PURE__ */ jsxs3("div", { class: "scui-request-done", children: [
|
|
721
859
|
"\u2713 Request answered \xB7 ",
|
|
722
860
|
request.resolution?.name ?? request.requestKind
|
|
723
861
|
] });
|
|
724
862
|
}
|
|
725
|
-
return /* @__PURE__ */
|
|
726
|
-
/* @__PURE__ */
|
|
727
|
-
/* @__PURE__ */
|
|
728
|
-
/* @__PURE__ */
|
|
729
|
-
request.options.map((option) => /* @__PURE__ */
|
|
730
|
-
request.cancellable ? /* @__PURE__ */
|
|
863
|
+
return /* @__PURE__ */ jsxs3("section", { class: "scui-request", role: "alert", "aria-label": `${request.requestKind} needs input`, children: [
|
|
864
|
+
/* @__PURE__ */ jsx4("strong", { children: "Agent needs input" }),
|
|
865
|
+
/* @__PURE__ */ jsx4(Markdown, { value: request.payloadText || entry.text, copyText: adapter?.copyText }),
|
|
866
|
+
/* @__PURE__ */ jsxs3("div", { class: "scui-request-actions", children: [
|
|
867
|
+
request.options.map((option) => /* @__PURE__ */ jsx4("button", { type: "button", disabled: !canRespond, onClick: () => adapter.onIntent({ action: "respond", requestId: request.requestId, optionId: option.optionId }), children: option.name }, option.optionId)),
|
|
868
|
+
request.cancellable ? /* @__PURE__ */ jsx4("button", { type: "button", disabled: !canRespond, onClick: () => adapter.onIntent({ action: "respond", requestId: request.requestId, optionId: null }), children: "Cancel" }) : null
|
|
731
869
|
] })
|
|
732
870
|
] });
|
|
733
871
|
}
|
|
734
872
|
function ContextDisclosure({ context }) {
|
|
735
873
|
if (!context?.length) return null;
|
|
736
|
-
return /* @__PURE__ */
|
|
737
|
-
/* @__PURE__ */
|
|
874
|
+
return /* @__PURE__ */ jsxs3("details", { class: "scui-context", children: [
|
|
875
|
+
/* @__PURE__ */ jsxs3("summary", { children: [
|
|
738
876
|
"Context \xB7 ",
|
|
739
877
|
context.length
|
|
740
878
|
] }),
|
|
741
|
-
/* @__PURE__ */
|
|
742
|
-
/* @__PURE__ */
|
|
743
|
-
/* @__PURE__ */
|
|
879
|
+
/* @__PURE__ */ jsx4("div", { children: context.map((item, index) => /* @__PURE__ */ jsxs3("p", { children: [
|
|
880
|
+
/* @__PURE__ */ jsx4("strong", { children: item.label }),
|
|
881
|
+
/* @__PURE__ */ jsx4("span", { children: item.detail })
|
|
744
882
|
] }, item.id ?? index)) })
|
|
745
883
|
] });
|
|
746
884
|
}
|
|
885
|
+
function MessageMeta({ entry, adapter }) {
|
|
886
|
+
const [copyState, setCopyState2] = useState2("idle");
|
|
887
|
+
const reset = useRef3(null);
|
|
888
|
+
useEffect3(() => () => clearTimeout(reset.current), []);
|
|
889
|
+
const date = entry.ts === null ? null : new Date(entry.ts);
|
|
890
|
+
const validDate = date && Number.isFinite(date.valueOf()) ? date : null;
|
|
891
|
+
if (!validDate && (!adapter?.copyText || !entry.text)) return null;
|
|
892
|
+
const copy = async () => {
|
|
893
|
+
try {
|
|
894
|
+
await adapter.copyText(entry.text);
|
|
895
|
+
setCopyState2("copied");
|
|
896
|
+
} catch {
|
|
897
|
+
setCopyState2("failed");
|
|
898
|
+
}
|
|
899
|
+
clearTimeout(reset.current);
|
|
900
|
+
reset.current = setTimeout(() => setCopyState2("idle"), 1500);
|
|
901
|
+
};
|
|
902
|
+
const copyLabel = copyState === "copied" ? "Message copied" : copyState === "failed" ? "Copy failed \xB7 retry" : "Copy message";
|
|
903
|
+
return /* @__PURE__ */ jsxs3("footer", { class: "scui-message-meta", children: [
|
|
904
|
+
validDate ? /* @__PURE__ */ jsx4("time", { dateTime: validDate.toISOString(), title: validDate.toLocaleString(), children: validDate.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }) }) : null,
|
|
905
|
+
adapter?.copyText && entry.text ? /* @__PURE__ */ jsx4("button", { type: "button", "aria-label": copyLabel, title: copyLabel, onClick: copy, "data-status": copyState, children: /* @__PURE__ */ jsx4(UiIcon, { name: "copy", size: 13 }) }) : null
|
|
906
|
+
] });
|
|
907
|
+
}
|
|
747
908
|
var TOOL_ICONS = {
|
|
748
|
-
read: () => /* @__PURE__ */
|
|
749
|
-
/* @__PURE__ */
|
|
750
|
-
/* @__PURE__ */
|
|
909
|
+
read: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
910
|
+
/* @__PURE__ */ jsx4("path", { d: "M5 2.75h7.25L15 5.5v7.75A1.75 1.75 0 0 1 13.25 15h-8.5A1.75 1.75 0 0 1 3 13.25v-8.5A2 2 0 0 1 5 2.75Z" }),
|
|
911
|
+
/* @__PURE__ */ jsx4("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
|
|
751
912
|
] }),
|
|
752
|
-
search: () => /* @__PURE__ */
|
|
753
|
-
/* @__PURE__ */
|
|
754
|
-
/* @__PURE__ */
|
|
913
|
+
search: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
914
|
+
/* @__PURE__ */ jsx4("circle", { cx: "8", cy: "8", r: "4.5" }),
|
|
915
|
+
/* @__PURE__ */ jsx4("path", { d: "m11.5 11.5 3 3" })
|
|
755
916
|
] }),
|
|
756
|
-
edit: () => /* @__PURE__ */
|
|
757
|
-
/* @__PURE__ */
|
|
758
|
-
/* @__PURE__ */
|
|
917
|
+
edit: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
918
|
+
/* @__PURE__ */ jsx4("path", { d: "m11.75 3.25 3 3-8.5 8.5-3.75.75.75-3.75 8.5-8.5Z" }),
|
|
919
|
+
/* @__PURE__ */ jsx4("path", { d: "m10 5 3 3" })
|
|
759
920
|
] }),
|
|
760
|
-
command: () => /* @__PURE__ */
|
|
761
|
-
test: () => /* @__PURE__ */
|
|
762
|
-
/* @__PURE__ */
|
|
763
|
-
/* @__PURE__ */
|
|
921
|
+
command: () => /* @__PURE__ */ jsx4(Fragment3, { children: /* @__PURE__ */ jsx4("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
|
|
922
|
+
test: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
923
|
+
/* @__PURE__ */ jsx4("path", { d: "M6 2.5v3L3 12a2 2 0 0 0 1.8 3h8.4a2 2 0 0 0 1.8-3l-3-6.5v-3M5 9h8" }),
|
|
924
|
+
/* @__PURE__ */ jsx4("path", { d: "M5 2.5h8" })
|
|
764
925
|
] }),
|
|
765
|
-
web: () => /* @__PURE__ */
|
|
766
|
-
/* @__PURE__ */
|
|
767
|
-
/* @__PURE__ */
|
|
926
|
+
web: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
927
|
+
/* @__PURE__ */ jsx4("circle", { cx: "9", cy: "9", r: "6.5" }),
|
|
928
|
+
/* @__PURE__ */ jsx4("path", { d: "M2.75 9h12.5M9 2.5c2 1.8 3 4 3 6.5s-1 4.7-3 6.5c-2-1.8-3-4-3-6.5s1-4.7 3-6.5Z" })
|
|
768
929
|
] }),
|
|
769
|
-
agent: () => /* @__PURE__ */
|
|
770
|
-
/* @__PURE__ */
|
|
771
|
-
/* @__PURE__ */
|
|
930
|
+
agent: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
931
|
+
/* @__PURE__ */ jsx4("circle", { cx: "9", cy: "6", r: "2.5" }),
|
|
932
|
+
/* @__PURE__ */ jsx4("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
|
|
772
933
|
] }),
|
|
773
|
-
plan: () => /* @__PURE__ */
|
|
774
|
-
other: () => /* @__PURE__ */
|
|
775
|
-
/* @__PURE__ */
|
|
776
|
-
/* @__PURE__ */
|
|
934
|
+
plan: () => /* @__PURE__ */ jsx4(Fragment3, { children: /* @__PURE__ */ jsx4("path", { d: "m3 5 1 1 2-2M3 9l1 1 2-2M3 13l1 1 2-2M8 5h7M8 9h7M8 13h7" }) }),
|
|
935
|
+
other: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
936
|
+
/* @__PURE__ */ jsx4("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
|
|
937
|
+
/* @__PURE__ */ jsx4("circle", { cx: "9", cy: "9", r: "3.5" })
|
|
777
938
|
] })
|
|
778
939
|
};
|
|
779
940
|
function ToolIcon({ category }) {
|
|
780
941
|
const Glyph = TOOL_ICONS[category] ?? TOOL_ICONS.other;
|
|
781
|
-
return /* @__PURE__ */
|
|
942
|
+
return /* @__PURE__ */ jsx4("svg", { class: "scui-tool-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", "stroke-width": "1.35", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx4(Glyph, {}) });
|
|
782
943
|
}
|
|
783
944
|
function toolAction2(entry, category, presentation) {
|
|
784
945
|
if (presentation?.action) return presentation.action;
|
|
@@ -833,86 +994,86 @@ function ToolMetrics({ presentation }) {
|
|
|
833
994
|
presentation.exitCode === null ? "" : `exit ${presentation.exitCode}`,
|
|
834
995
|
formatDuration(presentation.durationMs)
|
|
835
996
|
].filter(Boolean);
|
|
836
|
-
return metrics.length ? /* @__PURE__ */
|
|
997
|
+
return metrics.length ? /* @__PURE__ */ jsx4("div", { class: "scui-tool-metrics", children: metrics.map((metric) => /* @__PURE__ */ jsx4("span", { children: metric }, metric)) }) : null;
|
|
837
998
|
}
|
|
838
999
|
function PendingElapsed({ now }) {
|
|
839
1000
|
const clock = now ?? Date.now;
|
|
840
|
-
const started =
|
|
1001
|
+
const started = useRef3(clock());
|
|
841
1002
|
const [elapsed, setElapsed] = useState2(0);
|
|
842
|
-
|
|
1003
|
+
useEffect3(() => {
|
|
843
1004
|
const timer = setInterval(() => setElapsed(Math.max(0, clock() - started.current)), 1e3);
|
|
844
1005
|
return () => clearInterval(timer);
|
|
845
1006
|
}, [clock]);
|
|
846
|
-
return /* @__PURE__ */
|
|
1007
|
+
return /* @__PURE__ */ jsx4("small", { class: "scui-tool-elapsed", children: elapsed < 1e3 ? "now" : formatDuration(elapsed) });
|
|
847
1008
|
}
|
|
848
1009
|
function LinePreview({ value, kind }) {
|
|
849
1010
|
if (!value) return null;
|
|
850
|
-
return /* @__PURE__ */
|
|
1011
|
+
return /* @__PURE__ */ jsx4("ol", { class: "scui-code-preview", "data-kind": kind, children: stripAnsi(value).split("\n").map((line, index) => {
|
|
851
1012
|
const tone = kind === "diff" ? line.startsWith("+") && !line.startsWith("+++") ? "add" : line.startsWith("-") && !line.startsWith("---") ? "remove" : line.startsWith("@@") ? "hunk" : "plain" : "plain";
|
|
852
|
-
return /* @__PURE__ */
|
|
853
|
-
/* @__PURE__ */
|
|
854
|
-
/* @__PURE__ */
|
|
1013
|
+
return /* @__PURE__ */ jsxs3("li", { "data-tone": tone, children: [
|
|
1014
|
+
/* @__PURE__ */ jsx4("span", { children: index + 1 }),
|
|
1015
|
+
/* @__PURE__ */ jsx4("code", { children: line || " " })
|
|
855
1016
|
] }, index);
|
|
856
1017
|
}) });
|
|
857
1018
|
}
|
|
858
1019
|
function TerminalPreview({ presentation, pending, failed }) {
|
|
859
|
-
return /* @__PURE__ */
|
|
860
|
-
/* @__PURE__ */
|
|
861
|
-
/* @__PURE__ */
|
|
862
|
-
/* @__PURE__ */
|
|
863
|
-
/* @__PURE__ */
|
|
864
|
-
/* @__PURE__ */
|
|
1020
|
+
return /* @__PURE__ */ jsxs3("section", { class: "scui-terminal", children: [
|
|
1021
|
+
/* @__PURE__ */ jsxs3("header", { children: [
|
|
1022
|
+
/* @__PURE__ */ jsxs3("span", { "aria-hidden": "true", children: [
|
|
1023
|
+
/* @__PURE__ */ jsx4("i", {}),
|
|
1024
|
+
/* @__PURE__ */ jsx4("i", {}),
|
|
1025
|
+
/* @__PURE__ */ jsx4("i", {})
|
|
865
1026
|
] }),
|
|
866
|
-
/* @__PURE__ */
|
|
1027
|
+
/* @__PURE__ */ jsx4("code", { children: presentation.command ? `$ ${presentation.command}` : "Terminal" })
|
|
867
1028
|
] }),
|
|
868
|
-
presentation.preview ? /* @__PURE__ */
|
|
869
|
-
/* @__PURE__ */
|
|
1029
|
+
presentation.preview ? /* @__PURE__ */ jsx4("pre", { "data-error": failed, children: stripAnsi(presentation.preview) }) : pending ? /* @__PURE__ */ jsxs3("div", { class: "scui-terminal-wait", children: [
|
|
1030
|
+
/* @__PURE__ */ jsx4("i", {}),
|
|
870
1031
|
" Waiting for output"
|
|
871
|
-
] }) : /* @__PURE__ */
|
|
1032
|
+
] }) : /* @__PURE__ */ jsx4("div", { class: "scui-terminal-empty", children: "No output" })
|
|
872
1033
|
] });
|
|
873
1034
|
}
|
|
874
1035
|
function SearchPreview({ presentation }) {
|
|
875
1036
|
const lines = stripAnsi(presentation.preview).split("\n").filter(Boolean);
|
|
876
|
-
return /* @__PURE__ */
|
|
877
|
-
presentation.query ? /* @__PURE__ */
|
|
878
|
-
/* @__PURE__ */
|
|
879
|
-
/* @__PURE__ */
|
|
1037
|
+
return /* @__PURE__ */ jsxs3("section", { class: "scui-search-preview", children: [
|
|
1038
|
+
presentation.query ? /* @__PURE__ */ jsxs3("header", { children: [
|
|
1039
|
+
/* @__PURE__ */ jsx4("span", { children: "Search" }),
|
|
1040
|
+
/* @__PURE__ */ jsx4("code", { children: presentation.query })
|
|
880
1041
|
] }) : null,
|
|
881
|
-
lines.length ? /* @__PURE__ */
|
|
1042
|
+
lines.length ? /* @__PURE__ */ jsx4("ol", { children: lines.map((line, index) => {
|
|
882
1043
|
const match = /^(.*?):(\d+)(?::(\d+))?:(.*)$/.exec(line);
|
|
883
|
-
return /* @__PURE__ */
|
|
884
|
-
/* @__PURE__ */
|
|
885
|
-
/* @__PURE__ */
|
|
1044
|
+
return /* @__PURE__ */ jsx4("li", { children: match ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1045
|
+
/* @__PURE__ */ jsx4("code", { children: match[1] }),
|
|
1046
|
+
/* @__PURE__ */ jsxs3("small", { children: [
|
|
886
1047
|
match[2],
|
|
887
1048
|
match[3] ? `:${match[3]}` : ""
|
|
888
1049
|
] }),
|
|
889
|
-
/* @__PURE__ */
|
|
890
|
-
] }) : /* @__PURE__ */
|
|
891
|
-
}) }) : /* @__PURE__ */
|
|
1050
|
+
/* @__PURE__ */ jsx4("span", { children: match[4] })
|
|
1051
|
+
] }) : /* @__PURE__ */ jsx4("span", { children: line }) }, index);
|
|
1052
|
+
}) }) : /* @__PURE__ */ jsx4("p", { children: "No textual results" })
|
|
892
1053
|
] });
|
|
893
1054
|
}
|
|
894
1055
|
function ToolPreview({ presentation, entry }) {
|
|
895
1056
|
const pending = entry.status === "pending";
|
|
896
1057
|
const failed = entry.status === "error";
|
|
897
|
-
if (presentation.detail === "terminal") return /* @__PURE__ */
|
|
898
|
-
if (presentation.detail === "diff") return presentation.preview ? /* @__PURE__ */
|
|
899
|
-
if (presentation.detail === "file") return presentation.preview ? /* @__PURE__ */
|
|
900
|
-
if (presentation.detail === "matches") return /* @__PURE__ */
|
|
901
|
-
if (presentation.detail === "web") return /* @__PURE__ */
|
|
902
|
-
presentation.url ? /* @__PURE__ */
|
|
903
|
-
presentation.preview ? /* @__PURE__ */
|
|
1058
|
+
if (presentation.detail === "terminal") return /* @__PURE__ */ jsx4(TerminalPreview, { presentation, pending, failed });
|
|
1059
|
+
if (presentation.detail === "diff") return presentation.preview ? /* @__PURE__ */ jsx4(LinePreview, { value: presentation.preview, kind: "diff" }) : /* @__PURE__ */ jsx4("div", { class: "scui-tool-empty", children: "Edit completed without a textual diff" });
|
|
1060
|
+
if (presentation.detail === "file") return presentation.preview ? /* @__PURE__ */ jsx4(LinePreview, { value: presentation.preview, kind: "file" }) : /* @__PURE__ */ jsx4("div", { class: "scui-tool-empty", children: "File contents were not included in this event" });
|
|
1061
|
+
if (presentation.detail === "matches") return /* @__PURE__ */ jsx4(SearchPreview, { presentation });
|
|
1062
|
+
if (presentation.detail === "web") return /* @__PURE__ */ jsxs3("section", { class: "scui-web-preview", children: [
|
|
1063
|
+
presentation.url ? /* @__PURE__ */ jsx4("code", { children: presentation.url }) : null,
|
|
1064
|
+
presentation.preview ? /* @__PURE__ */ jsx4("p", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx4("small", { children: pending ? "Waiting for the page" : "No page summary returned" })
|
|
904
1065
|
] });
|
|
905
|
-
if (presentation.detail === "agent") return /* @__PURE__ */
|
|
906
|
-
if (presentation.detail === "plan") return /* @__PURE__ */
|
|
907
|
-
/* @__PURE__ */
|
|
908
|
-
/* @__PURE__ */
|
|
1066
|
+
if (presentation.detail === "agent") return /* @__PURE__ */ jsx4("section", { class: "scui-agent-preview", children: presentation.preview ? /* @__PURE__ */ jsx4("pre", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx4("small", { children: pending ? "Agent is working" : "No textual handoff returned" }) });
|
|
1067
|
+
if (presentation.detail === "plan") return /* @__PURE__ */ jsx4("ol", { class: "scui-plan-preview", children: presentation.items?.map((item, index) => /* @__PURE__ */ jsxs3("li", { "data-status": item.status, children: [
|
|
1068
|
+
/* @__PURE__ */ jsx4("i", { "aria-hidden": "true" }),
|
|
1069
|
+
/* @__PURE__ */ jsx4("span", { children: item.label })
|
|
909
1070
|
] }, `${item.label}:${index}`)) });
|
|
910
|
-
return presentation.preview ? /* @__PURE__ */
|
|
1071
|
+
return presentation.preview ? /* @__PURE__ */ jsx4("pre", { class: "scui-tool-output", "data-error": failed, children: stripAnsi(presentation.preview) }) : null;
|
|
911
1072
|
}
|
|
912
1073
|
function ToolActions({ presentation, adapter }) {
|
|
913
1074
|
const [copied, setCopied] = useState2(false);
|
|
914
|
-
const reset =
|
|
915
|
-
|
|
1075
|
+
const reset = useRef3(null);
|
|
1076
|
+
useEffect3(() => () => clearTimeout(reset.current), []);
|
|
916
1077
|
if (!adapter?.copyText) return null;
|
|
917
1078
|
const action = presentation.command ? ["Copy command", presentation.command] : presentation.path ? ["Copy path", presentation.path] : presentation.url ? ["Copy URL", presentation.url] : presentation.query ? ["Copy query", presentation.query] : null;
|
|
918
1079
|
const copy = async () => {
|
|
@@ -921,27 +1082,27 @@ function ToolActions({ presentation, adapter }) {
|
|
|
921
1082
|
clearTimeout(reset.current);
|
|
922
1083
|
reset.current = setTimeout(() => setCopied(false), 1500);
|
|
923
1084
|
};
|
|
924
|
-
return action ? /* @__PURE__ */
|
|
1085
|
+
return action ? /* @__PURE__ */ jsx4("div", { class: "scui-tool-actions", children: /* @__PURE__ */ jsx4("button", { type: "button", onClick: copy, children: copied ? "Copied" : action[0] }) }) : null;
|
|
925
1086
|
}
|
|
926
1087
|
function ToolStack({ tools }) {
|
|
927
1088
|
if (tools.length < 2) return null;
|
|
928
|
-
return /* @__PURE__ */
|
|
1089
|
+
return /* @__PURE__ */ jsx4("div", { class: "scui-tool-stack", "aria-label": "Coordinated tools", children: tools.map((tool) => /* @__PURE__ */ jsx4("span", { children: tool.replaceAll("__", " \xB7 ").replaceAll("_", " ") }, tool)) });
|
|
929
1090
|
}
|
|
930
1091
|
function TechnicalDetails({ entry }) {
|
|
931
1092
|
if (!entry.arguments && !entry.resultText) return null;
|
|
932
|
-
return /* @__PURE__ */
|
|
933
|
-
/* @__PURE__ */
|
|
934
|
-
/* @__PURE__ */
|
|
935
|
-
entry.arguments ? /* @__PURE__ */
|
|
936
|
-
/* @__PURE__ */
|
|
937
|
-
/* @__PURE__ */
|
|
938
|
-
/* @__PURE__ */
|
|
939
|
-
/* @__PURE__ */
|
|
1093
|
+
return /* @__PURE__ */ jsxs3("details", { class: "scui-tool-technical", children: [
|
|
1094
|
+
/* @__PURE__ */ jsx4("summary", { children: "Technical details" }),
|
|
1095
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
1096
|
+
entry.arguments ? /* @__PURE__ */ jsxs3("section", { children: [
|
|
1097
|
+
/* @__PURE__ */ jsx4("strong", { children: "Native arguments" }),
|
|
1098
|
+
/* @__PURE__ */ jsx4("dl", { children: argumentRows(entry.arguments).map((row) => /* @__PURE__ */ jsxs3("div", { children: [
|
|
1099
|
+
/* @__PURE__ */ jsx4("dt", { children: row.label }),
|
|
1100
|
+
/* @__PURE__ */ jsx4("dd", { children: /* @__PURE__ */ jsx4("pre", { children: row.value }) })
|
|
940
1101
|
] }, row.key)) })
|
|
941
1102
|
] }) : null,
|
|
942
|
-
entry.resultText ? /* @__PURE__ */
|
|
943
|
-
/* @__PURE__ */
|
|
944
|
-
/* @__PURE__ */
|
|
1103
|
+
entry.resultText ? /* @__PURE__ */ jsxs3("section", { children: [
|
|
1104
|
+
/* @__PURE__ */ jsx4("strong", { children: "Native result" }),
|
|
1105
|
+
/* @__PURE__ */ jsxs3("pre", { "data-error": entry.status === "error", children: [
|
|
945
1106
|
entry.resultText,
|
|
946
1107
|
entry.truncated ? "\n[truncated]" : ""
|
|
947
1108
|
] })
|
|
@@ -950,125 +1111,126 @@ function TechnicalDetails({ entry }) {
|
|
|
950
1111
|
] });
|
|
951
1112
|
}
|
|
952
1113
|
function TranscriptEntry({ entry, state, adapter }) {
|
|
953
|
-
if (entry.role === "request") return /* @__PURE__ */
|
|
1114
|
+
if (entry.role === "request") return /* @__PURE__ */ jsx4(RequestCard, { entry, adapter, canRespond: state.canRespond });
|
|
954
1115
|
if (entry.role === "reasoning") {
|
|
955
|
-
return /* @__PURE__ */
|
|
956
|
-
/* @__PURE__ */
|
|
957
|
-
/* @__PURE__ */
|
|
1116
|
+
return /* @__PURE__ */ jsxs3("details", { class: "scui-reasoning", open: entry.streaming, children: [
|
|
1117
|
+
/* @__PURE__ */ jsx4("summary", { children: entry.streaming ? "Reasoning\u2026" : "Reasoning" }),
|
|
1118
|
+
/* @__PURE__ */ jsx4(Markdown, { value: entry.text, copyText: adapter?.copyText })
|
|
958
1119
|
] });
|
|
959
1120
|
}
|
|
960
|
-
if (entry.role === "notice" || entry.role === "system") return /* @__PURE__ */
|
|
961
|
-
return /* @__PURE__ */
|
|
962
|
-
/* @__PURE__ */
|
|
963
|
-
/* @__PURE__ */
|
|
964
|
-
entry.truncated ? /* @__PURE__ */
|
|
1121
|
+
if (entry.role === "notice" || entry.role === "system") return /* @__PURE__ */ jsx4("div", { class: "scui-notice", "data-code": entry.code, children: entry.text });
|
|
1122
|
+
return /* @__PURE__ */ jsxs3("article", { class: "scui-message", "data-role": entry.role, "aria-label": `${entry.role === "user" ? "Your" : "Assistant"} message`, children: [
|
|
1123
|
+
/* @__PURE__ */ jsx4(Markdown, { value: entry.text, copyText: adapter?.copyText }),
|
|
1124
|
+
/* @__PURE__ */ jsx4(ContextDisclosure, { context: entry.context }),
|
|
1125
|
+
entry.truncated ? /* @__PURE__ */ jsx4("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null,
|
|
1126
|
+
/* @__PURE__ */ jsx4(MessageMeta, { entry, adapter })
|
|
965
1127
|
] });
|
|
966
1128
|
}
|
|
967
1129
|
function ToolRow({ entry, workspace, open = false, adapter }) {
|
|
968
1130
|
const presentation = entry.presentation ?? createToolPresentation(entry);
|
|
969
1131
|
const [expanded, setExpanded] = useState2(open || entry.status === "pending");
|
|
970
|
-
|
|
1132
|
+
useEffect3(() => {
|
|
971
1133
|
if (entry.status === "pending") setExpanded(true);
|
|
972
1134
|
}, [entry.status]);
|
|
973
1135
|
const target = compactToolTarget(presentation.target, workspace);
|
|
974
1136
|
const hasDetail = Boolean(entry.arguments || entry.resultText || presentation.preview || presentation.fields.length);
|
|
975
1137
|
const category = presentation.category ?? toolCategory(entry);
|
|
976
|
-
const summary = /* @__PURE__ */
|
|
977
|
-
/* @__PURE__ */
|
|
978
|
-
/* @__PURE__ */
|
|
979
|
-
target ? /* @__PURE__ */
|
|
980
|
-
/* @__PURE__ */
|
|
981
|
-
entry.status === "pending" ? /* @__PURE__ */
|
|
982
|
-
/* @__PURE__ */
|
|
983
|
-
hasDetail ? /* @__PURE__ */
|
|
1138
|
+
const summary = /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1139
|
+
/* @__PURE__ */ jsx4(ToolIcon, { category }),
|
|
1140
|
+
/* @__PURE__ */ jsx4("strong", { children: toolAction2(entry, category, presentation) }),
|
|
1141
|
+
target ? /* @__PURE__ */ jsx4("code", { class: "scui-tool-target", title: presentation.target, children: target }) : null,
|
|
1142
|
+
/* @__PURE__ */ jsx4("span", { class: "scui-spacer" }),
|
|
1143
|
+
entry.status === "pending" ? /* @__PURE__ */ jsx4(PendingElapsed, { now: adapter?.now }) : null,
|
|
1144
|
+
/* @__PURE__ */ jsx4("span", { class: "scui-tool-status", role: "status", "data-status": entry.status ?? "completed", "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? /* @__PURE__ */ jsx4("i", {}) : /* @__PURE__ */ jsx4(UiIcon, { name: entry.status === "error" ? "close" : "check", size: 12 }) }),
|
|
1145
|
+
hasDetail ? /* @__PURE__ */ jsx4(UiIcon, { name: "chevron", size: 14, class: "scui-tool-chevron" }) : null
|
|
984
1146
|
] });
|
|
985
|
-
if (!hasDetail) return /* @__PURE__ */
|
|
986
|
-
return /* @__PURE__ */
|
|
987
|
-
/* @__PURE__ */
|
|
988
|
-
/* @__PURE__ */
|
|
989
|
-
/* @__PURE__ */
|
|
990
|
-
/* @__PURE__ */
|
|
991
|
-
/* @__PURE__ */
|
|
992
|
-
presentation.fields.length ? /* @__PURE__ */
|
|
993
|
-
/* @__PURE__ */
|
|
994
|
-
/* @__PURE__ */
|
|
1147
|
+
if (!hasDetail) return /* @__PURE__ */ jsx4("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: /* @__PURE__ */ jsx4("div", { class: "scui-tool-head", children: summary }) });
|
|
1148
|
+
return /* @__PURE__ */ jsxs3("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, open: expanded, onToggle: (event) => setExpanded(event.currentTarget.open), children: [
|
|
1149
|
+
/* @__PURE__ */ jsx4("summary", { class: "scui-tool-head", children: summary }),
|
|
1150
|
+
/* @__PURE__ */ jsxs3("div", { class: "scui-tool-detail", children: [
|
|
1151
|
+
/* @__PURE__ */ jsx4(ToolMetrics, { presentation }),
|
|
1152
|
+
/* @__PURE__ */ jsx4(ToolStack, { tools: presentation.tools ?? [] }),
|
|
1153
|
+
/* @__PURE__ */ jsx4(ToolPreview, { presentation, entry }),
|
|
1154
|
+
presentation.fields.length ? /* @__PURE__ */ jsx4("dl", { class: "scui-tool-fields", children: presentation.fields.map((field) => /* @__PURE__ */ jsxs3("div", { children: [
|
|
1155
|
+
/* @__PURE__ */ jsx4("dt", { children: field.label }),
|
|
1156
|
+
/* @__PURE__ */ jsx4("dd", { children: field.value })
|
|
995
1157
|
] }, field.label)) }) : null,
|
|
996
|
-
/* @__PURE__ */
|
|
997
|
-
/* @__PURE__ */
|
|
1158
|
+
/* @__PURE__ */ jsx4(ToolActions, { presentation, adapter }),
|
|
1159
|
+
/* @__PURE__ */ jsx4(TechnicalDetails, { entry })
|
|
998
1160
|
] })
|
|
999
1161
|
] });
|
|
1000
1162
|
}
|
|
1001
1163
|
function ActivityGroup({ entries, state, adapter }) {
|
|
1002
|
-
if (entries.length === 1) return /* @__PURE__ */
|
|
1164
|
+
if (entries.length === 1) return /* @__PURE__ */ jsx4("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx4(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
|
|
1003
1165
|
const active = entries.some((entry) => entry.status === "pending");
|
|
1004
1166
|
const [open, setOpen] = useState2(active);
|
|
1005
1167
|
const id = useId();
|
|
1006
|
-
|
|
1168
|
+
useEffect3(() => {
|
|
1007
1169
|
if (active) setOpen(true);
|
|
1008
1170
|
}, [active]);
|
|
1009
|
-
return /* @__PURE__ */
|
|
1010
|
-
/* @__PURE__ */
|
|
1011
|
-
/* @__PURE__ */
|
|
1012
|
-
/* @__PURE__ */
|
|
1013
|
-
/* @__PURE__ */
|
|
1014
|
-
/* @__PURE__ */
|
|
1171
|
+
return /* @__PURE__ */ jsxs3("section", { class: "scui-activity", children: [
|
|
1172
|
+
/* @__PURE__ */ jsxs3("button", { class: "scui-activity-head", type: "button", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
|
|
1173
|
+
/* @__PURE__ */ jsx4("span", { class: "scui-fold", "data-open": open, children: /* @__PURE__ */ jsx4(UiIcon, { name: "chevron", size: 14 }) }),
|
|
1174
|
+
/* @__PURE__ */ jsx4("strong", { children: activitySummary(entries) }),
|
|
1175
|
+
/* @__PURE__ */ jsx4("span", { class: "scui-spacer" }),
|
|
1176
|
+
/* @__PURE__ */ jsxs3("small", { children: [
|
|
1015
1177
|
entries.filter((entry) => entry.status !== "pending").length,
|
|
1016
1178
|
"/",
|
|
1017
1179
|
entries.length
|
|
1018
1180
|
] })
|
|
1019
1181
|
] }),
|
|
1020
|
-
open ? /* @__PURE__ */
|
|
1182
|
+
open ? /* @__PURE__ */ jsx4("div", { id, children: entries.map((entry) => /* @__PURE__ */ jsx4(ToolRow, { entry, workspace: state.workspace, adapter }, entry.id)) }) : null
|
|
1021
1183
|
] });
|
|
1022
1184
|
}
|
|
1023
1185
|
function TaskPlan({ plan }) {
|
|
1024
1186
|
if (!plan.items.length) return null;
|
|
1025
1187
|
const complete = plan.items.filter((item) => item.status === "completed" || item.status === "cancelled").length;
|
|
1026
|
-
return /* @__PURE__ */
|
|
1027
|
-
/* @__PURE__ */
|
|
1028
|
-
/* @__PURE__ */
|
|
1029
|
-
/* @__PURE__ */
|
|
1188
|
+
return /* @__PURE__ */ jsxs3("details", { class: "scui-plan", children: [
|
|
1189
|
+
/* @__PURE__ */ jsxs3("summary", { children: [
|
|
1190
|
+
/* @__PURE__ */ jsx4("span", { children: "Plan" }),
|
|
1191
|
+
/* @__PURE__ */ jsxs3("small", { children: [
|
|
1030
1192
|
complete,
|
|
1031
1193
|
"/",
|
|
1032
1194
|
plan.items.length
|
|
1033
1195
|
] })
|
|
1034
1196
|
] }),
|
|
1035
|
-
/* @__PURE__ */
|
|
1036
|
-
/* @__PURE__ */
|
|
1197
|
+
/* @__PURE__ */ jsx4("ol", { tabIndex: 0, "aria-label": "Task plan steps", children: plan.items.map((item) => /* @__PURE__ */ jsxs3("li", { "data-status": item.status, children: [
|
|
1198
|
+
/* @__PURE__ */ jsx4("i", { "aria-hidden": "true" }),
|
|
1037
1199
|
" ",
|
|
1038
|
-
/* @__PURE__ */
|
|
1200
|
+
/* @__PURE__ */ jsx4("span", { children: item.title })
|
|
1039
1201
|
] }, item.id)) })
|
|
1040
1202
|
] });
|
|
1041
1203
|
}
|
|
1042
1204
|
function SessionDetails({ semantics }) {
|
|
1043
1205
|
if (!semantics.fidelity && !semantics.residueCount && !semantics.parseErrors && !semantics.subagents.length) return null;
|
|
1044
|
-
return /* @__PURE__ */
|
|
1045
|
-
/* @__PURE__ */
|
|
1046
|
-
/* @__PURE__ */
|
|
1047
|
-
semantics.fidelity ? /* @__PURE__ */
|
|
1048
|
-
/* @__PURE__ */
|
|
1049
|
-
/* @__PURE__ */
|
|
1206
|
+
return /* @__PURE__ */ jsxs3("details", { class: "scui-details", children: [
|
|
1207
|
+
/* @__PURE__ */ jsx4("summary", { children: "Session details" }),
|
|
1208
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
1209
|
+
semantics.fidelity ? /* @__PURE__ */ jsxs3("p", { children: [
|
|
1210
|
+
/* @__PURE__ */ jsx4("strong", { children: "Fidelity" }),
|
|
1211
|
+
/* @__PURE__ */ jsx4("span", { children: semantics.fidelity.replaceAll("_", " ") })
|
|
1050
1212
|
] }) : null,
|
|
1051
|
-
/* @__PURE__ */
|
|
1052
|
-
/* @__PURE__ */
|
|
1053
|
-
/* @__PURE__ */
|
|
1213
|
+
/* @__PURE__ */ jsxs3("p", { children: [
|
|
1214
|
+
/* @__PURE__ */ jsx4("strong", { children: "Native records" }),
|
|
1215
|
+
/* @__PURE__ */ jsx4("span", { children: semantics.rawRecords })
|
|
1054
1216
|
] }),
|
|
1055
|
-
semantics.residueCount ? /* @__PURE__ */
|
|
1056
|
-
/* @__PURE__ */
|
|
1057
|
-
/* @__PURE__ */
|
|
1217
|
+
semantics.residueCount ? /* @__PURE__ */ jsxs3("p", { children: [
|
|
1218
|
+
/* @__PURE__ */ jsx4("strong", { children: "Residue" }),
|
|
1219
|
+
/* @__PURE__ */ jsxs3("span", { children: [
|
|
1058
1220
|
semantics.residueCount,
|
|
1059
1221
|
" retained"
|
|
1060
1222
|
] })
|
|
1061
1223
|
] }) : null,
|
|
1062
|
-
semantics.parseErrors ? /* @__PURE__ */
|
|
1063
|
-
/* @__PURE__ */
|
|
1064
|
-
/* @__PURE__ */
|
|
1224
|
+
semantics.parseErrors ? /* @__PURE__ */ jsxs3("p", { children: [
|
|
1225
|
+
/* @__PURE__ */ jsx4("strong", { children: "Parse diagnostics" }),
|
|
1226
|
+
/* @__PURE__ */ jsx4("span", { children: semantics.parseErrors })
|
|
1065
1227
|
] }) : null,
|
|
1066
|
-
semantics.subagents.map((agent) => /* @__PURE__ */
|
|
1067
|
-
/* @__PURE__ */
|
|
1228
|
+
semantics.subagents.map((agent) => /* @__PURE__ */ jsxs3("p", { children: [
|
|
1229
|
+
/* @__PURE__ */ jsxs3("strong", { children: [
|
|
1068
1230
|
agent.source,
|
|
1069
1231
|
" subagent"
|
|
1070
1232
|
] }),
|
|
1071
|
-
/* @__PURE__ */
|
|
1233
|
+
/* @__PURE__ */ jsxs3("span", { children: [
|
|
1072
1234
|
agent.messages,
|
|
1073
1235
|
" messages \xB7 ",
|
|
1074
1236
|
agent.fidelity.replaceAll("_", " ")
|
|
@@ -1078,13 +1240,29 @@ function SessionDetails({ semantics }) {
|
|
|
1078
1240
|
] });
|
|
1079
1241
|
}
|
|
1080
1242
|
var conversationMemory = /* @__PURE__ */ new Map();
|
|
1081
|
-
function
|
|
1082
|
-
const
|
|
1243
|
+
function ConversationAnnouncements({ state }) {
|
|
1244
|
+
const previousBusy = useRef3(state.busy);
|
|
1245
|
+
const [announcement, setAnnouncement] = useState2("");
|
|
1246
|
+
useEffect3(() => {
|
|
1247
|
+
if (previousBusy.current && !state.busy && !state.error) {
|
|
1248
|
+
setAnnouncement(`${harnessDisplayName(state.harness) || "Coding agent"} finished working`);
|
|
1249
|
+
}
|
|
1250
|
+
previousBusy.current = state.busy;
|
|
1251
|
+
}, [state.busy, state.error, state.harness]);
|
|
1252
|
+
return /* @__PURE__ */ jsx4("span", { class: "scui-sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: announcement });
|
|
1253
|
+
}
|
|
1254
|
+
function Conversation({ state, adapter, components = {}, slots = {}, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pending = null, unreadAfterMessages = null }) {
|
|
1255
|
+
const scroller = useRef3(null);
|
|
1083
1256
|
const remembered = conversationMemory.get(memoryKey) ?? { top: null, atBottom: true };
|
|
1084
1257
|
const [atBottom, setAtBottom] = useState2(remembered.atBottom);
|
|
1085
|
-
const earlierAnchor =
|
|
1086
|
-
const restored =
|
|
1258
|
+
const earlierAnchor = useRef3(null);
|
|
1259
|
+
const restored = useRef3(false);
|
|
1087
1260
|
const blocks = groupConversation(state.transcript);
|
|
1261
|
+
const unreadBoundary = useRef3(Number.isSafeInteger(unreadAfterMessages) && unreadAfterMessages >= 0 ? unreadAfterMessages : null);
|
|
1262
|
+
const unreadBlock = unreadBoundary.current === null ? -1 : blocks.findIndex((block) => {
|
|
1263
|
+
const entries = block.kind === "activity" ? block.entries : [block.entry];
|
|
1264
|
+
return entries.some((entry) => Number.isSafeInteger(entry.messageIndex) && entry.messageIndex > unreadBoundary.current);
|
|
1265
|
+
});
|
|
1088
1266
|
const Entry = components.TranscriptEntry ?? TranscriptEntry;
|
|
1089
1267
|
const Group = components.ActivityGroup ?? ActivityGroup;
|
|
1090
1268
|
const Plan = components.TaskPlan ?? TaskPlan;
|
|
@@ -1099,7 +1277,7 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
|
|
|
1099
1277
|
setAtBottom(true);
|
|
1100
1278
|
remember({ top: scroller.current.scrollTop, atBottom: true });
|
|
1101
1279
|
};
|
|
1102
|
-
|
|
1280
|
+
useLayoutEffect2(() => {
|
|
1103
1281
|
const element = scroller.current;
|
|
1104
1282
|
if (!element) return;
|
|
1105
1283
|
const anchor = earlierAnchor.current;
|
|
@@ -1115,54 +1293,61 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
|
|
|
1115
1293
|
else pin();
|
|
1116
1294
|
} else if (atBottom) pin();
|
|
1117
1295
|
}, [memoryKey, state.transcript, state.busy, state.operation, pendingMessage?.text, pendingMessage?.status]);
|
|
1118
|
-
return /* @__PURE__ */
|
|
1119
|
-
/* @__PURE__ */
|
|
1296
|
+
return /* @__PURE__ */ jsxs3("div", { class: "scui-conversation-wrap", children: [
|
|
1297
|
+
/* @__PURE__ */ jsx4(ConversationAnnouncements, { state }),
|
|
1298
|
+
/* @__PURE__ */ jsx4("div", { class: "scui-conversation", ref: scroller, tabIndex: 0, "aria-label": "Conversation", onScroll: (event) => {
|
|
1120
1299
|
const element = event.currentTarget;
|
|
1121
1300
|
const bottom = element.scrollHeight - element.scrollTop - element.clientHeight <= 64;
|
|
1122
1301
|
setAtBottom(bottom);
|
|
1123
1302
|
remember({ top: element.scrollTop, atBottom: bottom });
|
|
1124
|
-
}, children: /* @__PURE__ */
|
|
1125
|
-
Before ? /* @__PURE__ */
|
|
1126
|
-
/* @__PURE__ */
|
|
1127
|
-
/* @__PURE__ */
|
|
1128
|
-
state.history.hasEarlier ? /* @__PURE__ */
|
|
1303
|
+
}, children: /* @__PURE__ */ jsxs3("div", { children: [
|
|
1304
|
+
Before ? /* @__PURE__ */ jsx4(Before, { state, adapter, value: null }) : null,
|
|
1305
|
+
/* @__PURE__ */ jsx4(Plan, { plan: state.taskPlan, value: state.taskPlan, state, adapter }),
|
|
1306
|
+
/* @__PURE__ */ jsx4(SessionDetails, { semantics: state.semantics }),
|
|
1307
|
+
state.history.hasEarlier ? /* @__PURE__ */ jsx4("button", { class: "scui-load", type: "button", disabled: Boolean(state.operation), onClick: () => {
|
|
1129
1308
|
const element = scroller.current;
|
|
1130
1309
|
if (element) earlierAnchor.current = { height: element.scrollHeight, top: element.scrollTop, entries: state.transcript.length };
|
|
1131
1310
|
setAtBottom(false);
|
|
1132
1311
|
adapter.onIntent({ action: "loadEarlier" });
|
|
1133
1312
|
}, children: "Load earlier messages" }) : null,
|
|
1134
|
-
!blocks.length && state.startup !== "ready" ? /* @__PURE__ */
|
|
1135
|
-
!blocks.length && state.startup === "ready" ? Empty ? /* @__PURE__ */
|
|
1136
|
-
blocks.map((block
|
|
1137
|
-
|
|
1138
|
-
/* @__PURE__ */
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1313
|
+
!blocks.length && state.startup !== "ready" ? /* @__PURE__ */ jsx4(LoadingStatus, { state }) : null,
|
|
1314
|
+
!blocks.length && state.startup === "ready" ? Empty ? /* @__PURE__ */ jsx4(Empty, { state, adapter, value: null }) : /* @__PURE__ */ jsx4("div", { class: "scui-empty", children: state.error ?? (state.harness ? `${harnessDisplayName(state.harness)} is listening. Say something.` : "No transcript yet.") }) : null,
|
|
1315
|
+
blocks.map((block, index) => /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1316
|
+
index === unreadBlock ? /* @__PURE__ */ jsx4("div", { class: "scui-unread-divider", role: "separator", "aria-label": "New messages", children: /* @__PURE__ */ jsx4("span", { children: "New" }) }) : null,
|
|
1317
|
+
block.kind === "activity" ? /* @__PURE__ */ jsx4(Group, { value: block.entries, entries: block.entries, state, adapter }) : /* @__PURE__ */ jsx4(Entry, { value: block.entry, entry: block.entry, state, adapter })
|
|
1318
|
+
] }, block.id)),
|
|
1319
|
+
pendingMessage ? /* @__PURE__ */ jsxs3("article", { class: "scui-message scui-pending", "data-role": "user", "data-status": pendingMessage.status, "aria-label": "Your pending message", children: [
|
|
1320
|
+
/* @__PURE__ */ jsx4(Markdown, { value: pendingMessage.text, copyText: adapter?.copyText }),
|
|
1321
|
+
/* @__PURE__ */ jsxs3("footer", { children: [
|
|
1322
|
+
/* @__PURE__ */ jsx4("small", { children: pendingMessage.status === "failed" ? "Not sent" : "Sending\u2026" }),
|
|
1323
|
+
pendingMessage.status === "failed" ? /* @__PURE__ */ jsxs3("span", { children: [
|
|
1324
|
+
/* @__PURE__ */ jsx4("button", { type: "button", onClick: pendingMessage.onRetry, children: "Retry" }),
|
|
1325
|
+
/* @__PURE__ */ jsx4("button", { type: "button", onClick: pendingMessage.onEdit, children: "Edit" })
|
|
1144
1326
|
] }) : null
|
|
1145
1327
|
] })
|
|
1146
1328
|
] }) : null,
|
|
1147
|
-
state.busy ? /* @__PURE__ */
|
|
1148
|
-
/* @__PURE__ */
|
|
1149
|
-
/* @__PURE__ */
|
|
1150
|
-
/* @__PURE__ */
|
|
1151
|
-
/* @__PURE__ */
|
|
1152
|
-
/* @__PURE__ */
|
|
1329
|
+
state.busy ? /* @__PURE__ */ jsxs3("div", { class: "scui-working", role: "status", children: [
|
|
1330
|
+
/* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: "\u2726" }),
|
|
1331
|
+
/* @__PURE__ */ jsx4("i", {}),
|
|
1332
|
+
/* @__PURE__ */ jsx4("i", {}),
|
|
1333
|
+
/* @__PURE__ */ jsx4("i", {}),
|
|
1334
|
+
/* @__PURE__ */ jsxs3("small", { children: [
|
|
1153
1335
|
harnessDisplayName(state.harness),
|
|
1154
1336
|
" is working"
|
|
1155
1337
|
] })
|
|
1156
1338
|
] }) : null,
|
|
1157
|
-
After ? /* @__PURE__ */
|
|
1339
|
+
After ? /* @__PURE__ */ jsx4(After, { state, adapter, value: null }) : null
|
|
1158
1340
|
] }) }),
|
|
1159
|
-
!atBottom ? /* @__PURE__ */
|
|
1341
|
+
!atBottom ? /* @__PURE__ */ jsxs3("button", { class: "scui-latest", type: "button", onClick: pin, children: [
|
|
1342
|
+
/* @__PURE__ */ jsx4(UiIcon, { name: "down", size: 13 }),
|
|
1343
|
+
" Latest"
|
|
1344
|
+
] }) : null
|
|
1160
1345
|
] });
|
|
1161
1346
|
}
|
|
1162
1347
|
|
|
1163
1348
|
// src/logo.jsx
|
|
1164
|
-
import { useEffect as
|
|
1165
|
-
import { jsx as
|
|
1349
|
+
import { useEffect as useEffect4 } from "preact/hooks";
|
|
1350
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
1166
1351
|
var LOGOS = {
|
|
1167
1352
|
"claude-code": {
|
|
1168
1353
|
viewBox: "-1 3.5 26 18",
|
|
@@ -1209,81 +1394,106 @@ var LOGOS = {
|
|
|
1209
1394
|
};
|
|
1210
1395
|
function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
|
|
1211
1396
|
const logo = LOGOS[id];
|
|
1212
|
-
|
|
1397
|
+
useEffect4(() => {
|
|
1213
1398
|
if (!logo) onMissingLogo?.(id);
|
|
1214
1399
|
}, [id, logo, onMissingLogo]);
|
|
1215
1400
|
if (!logo) return null;
|
|
1216
|
-
return /* @__PURE__ */
|
|
1217
|
-
/* @__PURE__ */
|
|
1218
|
-
activity && activity !== "idle" ? /* @__PURE__ */
|
|
1401
|
+
return /* @__PURE__ */ jsxs4("span", { class: "scui-logo", "data-harness": id, "data-activity": activity, style: `--scui-logo-size:${size}px`, "aria-hidden": "true", children: [
|
|
1402
|
+
/* @__PURE__ */ jsx5("svg", { viewBox: logo.viewBox, preserveAspectRatio: "xMidYMid meet", focusable: "false", children: logo.paths.map(([Tag, props], index) => /* @__PURE__ */ jsx5(Tag, { ...props }, index)) }),
|
|
1403
|
+
activity && activity !== "idle" ? /* @__PURE__ */ jsx5("i", {}) : null
|
|
1219
1404
|
] });
|
|
1220
1405
|
}
|
|
1221
1406
|
|
|
1222
1407
|
// src/sessions.jsx
|
|
1223
|
-
import { useEffect as
|
|
1224
|
-
import { jsx as
|
|
1408
|
+
import { useEffect as useEffect5, useLayoutEffect as useLayoutEffect3, useRef as useRef4, useState as useState3 } from "preact/hooks";
|
|
1409
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
1410
|
+
var sessionListMemory = /* @__PURE__ */ new Map();
|
|
1225
1411
|
function SessionRow({ row, state, onOpen }) {
|
|
1226
1412
|
const activity = sessionActivity(state, row);
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
/* @__PURE__ */ jsx5("b", { children: harnessDisplayName(row.harness) }),
|
|
1237
|
-
/* @__PURE__ */ jsx5("small", { children: preview || row.cwd || row.name })
|
|
1413
|
+
const attentionPreview = state.attention.find((item) => item.key === row.key)?.preview;
|
|
1414
|
+
const title = sessionDisplayName(row);
|
|
1415
|
+
const detail = attentionPreview || row.preview || (row.name && row.name !== title ? row.name : row.cwd);
|
|
1416
|
+
return /* @__PURE__ */ jsxs5("button", { class: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${row.age ? ` \xB7 ${row.age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
|
|
1417
|
+
/* @__PURE__ */ jsx6(HarnessLogo, { id: row.harness, activity, size: 34 }),
|
|
1418
|
+
/* @__PURE__ */ jsxs5("span", { class: "scui-session-copy", children: [
|
|
1419
|
+
/* @__PURE__ */ jsxs5("span", { children: [
|
|
1420
|
+
/* @__PURE__ */ jsx6("strong", { children: title }),
|
|
1421
|
+
/* @__PURE__ */ jsx6("small", { children: row.age })
|
|
1238
1422
|
] }),
|
|
1239
|
-
|
|
1423
|
+
detail ? /* @__PURE__ */ jsx6("span", { children: /* @__PURE__ */ jsx6("small", { title: row.cwd, children: detail }) }) : null,
|
|
1424
|
+
state.attachError?.key === row.key ? /* @__PURE__ */ jsx6("em", { children: state.attachError.message }) : null
|
|
1240
1425
|
] })
|
|
1241
1426
|
] });
|
|
1242
1427
|
}
|
|
1243
|
-
function SessionList({ state, adapter, onOpen, onNew, onClose, components = {}, labels = DEFAULT_LABELS, focusKey = null }) {
|
|
1244
|
-
const
|
|
1245
|
-
const
|
|
1428
|
+
function SessionList({ state, adapter, onOpen, onNew, onClose, components = {}, labels = DEFAULT_LABELS, focusKey = null, memoryKey = state.workspace || "@default" }) {
|
|
1429
|
+
const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
|
|
1430
|
+
const [query, setQuery] = useState3(remembered.query);
|
|
1431
|
+
const [loadingMore, setLoadingMore] = useState3(false);
|
|
1432
|
+
const root = useRef4(null);
|
|
1433
|
+
const rowScroller = useRef4(null);
|
|
1246
1434
|
const rows = filterSessions(state.sessions, query);
|
|
1247
1435
|
const Row = components.SessionRow ?? SessionRow;
|
|
1248
|
-
|
|
1436
|
+
useLayoutEffect3(() => {
|
|
1437
|
+
if (rowScroller.current) rowScroller.current.scrollTop = remembered.top;
|
|
1438
|
+
}, [memoryKey]);
|
|
1439
|
+
useEffect5(() => {
|
|
1249
1440
|
if (!focusKey || !root.current) return;
|
|
1250
1441
|
const target = focusKey === "@new" ? root.current.querySelector('[data-list-focus="new"]') : [...root.current.querySelectorAll("[data-session-key]")].find((element) => element.dataset.sessionKey === focusKey);
|
|
1251
1442
|
(target ?? root.current.querySelector("input,button"))?.focus({ preventScroll: true });
|
|
1252
1443
|
}, [focusKey, rows.length]);
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1444
|
+
useEffect5(() => {
|
|
1445
|
+
if (loadingMore) setLoadingMore(false);
|
|
1446
|
+
}, [state.error, state.history.hasMoreSessions, state.sessions.length]);
|
|
1447
|
+
const loadMore = () => {
|
|
1448
|
+
if (loadingMore) return;
|
|
1449
|
+
setLoadingMore(true);
|
|
1450
|
+
const result = adapter.onIntent({ action: "loadSessions" });
|
|
1451
|
+
if (result && typeof result.then === "function") {
|
|
1452
|
+
Promise.resolve(result).then(() => setLoadingMore(false), () => setLoadingMore(false));
|
|
1453
|
+
}
|
|
1454
|
+
};
|
|
1455
|
+
return /* @__PURE__ */ jsxs5("section", { class: "scui-list", ref: root, children: [
|
|
1456
|
+
/* @__PURE__ */ jsxs5("header", { class: "scui-head", children: [
|
|
1457
|
+
/* @__PURE__ */ jsxs5("span", { class: "scui-head-copy", children: [
|
|
1458
|
+
/* @__PURE__ */ jsx6("strong", { children: labels.chats }),
|
|
1459
|
+
/* @__PURE__ */ jsxs5("small", { children: [
|
|
1258
1460
|
state.sessions.length,
|
|
1259
1461
|
" recent conversations"
|
|
1260
1462
|
] })
|
|
1261
1463
|
] }),
|
|
1262
|
-
/* @__PURE__ */
|
|
1263
|
-
onClose ? /* @__PURE__ */
|
|
1464
|
+
/* @__PURE__ */ jsx6("button", { type: "button", "data-list-focus": "new", "aria-label": labels.newChat, disabled: !state.harnesses.some((item) => item.startable), onClick: onNew, children: /* @__PURE__ */ jsx6(UiIcon, { name: "plus", size: 18 }) }),
|
|
1465
|
+
onClose ? /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx6(UiIcon, { name: "close", size: 18 }) }) : null
|
|
1264
1466
|
] }),
|
|
1265
|
-
state.startup !== "ready" ? /* @__PURE__ */
|
|
1266
|
-
state.sessions.length > 4 ? /* @__PURE__ */
|
|
1267
|
-
/* @__PURE__ */
|
|
1268
|
-
/* @__PURE__ */
|
|
1269
|
-
|
|
1467
|
+
state.startup !== "ready" ? /* @__PURE__ */ jsx6(LoadingStatus, { state, compact: rows.length > 0 }) : null,
|
|
1468
|
+
state.sessions.length > 4 ? /* @__PURE__ */ jsxs5("label", { class: "scui-search", children: [
|
|
1469
|
+
/* @__PURE__ */ jsx6(UiIcon, { name: "search", size: 15 }),
|
|
1470
|
+
/* @__PURE__ */ jsx6("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
|
|
1471
|
+
const value = event.currentTarget.value;
|
|
1472
|
+
setQuery(value);
|
|
1473
|
+
boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
|
|
1474
|
+
if (rowScroller.current) rowScroller.current.scrollTop = 0;
|
|
1475
|
+
} }),
|
|
1476
|
+
/* @__PURE__ */ jsx6("small", { children: rows.length })
|
|
1270
1477
|
] }) : null,
|
|
1271
|
-
/* @__PURE__ */
|
|
1272
|
-
!rows.length && state.startup === "ready" ? /* @__PURE__ */
|
|
1273
|
-
rows.map((row) => /* @__PURE__ */
|
|
1274
|
-
state.history.hasMoreSessions ? /* @__PURE__ */
|
|
1478
|
+
/* @__PURE__ */ jsxs5("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
|
|
1479
|
+
!rows.length && state.startup === "ready" ? /* @__PURE__ */ jsx6("div", { class: "scui-empty", children: query ? "No chats match your search." : state.error ?? "No coding chats found." }) : null,
|
|
1480
|
+
rows.map((row) => /* @__PURE__ */ jsx6(Row, { value: row, row, state, adapter, onOpen }, row.key)),
|
|
1481
|
+
state.history.hasMoreSessions ? /* @__PURE__ */ jsx6("button", { class: "scui-load", type: "button", disabled: loadingMore, onClick: loadMore, children: loadingMore ? "Loading older chats\u2026" : "Load older chats" }) : null
|
|
1275
1482
|
] })
|
|
1276
1483
|
] });
|
|
1277
1484
|
}
|
|
1278
1485
|
|
|
1279
1486
|
// src/messenger.jsx
|
|
1280
|
-
import { jsx as
|
|
1487
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "preact/jsx-runtime";
|
|
1281
1488
|
var pendingMessageMemory = /* @__PURE__ */ new Map();
|
|
1489
|
+
var messengerViewMemory = /* @__PURE__ */ new Map();
|
|
1490
|
+
var newChatMemory = /* @__PURE__ */ new Map();
|
|
1491
|
+
var TRACKED_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "detach", "branch", "reduce", "terminal", "export", "interrupt", "respond", "refresh", "loadEarlier", "loadSessions"]);
|
|
1282
1492
|
function Receipt({ state, adapter }) {
|
|
1283
1493
|
const receipt = state.reductionReceipt;
|
|
1284
|
-
if (receipt) return /* @__PURE__ */
|
|
1285
|
-
/* @__PURE__ */
|
|
1286
|
-
/* @__PURE__ */
|
|
1494
|
+
if (receipt) return /* @__PURE__ */ jsx7("div", { class: "scui-receipt", children: /* @__PURE__ */ jsxs6("span", { children: [
|
|
1495
|
+
/* @__PURE__ */ jsx7("strong", { children: "Reduced and verified" }),
|
|
1496
|
+
/* @__PURE__ */ jsxs6("small", { children: [
|
|
1287
1497
|
receipt.sourceTokens.toLocaleString(),
|
|
1288
1498
|
" \u2192 ",
|
|
1289
1499
|
receipt.reducedTokens.toLocaleString(),
|
|
@@ -1292,82 +1502,150 @@ function Receipt({ state, adapter }) {
|
|
|
1292
1502
|
"\xD7 \xB7 reversible"
|
|
1293
1503
|
] })
|
|
1294
1504
|
] }) });
|
|
1295
|
-
if (state.exportReceipt) return /* @__PURE__ */
|
|
1296
|
-
/* @__PURE__ */
|
|
1297
|
-
/* @__PURE__ */
|
|
1505
|
+
if (state.exportReceipt) return /* @__PURE__ */ jsxs6("div", { class: "scui-receipt", children: [
|
|
1506
|
+
/* @__PURE__ */ jsxs6("span", { children: [
|
|
1507
|
+
/* @__PURE__ */ jsxs6("strong", { children: [
|
|
1298
1508
|
"Lossless export ready \xB7 ",
|
|
1299
1509
|
harnessDisplayName(state.exportReceipt.targetHarness)
|
|
1300
1510
|
] }),
|
|
1301
|
-
/* @__PURE__ */
|
|
1511
|
+
/* @__PURE__ */ jsxs6("small", { children: [
|
|
1302
1512
|
state.exportReceipt.path,
|
|
1303
1513
|
" \xB7 ",
|
|
1304
1514
|
state.exportReceipt.files,
|
|
1305
1515
|
" files"
|
|
1306
1516
|
] })
|
|
1307
1517
|
] }),
|
|
1308
|
-
/* @__PURE__ */
|
|
1518
|
+
/* @__PURE__ */ jsx7("button", { type: "button", onClick: () => adapter.copyText?.(state.exportReceipt.path), children: "Copy path" })
|
|
1309
1519
|
] });
|
|
1310
|
-
if (state.terminalHandoff) return /* @__PURE__ */
|
|
1311
|
-
/* @__PURE__ */
|
|
1312
|
-
/* @__PURE__ */
|
|
1313
|
-
/* @__PURE__ */
|
|
1520
|
+
if (state.terminalHandoff) return /* @__PURE__ */ jsxs6("div", { class: "scui-receipt", children: [
|
|
1521
|
+
/* @__PURE__ */ jsxs6("span", { children: [
|
|
1522
|
+
/* @__PURE__ */ jsx7("strong", { children: "Terminal handoff ready" }),
|
|
1523
|
+
/* @__PURE__ */ jsx7("small", { children: state.terminalHandoff.cwd })
|
|
1314
1524
|
] }),
|
|
1315
|
-
/* @__PURE__ */
|
|
1525
|
+
/* @__PURE__ */ jsx7("button", { type: "button", onClick: () => adapter.copyText?.(terminalCommand(state.terminalHandoff)), children: "Copy command" })
|
|
1316
1526
|
] });
|
|
1317
1527
|
return null;
|
|
1318
1528
|
}
|
|
1319
|
-
function
|
|
1320
|
-
const
|
|
1529
|
+
function ConversationActions({ state, adapter, actionPending }) {
|
|
1530
|
+
const [open, setOpen] = useState4(false);
|
|
1531
|
+
const root = useRef5(null);
|
|
1532
|
+
const panel = useRef5(null);
|
|
1533
|
+
const trigger = useRef5(null);
|
|
1534
|
+
const menuId = useId2();
|
|
1321
1535
|
const targets = state.harnesses.filter((item) => item.startable);
|
|
1536
|
+
const groups = [
|
|
1537
|
+
{
|
|
1538
|
+
label: "Session",
|
|
1539
|
+
items: [
|
|
1540
|
+
state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
|
|
1541
|
+
state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
|
|
1542
|
+
state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null
|
|
1543
|
+
].filter(Boolean)
|
|
1544
|
+
},
|
|
1545
|
+
{
|
|
1546
|
+
label: "Lower-cost continuation",
|
|
1547
|
+
items: state.canReduce ? targets.map((target) => ({ key: `reduce:${target.id}`, label: target.id === state.harness ? `Reduce and continue in ${target.label}` : `Reduce and switch to ${target.label}`, intent: { action: "reduce", targetHarness: target.id } })) : []
|
|
1548
|
+
},
|
|
1549
|
+
{
|
|
1550
|
+
label: "Independent continuation",
|
|
1551
|
+
items: state.canBranch ? targets.map((target) => ({ key: `branch:${target.id}`, label: target.id === state.harness ? `Fork in ${target.label}` : `Continue with ${target.label}`, intent: { action: "branch", targetHarness: target.id } })) : []
|
|
1552
|
+
}
|
|
1553
|
+
].filter((group) => group.items.length);
|
|
1554
|
+
useEffect6(() => {
|
|
1555
|
+
if (!open) return;
|
|
1556
|
+
panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
|
|
1557
|
+
const dismiss = (event) => {
|
|
1558
|
+
if (event.type === "keydown" && event.key === "Escape") {
|
|
1559
|
+
event.preventDefault();
|
|
1560
|
+
setOpen(false);
|
|
1561
|
+
trigger.current?.focus({ preventScroll: true });
|
|
1562
|
+
} else if (event.type === "pointerdown" && !root.current?.contains(event.target)) {
|
|
1563
|
+
setOpen(false);
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
document.addEventListener("keydown", dismiss);
|
|
1567
|
+
document.addEventListener("pointerdown", dismiss, true);
|
|
1568
|
+
return () => {
|
|
1569
|
+
document.removeEventListener("keydown", dismiss);
|
|
1570
|
+
document.removeEventListener("pointerdown", dismiss, true);
|
|
1571
|
+
};
|
|
1572
|
+
}, [open]);
|
|
1573
|
+
const dispatch = (intent) => {
|
|
1574
|
+
setOpen(false);
|
|
1575
|
+
adapter.onIntent(intent);
|
|
1576
|
+
};
|
|
1577
|
+
const navigate = (event) => {
|
|
1578
|
+
if (event.key === "Escape") {
|
|
1579
|
+
event.preventDefault();
|
|
1580
|
+
event.stopPropagation();
|
|
1581
|
+
setOpen(false);
|
|
1582
|
+
trigger.current?.focus({ preventScroll: true });
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
|
|
1586
|
+
const items = [...panel.current.querySelectorAll("button:not(:disabled)")];
|
|
1587
|
+
if (!items.length) return;
|
|
1588
|
+
event.preventDefault();
|
|
1589
|
+
const current = items.indexOf(document.activeElement);
|
|
1590
|
+
const index = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : event.key === "ArrowDown" ? (current + 1) % items.length : (current <= 0 ? items.length : current) - 1;
|
|
1591
|
+
items[index].focus({ preventScroll: true });
|
|
1592
|
+
};
|
|
1593
|
+
return /* @__PURE__ */ jsxs6("div", { class: "scui-menu", ref: root, onBlur: (event) => {
|
|
1594
|
+
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
1595
|
+
}, children: [
|
|
1596
|
+
/* @__PURE__ */ jsx7("button", { ref: trigger, class: "scui-menu-trigger", type: "button", "aria-label": "Conversation actions", "aria-haspopup": "menu", "aria-expanded": open, "aria-controls": open ? menuId : void 0, onClick: () => setOpen((value) => !value), children: /* @__PURE__ */ jsx7(UiIcon, { name: "menu", size: 18 }) }),
|
|
1597
|
+
open ? /* @__PURE__ */ jsx7("div", { ref: panel, id: menuId, class: "scui-menu-panel", role: "menu", "aria-label": "Conversation actions", onKeyDown: navigate, children: groups.map((group) => /* @__PURE__ */ jsxs6("section", { role: "group", "aria-label": group.label, children: [
|
|
1598
|
+
/* @__PURE__ */ jsx7("strong", { children: group.label }),
|
|
1599
|
+
group.items.map((item) => /* @__PURE__ */ jsx7("button", { role: "menuitem", type: "button", disabled: actionPending, onClick: () => dispatch(item.intent), children: item.label }, item.key))
|
|
1600
|
+
] }, group.label)) }) : null
|
|
1601
|
+
] });
|
|
1602
|
+
}
|
|
1603
|
+
function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
|
|
1604
|
+
const back = useRef5(null);
|
|
1605
|
+
const harness = state.attached?.harness ?? state.harness;
|
|
1322
1606
|
const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
|
|
1323
1607
|
const status = state.needsInput ? "Needs input" : pendingStatus === "failed" ? "Send failed" : state.busy ? "Working" : pendingStatus === "sending" ? "Sending" : pendingStatus === "editing" ? "Editing message" : state.mode === "mirror" ? "Read-only" : "Ready";
|
|
1324
1608
|
const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1609
|
+
useEffect6(() => {
|
|
1610
|
+
if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
|
|
1611
|
+
}, []);
|
|
1612
|
+
return /* @__PURE__ */ jsxs6("header", { class: "scui-head scui-chat-head", children: [
|
|
1613
|
+
/* @__PURE__ */ jsx7("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx7(UiIcon, { name: "back", size: 18 }) }),
|
|
1614
|
+
/* @__PURE__ */ jsx7(HarnessLogo, { id: harness, size: 28 }),
|
|
1615
|
+
/* @__PURE__ */ jsxs6("span", { class: "scui-head-copy", children: [
|
|
1616
|
+
/* @__PURE__ */ jsx7("strong", { children: title }),
|
|
1617
|
+
/* @__PURE__ */ jsxs6("small", { children: [
|
|
1331
1618
|
harnessDisplayName(harness),
|
|
1332
1619
|
" \xB7 ",
|
|
1333
1620
|
status
|
|
1334
1621
|
] })
|
|
1335
1622
|
] }),
|
|
1336
|
-
menu ? /* @__PURE__ */
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
state.canDetach ? /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => adapter.onIntent({ action: "detach" }), children: "Detach to read-only" }) : null,
|
|
1340
|
-
state.canOpenTerminal ? /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => adapter.onIntent({ action: "terminal" }), children: "Prepare terminal handoff" }) : null,
|
|
1341
|
-
state.canReduce ? targets.map((target) => /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => adapter.onIntent({ action: "reduce", targetHarness: target.id }), children: target.id === state.harness ? `Reduce and continue in ${target.label}` : `Reduce and switch to ${target.label}` }, `reduce:${target.id}`)) : null,
|
|
1342
|
-
state.canBranch ? targets.map((target) => /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => adapter.onIntent({ action: "branch", targetHarness: target.id }), children: target.id === state.harness ? `Fork in ${target.label}` : `Continue with ${target.label}` }, `branch:${target.id}`)) : null,
|
|
1343
|
-
state.canExport && state.exportBackTarget ? /* @__PURE__ */ jsxs5("button", { type: "button", onClick: () => adapter.onIntent({ action: "export", targetHarness: state.exportBackTarget }), children: [
|
|
1344
|
-
"Export back to ",
|
|
1345
|
-
harnessDisplayName(state.exportBackTarget)
|
|
1346
|
-
] }) : null
|
|
1347
|
-
] })
|
|
1348
|
-
] }) : null,
|
|
1349
|
-
/* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "New chat", onClick: onNew, children: "\uFF0B" }),
|
|
1350
|
-
onClose ? /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Close", onClick: onClose, children: "\xD7" }) : null
|
|
1623
|
+
menu ? /* @__PURE__ */ jsx7(ConversationActions, { state, adapter, actionPending }) : null,
|
|
1624
|
+
/* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx7(UiIcon, { name: "plus", size: 18 }) }),
|
|
1625
|
+
onClose ? /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx7(UiIcon, { name: "close", size: 18 }) }) : null
|
|
1351
1626
|
] });
|
|
1352
1627
|
}
|
|
1353
1628
|
function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
|
|
1354
1629
|
const Header = slots.header;
|
|
1355
1630
|
const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
|
|
1356
1631
|
const [pending, setPendingState] = useState4(() => pendingMessageMemory.get(memoryKey) ?? null);
|
|
1632
|
+
const [pendingAction, setPendingAction] = useState4(null);
|
|
1357
1633
|
const [restoreDraft, setRestoreDraft] = useState4(null);
|
|
1358
|
-
const restoreSequence =
|
|
1359
|
-
const
|
|
1634
|
+
const restoreSequence = useRef5(0);
|
|
1635
|
+
const actionSequence = useRef5(0);
|
|
1636
|
+
const acknowledged = useRef5(/* @__PURE__ */ new Set());
|
|
1360
1637
|
const setPending = (update) => setPendingState((current) => {
|
|
1361
1638
|
const next = typeof update === "function" ? update(current) : update;
|
|
1362
1639
|
if (next) boundedSet(pendingMessageMemory, memoryKey, next);
|
|
1363
1640
|
else pendingMessageMemory.delete(memoryKey);
|
|
1364
1641
|
return next;
|
|
1365
1642
|
});
|
|
1366
|
-
|
|
1643
|
+
useEffect6(() => {
|
|
1367
1644
|
setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
|
|
1645
|
+
setPendingAction(null);
|
|
1368
1646
|
setRestoreDraft(null);
|
|
1369
1647
|
}, [memoryKey]);
|
|
1370
|
-
|
|
1648
|
+
useEffect6(() => {
|
|
1371
1649
|
if (!pending) return;
|
|
1372
1650
|
if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
|
|
1373
1651
|
setPending(null);
|
|
@@ -1394,7 +1672,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
1394
1672
|
setRestoreDraft({ id: restoreSequence.current, text: pending.text });
|
|
1395
1673
|
setPending({ ...pending, status: "editing" });
|
|
1396
1674
|
};
|
|
1397
|
-
|
|
1675
|
+
useEffect6(() => {
|
|
1398
1676
|
const key = state.attached?.key;
|
|
1399
1677
|
if (!key || !state.attention.some((item) => item.key === key)) {
|
|
1400
1678
|
if (key) acknowledged.current.delete(key);
|
|
@@ -1405,75 +1683,143 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
1405
1683
|
adapter.onIntent({ action: "ack", key });
|
|
1406
1684
|
}
|
|
1407
1685
|
}, [adapter, state.attached?.key, state.attention]);
|
|
1686
|
+
const trackedAdapter = useMemo2(() => ({
|
|
1687
|
+
...adapter,
|
|
1688
|
+
onIntent(intent) {
|
|
1689
|
+
if (!TRACKED_ACTIONS.has(intent.action)) return adapter.onIntent(intent);
|
|
1690
|
+
const id = actionSequence.current + 1;
|
|
1691
|
+
actionSequence.current = id;
|
|
1692
|
+
const action2 = intent.action;
|
|
1693
|
+
setPendingAction({ id, action: action2, initialError: state.error, seenOperation: false });
|
|
1694
|
+
let result;
|
|
1695
|
+
try {
|
|
1696
|
+
result = adapter.onIntent(intent);
|
|
1697
|
+
} catch (error) {
|
|
1698
|
+
setPendingAction((current) => current?.id === id ? null : current);
|
|
1699
|
+
throw error;
|
|
1700
|
+
}
|
|
1701
|
+
if (result && typeof result.then === "function") {
|
|
1702
|
+
Promise.resolve(result).then(
|
|
1703
|
+
() => setPendingAction((current) => current?.id === id ? null : current),
|
|
1704
|
+
() => setPendingAction((current) => current?.id === id ? null : current)
|
|
1705
|
+
);
|
|
1706
|
+
}
|
|
1707
|
+
return result;
|
|
1708
|
+
}
|
|
1709
|
+
}), [adapter, state.error]);
|
|
1710
|
+
useEffect6(() => {
|
|
1711
|
+
if (!pendingAction) return;
|
|
1712
|
+
if (state.operation && !pendingAction.seenOperation) {
|
|
1713
|
+
setPendingAction({ ...pendingAction, seenOperation: true });
|
|
1714
|
+
} else if (!state.operation && (pendingAction.seenOperation || state.error !== pendingAction.initialError)) {
|
|
1715
|
+
setPendingAction(null);
|
|
1716
|
+
}
|
|
1717
|
+
}, [pendingAction, state.error, state.operation]);
|
|
1408
1718
|
const pendingMessage = pending && pending.status !== "editing" ? { text: pending.text, status: pending.status, onRetry: retryPending, onEdit: editPending } : null;
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1719
|
+
const action = state.operation || pendingAction?.action || null;
|
|
1720
|
+
const actionLabel = operationLabel(action);
|
|
1721
|
+
const actionState = action ? { ...state, operation: action, canInterrupt: false, canRespond: false } : state;
|
|
1722
|
+
const unreadAfterMessages = state.attention.find((item) => item.key === state.attached?.key)?.afterMessages ?? null;
|
|
1723
|
+
return /* @__PURE__ */ jsxs6("section", { class: "scui-chat", children: [
|
|
1724
|
+
Header ? /* @__PURE__ */ jsx7(Header, { state: actionState, adapter: trackedAdapter, value: null }) : /* @__PURE__ */ jsx7(ChatHeader, { state: actionState, adapter: trackedAdapter, pendingStatus: pending?.status ?? null, actionPending: Boolean(action), onBack, onNew, onClose }),
|
|
1725
|
+
actionLabel ? /* @__PURE__ */ jsxs6("div", { class: "scui-operation", role: "status", children: [
|
|
1726
|
+
/* @__PURE__ */ jsx7("i", {}),
|
|
1727
|
+
actionLabel
|
|
1414
1728
|
] }) : null,
|
|
1415
|
-
state.error ? /* @__PURE__ */
|
|
1416
|
-
/* @__PURE__ */
|
|
1417
|
-
state.recoverable ? /* @__PURE__ */
|
|
1729
|
+
state.error ? /* @__PURE__ */ jsxs6("div", { class: "scui-error", role: "alert", children: [
|
|
1730
|
+
/* @__PURE__ */ jsx7("span", { children: state.error }),
|
|
1731
|
+
state.recoverable ? /* @__PURE__ */ jsx7("button", { type: "button", disabled: Boolean(action), onClick: () => trackedAdapter.onIntent({ action: "refresh" }), children: "Retry" }) : null
|
|
1418
1732
|
] }) : null,
|
|
1419
|
-
/* @__PURE__ */
|
|
1420
|
-
/* @__PURE__ */
|
|
1421
|
-
/* @__PURE__ */
|
|
1422
|
-
state.mode !== "mirror" || state.canSend ? /* @__PURE__ */
|
|
1733
|
+
/* @__PURE__ */ jsx7(Receipt, { state, adapter }),
|
|
1734
|
+
/* @__PURE__ */ jsx7(Conversation, { state: actionState, adapter: trackedAdapter, components, slots, memoryKey, pending: pendingMessage, unreadAfterMessages }, memoryKey),
|
|
1735
|
+
/* @__PURE__ */ jsx7(ContinuationBar, { state: actionState, adapter: trackedAdapter, labels }),
|
|
1736
|
+
state.mode !== "mirror" || state.canSend ? /* @__PURE__ */ jsx7(Composer, { state: actionState, adapter: trackedAdapter, labels, memoryKey, pendingStatus: pending?.status ?? null, restoreDraft, onDraftRestored: (id) => setRestoreDraft((value) => value?.id === id ? null : value), onPending: beginPending }, memoryKey) : null
|
|
1423
1737
|
] });
|
|
1424
1738
|
}
|
|
1425
|
-
function NewChat({ state, adapter, onBack, onClose, onStarted, labels }) {
|
|
1739
|
+
function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey }) {
|
|
1426
1740
|
const startable = state.harnesses.filter((item) => item.startable);
|
|
1427
1741
|
const startableKey = startable.map((item) => item.id).join("\0");
|
|
1428
|
-
const
|
|
1429
|
-
const [
|
|
1430
|
-
const [
|
|
1431
|
-
|
|
1432
|
-
|
|
1742
|
+
const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: "" };
|
|
1743
|
+
const [harness, setHarness] = useState4(remembered.harness);
|
|
1744
|
+
const [draft, setDraft] = useState4(remembered.draft);
|
|
1745
|
+
const [starting, setStarting] = useState4(null);
|
|
1746
|
+
const startSequence = useRef5(0);
|
|
1747
|
+
const textarea = useRef5(null);
|
|
1748
|
+
useAutosizeTextarea(textarea, draft);
|
|
1749
|
+
useEffect6(() => {
|
|
1750
|
+
if (startable.some((item) => item.id === harness)) return;
|
|
1751
|
+
const next = startable[0]?.id ?? "";
|
|
1752
|
+
setHarness(next);
|
|
1753
|
+
boundedSet(newChatMemory, memoryKey, { harness: next, draft });
|
|
1433
1754
|
}, [harness, startableKey]);
|
|
1434
|
-
|
|
1435
|
-
if (starting
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
if (
|
|
1755
|
+
useEffect6(() => {
|
|
1756
|
+
if (!starting) return;
|
|
1757
|
+
const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
|
|
1758
|
+
const beganWorking = !starting.busy && state.busy;
|
|
1759
|
+
if (!sessionChanged && !beganWorking) return;
|
|
1760
|
+
newChatMemory.delete(memoryKey);
|
|
1761
|
+
onStarted();
|
|
1762
|
+
}, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
|
|
1763
|
+
useEffect6(() => {
|
|
1764
|
+
if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
|
|
1439
1765
|
}, [starting, state.busy, state.error, state.operation]);
|
|
1766
|
+
useEffect6(() => {
|
|
1767
|
+
textarea.current?.focus({ preventScroll: true });
|
|
1768
|
+
}, []);
|
|
1440
1769
|
const send = () => {
|
|
1441
1770
|
const text = draft.trim();
|
|
1442
1771
|
if (!text || !harness || starting) return;
|
|
1443
|
-
|
|
1444
|
-
|
|
1772
|
+
const id = startSequence.current + 1;
|
|
1773
|
+
startSequence.current = id;
|
|
1774
|
+
setStarting({ id, attachedKey: state.attached?.key ?? null, busy: state.busy, initialError: state.error });
|
|
1775
|
+
const result = adapter.onIntent({ action: "new", harness, text });
|
|
1776
|
+
if (result && typeof result.then === "function") {
|
|
1777
|
+
Promise.resolve(result).catch(() => setStarting((current) => current?.id === id ? null : current));
|
|
1778
|
+
}
|
|
1445
1779
|
};
|
|
1446
|
-
return /* @__PURE__ */
|
|
1447
|
-
/* @__PURE__ */
|
|
1448
|
-
/* @__PURE__ */
|
|
1449
|
-
/* @__PURE__ */
|
|
1450
|
-
/* @__PURE__ */
|
|
1451
|
-
/* @__PURE__ */
|
|
1780
|
+
return /* @__PURE__ */ jsxs6("section", { class: "scui-chat", children: [
|
|
1781
|
+
/* @__PURE__ */ jsxs6("header", { class: "scui-head", children: [
|
|
1782
|
+
/* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "Back", onClick: onBack, children: /* @__PURE__ */ jsx7(UiIcon, { name: "back", size: 18 }) }),
|
|
1783
|
+
/* @__PURE__ */ jsxs6("span", { class: "scui-head-copy", children: [
|
|
1784
|
+
/* @__PURE__ */ jsx7("strong", { children: labels.newChat }),
|
|
1785
|
+
/* @__PURE__ */ jsx7("small", { children: "No session is created until you send" })
|
|
1452
1786
|
] }),
|
|
1453
|
-
onClose ? /* @__PURE__ */
|
|
1787
|
+
onClose ? /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx7(UiIcon, { name: "close", size: 18 }) }) : null
|
|
1454
1788
|
] }),
|
|
1455
|
-
/* @__PURE__ */
|
|
1456
|
-
/* @__PURE__ */
|
|
1457
|
-
|
|
1458
|
-
|
|
1789
|
+
starting ? /* @__PURE__ */ jsxs6("div", { class: "scui-operation", role: "status", children: [
|
|
1790
|
+
/* @__PURE__ */ jsx7("i", {}),
|
|
1791
|
+
operationLabel("start")
|
|
1792
|
+
] }) : null,
|
|
1793
|
+
/* @__PURE__ */ jsxs6("div", { class: "scui-new", children: [
|
|
1794
|
+
/* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\u2726" }),
|
|
1795
|
+
/* @__PURE__ */ jsx7("strong", { children: "What should the agent build or fix?" }),
|
|
1796
|
+
/* @__PURE__ */ jsx7("small", { children: "Choose a coding harness and send the first message." })
|
|
1459
1797
|
] }),
|
|
1460
|
-
/* @__PURE__ */
|
|
1461
|
-
/* @__PURE__ */
|
|
1462
|
-
/* @__PURE__ */
|
|
1463
|
-
/* @__PURE__ */
|
|
1464
|
-
/* @__PURE__ */
|
|
1798
|
+
/* @__PURE__ */ jsxs6("div", { class: "scui-compose", children: [
|
|
1799
|
+
/* @__PURE__ */ jsxs6("label", { class: "scui-harness-picker", children: [
|
|
1800
|
+
/* @__PURE__ */ jsx7(HarnessLogo, { id: harness, size: 24 }),
|
|
1801
|
+
/* @__PURE__ */ jsx7("span", { children: "Coding harness" }),
|
|
1802
|
+
/* @__PURE__ */ jsx7("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
|
|
1803
|
+
const value = event.currentTarget.value;
|
|
1804
|
+
setHarness(value);
|
|
1805
|
+
boundedSet(newChatMemory, memoryKey, { harness: value, draft });
|
|
1806
|
+
}, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs6("option", { value: item.id, disabled: !item.startable, children: [
|
|
1465
1807
|
item.label,
|
|
1466
1808
|
item.startable ? "" : " \xB7 unavailable"
|
|
1467
1809
|
] }, item.id)) })
|
|
1468
1810
|
] }),
|
|
1469
|
-
/* @__PURE__ */
|
|
1470
|
-
/* @__PURE__ */
|
|
1811
|
+
/* @__PURE__ */ jsxs6("div", { class: "scui-envelope", children: [
|
|
1812
|
+
/* @__PURE__ */ jsx7("textarea", { ref: textarea, rows: 3, "aria-label": "Message coding agent", placeholder: startable.length ? "What should the agent do?" : "No coding harness is available", value: draft, disabled: !startable.length || Boolean(starting), onInput: (event) => {
|
|
1813
|
+
const value = event.currentTarget.value;
|
|
1814
|
+
setDraft(value);
|
|
1815
|
+
boundedSet(newChatMemory, memoryKey, { harness, draft: value });
|
|
1816
|
+
}, onKeyDown: (event) => {
|
|
1471
1817
|
if (isSendKey(event)) {
|
|
1472
1818
|
event.preventDefault();
|
|
1473
1819
|
send();
|
|
1474
1820
|
}
|
|
1475
1821
|
} }),
|
|
1476
|
-
/* @__PURE__ */
|
|
1822
|
+
/* @__PURE__ */ jsx7("span", { children: /* @__PURE__ */ jsx7("button", { type: "button", class: "scui-send", "aria-label": "Start chat", disabled: !draft.trim() || !harness || Boolean(starting), onClick: send, children: starting ? /* @__PURE__ */ jsx7("i", { class: "scui-control-spinner" }) : /* @__PURE__ */ jsx7(UiIcon, { name: "send", size: 17 }) }) })
|
|
1477
1823
|
] })
|
|
1478
1824
|
] })
|
|
1479
1825
|
] });
|
|
@@ -1481,10 +1827,15 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels }) {
|
|
|
1481
1827
|
function SupercodeMessenger({ state: stateInput, adapter, class: className = "", initialView, labels, components = {}, slots = {} }) {
|
|
1482
1828
|
const state = useMemo2(() => normalizeUiState(stateInput), [stateInput]);
|
|
1483
1829
|
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
1484
|
-
const
|
|
1830
|
+
const memoryKey = state.workspace || "@default";
|
|
1831
|
+
const [view, setViewState] = useState4(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
|
|
1485
1832
|
const [opening, setOpening] = useState4(null);
|
|
1486
1833
|
const [listFocus, setListFocus] = useState4(null);
|
|
1487
|
-
|
|
1834
|
+
const setView = (next) => {
|
|
1835
|
+
boundedSet(messengerViewMemory, memoryKey, next);
|
|
1836
|
+
setViewState(next);
|
|
1837
|
+
};
|
|
1838
|
+
useEffect6(() => {
|
|
1488
1839
|
if (!opening) return;
|
|
1489
1840
|
if (state.attached?.key === opening.key) {
|
|
1490
1841
|
setOpening(null);
|
|
@@ -1496,45 +1847,44 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
|
|
|
1496
1847
|
const open = (row) => {
|
|
1497
1848
|
setListFocus(row.key);
|
|
1498
1849
|
setOpening(row);
|
|
1499
|
-
adapter.onIntent({ action: "ack", key: row.key });
|
|
1500
1850
|
adapter.onIntent({ action: "attach", key: row.key });
|
|
1501
1851
|
};
|
|
1502
1852
|
const close = () => adapter.onClose?.();
|
|
1503
1853
|
const Footer = slots.footer;
|
|
1504
|
-
return /* @__PURE__ */
|
|
1505
|
-
view === "list" ? /* @__PURE__ */
|
|
1854
|
+
return /* @__PURE__ */ jsxs6("main", { class: `scui-root ${className}`, "data-view": view, "data-mode": state.mode, "aria-label": "Supercode messenger", children: [
|
|
1855
|
+
view === "list" ? /* @__PURE__ */ jsx7(SessionList, { state, adapter, focusKey: listFocus, onOpen: open, onNew: () => {
|
|
1506
1856
|
setListFocus("@new");
|
|
1507
1857
|
setView("new");
|
|
1508
|
-
}, onClose: adapter.onClose ? close : void 0, components, labels: copy }) : null,
|
|
1509
|
-
view === "new" ? /* @__PURE__ */
|
|
1510
|
-
view === "chat" ? /* @__PURE__ */
|
|
1858
|
+
}, onClose: adapter.onClose ? close : void 0, components, labels: copy, memoryKey }) : null,
|
|
1859
|
+
view === "new" ? /* @__PURE__ */ jsx7(NewChat, { state, adapter, onBack: () => setView("list"), onClose: adapter.onClose ? close : void 0, onStarted: () => setView("chat"), labels: copy, memoryKey }) : null,
|
|
1860
|
+
view === "chat" ? /* @__PURE__ */ jsx7(Chat, { state, adapter, onBack: () => {
|
|
1511
1861
|
setListFocus(state.attached?.key ?? listFocus);
|
|
1512
1862
|
setView("list");
|
|
1513
1863
|
}, onNew: () => {
|
|
1514
1864
|
setListFocus("@new");
|
|
1515
1865
|
setView("new");
|
|
1516
1866
|
}, onClose: adapter.onClose ? close : void 0, components, slots, labels: copy }) : null,
|
|
1517
|
-
opening ? /* @__PURE__ */
|
|
1518
|
-
/* @__PURE__ */
|
|
1519
|
-
/* @__PURE__ */
|
|
1520
|
-
/* @__PURE__ */
|
|
1867
|
+
opening ? /* @__PURE__ */ jsxs6("div", { class: "scui-opening", role: "status", "aria-busy": "true", children: [
|
|
1868
|
+
/* @__PURE__ */ jsx7(HarnessLogo, { id: opening.harness, size: 34 }),
|
|
1869
|
+
/* @__PURE__ */ jsxs6("span", { children: [
|
|
1870
|
+
/* @__PURE__ */ jsxs6("strong", { children: [
|
|
1521
1871
|
"Opening ",
|
|
1522
1872
|
sessionDisplayName(opening)
|
|
1523
1873
|
] }),
|
|
1524
|
-
/* @__PURE__ */
|
|
1874
|
+
/* @__PURE__ */ jsx7("small", { children: "Loading the latest transcript window\u2026" })
|
|
1525
1875
|
] }),
|
|
1526
|
-
/* @__PURE__ */
|
|
1876
|
+
/* @__PURE__ */ jsx7("i", {})
|
|
1527
1877
|
] }) : null,
|
|
1528
|
-
Footer ? /* @__PURE__ */
|
|
1878
|
+
Footer ? /* @__PURE__ */ jsx7(Footer, { state, adapter, value: copy }) : null
|
|
1529
1879
|
] });
|
|
1530
1880
|
}
|
|
1531
1881
|
|
|
1532
1882
|
// src/embed.jsx
|
|
1533
|
-
import { jsx as
|
|
1883
|
+
import { jsx as jsx8 } from "preact/jsx-runtime";
|
|
1534
1884
|
function mountSupercodeMessenger(element, options) {
|
|
1535
1885
|
if (!(element instanceof Element)) throw new TypeError("mountSupercodeMessenger requires an Element");
|
|
1536
1886
|
let state = options.state;
|
|
1537
|
-
const draw = () => render(/* @__PURE__ */
|
|
1887
|
+
const draw = () => render(/* @__PURE__ */ jsx8(SupercodeMessenger, { ...options, state }), element);
|
|
1538
1888
|
options.adapter.onIntent({ action: "mounted" });
|
|
1539
1889
|
draw();
|
|
1540
1890
|
return {
|