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