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