@volter-ai-dev/supercode-ui 0.1.28 → 0.1.30
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 +10 -2
- package/components.mjs +330 -133
- package/composer.mjs +5 -2
- package/controller.mjs +10 -11
- package/conversation.mjs +4 -1
- package/core.d.ts +1 -0
- package/core.mjs +97 -4
- package/embed.mjs +330 -135
- package/index.d.ts +82 -3
- package/messenger.mjs +328 -133
- package/package.json +8 -2
- package/sessions.mjs +31 -6
- package/settings.d.ts +2 -0
- package/settings.mjs +216 -0
- package/styles.css +12 -2
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 useEffect8, useId as useId3, useMemo as useMemo3, useRef as useRef7, useState as useState6 } from "preact/hooks";
|
|
6
6
|
|
|
7
7
|
// core.mjs
|
|
8
8
|
var HARNESS_NAMES = Object.freeze({
|
|
@@ -43,6 +43,7 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
43
43
|
canReduce: false,
|
|
44
44
|
canInterrupt: false,
|
|
45
45
|
canRespond: false,
|
|
46
|
+
canConfigureSettings: false,
|
|
46
47
|
messaging: null,
|
|
47
48
|
workspace: "",
|
|
48
49
|
taskPlan: Object.freeze({ source: "none", items: Object.freeze([]), residueCount: 0, observedAt: null }),
|
|
@@ -51,6 +52,8 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
51
52
|
exportBackTarget: null,
|
|
52
53
|
exportReceipt: null,
|
|
53
54
|
reductionReceipt: null,
|
|
55
|
+
interopSettings: null,
|
|
56
|
+
interopSettingsError: null,
|
|
54
57
|
error: null,
|
|
55
58
|
recoverable: false,
|
|
56
59
|
harnesses: Object.freeze([]),
|
|
@@ -84,7 +87,16 @@ function number(value, fallback = 0) {
|
|
|
84
87
|
function nullableNumber(value) {
|
|
85
88
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
86
89
|
}
|
|
87
|
-
function
|
|
90
|
+
function relativeAge(updatedAt, now = Date.now()) {
|
|
91
|
+
if (typeof updatedAt !== "number" || !Number.isFinite(updatedAt) || updatedAt <= 0) return "";
|
|
92
|
+
const delta = Math.max(0, now - updatedAt);
|
|
93
|
+
if (delta < 6e4) return "now";
|
|
94
|
+
if (delta < 36e5) return `${Math.floor(delta / 6e4)}m ago`;
|
|
95
|
+
if (delta < 864e5) return `${Math.floor(delta / 36e5)}h ago`;
|
|
96
|
+
if (delta < 6048e5) return `${Math.floor(delta / 864e5)}d ago`;
|
|
97
|
+
return `${Math.floor(delta / 6048e5)}w ago`;
|
|
98
|
+
}
|
|
99
|
+
function boundedString(value, max = 2e3) {
|
|
88
100
|
if (typeof value !== "string") return "";
|
|
89
101
|
return value.length <= max ? value : `${value.slice(0, max)}\u2026`;
|
|
90
102
|
}
|
|
@@ -549,11 +561,12 @@ function readSessions(value) {
|
|
|
549
561
|
title: string(row.title),
|
|
550
562
|
preview: string(row.preview),
|
|
551
563
|
age: string(row.age),
|
|
564
|
+
previewUpdatedAt: nullableNumber(row.previewUpdatedAt),
|
|
552
565
|
updatedAt: nullableNumber(row.updatedAt),
|
|
553
566
|
messages: nullableNumber(row.messages),
|
|
554
567
|
active: row.active === true,
|
|
555
568
|
live: row.live === true,
|
|
556
|
-
runtimeStatus: row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
|
|
569
|
+
runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
|
|
557
570
|
}];
|
|
558
571
|
});
|
|
559
572
|
}
|
|
@@ -627,6 +640,68 @@ function readReductionReceipt(value) {
|
|
|
627
640
|
targetHarness: receipt.targetHarness
|
|
628
641
|
};
|
|
629
642
|
}
|
|
643
|
+
function readInteropSettings(value) {
|
|
644
|
+
const report = record(value);
|
|
645
|
+
if (report?.schema !== "supercode.harness-interop-settings.v1" || typeof report.harness !== "string" || typeof report.revision !== "string" || !Array.isArray(report.controls) || !Array.isArray(report.advisories)) return null;
|
|
646
|
+
const controls = report.controls.slice(0, 20).flatMap((raw) => {
|
|
647
|
+
const control = record(raw);
|
|
648
|
+
if (!control || typeof control.key !== "string" || typeof control.label !== "string") return [];
|
|
649
|
+
return [{
|
|
650
|
+
key: boundedString(control.key, 200),
|
|
651
|
+
nativeKey: boundedString(control.native_key ?? control.nativeKey, 200),
|
|
652
|
+
label: boundedString(control.label, 300),
|
|
653
|
+
description: boundedString(control.description),
|
|
654
|
+
scope: ["user", "project", "managed", "command_line"].includes(control.scope) ? control.scope : "user",
|
|
655
|
+
sourcePath: boundedString(control.source_path ?? control.sourcePath, 4e3),
|
|
656
|
+
configuredValue: typeof (control.configured_value ?? control.configuredValue) === "string" ? boundedString(control.configured_value ?? control.configuredValue, 500) : null,
|
|
657
|
+
effectiveValue: typeof (control.effective_value ?? control.effectiveValue) === "string" ? boundedString(control.effective_value ?? control.effectiveValue, 500) : null,
|
|
658
|
+
effectiveKnown: (control.effective_known ?? control.effectiveKnown) === true,
|
|
659
|
+
effectiveNote: boundedString(control.effective_note ?? control.effectiveNote),
|
|
660
|
+
choices: Array.isArray(control.choices) ? control.choices.slice(0, 20).flatMap((candidate) => {
|
|
661
|
+
const choice = record(candidate);
|
|
662
|
+
return choice && typeof choice.value === "string" && typeof choice.label === "string" ? [{
|
|
663
|
+
value: boundedString(choice.value, 500),
|
|
664
|
+
label: boundedString(choice.label, 300),
|
|
665
|
+
description: boundedString(choice.description),
|
|
666
|
+
...typeof choice.risk === "string" ? { risk: boundedString(choice.risk) } : {}
|
|
667
|
+
}] : [];
|
|
668
|
+
}) : [],
|
|
669
|
+
writable: control.writable === true,
|
|
670
|
+
resettable: control.resettable === true,
|
|
671
|
+
requiresRestart: (control.requires_restart ?? control.requiresRestart) === true
|
|
672
|
+
}];
|
|
673
|
+
});
|
|
674
|
+
const advisories = report.advisories.slice(0, 20).flatMap((raw) => {
|
|
675
|
+
const advisory = record(raw);
|
|
676
|
+
const recommendation = record(advisory?.recommendation);
|
|
677
|
+
const change = record(recommendation?.change);
|
|
678
|
+
if (!advisory || !recommendation || !change || typeof advisory.code !== "string" || typeof advisory.title !== "string" || typeof advisory.setting !== "string" || typeof change.key !== "string") return [];
|
|
679
|
+
return [{
|
|
680
|
+
code: boundedString(advisory.code, 200),
|
|
681
|
+
severity: ["info", "warning", "error"].includes(advisory.severity) ? advisory.severity : "warning",
|
|
682
|
+
title: boundedString(advisory.title, 500),
|
|
683
|
+
message: boundedString(advisory.message),
|
|
684
|
+
setting: boundedString(advisory.setting, 200),
|
|
685
|
+
recommendation: {
|
|
686
|
+
label: boundedString(recommendation.label, 500),
|
|
687
|
+
description: boundedString(recommendation.description),
|
|
688
|
+
consequence: boundedString(recommendation.consequence),
|
|
689
|
+
change: {
|
|
690
|
+
key: boundedString(change.key, 200),
|
|
691
|
+
value: typeof change.value === "string" ? boundedString(change.value, 500) : null
|
|
692
|
+
},
|
|
693
|
+
command: boundedString(recommendation.command, 4e3)
|
|
694
|
+
}
|
|
695
|
+
}];
|
|
696
|
+
});
|
|
697
|
+
return {
|
|
698
|
+
schema: "supercode.harness-interop-settings.v1",
|
|
699
|
+
harness: report.harness,
|
|
700
|
+
revision: boundedString(report.revision, 500),
|
|
701
|
+
controls,
|
|
702
|
+
advisories
|
|
703
|
+
};
|
|
704
|
+
}
|
|
630
705
|
function normalizeUiState(value) {
|
|
631
706
|
const raw = record(value) ?? {};
|
|
632
707
|
const pill = record(raw.pill);
|
|
@@ -652,6 +727,7 @@ function normalizeUiState(value) {
|
|
|
652
727
|
canReduce: raw.canReduce === true,
|
|
653
728
|
canInterrupt: raw.canInterrupt === true,
|
|
654
729
|
canRespond: raw.canRespond === true,
|
|
730
|
+
canConfigureSettings: raw.canConfigureSettings === true,
|
|
655
731
|
messaging: raw.messaging === "live_peer" ? "live_peer" : null,
|
|
656
732
|
workspace: string(raw.workspace),
|
|
657
733
|
taskPlan: readTaskPlan(raw.taskPlan),
|
|
@@ -660,6 +736,8 @@ function normalizeUiState(value) {
|
|
|
660
736
|
exportBackTarget: typeof raw.exportBackTarget === "string" ? raw.exportBackTarget : null,
|
|
661
737
|
exportReceipt: readExportReceipt(raw.exportReceipt),
|
|
662
738
|
reductionReceipt: readReductionReceipt(raw.reductionReceipt),
|
|
739
|
+
interopSettings: readInteropSettings(raw.interopSettings),
|
|
740
|
+
interopSettingsError: typeof raw.interopSettingsError === "string" ? boundedString(raw.interopSettingsError) : null,
|
|
663
741
|
error: typeof raw.error === "string" ? raw.error : null,
|
|
664
742
|
recoverable: raw.recoverable === true,
|
|
665
743
|
harnesses: Array.isArray(raw.harnesses) ? raw.harnesses.flatMap((candidate) => {
|
|
@@ -691,6 +769,7 @@ function sessionActivity(state, row) {
|
|
|
691
769
|
const attention = state.attention.find((item) => item.key === row.key)?.kind;
|
|
692
770
|
if (attention) return attention;
|
|
693
771
|
if (row.runtimeStatus === "busy") return "working";
|
|
772
|
+
if (row.runtimeStatus === "running") return "running";
|
|
694
773
|
if (row.live || row.runtimeStatus === "idle") return "recent";
|
|
695
774
|
return "idle";
|
|
696
775
|
}
|
|
@@ -748,11 +827,11 @@ function activitySummary(entries) {
|
|
|
748
827
|
function canContinueHere(state) {
|
|
749
828
|
if (state.mode !== "mirror" || state.canSend) return false;
|
|
750
829
|
const row = state.attached?.key ? state.sessions.find((item) => item.key === state.attached.key) : null;
|
|
751
|
-
return state.canResume && row?.runtimeStatus !== "busy" && row?.runtimeStatus !== "idle";
|
|
830
|
+
return state.canResume && row?.runtimeStatus !== "running" && row?.runtimeStatus !== "busy" && row?.runtimeStatus !== "idle";
|
|
752
831
|
}
|
|
753
832
|
function operationLabel(operation) {
|
|
754
833
|
if (!operation) return "";
|
|
755
|
-
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" };
|
|
834
|
+
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", configureHarness: "Updating harness settings\u2026", loadEarlier: "Loading earlier messages\u2026", loadSessions: "Loading more chats\u2026", refresh: "Retrying\u2026" };
|
|
756
835
|
return labels[operation] ?? `${operation.replaceAll("_", " ")}\u2026`;
|
|
757
836
|
}
|
|
758
837
|
function terminalCommand(handoff) {
|
|
@@ -1098,7 +1177,7 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
1098
1177
|
if (state.mode !== "mirror" || state.canSend) return null;
|
|
1099
1178
|
const attached = state.attached;
|
|
1100
1179
|
const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
|
|
1101
|
-
const activeElsewhere = row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
|
|
1180
|
+
const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
|
|
1102
1181
|
const resume = canContinueHere(state);
|
|
1103
1182
|
const join = state.canAttach;
|
|
1104
1183
|
const branch = state.canBranch;
|
|
@@ -1982,28 +2061,35 @@ function sessionPathParts(value) {
|
|
|
1982
2061
|
trailing: complete.slice(boundary + 1)
|
|
1983
2062
|
};
|
|
1984
2063
|
}
|
|
1985
|
-
function SessionRow({ row, state, onOpen }) {
|
|
2064
|
+
function SessionRow({ row, state, onOpen, now = Date.now() }) {
|
|
1986
2065
|
const activity = sessionActivity(state, row);
|
|
2066
|
+
const working = activity === "working";
|
|
1987
2067
|
const attention = state.attention.find((item) => item.key === row.key);
|
|
1988
2068
|
const title = sessionDisplayName(row);
|
|
1989
2069
|
const path = sessionPathParts(row.cwd);
|
|
1990
2070
|
const preview = row.preview || attention?.preview || "";
|
|
1991
2071
|
const unreadCount = attention?.unreadCount ?? 0;
|
|
1992
2072
|
const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
|
|
1993
|
-
|
|
2073
|
+
const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
|
|
2074
|
+
return /* @__PURE__ */ jsxs6("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)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
|
|
1994
2075
|
/* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
|
|
1995
2076
|
/* @__PURE__ */ jsxs6("span", { class: "scui-session-copy", children: [
|
|
1996
2077
|
/* @__PURE__ */ jsxs6("span", { class: "scui-session-title", children: [
|
|
1997
2078
|
/* @__PURE__ */ jsx7("strong", { children: title }),
|
|
1998
|
-
|
|
2079
|
+
/* @__PURE__ */ jsx7("span", { class: "scui-session-meta", children: age ? /* @__PURE__ */ jsx7("time", { children: age }) : null })
|
|
1999
2080
|
] }),
|
|
2000
2081
|
path.complete ? /* @__PURE__ */ jsxs6("small", { class: "scui-session-path", title: row.cwd, children: [
|
|
2001
2082
|
/* @__PURE__ */ jsx7("span", { class: "scui-session-path-leading", children: path.leading }),
|
|
2002
2083
|
path.separator ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-separator", children: path.separator }) : null,
|
|
2003
2084
|
path.trailing ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-trailing", children: path.trailing }) : null
|
|
2004
2085
|
] }) : null,
|
|
2005
|
-
preview || unreadCount ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-preview", children: [
|
|
2006
|
-
|
|
2086
|
+
working || preview || unreadCount ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-preview", children: [
|
|
2087
|
+
working ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-working", "aria-hidden": "true", children: [
|
|
2088
|
+
/* @__PURE__ */ jsx7("i", {}),
|
|
2089
|
+
/* @__PURE__ */ jsx7("i", {}),
|
|
2090
|
+
/* @__PURE__ */ jsx7("i", {})
|
|
2091
|
+
] }) : null,
|
|
2092
|
+
preview || working ? /* @__PURE__ */ jsx7("small", { children: preview || "Working\u2026" }) : null,
|
|
2007
2093
|
unreadCount ? /* @__PURE__ */ jsx7("b", { "aria-label": `${unreadCount} unread messages`, children: unreadLabel }) : null
|
|
2008
2094
|
] }) : null,
|
|
2009
2095
|
state.attachError?.key === row.key ? /* @__PURE__ */ jsx7("em", { children: state.attachError.message }) : null
|
|
@@ -2014,6 +2100,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
2014
2100
|
const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
|
|
2015
2101
|
const [query, setQuery] = useState4(remembered.query);
|
|
2016
2102
|
const [loadingMore, setLoadingMore] = useState4(false);
|
|
2103
|
+
const [now, setNow] = useState4(() => Date.now());
|
|
2017
2104
|
const root = useRef5(null);
|
|
2018
2105
|
const rowScroller = useRef5(null);
|
|
2019
2106
|
const rows = filterSessions(state.sessions, query);
|
|
@@ -2029,6 +2116,10 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
2029
2116
|
useEffect6(() => {
|
|
2030
2117
|
if (loadingMore) setLoadingMore(false);
|
|
2031
2118
|
}, [state.error, state.history.hasMoreSessions, state.sessions.length]);
|
|
2119
|
+
useEffect6(() => {
|
|
2120
|
+
const timer = setInterval(() => setNow(Date.now()), 1e4);
|
|
2121
|
+
return () => clearInterval(timer);
|
|
2122
|
+
}, []);
|
|
2032
2123
|
const loadMore = () => {
|
|
2033
2124
|
if (loadingMore) return;
|
|
2034
2125
|
setLoadingMore(true);
|
|
@@ -2062,23 +2153,120 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
2062
2153
|
] }) : null,
|
|
2063
2154
|
/* @__PURE__ */ jsxs6("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
|
|
2064
2155
|
!rows.length && state.startup === "ready" ? /* @__PURE__ */ jsx7("div", { class: "scui-empty", children: query ? "No chats match your search." : state.error ?? "No coding chats found." }) : null,
|
|
2065
|
-
rows.map((row) => /* @__PURE__ */ jsx7(Row, { value: row, row, state, adapter, onOpen }, row.key)),
|
|
2156
|
+
rows.map((row) => /* @__PURE__ */ jsx7(Row, { value: row, row, state, adapter, onOpen, now }, row.key)),
|
|
2066
2157
|
state.history.hasMoreSessions ? /* @__PURE__ */ jsx7("button", { class: "scui-load", type: "button", disabled: loadingMore, onClick: loadMore, children: loadingMore ? "Loading older chats\u2026" : "Load older chats" }) : null
|
|
2067
2158
|
] })
|
|
2068
2159
|
] });
|
|
2069
2160
|
}
|
|
2070
2161
|
|
|
2071
|
-
// src/
|
|
2162
|
+
// src/settings.jsx
|
|
2163
|
+
import { useEffect as useEffect7, useId as useId2, useMemo as useMemo2, useRef as useRef6, useState as useState5 } from "preact/hooks";
|
|
2072
2164
|
import { jsx as jsx8, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
2165
|
+
function HarnessAdvisory({ state, onReview }) {
|
|
2166
|
+
const advisory = state.interopSettings?.advisories[0];
|
|
2167
|
+
if (!advisory && !state.interopSettingsError) return null;
|
|
2168
|
+
return /* @__PURE__ */ jsxs7("aside", { class: "scui-advisory", "data-severity": advisory?.severity ?? "error", role: advisory?.severity === "error" ? "alert" : "status", children: [
|
|
2169
|
+
/* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "!" }),
|
|
2170
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2171
|
+
/* @__PURE__ */ jsx8("strong", { children: advisory?.title ?? "Could not inspect harness settings" }),
|
|
2172
|
+
/* @__PURE__ */ jsx8("small", { children: advisory?.message ?? state.interopSettingsError })
|
|
2173
|
+
] }),
|
|
2174
|
+
advisory && state.canConfigureSettings ? /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => onReview(advisory.recommendation.change), children: "Review" }) : null
|
|
2175
|
+
] });
|
|
2176
|
+
}
|
|
2177
|
+
function initialValues(report, recommendedChange) {
|
|
2178
|
+
return Object.fromEntries(report.controls.map((control) => [
|
|
2179
|
+
control.key,
|
|
2180
|
+
recommendedChange?.key === control.key ? recommendedChange.value : control.configuredValue ?? control.effectiveValue ?? control.choices[0]?.value ?? null
|
|
2181
|
+
]));
|
|
2182
|
+
}
|
|
2183
|
+
function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = null }) {
|
|
2184
|
+
const report = state.interopSettings;
|
|
2185
|
+
const titleId = useId2();
|
|
2186
|
+
const panel = useRef6(null);
|
|
2187
|
+
const valuesKey = `${report?.revision ?? ""}:${recommendedChange?.key ?? ""}:${recommendedChange?.value ?? ""}`;
|
|
2188
|
+
const defaults = useMemo2(() => report ? initialValues(report, recommendedChange) : {}, [valuesKey]);
|
|
2189
|
+
const [values, setValues] = useState5(defaults);
|
|
2190
|
+
useEffect7(() => setValues(defaults), [defaults]);
|
|
2191
|
+
useEffect7(() => {
|
|
2192
|
+
panel.current?.querySelector("select, button")?.focus({ preventScroll: true });
|
|
2193
|
+
const dismiss = (event) => {
|
|
2194
|
+
if (event.key === "Escape") {
|
|
2195
|
+
event.preventDefault();
|
|
2196
|
+
onClose();
|
|
2197
|
+
}
|
|
2198
|
+
};
|
|
2199
|
+
document.addEventListener("keydown", dismiss);
|
|
2200
|
+
return () => document.removeEventListener("keydown", dismiss);
|
|
2201
|
+
}, [onClose]);
|
|
2202
|
+
if (!report) return /* @__PURE__ */ jsx8("section", { ref: panel, class: "scui-settings", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: /* @__PURE__ */ jsxs7("header", { children: [
|
|
2203
|
+
/* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close settings", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }),
|
|
2204
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2205
|
+
/* @__PURE__ */ jsx8("strong", { id: titleId, children: "Harness settings" }),
|
|
2206
|
+
/* @__PURE__ */ jsx8("small", { children: state.interopSettingsError ?? "No interoperability controls are available." })
|
|
2207
|
+
] })
|
|
2208
|
+
] }) });
|
|
2209
|
+
const changed = report.controls.flatMap((control) => values[control.key] !== (control.configuredValue ?? control.effectiveValue ?? control.choices[0]?.value ?? null) ? [{ key: control.key, value: values[control.key] ?? null }] : []);
|
|
2210
|
+
const submit = (event) => {
|
|
2211
|
+
event.preventDefault();
|
|
2212
|
+
if (!changed.length || !state.canConfigureSettings) return;
|
|
2213
|
+
adapter.onIntent({ action: "configureHarness", harness: report.harness, changes: changed, expectedRevision: report.revision });
|
|
2214
|
+
};
|
|
2215
|
+
return /* @__PURE__ */ jsxs7("section", { ref: panel, class: "scui-settings", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [
|
|
2216
|
+
/* @__PURE__ */ jsxs7("header", { children: [
|
|
2217
|
+
/* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close settings", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }),
|
|
2218
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2219
|
+
/* @__PURE__ */ jsxs7("strong", { id: titleId, children: [
|
|
2220
|
+
harnessDisplayName(report.harness),
|
|
2221
|
+
" interoperability"
|
|
2222
|
+
] }),
|
|
2223
|
+
/* @__PURE__ */ jsx8("small", { children: "Native harness settings used by Supercode" })
|
|
2224
|
+
] })
|
|
2225
|
+
] }),
|
|
2226
|
+
/* @__PURE__ */ jsxs7("form", { onSubmit: submit, children: [
|
|
2227
|
+
report.controls.map((control) => {
|
|
2228
|
+
const choice = control.choices.find((item) => item.value === values[control.key]);
|
|
2229
|
+
const recommendation = report.advisories.map((advisory) => advisory.recommendation).find((item) => item.change.key === control.key && item.change.value === values[control.key]);
|
|
2230
|
+
const consequence = choice?.risk ?? recommendation?.consequence;
|
|
2231
|
+
return /* @__PURE__ */ jsxs7("fieldset", { disabled: !control.writable || Boolean(state.operation), children: [
|
|
2232
|
+
/* @__PURE__ */ jsxs7("label", { for: `scui-setting-${control.key}`, children: [
|
|
2233
|
+
/* @__PURE__ */ jsx8("strong", { children: control.label }),
|
|
2234
|
+
/* @__PURE__ */ jsx8("small", { children: control.description })
|
|
2235
|
+
] }),
|
|
2236
|
+
/* @__PURE__ */ jsxs7("select", { id: `scui-setting-${control.key}`, value: values[control.key] ?? "@default", onChange: (event) => setValues((current) => ({ ...current, [control.key]: event.currentTarget.value === "@default" ? null : event.currentTarget.value })), children: [
|
|
2237
|
+
control.resettable ? /* @__PURE__ */ jsx8("option", { value: "@default", children: "Use harness default" }) : null,
|
|
2238
|
+
control.choices.map((item) => /* @__PURE__ */ jsx8("option", { value: item.value, children: item.label }, item.value))
|
|
2239
|
+
] }),
|
|
2240
|
+
choice ? /* @__PURE__ */ jsx8("p", { children: choice.description }) : null,
|
|
2241
|
+
consequence ? /* @__PURE__ */ jsxs7("p", { class: "scui-setting-risk", children: [
|
|
2242
|
+
/* @__PURE__ */ jsx8("strong", { children: "Security consequence" }),
|
|
2243
|
+
consequence
|
|
2244
|
+
] }) : null,
|
|
2245
|
+
/* @__PURE__ */ jsxs7("small", { class: "scui-setting-source", children: [
|
|
2246
|
+
control.effectiveNote,
|
|
2247
|
+
control.sourcePath ? ` Source: ${control.sourcePath}` : ""
|
|
2248
|
+
] })
|
|
2249
|
+
] }, control.key);
|
|
2250
|
+
}),
|
|
2251
|
+
/* @__PURE__ */ jsxs7("footer", { children: [
|
|
2252
|
+
/* @__PURE__ */ jsx8("button", { type: "button", onClick: onClose, children: "Cancel" }),
|
|
2253
|
+
/* @__PURE__ */ jsx8("button", { type: "submit", disabled: !changed.length || !state.canConfigureSettings || Boolean(state.operation), children: state.operation === "configureHarness" ? "Applying\u2026" : "Apply changes" })
|
|
2254
|
+
] })
|
|
2255
|
+
] })
|
|
2256
|
+
] });
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
// src/messenger.jsx
|
|
2260
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "preact/jsx-runtime";
|
|
2073
2261
|
var pendingMessageMemory = /* @__PURE__ */ new Map();
|
|
2074
2262
|
var messengerViewMemory = /* @__PURE__ */ new Map();
|
|
2075
2263
|
var newChatMemory = /* @__PURE__ */ new Map();
|
|
2076
|
-
var TRACKED_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "detach", "branch", "reduce", "terminal", "export", "interrupt", "respond", "refresh", "loadEarlier", "loadSessions"]);
|
|
2264
|
+
var TRACKED_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "detach", "branch", "reduce", "terminal", "export", "interrupt", "respond", "configureHarness", "refresh", "loadEarlier", "loadSessions"]);
|
|
2077
2265
|
function Receipt({ state, adapter }) {
|
|
2078
2266
|
const receipt = state.reductionReceipt;
|
|
2079
|
-
if (receipt) return /* @__PURE__ */
|
|
2080
|
-
/* @__PURE__ */
|
|
2081
|
-
/* @__PURE__ */
|
|
2267
|
+
if (receipt) return /* @__PURE__ */ jsx9("div", { class: "scui-receipt", children: /* @__PURE__ */ jsxs8("span", { children: [
|
|
2268
|
+
/* @__PURE__ */ jsx9("strong", { children: "Reduced and verified" }),
|
|
2269
|
+
/* @__PURE__ */ jsxs8("small", { children: [
|
|
2082
2270
|
receipt.sourceTokens.toLocaleString(),
|
|
2083
2271
|
" \u2192 ",
|
|
2084
2272
|
receipt.reducedTokens.toLocaleString(),
|
|
@@ -2087,36 +2275,36 @@ function Receipt({ state, adapter }) {
|
|
|
2087
2275
|
"\xD7 \xB7 reversible"
|
|
2088
2276
|
] })
|
|
2089
2277
|
] }) });
|
|
2090
|
-
if (state.exportReceipt) return /* @__PURE__ */
|
|
2091
|
-
/* @__PURE__ */
|
|
2092
|
-
/* @__PURE__ */
|
|
2278
|
+
if (state.exportReceipt) return /* @__PURE__ */ jsxs8("div", { class: "scui-receipt", children: [
|
|
2279
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
2280
|
+
/* @__PURE__ */ jsxs8("strong", { children: [
|
|
2093
2281
|
"Lossless export ready \xB7 ",
|
|
2094
2282
|
harnessDisplayName(state.exportReceipt.targetHarness)
|
|
2095
2283
|
] }),
|
|
2096
|
-
/* @__PURE__ */
|
|
2284
|
+
/* @__PURE__ */ jsxs8("small", { children: [
|
|
2097
2285
|
state.exportReceipt.path,
|
|
2098
2286
|
" \xB7 ",
|
|
2099
2287
|
state.exportReceipt.files,
|
|
2100
2288
|
" files"
|
|
2101
2289
|
] })
|
|
2102
2290
|
] }),
|
|
2103
|
-
/* @__PURE__ */
|
|
2291
|
+
/* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter.copyText?.(state.exportReceipt.path), children: "Copy path" })
|
|
2104
2292
|
] });
|
|
2105
|
-
if (state.terminalHandoff) return /* @__PURE__ */
|
|
2106
|
-
/* @__PURE__ */
|
|
2107
|
-
/* @__PURE__ */
|
|
2108
|
-
/* @__PURE__ */
|
|
2293
|
+
if (state.terminalHandoff) return /* @__PURE__ */ jsxs8("div", { class: "scui-receipt", children: [
|
|
2294
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
2295
|
+
/* @__PURE__ */ jsx9("strong", { children: "Terminal handoff ready" }),
|
|
2296
|
+
/* @__PURE__ */ jsx9("small", { children: state.terminalHandoff.cwd })
|
|
2109
2297
|
] }),
|
|
2110
|
-
/* @__PURE__ */
|
|
2298
|
+
/* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter.copyText?.(terminalCommand(state.terminalHandoff)), children: "Copy command" })
|
|
2111
2299
|
] });
|
|
2112
2300
|
return null;
|
|
2113
2301
|
}
|
|
2114
|
-
function ConversationActions({ state, adapter, actionPending }) {
|
|
2115
|
-
const [open, setOpen] =
|
|
2116
|
-
const root =
|
|
2117
|
-
const panel =
|
|
2118
|
-
const trigger =
|
|
2119
|
-
const menuId =
|
|
2302
|
+
function ConversationActions({ state, adapter, actionPending, onSettings }) {
|
|
2303
|
+
const [open, setOpen] = useState6(false);
|
|
2304
|
+
const root = useRef7(null);
|
|
2305
|
+
const panel = useRef7(null);
|
|
2306
|
+
const trigger = useRef7(null);
|
|
2307
|
+
const menuId = useId3();
|
|
2120
2308
|
const targets = state.harnesses.filter((item) => item.startable);
|
|
2121
2309
|
const groups = [
|
|
2122
2310
|
{
|
|
@@ -2124,7 +2312,8 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2124
2312
|
items: [
|
|
2125
2313
|
state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
|
|
2126
2314
|
state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
|
|
2127
|
-
state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null
|
|
2315
|
+
state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null,
|
|
2316
|
+
state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null
|
|
2128
2317
|
].filter(Boolean)
|
|
2129
2318
|
},
|
|
2130
2319
|
{
|
|
@@ -2136,7 +2325,7 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2136
2325
|
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 } })) : []
|
|
2137
2326
|
}
|
|
2138
2327
|
].filter((group) => group.items.length);
|
|
2139
|
-
|
|
2328
|
+
useEffect8(() => {
|
|
2140
2329
|
if (!open) return;
|
|
2141
2330
|
panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
|
|
2142
2331
|
const dismiss = (event) => {
|
|
@@ -2155,9 +2344,10 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2155
2344
|
document.removeEventListener("pointerdown", dismiss, true);
|
|
2156
2345
|
};
|
|
2157
2346
|
}, [open]);
|
|
2158
|
-
const dispatch = (
|
|
2347
|
+
const dispatch = (item) => {
|
|
2159
2348
|
setOpen(false);
|
|
2160
|
-
|
|
2349
|
+
if (item.onSelect) item.onSelect();
|
|
2350
|
+
else adapter.onIntent(item.intent);
|
|
2161
2351
|
};
|
|
2162
2352
|
const navigate = (event) => {
|
|
2163
2353
|
if (event.key === "Escape") {
|
|
@@ -2175,62 +2365,63 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2175
2365
|
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;
|
|
2176
2366
|
items[index].focus({ preventScroll: true });
|
|
2177
2367
|
};
|
|
2178
|
-
return /* @__PURE__ */
|
|
2368
|
+
return /* @__PURE__ */ jsxs8("div", { class: "scui-menu", ref: root, onBlur: (event) => {
|
|
2179
2369
|
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
2180
2370
|
}, children: [
|
|
2181
|
-
/* @__PURE__ */
|
|
2182
|
-
open ? /* @__PURE__ */
|
|
2183
|
-
/* @__PURE__ */
|
|
2184
|
-
group.items.map((item) => /* @__PURE__ */
|
|
2371
|
+
/* @__PURE__ */ jsx9("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__ */ jsx9(UiIcon, { name: "menu", size: 18 }) }),
|
|
2372
|
+
open ? /* @__PURE__ */ jsx9("div", { ref: panel, id: menuId, class: "scui-menu-panel", role: "menu", "aria-label": "Conversation actions", onKeyDown: navigate, children: groups.map((group) => /* @__PURE__ */ jsxs8("section", { role: "group", "aria-label": group.label, children: [
|
|
2373
|
+
/* @__PURE__ */ jsx9("strong", { children: group.label }),
|
|
2374
|
+
group.items.map((item) => /* @__PURE__ */ jsx9("button", { role: "menuitem", type: "button", disabled: actionPending, onClick: () => dispatch(item), children: item.label }, item.key))
|
|
2185
2375
|
] }, group.label)) }) : null
|
|
2186
2376
|
] });
|
|
2187
2377
|
}
|
|
2188
|
-
function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
|
|
2189
|
-
const back =
|
|
2378
|
+
function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose, onSettings }) {
|
|
2379
|
+
const back = useRef7(null);
|
|
2190
2380
|
const harness = state.attached?.harness ?? state.harness;
|
|
2191
2381
|
const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
|
|
2192
2382
|
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";
|
|
2193
|
-
const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
|
|
2194
|
-
|
|
2383
|
+
const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce || state.interopSettings || state.interopSettingsError;
|
|
2384
|
+
useEffect8(() => {
|
|
2195
2385
|
if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
|
|
2196
2386
|
}, []);
|
|
2197
|
-
return /* @__PURE__ */
|
|
2198
|
-
/* @__PURE__ */
|
|
2199
|
-
/* @__PURE__ */
|
|
2200
|
-
/* @__PURE__ */
|
|
2201
|
-
/* @__PURE__ */
|
|
2202
|
-
/* @__PURE__ */
|
|
2387
|
+
return /* @__PURE__ */ jsxs8("header", { class: "scui-head scui-chat-head", children: [
|
|
2388
|
+
/* @__PURE__ */ jsx9("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 18 }) }),
|
|
2389
|
+
/* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 28 }),
|
|
2390
|
+
/* @__PURE__ */ jsxs8("span", { class: "scui-head-copy", children: [
|
|
2391
|
+
/* @__PURE__ */ jsx9("strong", { children: title }),
|
|
2392
|
+
/* @__PURE__ */ jsxs8("small", { children: [
|
|
2203
2393
|
harnessDisplayName(harness),
|
|
2204
2394
|
" \xB7 ",
|
|
2205
2395
|
status
|
|
2206
2396
|
] })
|
|
2207
2397
|
] }),
|
|
2208
|
-
menu ? /* @__PURE__ */
|
|
2209
|
-
/* @__PURE__ */
|
|
2210
|
-
onClose ? /* @__PURE__ */
|
|
2398
|
+
menu ? /* @__PURE__ */ jsx9(ConversationActions, { state, adapter, actionPending, onSettings }) : null,
|
|
2399
|
+
/* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx9(UiIcon, { name: "plus", size: 18 }) }),
|
|
2400
|
+
onClose ? /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx9(UiIcon, { name: "close", size: 18 }) }) : null
|
|
2211
2401
|
] });
|
|
2212
2402
|
}
|
|
2213
2403
|
function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
|
|
2214
2404
|
const Header = slots.header;
|
|
2215
2405
|
const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
|
|
2216
|
-
const [pending, setPendingState] =
|
|
2217
|
-
const [pendingAction, setPendingAction] =
|
|
2218
|
-
const [restoreDraft, setRestoreDraft] =
|
|
2219
|
-
const
|
|
2220
|
-
const
|
|
2221
|
-
const
|
|
2406
|
+
const [pending, setPendingState] = useState6(() => pendingMessageMemory.get(memoryKey) ?? null);
|
|
2407
|
+
const [pendingAction, setPendingAction] = useState6(null);
|
|
2408
|
+
const [restoreDraft, setRestoreDraft] = useState6(null);
|
|
2409
|
+
const [settings, setSettings] = useState6(null);
|
|
2410
|
+
const restoreSequence = useRef7(0);
|
|
2411
|
+
const actionSequence = useRef7(0);
|
|
2412
|
+
const acknowledged = useRef7(/* @__PURE__ */ new Set());
|
|
2222
2413
|
const setPending = (update) => setPendingState((current) => {
|
|
2223
2414
|
const next = typeof update === "function" ? update(current) : update;
|
|
2224
2415
|
if (next) boundedSet(pendingMessageMemory, memoryKey, next);
|
|
2225
2416
|
else pendingMessageMemory.delete(memoryKey);
|
|
2226
2417
|
return next;
|
|
2227
2418
|
});
|
|
2228
|
-
|
|
2419
|
+
useEffect8(() => {
|
|
2229
2420
|
setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
|
|
2230
2421
|
setPendingAction(null);
|
|
2231
2422
|
setRestoreDraft(null);
|
|
2232
2423
|
}, [memoryKey]);
|
|
2233
|
-
|
|
2424
|
+
useEffect8(() => {
|
|
2234
2425
|
if (!pending) return;
|
|
2235
2426
|
if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
|
|
2236
2427
|
setPending(null);
|
|
@@ -2259,7 +2450,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2259
2450
|
setRestoreDraft({ id: restoreSequence.current, text: pending.text, context: pending.context, images: pending.images });
|
|
2260
2451
|
setPending({ ...pending, status: "editing" });
|
|
2261
2452
|
};
|
|
2262
|
-
|
|
2453
|
+
useEffect8(() => {
|
|
2263
2454
|
const key = state.attached?.key;
|
|
2264
2455
|
if (!key || !state.attention.some((item) => item.key === key)) {
|
|
2265
2456
|
if (key) acknowledged.current.delete(key);
|
|
@@ -2270,7 +2461,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2270
2461
|
adapter.onIntent({ action: "ack", key });
|
|
2271
2462
|
}
|
|
2272
2463
|
}, [adapter, state.attached?.key, state.attention]);
|
|
2273
|
-
const trackedAdapter =
|
|
2464
|
+
const trackedAdapter = useMemo3(() => ({
|
|
2274
2465
|
...adapter,
|
|
2275
2466
|
onIntent(intent) {
|
|
2276
2467
|
if (!TRACKED_ACTIONS.has(intent.action)) return adapter.onIntent(intent);
|
|
@@ -2294,7 +2485,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2294
2485
|
return result;
|
|
2295
2486
|
}
|
|
2296
2487
|
}), [adapter, state.error]);
|
|
2297
|
-
|
|
2488
|
+
useEffect8(() => {
|
|
2298
2489
|
if (!pendingAction) return;
|
|
2299
2490
|
if (state.operation && !pendingAction.seenOperation) {
|
|
2300
2491
|
setPendingAction({ ...pendingAction, seenOperation: true });
|
|
@@ -2307,44 +2498,48 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2307
2498
|
const actionLabel = operationLabel(action);
|
|
2308
2499
|
const actionState = action ? { ...state, operation: action, canInterrupt: false, canRespond: false } : state;
|
|
2309
2500
|
const unreadAfterMessages = state.attention.find((item) => item.key === state.attached?.key)?.afterMessages ?? null;
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2501
|
+
const Advisory = components.HarnessAdvisory ?? HarnessAdvisory;
|
|
2502
|
+
const SettingsPanel = components.HarnessSettingsPanel ?? HarnessSettingsPanel;
|
|
2503
|
+
return /* @__PURE__ */ jsxs8("section", { class: "scui-chat", children: [
|
|
2504
|
+
Header ? /* @__PURE__ */ jsx9(Header, { state: actionState, adapter: trackedAdapter, value: null }) : /* @__PURE__ */ jsx9(ChatHeader, { state: actionState, adapter: trackedAdapter, pendingStatus: pending?.status ?? null, actionPending: Boolean(action), onBack, onNew, onClose, onSettings: () => setSettings({ recommendedChange: null }) }),
|
|
2505
|
+
actionLabel ? /* @__PURE__ */ jsxs8("div", { class: "scui-operation", role: "status", children: [
|
|
2506
|
+
/* @__PURE__ */ jsx9("i", {}),
|
|
2314
2507
|
actionLabel
|
|
2315
2508
|
] }) : null,
|
|
2316
|
-
state.error ? /* @__PURE__ */
|
|
2317
|
-
/* @__PURE__ */
|
|
2318
|
-
state.recoverable ? /* @__PURE__ */
|
|
2509
|
+
state.error ? /* @__PURE__ */ jsxs8("div", { class: "scui-error", role: "alert", children: [
|
|
2510
|
+
/* @__PURE__ */ jsx9("span", { children: state.error }),
|
|
2511
|
+
state.recoverable ? /* @__PURE__ */ jsx9("button", { type: "button", disabled: Boolean(action), onClick: () => trackedAdapter.onIntent({ action: "refresh" }), children: "Retry" }) : null
|
|
2319
2512
|
] }) : null,
|
|
2320
|
-
/* @__PURE__ */
|
|
2321
|
-
/* @__PURE__ */
|
|
2322
|
-
/* @__PURE__ */
|
|
2323
|
-
|
|
2513
|
+
/* @__PURE__ */ jsx9(Receipt, { state, adapter }),
|
|
2514
|
+
/* @__PURE__ */ jsx9(Conversation, { state: actionState, adapter: trackedAdapter, components, slots, memoryKey, pending: pendingMessage, unreadAfterMessages }, memoryKey),
|
|
2515
|
+
/* @__PURE__ */ jsx9(ContinuationBar, { state: actionState, adapter: trackedAdapter, labels }),
|
|
2516
|
+
/* @__PURE__ */ jsx9(Advisory, { state: actionState, adapter: trackedAdapter, value: state.interopSettings?.advisories[0] ?? null, onReview: (recommendedChange) => setSettings({ recommendedChange }) }),
|
|
2517
|
+
state.mode !== "mirror" || state.canSend ? /* @__PURE__ */ jsx9(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,
|
|
2518
|
+
settings ? /* @__PURE__ */ jsx9(SettingsPanel, { state: actionState, adapter: trackedAdapter, value: state.interopSettings, recommendedChange: settings.recommendedChange, onClose: () => setSettings(null) }) : null
|
|
2324
2519
|
] });
|
|
2325
2520
|
}
|
|
2326
2521
|
function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey }) {
|
|
2327
2522
|
const startable = state.harnesses.filter((item) => item.startable);
|
|
2328
2523
|
const startableKey = startable.map((item) => item.id).join("\0");
|
|
2329
2524
|
const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: "", context: [], images: [] };
|
|
2330
|
-
const [harness, setHarness] =
|
|
2331
|
-
const [draft, setDraft] =
|
|
2332
|
-
const [context, setContext] =
|
|
2333
|
-
const [images, setImages] =
|
|
2334
|
-
const [starting, setStarting] =
|
|
2335
|
-
const [picking, setPicking] =
|
|
2336
|
-
const [dragging, setDragging] =
|
|
2337
|
-
const [pickerError, setPickerError] =
|
|
2338
|
-
const startSequence =
|
|
2339
|
-
const textarea =
|
|
2525
|
+
const [harness, setHarness] = useState6(remembered.harness);
|
|
2526
|
+
const [draft, setDraft] = useState6(remembered.draft);
|
|
2527
|
+
const [context, setContext] = useState6(remembered.context);
|
|
2528
|
+
const [images, setImages] = useState6(remembered.images ?? []);
|
|
2529
|
+
const [starting, setStarting] = useState6(null);
|
|
2530
|
+
const [picking, setPicking] = useState6(false);
|
|
2531
|
+
const [dragging, setDragging] = useState6(false);
|
|
2532
|
+
const [pickerError, setPickerError] = useState6(null);
|
|
2533
|
+
const startSequence = useRef7(0);
|
|
2534
|
+
const textarea = useRef7(null);
|
|
2340
2535
|
useAutosizeTextarea(textarea, draft);
|
|
2341
|
-
|
|
2536
|
+
useEffect8(() => {
|
|
2342
2537
|
if (startable.some((item) => item.id === harness)) return;
|
|
2343
2538
|
const next = startable[0]?.id ?? "";
|
|
2344
2539
|
setHarness(next);
|
|
2345
2540
|
boundedSet(newChatMemory, memoryKey, { harness: next, draft, context, images });
|
|
2346
2541
|
}, [harness, startableKey]);
|
|
2347
|
-
|
|
2542
|
+
useEffect8(() => {
|
|
2348
2543
|
if (!starting) return;
|
|
2349
2544
|
const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
|
|
2350
2545
|
const beganWorking = !starting.busy && state.busy;
|
|
@@ -2352,10 +2547,10 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2352
2547
|
newChatMemory.delete(memoryKey);
|
|
2353
2548
|
onStarted();
|
|
2354
2549
|
}, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
|
|
2355
|
-
|
|
2550
|
+
useEffect8(() => {
|
|
2356
2551
|
if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
|
|
2357
2552
|
}, [starting, state.busy, state.error, state.operation]);
|
|
2358
|
-
|
|
2553
|
+
useEffect8(() => {
|
|
2359
2554
|
textarea.current?.focus({ preventScroll: true });
|
|
2360
2555
|
}, []);
|
|
2361
2556
|
const pickContext = () => {
|
|
@@ -2417,49 +2612,49 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2417
2612
|
Promise.resolve(result).catch(() => setStarting((current) => current?.id === id ? null : current));
|
|
2418
2613
|
}
|
|
2419
2614
|
};
|
|
2420
|
-
return /* @__PURE__ */
|
|
2421
|
-
/* @__PURE__ */
|
|
2422
|
-
/* @__PURE__ */
|
|
2423
|
-
/* @__PURE__ */
|
|
2424
|
-
/* @__PURE__ */
|
|
2425
|
-
/* @__PURE__ */
|
|
2615
|
+
return /* @__PURE__ */ jsxs8("section", { class: "scui-chat", children: [
|
|
2616
|
+
/* @__PURE__ */ jsxs8("header", { class: "scui-head", children: [
|
|
2617
|
+
/* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Back", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 18 }) }),
|
|
2618
|
+
/* @__PURE__ */ jsxs8("span", { class: "scui-head-copy", children: [
|
|
2619
|
+
/* @__PURE__ */ jsx9("strong", { children: labels.newChat }),
|
|
2620
|
+
/* @__PURE__ */ jsx9("small", { children: "No session is created until you send" })
|
|
2426
2621
|
] }),
|
|
2427
|
-
onClose ? /* @__PURE__ */
|
|
2622
|
+
onClose ? /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx9(UiIcon, { name: "close", size: 18 }) }) : null
|
|
2428
2623
|
] }),
|
|
2429
|
-
starting ? /* @__PURE__ */
|
|
2430
|
-
/* @__PURE__ */
|
|
2624
|
+
starting ? /* @__PURE__ */ jsxs8("div", { class: "scui-operation", role: "status", children: [
|
|
2625
|
+
/* @__PURE__ */ jsx9("i", {}),
|
|
2431
2626
|
operationLabel("start")
|
|
2432
2627
|
] }) : null,
|
|
2433
|
-
/* @__PURE__ */
|
|
2434
|
-
/* @__PURE__ */
|
|
2435
|
-
/* @__PURE__ */
|
|
2436
|
-
/* @__PURE__ */
|
|
2628
|
+
/* @__PURE__ */ jsxs8("div", { class: "scui-new", children: [
|
|
2629
|
+
/* @__PURE__ */ jsx9("span", { "aria-hidden": "true", children: "\u2726" }),
|
|
2630
|
+
/* @__PURE__ */ jsx9("strong", { children: "What should the agent build or fix?" }),
|
|
2631
|
+
/* @__PURE__ */ jsx9("small", { children: "Choose a coding harness and send the first message." })
|
|
2437
2632
|
] }),
|
|
2438
|
-
/* @__PURE__ */
|
|
2439
|
-
/* @__PURE__ */
|
|
2440
|
-
/* @__PURE__ */
|
|
2441
|
-
/* @__PURE__ */
|
|
2442
|
-
/* @__PURE__ */
|
|
2633
|
+
/* @__PURE__ */ jsxs8("div", { class: "scui-compose", children: [
|
|
2634
|
+
/* @__PURE__ */ jsxs8("label", { class: "scui-harness-picker", children: [
|
|
2635
|
+
/* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 24 }),
|
|
2636
|
+
/* @__PURE__ */ jsx9("span", { children: "Coding harness" }),
|
|
2637
|
+
/* @__PURE__ */ jsx9("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
|
|
2443
2638
|
const value = event.currentTarget.value;
|
|
2444
2639
|
setHarness(value);
|
|
2445
2640
|
boundedSet(newChatMemory, memoryKey, { harness: value, draft, context, images });
|
|
2446
|
-
}, children: state.harnesses.map((item) => /* @__PURE__ */
|
|
2641
|
+
}, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs8("option", { value: item.id, disabled: !item.startable, children: [
|
|
2447
2642
|
item.label,
|
|
2448
2643
|
item.startable ? "" : " \xB7 unavailable"
|
|
2449
2644
|
] }, item.id)) })
|
|
2450
2645
|
] }),
|
|
2451
|
-
/* @__PURE__ */
|
|
2646
|
+
/* @__PURE__ */ jsx9(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
|
|
2452
2647
|
const next = items.filter((_, itemIndex) => itemIndex !== index);
|
|
2453
2648
|
boundedSet(newChatMemory, memoryKey, { harness, draft, context, images: next });
|
|
2454
2649
|
return next;
|
|
2455
2650
|
}) }),
|
|
2456
|
-
/* @__PURE__ */
|
|
2651
|
+
/* @__PURE__ */ jsx9(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
|
|
2457
2652
|
const next = items.filter((_, itemIndex) => itemIndex !== index);
|
|
2458
2653
|
boundedSet(newChatMemory, memoryKey, { harness, draft, context: next, images });
|
|
2459
2654
|
return next;
|
|
2460
2655
|
}) }),
|
|
2461
|
-
pickerError ? /* @__PURE__ */
|
|
2462
|
-
/* @__PURE__ */
|
|
2656
|
+
pickerError ? /* @__PURE__ */ jsx9("small", { class: "scui-context-error", role: "alert", children: pickerError }) : null,
|
|
2657
|
+
/* @__PURE__ */ jsxs8("div", { class: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
2463
2658
|
if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
|
|
2464
2659
|
event.preventDefault();
|
|
2465
2660
|
setDragging(true);
|
|
@@ -2469,8 +2664,8 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2469
2664
|
}, onDragLeave: (event) => {
|
|
2470
2665
|
if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
|
|
2471
2666
|
}, onDrop: dropImages, children: [
|
|
2472
|
-
adapter.pickContext ? /* @__PURE__ */
|
|
2473
|
-
/* @__PURE__ */
|
|
2667
|
+
adapter.pickContext ? /* @__PURE__ */ jsx9("button", { class: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx9("i", { class: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
2668
|
+
/* @__PURE__ */ jsx9("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), onPaste: pasteImages, onInput: (event) => {
|
|
2474
2669
|
const value = event.currentTarget.value;
|
|
2475
2670
|
setDraft(value);
|
|
2476
2671
|
boundedSet(newChatMemory, memoryKey, { harness, draft: value, context, images });
|
|
@@ -2480,23 +2675,23 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2480
2675
|
send();
|
|
2481
2676
|
}
|
|
2482
2677
|
} }),
|
|
2483
|
-
/* @__PURE__ */
|
|
2678
|
+
/* @__PURE__ */ jsx9("span", { children: /* @__PURE__ */ jsx9("button", { type: "button", class: "scui-send", "aria-label": "Start chat", disabled: !draft.trim() && !images.length || !harness || Boolean(starting), onClick: send, children: starting ? /* @__PURE__ */ jsx9("i", { class: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "send", size: 17 }) }) })
|
|
2484
2679
|
] })
|
|
2485
2680
|
] })
|
|
2486
2681
|
] });
|
|
2487
2682
|
}
|
|
2488
2683
|
function SupercodeMessenger({ state: stateInput, adapter, class: className = "", initialView, labels, components = {}, slots = {} }) {
|
|
2489
|
-
const state =
|
|
2684
|
+
const state = useMemo3(() => normalizeUiState(stateInput), [stateInput]);
|
|
2490
2685
|
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
2491
2686
|
const memoryKey = state.workspace || "@default";
|
|
2492
|
-
const [view, setViewState] =
|
|
2493
|
-
const [opening, setOpening] =
|
|
2494
|
-
const [listFocus, setListFocus] =
|
|
2687
|
+
const [view, setViewState] = useState6(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
|
|
2688
|
+
const [opening, setOpening] = useState6(null);
|
|
2689
|
+
const [listFocus, setListFocus] = useState6(null);
|
|
2495
2690
|
const setView = (next) => {
|
|
2496
2691
|
boundedSet(messengerViewMemory, memoryKey, next);
|
|
2497
2692
|
setViewState(next);
|
|
2498
2693
|
};
|
|
2499
|
-
|
|
2694
|
+
useEffect8(() => {
|
|
2500
2695
|
if (!opening) return;
|
|
2501
2696
|
if (state.attached?.key === opening.key) {
|
|
2502
2697
|
setOpening(null);
|
|
@@ -2512,40 +2707,40 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
|
|
|
2512
2707
|
};
|
|
2513
2708
|
const close = () => adapter.onClose?.();
|
|
2514
2709
|
const Footer = slots.footer;
|
|
2515
|
-
return /* @__PURE__ */
|
|
2516
|
-
view === "list" ? /* @__PURE__ */
|
|
2710
|
+
return /* @__PURE__ */ jsxs8("main", { class: `scui-root ${className}`, "data-view": view, "data-mode": state.mode, "aria-label": "Supercode messenger", children: [
|
|
2711
|
+
view === "list" ? /* @__PURE__ */ jsx9(SessionList, { state, adapter, focusKey: listFocus, onOpen: open, onNew: () => {
|
|
2517
2712
|
setListFocus("@new");
|
|
2518
2713
|
setView("new");
|
|
2519
2714
|
}, onClose: adapter.onClose ? close : void 0, components, labels: copy, memoryKey }) : null,
|
|
2520
|
-
view === "new" ? /* @__PURE__ */
|
|
2521
|
-
view === "chat" ? /* @__PURE__ */
|
|
2715
|
+
view === "new" ? /* @__PURE__ */ jsx9(NewChat, { state, adapter, onBack: () => setView("list"), onClose: adapter.onClose ? close : void 0, onStarted: () => setView("chat"), labels: copy, memoryKey }) : null,
|
|
2716
|
+
view === "chat" ? /* @__PURE__ */ jsx9(Chat, { state, adapter, onBack: () => {
|
|
2522
2717
|
setListFocus(state.attached?.key ?? listFocus);
|
|
2523
2718
|
setView("list");
|
|
2524
2719
|
}, onNew: () => {
|
|
2525
2720
|
setListFocus("@new");
|
|
2526
2721
|
setView("new");
|
|
2527
2722
|
}, onClose: adapter.onClose ? close : void 0, components, slots, labels: copy }) : null,
|
|
2528
|
-
opening ? /* @__PURE__ */
|
|
2529
|
-
/* @__PURE__ */
|
|
2530
|
-
/* @__PURE__ */
|
|
2531
|
-
/* @__PURE__ */
|
|
2723
|
+
opening ? /* @__PURE__ */ jsxs8("div", { class: "scui-opening", role: "status", "aria-busy": "true", children: [
|
|
2724
|
+
/* @__PURE__ */ jsx9(HarnessLogo, { id: opening.harness, size: 34 }),
|
|
2725
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
2726
|
+
/* @__PURE__ */ jsxs8("strong", { children: [
|
|
2532
2727
|
"Opening ",
|
|
2533
2728
|
sessionDisplayName(opening)
|
|
2534
2729
|
] }),
|
|
2535
|
-
/* @__PURE__ */
|
|
2730
|
+
/* @__PURE__ */ jsx9("small", { children: "Loading the latest transcript window\u2026" })
|
|
2536
2731
|
] }),
|
|
2537
|
-
/* @__PURE__ */
|
|
2732
|
+
/* @__PURE__ */ jsx9("i", {})
|
|
2538
2733
|
] }) : null,
|
|
2539
|
-
Footer ? /* @__PURE__ */
|
|
2734
|
+
Footer ? /* @__PURE__ */ jsx9(Footer, { state, adapter, value: copy }) : null
|
|
2540
2735
|
] });
|
|
2541
2736
|
}
|
|
2542
2737
|
|
|
2543
2738
|
// src/embed.jsx
|
|
2544
|
-
import { jsx as
|
|
2739
|
+
import { jsx as jsx10 } from "preact/jsx-runtime";
|
|
2545
2740
|
function mountSupercodeMessenger(element, options) {
|
|
2546
2741
|
if (!(element instanceof Element)) throw new TypeError("mountSupercodeMessenger requires an Element");
|
|
2547
2742
|
let state = options.state;
|
|
2548
|
-
const draw = () => render(/* @__PURE__ */
|
|
2743
|
+
const draw = () => render(/* @__PURE__ */ jsx10(SupercodeMessenger, { ...options, state }), element);
|
|
2549
2744
|
options.adapter.onIntent({ action: "mounted" });
|
|
2550
2745
|
draw();
|
|
2551
2746
|
return {
|