@volter-ai-dev/supercode-ui 0.1.29 → 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 +326 -130
- package/composer.mjs +3 -0
- package/controller.mjs +10 -11
- package/conversation.mjs +4 -1
- package/core.d.ts +1 -0
- package/core.mjs +94 -2
- package/embed.mjs +326 -132
- package/index.d.ts +80 -1
- package/messenger.mjs +324 -130
- package/package.json +8 -2
- package/sessions.mjs +30 -6
- package/settings.d.ts +2 -0
- package/settings.mjs +216 -0
- package/styles.css +12 -3
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,6 +561,7 @@ 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,
|
|
@@ -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) => {
|
|
@@ -753,7 +831,7 @@ function canContinueHere(state) {
|
|
|
753
831
|
}
|
|
754
832
|
function operationLabel(operation) {
|
|
755
833
|
if (!operation) return "";
|
|
756
|
-
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" };
|
|
757
835
|
return labels[operation] ?? `${operation.replaceAll("_", " ")}\u2026`;
|
|
758
836
|
}
|
|
759
837
|
function terminalCommand(handoff) {
|
|
@@ -1983,28 +2061,35 @@ function sessionPathParts(value) {
|
|
|
1983
2061
|
trailing: complete.slice(boundary + 1)
|
|
1984
2062
|
};
|
|
1985
2063
|
}
|
|
1986
|
-
function SessionRow({ row, state, onOpen }) {
|
|
2064
|
+
function SessionRow({ row, state, onOpen, now = Date.now() }) {
|
|
1987
2065
|
const activity = sessionActivity(state, row);
|
|
2066
|
+
const working = activity === "working";
|
|
1988
2067
|
const attention = state.attention.find((item) => item.key === row.key);
|
|
1989
2068
|
const title = sessionDisplayName(row);
|
|
1990
2069
|
const path = sessionPathParts(row.cwd);
|
|
1991
2070
|
const preview = row.preview || attention?.preview || "";
|
|
1992
2071
|
const unreadCount = attention?.unreadCount ?? 0;
|
|
1993
2072
|
const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
|
|
1994
|
-
|
|
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: [
|
|
1995
2075
|
/* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
|
|
1996
2076
|
/* @__PURE__ */ jsxs6("span", { class: "scui-session-copy", children: [
|
|
1997
2077
|
/* @__PURE__ */ jsxs6("span", { class: "scui-session-title", children: [
|
|
1998
2078
|
/* @__PURE__ */ jsx7("strong", { children: title }),
|
|
1999
|
-
|
|
2079
|
+
/* @__PURE__ */ jsx7("span", { class: "scui-session-meta", children: age ? /* @__PURE__ */ jsx7("time", { children: age }) : null })
|
|
2000
2080
|
] }),
|
|
2001
2081
|
path.complete ? /* @__PURE__ */ jsxs6("small", { class: "scui-session-path", title: row.cwd, children: [
|
|
2002
2082
|
/* @__PURE__ */ jsx7("span", { class: "scui-session-path-leading", children: path.leading }),
|
|
2003
2083
|
path.separator ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-separator", children: path.separator }) : null,
|
|
2004
2084
|
path.trailing ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-trailing", children: path.trailing }) : null
|
|
2005
2085
|
] }) : null,
|
|
2006
|
-
preview || unreadCount ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-preview", children: [
|
|
2007
|
-
|
|
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,
|
|
2008
2093
|
unreadCount ? /* @__PURE__ */ jsx7("b", { "aria-label": `${unreadCount} unread messages`, children: unreadLabel }) : null
|
|
2009
2094
|
] }) : null,
|
|
2010
2095
|
state.attachError?.key === row.key ? /* @__PURE__ */ jsx7("em", { children: state.attachError.message }) : null
|
|
@@ -2015,6 +2100,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
2015
2100
|
const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
|
|
2016
2101
|
const [query, setQuery] = useState4(remembered.query);
|
|
2017
2102
|
const [loadingMore, setLoadingMore] = useState4(false);
|
|
2103
|
+
const [now, setNow] = useState4(() => Date.now());
|
|
2018
2104
|
const root = useRef5(null);
|
|
2019
2105
|
const rowScroller = useRef5(null);
|
|
2020
2106
|
const rows = filterSessions(state.sessions, query);
|
|
@@ -2030,6 +2116,10 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
2030
2116
|
useEffect6(() => {
|
|
2031
2117
|
if (loadingMore) setLoadingMore(false);
|
|
2032
2118
|
}, [state.error, state.history.hasMoreSessions, state.sessions.length]);
|
|
2119
|
+
useEffect6(() => {
|
|
2120
|
+
const timer = setInterval(() => setNow(Date.now()), 1e4);
|
|
2121
|
+
return () => clearInterval(timer);
|
|
2122
|
+
}, []);
|
|
2033
2123
|
const loadMore = () => {
|
|
2034
2124
|
if (loadingMore) return;
|
|
2035
2125
|
setLoadingMore(true);
|
|
@@ -2063,23 +2153,120 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
2063
2153
|
] }) : null,
|
|
2064
2154
|
/* @__PURE__ */ jsxs6("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
|
|
2065
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,
|
|
2066
|
-
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)),
|
|
2067
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
|
|
2068
2158
|
] })
|
|
2069
2159
|
] });
|
|
2070
2160
|
}
|
|
2071
2161
|
|
|
2072
|
-
// 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";
|
|
2073
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";
|
|
2074
2261
|
var pendingMessageMemory = /* @__PURE__ */ new Map();
|
|
2075
2262
|
var messengerViewMemory = /* @__PURE__ */ new Map();
|
|
2076
2263
|
var newChatMemory = /* @__PURE__ */ new Map();
|
|
2077
|
-
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"]);
|
|
2078
2265
|
function Receipt({ state, adapter }) {
|
|
2079
2266
|
const receipt = state.reductionReceipt;
|
|
2080
|
-
if (receipt) return /* @__PURE__ */
|
|
2081
|
-
/* @__PURE__ */
|
|
2082
|
-
/* @__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: [
|
|
2083
2270
|
receipt.sourceTokens.toLocaleString(),
|
|
2084
2271
|
" \u2192 ",
|
|
2085
2272
|
receipt.reducedTokens.toLocaleString(),
|
|
@@ -2088,36 +2275,36 @@ function Receipt({ state, adapter }) {
|
|
|
2088
2275
|
"\xD7 \xB7 reversible"
|
|
2089
2276
|
] })
|
|
2090
2277
|
] }) });
|
|
2091
|
-
if (state.exportReceipt) return /* @__PURE__ */
|
|
2092
|
-
/* @__PURE__ */
|
|
2093
|
-
/* @__PURE__ */
|
|
2278
|
+
if (state.exportReceipt) return /* @__PURE__ */ jsxs8("div", { class: "scui-receipt", children: [
|
|
2279
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
2280
|
+
/* @__PURE__ */ jsxs8("strong", { children: [
|
|
2094
2281
|
"Lossless export ready \xB7 ",
|
|
2095
2282
|
harnessDisplayName(state.exportReceipt.targetHarness)
|
|
2096
2283
|
] }),
|
|
2097
|
-
/* @__PURE__ */
|
|
2284
|
+
/* @__PURE__ */ jsxs8("small", { children: [
|
|
2098
2285
|
state.exportReceipt.path,
|
|
2099
2286
|
" \xB7 ",
|
|
2100
2287
|
state.exportReceipt.files,
|
|
2101
2288
|
" files"
|
|
2102
2289
|
] })
|
|
2103
2290
|
] }),
|
|
2104
|
-
/* @__PURE__ */
|
|
2291
|
+
/* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter.copyText?.(state.exportReceipt.path), children: "Copy path" })
|
|
2105
2292
|
] });
|
|
2106
|
-
if (state.terminalHandoff) return /* @__PURE__ */
|
|
2107
|
-
/* @__PURE__ */
|
|
2108
|
-
/* @__PURE__ */
|
|
2109
|
-
/* @__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 })
|
|
2110
2297
|
] }),
|
|
2111
|
-
/* @__PURE__ */
|
|
2298
|
+
/* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter.copyText?.(terminalCommand(state.terminalHandoff)), children: "Copy command" })
|
|
2112
2299
|
] });
|
|
2113
2300
|
return null;
|
|
2114
2301
|
}
|
|
2115
|
-
function ConversationActions({ state, adapter, actionPending }) {
|
|
2116
|
-
const [open, setOpen] =
|
|
2117
|
-
const root =
|
|
2118
|
-
const panel =
|
|
2119
|
-
const trigger =
|
|
2120
|
-
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();
|
|
2121
2308
|
const targets = state.harnesses.filter((item) => item.startable);
|
|
2122
2309
|
const groups = [
|
|
2123
2310
|
{
|
|
@@ -2125,7 +2312,8 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2125
2312
|
items: [
|
|
2126
2313
|
state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
|
|
2127
2314
|
state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
|
|
2128
|
-
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
|
|
2129
2317
|
].filter(Boolean)
|
|
2130
2318
|
},
|
|
2131
2319
|
{
|
|
@@ -2137,7 +2325,7 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2137
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 } })) : []
|
|
2138
2326
|
}
|
|
2139
2327
|
].filter((group) => group.items.length);
|
|
2140
|
-
|
|
2328
|
+
useEffect8(() => {
|
|
2141
2329
|
if (!open) return;
|
|
2142
2330
|
panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
|
|
2143
2331
|
const dismiss = (event) => {
|
|
@@ -2156,9 +2344,10 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2156
2344
|
document.removeEventListener("pointerdown", dismiss, true);
|
|
2157
2345
|
};
|
|
2158
2346
|
}, [open]);
|
|
2159
|
-
const dispatch = (
|
|
2347
|
+
const dispatch = (item) => {
|
|
2160
2348
|
setOpen(false);
|
|
2161
|
-
|
|
2349
|
+
if (item.onSelect) item.onSelect();
|
|
2350
|
+
else adapter.onIntent(item.intent);
|
|
2162
2351
|
};
|
|
2163
2352
|
const navigate = (event) => {
|
|
2164
2353
|
if (event.key === "Escape") {
|
|
@@ -2176,62 +2365,63 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2176
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;
|
|
2177
2366
|
items[index].focus({ preventScroll: true });
|
|
2178
2367
|
};
|
|
2179
|
-
return /* @__PURE__ */
|
|
2368
|
+
return /* @__PURE__ */ jsxs8("div", { class: "scui-menu", ref: root, onBlur: (event) => {
|
|
2180
2369
|
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
2181
2370
|
}, children: [
|
|
2182
|
-
/* @__PURE__ */
|
|
2183
|
-
open ? /* @__PURE__ */
|
|
2184
|
-
/* @__PURE__ */
|
|
2185
|
-
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))
|
|
2186
2375
|
] }, group.label)) }) : null
|
|
2187
2376
|
] });
|
|
2188
2377
|
}
|
|
2189
|
-
function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
|
|
2190
|
-
const back =
|
|
2378
|
+
function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose, onSettings }) {
|
|
2379
|
+
const back = useRef7(null);
|
|
2191
2380
|
const harness = state.attached?.harness ?? state.harness;
|
|
2192
2381
|
const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
|
|
2193
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";
|
|
2194
|
-
const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
|
|
2195
|
-
|
|
2383
|
+
const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce || state.interopSettings || state.interopSettingsError;
|
|
2384
|
+
useEffect8(() => {
|
|
2196
2385
|
if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
|
|
2197
2386
|
}, []);
|
|
2198
|
-
return /* @__PURE__ */
|
|
2199
|
-
/* @__PURE__ */
|
|
2200
|
-
/* @__PURE__ */
|
|
2201
|
-
/* @__PURE__ */
|
|
2202
|
-
/* @__PURE__ */
|
|
2203
|
-
/* @__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: [
|
|
2204
2393
|
harnessDisplayName(harness),
|
|
2205
2394
|
" \xB7 ",
|
|
2206
2395
|
status
|
|
2207
2396
|
] })
|
|
2208
2397
|
] }),
|
|
2209
|
-
menu ? /* @__PURE__ */
|
|
2210
|
-
/* @__PURE__ */
|
|
2211
|
-
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
|
|
2212
2401
|
] });
|
|
2213
2402
|
}
|
|
2214
2403
|
function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
|
|
2215
2404
|
const Header = slots.header;
|
|
2216
2405
|
const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
|
|
2217
|
-
const [pending, setPendingState] =
|
|
2218
|
-
const [pendingAction, setPendingAction] =
|
|
2219
|
-
const [restoreDraft, setRestoreDraft] =
|
|
2220
|
-
const
|
|
2221
|
-
const
|
|
2222
|
-
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());
|
|
2223
2413
|
const setPending = (update) => setPendingState((current) => {
|
|
2224
2414
|
const next = typeof update === "function" ? update(current) : update;
|
|
2225
2415
|
if (next) boundedSet(pendingMessageMemory, memoryKey, next);
|
|
2226
2416
|
else pendingMessageMemory.delete(memoryKey);
|
|
2227
2417
|
return next;
|
|
2228
2418
|
});
|
|
2229
|
-
|
|
2419
|
+
useEffect8(() => {
|
|
2230
2420
|
setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
|
|
2231
2421
|
setPendingAction(null);
|
|
2232
2422
|
setRestoreDraft(null);
|
|
2233
2423
|
}, [memoryKey]);
|
|
2234
|
-
|
|
2424
|
+
useEffect8(() => {
|
|
2235
2425
|
if (!pending) return;
|
|
2236
2426
|
if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
|
|
2237
2427
|
setPending(null);
|
|
@@ -2260,7 +2450,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2260
2450
|
setRestoreDraft({ id: restoreSequence.current, text: pending.text, context: pending.context, images: pending.images });
|
|
2261
2451
|
setPending({ ...pending, status: "editing" });
|
|
2262
2452
|
};
|
|
2263
|
-
|
|
2453
|
+
useEffect8(() => {
|
|
2264
2454
|
const key = state.attached?.key;
|
|
2265
2455
|
if (!key || !state.attention.some((item) => item.key === key)) {
|
|
2266
2456
|
if (key) acknowledged.current.delete(key);
|
|
@@ -2271,7 +2461,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2271
2461
|
adapter.onIntent({ action: "ack", key });
|
|
2272
2462
|
}
|
|
2273
2463
|
}, [adapter, state.attached?.key, state.attention]);
|
|
2274
|
-
const trackedAdapter =
|
|
2464
|
+
const trackedAdapter = useMemo3(() => ({
|
|
2275
2465
|
...adapter,
|
|
2276
2466
|
onIntent(intent) {
|
|
2277
2467
|
if (!TRACKED_ACTIONS.has(intent.action)) return adapter.onIntent(intent);
|
|
@@ -2295,7 +2485,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2295
2485
|
return result;
|
|
2296
2486
|
}
|
|
2297
2487
|
}), [adapter, state.error]);
|
|
2298
|
-
|
|
2488
|
+
useEffect8(() => {
|
|
2299
2489
|
if (!pendingAction) return;
|
|
2300
2490
|
if (state.operation && !pendingAction.seenOperation) {
|
|
2301
2491
|
setPendingAction({ ...pendingAction, seenOperation: true });
|
|
@@ -2308,44 +2498,48 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2308
2498
|
const actionLabel = operationLabel(action);
|
|
2309
2499
|
const actionState = action ? { ...state, operation: action, canInterrupt: false, canRespond: false } : state;
|
|
2310
2500
|
const unreadAfterMessages = state.attention.find((item) => item.key === state.attached?.key)?.afterMessages ?? null;
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
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", {}),
|
|
2315
2507
|
actionLabel
|
|
2316
2508
|
] }) : null,
|
|
2317
|
-
state.error ? /* @__PURE__ */
|
|
2318
|
-
/* @__PURE__ */
|
|
2319
|
-
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
|
|
2320
2512
|
] }) : null,
|
|
2321
|
-
/* @__PURE__ */
|
|
2322
|
-
/* @__PURE__ */
|
|
2323
|
-
/* @__PURE__ */
|
|
2324
|
-
|
|
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
|
|
2325
2519
|
] });
|
|
2326
2520
|
}
|
|
2327
2521
|
function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey }) {
|
|
2328
2522
|
const startable = state.harnesses.filter((item) => item.startable);
|
|
2329
2523
|
const startableKey = startable.map((item) => item.id).join("\0");
|
|
2330
2524
|
const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: "", context: [], images: [] };
|
|
2331
|
-
const [harness, setHarness] =
|
|
2332
|
-
const [draft, setDraft] =
|
|
2333
|
-
const [context, setContext] =
|
|
2334
|
-
const [images, setImages] =
|
|
2335
|
-
const [starting, setStarting] =
|
|
2336
|
-
const [picking, setPicking] =
|
|
2337
|
-
const [dragging, setDragging] =
|
|
2338
|
-
const [pickerError, setPickerError] =
|
|
2339
|
-
const startSequence =
|
|
2340
|
-
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);
|
|
2341
2535
|
useAutosizeTextarea(textarea, draft);
|
|
2342
|
-
|
|
2536
|
+
useEffect8(() => {
|
|
2343
2537
|
if (startable.some((item) => item.id === harness)) return;
|
|
2344
2538
|
const next = startable[0]?.id ?? "";
|
|
2345
2539
|
setHarness(next);
|
|
2346
2540
|
boundedSet(newChatMemory, memoryKey, { harness: next, draft, context, images });
|
|
2347
2541
|
}, [harness, startableKey]);
|
|
2348
|
-
|
|
2542
|
+
useEffect8(() => {
|
|
2349
2543
|
if (!starting) return;
|
|
2350
2544
|
const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
|
|
2351
2545
|
const beganWorking = !starting.busy && state.busy;
|
|
@@ -2353,10 +2547,10 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2353
2547
|
newChatMemory.delete(memoryKey);
|
|
2354
2548
|
onStarted();
|
|
2355
2549
|
}, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
|
|
2356
|
-
|
|
2550
|
+
useEffect8(() => {
|
|
2357
2551
|
if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
|
|
2358
2552
|
}, [starting, state.busy, state.error, state.operation]);
|
|
2359
|
-
|
|
2553
|
+
useEffect8(() => {
|
|
2360
2554
|
textarea.current?.focus({ preventScroll: true });
|
|
2361
2555
|
}, []);
|
|
2362
2556
|
const pickContext = () => {
|
|
@@ -2418,49 +2612,49 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2418
2612
|
Promise.resolve(result).catch(() => setStarting((current) => current?.id === id ? null : current));
|
|
2419
2613
|
}
|
|
2420
2614
|
};
|
|
2421
|
-
return /* @__PURE__ */
|
|
2422
|
-
/* @__PURE__ */
|
|
2423
|
-
/* @__PURE__ */
|
|
2424
|
-
/* @__PURE__ */
|
|
2425
|
-
/* @__PURE__ */
|
|
2426
|
-
/* @__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" })
|
|
2427
2621
|
] }),
|
|
2428
|
-
onClose ? /* @__PURE__ */
|
|
2622
|
+
onClose ? /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx9(UiIcon, { name: "close", size: 18 }) }) : null
|
|
2429
2623
|
] }),
|
|
2430
|
-
starting ? /* @__PURE__ */
|
|
2431
|
-
/* @__PURE__ */
|
|
2624
|
+
starting ? /* @__PURE__ */ jsxs8("div", { class: "scui-operation", role: "status", children: [
|
|
2625
|
+
/* @__PURE__ */ jsx9("i", {}),
|
|
2432
2626
|
operationLabel("start")
|
|
2433
2627
|
] }) : null,
|
|
2434
|
-
/* @__PURE__ */
|
|
2435
|
-
/* @__PURE__ */
|
|
2436
|
-
/* @__PURE__ */
|
|
2437
|
-
/* @__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." })
|
|
2438
2632
|
] }),
|
|
2439
|
-
/* @__PURE__ */
|
|
2440
|
-
/* @__PURE__ */
|
|
2441
|
-
/* @__PURE__ */
|
|
2442
|
-
/* @__PURE__ */
|
|
2443
|
-
/* @__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) => {
|
|
2444
2638
|
const value = event.currentTarget.value;
|
|
2445
2639
|
setHarness(value);
|
|
2446
2640
|
boundedSet(newChatMemory, memoryKey, { harness: value, draft, context, images });
|
|
2447
|
-
}, children: state.harnesses.map((item) => /* @__PURE__ */
|
|
2641
|
+
}, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs8("option", { value: item.id, disabled: !item.startable, children: [
|
|
2448
2642
|
item.label,
|
|
2449
2643
|
item.startable ? "" : " \xB7 unavailable"
|
|
2450
2644
|
] }, item.id)) })
|
|
2451
2645
|
] }),
|
|
2452
|
-
/* @__PURE__ */
|
|
2646
|
+
/* @__PURE__ */ jsx9(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
|
|
2453
2647
|
const next = items.filter((_, itemIndex) => itemIndex !== index);
|
|
2454
2648
|
boundedSet(newChatMemory, memoryKey, { harness, draft, context, images: next });
|
|
2455
2649
|
return next;
|
|
2456
2650
|
}) }),
|
|
2457
|
-
/* @__PURE__ */
|
|
2651
|
+
/* @__PURE__ */ jsx9(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
|
|
2458
2652
|
const next = items.filter((_, itemIndex) => itemIndex !== index);
|
|
2459
2653
|
boundedSet(newChatMemory, memoryKey, { harness, draft, context: next, images });
|
|
2460
2654
|
return next;
|
|
2461
2655
|
}) }),
|
|
2462
|
-
pickerError ? /* @__PURE__ */
|
|
2463
|
-
/* @__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) => {
|
|
2464
2658
|
if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
|
|
2465
2659
|
event.preventDefault();
|
|
2466
2660
|
setDragging(true);
|
|
@@ -2470,8 +2664,8 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2470
2664
|
}, onDragLeave: (event) => {
|
|
2471
2665
|
if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
|
|
2472
2666
|
}, onDrop: dropImages, children: [
|
|
2473
|
-
adapter.pickContext ? /* @__PURE__ */
|
|
2474
|
-
/* @__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) => {
|
|
2475
2669
|
const value = event.currentTarget.value;
|
|
2476
2670
|
setDraft(value);
|
|
2477
2671
|
boundedSet(newChatMemory, memoryKey, { harness, draft: value, context, images });
|
|
@@ -2481,23 +2675,23 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2481
2675
|
send();
|
|
2482
2676
|
}
|
|
2483
2677
|
} }),
|
|
2484
|
-
/* @__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 }) }) })
|
|
2485
2679
|
] })
|
|
2486
2680
|
] })
|
|
2487
2681
|
] });
|
|
2488
2682
|
}
|
|
2489
2683
|
function SupercodeMessenger({ state: stateInput, adapter, class: className = "", initialView, labels, components = {}, slots = {} }) {
|
|
2490
|
-
const state =
|
|
2684
|
+
const state = useMemo3(() => normalizeUiState(stateInput), [stateInput]);
|
|
2491
2685
|
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
2492
2686
|
const memoryKey = state.workspace || "@default";
|
|
2493
|
-
const [view, setViewState] =
|
|
2494
|
-
const [opening, setOpening] =
|
|
2495
|
-
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);
|
|
2496
2690
|
const setView = (next) => {
|
|
2497
2691
|
boundedSet(messengerViewMemory, memoryKey, next);
|
|
2498
2692
|
setViewState(next);
|
|
2499
2693
|
};
|
|
2500
|
-
|
|
2694
|
+
useEffect8(() => {
|
|
2501
2695
|
if (!opening) return;
|
|
2502
2696
|
if (state.attached?.key === opening.key) {
|
|
2503
2697
|
setOpening(null);
|
|
@@ -2513,40 +2707,40 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
|
|
|
2513
2707
|
};
|
|
2514
2708
|
const close = () => adapter.onClose?.();
|
|
2515
2709
|
const Footer = slots.footer;
|
|
2516
|
-
return /* @__PURE__ */
|
|
2517
|
-
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: () => {
|
|
2518
2712
|
setListFocus("@new");
|
|
2519
2713
|
setView("new");
|
|
2520
2714
|
}, onClose: adapter.onClose ? close : void 0, components, labels: copy, memoryKey }) : null,
|
|
2521
|
-
view === "new" ? /* @__PURE__ */
|
|
2522
|
-
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: () => {
|
|
2523
2717
|
setListFocus(state.attached?.key ?? listFocus);
|
|
2524
2718
|
setView("list");
|
|
2525
2719
|
}, onNew: () => {
|
|
2526
2720
|
setListFocus("@new");
|
|
2527
2721
|
setView("new");
|
|
2528
2722
|
}, onClose: adapter.onClose ? close : void 0, components, slots, labels: copy }) : null,
|
|
2529
|
-
opening ? /* @__PURE__ */
|
|
2530
|
-
/* @__PURE__ */
|
|
2531
|
-
/* @__PURE__ */
|
|
2532
|
-
/* @__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: [
|
|
2533
2727
|
"Opening ",
|
|
2534
2728
|
sessionDisplayName(opening)
|
|
2535
2729
|
] }),
|
|
2536
|
-
/* @__PURE__ */
|
|
2730
|
+
/* @__PURE__ */ jsx9("small", { children: "Loading the latest transcript window\u2026" })
|
|
2537
2731
|
] }),
|
|
2538
|
-
/* @__PURE__ */
|
|
2732
|
+
/* @__PURE__ */ jsx9("i", {})
|
|
2539
2733
|
] }) : null,
|
|
2540
|
-
Footer ? /* @__PURE__ */
|
|
2734
|
+
Footer ? /* @__PURE__ */ jsx9(Footer, { state, adapter, value: copy }) : null
|
|
2541
2735
|
] });
|
|
2542
2736
|
}
|
|
2543
2737
|
|
|
2544
2738
|
// src/embed.jsx
|
|
2545
|
-
import { jsx as
|
|
2739
|
+
import { jsx as jsx10 } from "preact/jsx-runtime";
|
|
2546
2740
|
function mountSupercodeMessenger(element, options) {
|
|
2547
2741
|
if (!(element instanceof Element)) throw new TypeError("mountSupercodeMessenger requires an Element");
|
|
2548
2742
|
let state = options.state;
|
|
2549
|
-
const draw = () => render(/* @__PURE__ */
|
|
2743
|
+
const draw = () => render(/* @__PURE__ */ jsx10(SupercodeMessenger, { ...options, state }), element);
|
|
2550
2744
|
options.adapter.onIntent({ action: "mounted" });
|
|
2551
2745
|
draw();
|
|
2552
2746
|
return {
|