@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/components.mjs
CHANGED
|
@@ -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) {
|
|
@@ -1092,7 +1171,7 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
1092
1171
|
if (state.mode !== "mirror" || state.canSend) return null;
|
|
1093
1172
|
const attached = state.attached;
|
|
1094
1173
|
const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
|
|
1095
|
-
const activeElsewhere = row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
|
|
1174
|
+
const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
|
|
1096
1175
|
const resume = canContinueHere(state);
|
|
1097
1176
|
const join = state.canAttach;
|
|
1098
1177
|
const branch = state.canBranch;
|
|
@@ -1986,7 +2065,7 @@ function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
|
|
|
1986
2065
|
}
|
|
1987
2066
|
|
|
1988
2067
|
// src/messenger.jsx
|
|
1989
|
-
import { useEffect as
|
|
2068
|
+
import { useEffect as useEffect8, useId as useId3, useMemo as useMemo3, useRef as useRef7, useState as useState6 } from "preact/hooks";
|
|
1990
2069
|
|
|
1991
2070
|
// src/sessions.jsx
|
|
1992
2071
|
import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useRef as useRef5, useState as useState4 } from "preact/hooks";
|
|
@@ -2003,28 +2082,35 @@ function sessionPathParts(value) {
|
|
|
2003
2082
|
trailing: complete.slice(boundary + 1)
|
|
2004
2083
|
};
|
|
2005
2084
|
}
|
|
2006
|
-
function SessionRow({ row, state, onOpen }) {
|
|
2085
|
+
function SessionRow({ row, state, onOpen, now = Date.now() }) {
|
|
2007
2086
|
const activity = sessionActivity(state, row);
|
|
2087
|
+
const working = activity === "working";
|
|
2008
2088
|
const attention = state.attention.find((item) => item.key === row.key);
|
|
2009
2089
|
const title = sessionDisplayName(row);
|
|
2010
2090
|
const path = sessionPathParts(row.cwd);
|
|
2011
2091
|
const preview = row.preview || attention?.preview || "";
|
|
2012
2092
|
const unreadCount = attention?.unreadCount ?? 0;
|
|
2013
2093
|
const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
|
|
2014
|
-
|
|
2094
|
+
const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
|
|
2095
|
+
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: [
|
|
2015
2096
|
/* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
|
|
2016
2097
|
/* @__PURE__ */ jsxs6("span", { class: "scui-session-copy", children: [
|
|
2017
2098
|
/* @__PURE__ */ jsxs6("span", { class: "scui-session-title", children: [
|
|
2018
2099
|
/* @__PURE__ */ jsx7("strong", { children: title }),
|
|
2019
|
-
|
|
2100
|
+
/* @__PURE__ */ jsx7("span", { class: "scui-session-meta", children: age ? /* @__PURE__ */ jsx7("time", { children: age }) : null })
|
|
2020
2101
|
] }),
|
|
2021
2102
|
path.complete ? /* @__PURE__ */ jsxs6("small", { class: "scui-session-path", title: row.cwd, children: [
|
|
2022
2103
|
/* @__PURE__ */ jsx7("span", { class: "scui-session-path-leading", children: path.leading }),
|
|
2023
2104
|
path.separator ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-separator", children: path.separator }) : null,
|
|
2024
2105
|
path.trailing ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-trailing", children: path.trailing }) : null
|
|
2025
2106
|
] }) : null,
|
|
2026
|
-
preview || unreadCount ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-preview", children: [
|
|
2027
|
-
|
|
2107
|
+
working || preview || unreadCount ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-preview", children: [
|
|
2108
|
+
working ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-working", "aria-hidden": "true", children: [
|
|
2109
|
+
/* @__PURE__ */ jsx7("i", {}),
|
|
2110
|
+
/* @__PURE__ */ jsx7("i", {}),
|
|
2111
|
+
/* @__PURE__ */ jsx7("i", {})
|
|
2112
|
+
] }) : null,
|
|
2113
|
+
preview || working ? /* @__PURE__ */ jsx7("small", { children: preview || "Working\u2026" }) : null,
|
|
2028
2114
|
unreadCount ? /* @__PURE__ */ jsx7("b", { "aria-label": `${unreadCount} unread messages`, children: unreadLabel }) : null
|
|
2029
2115
|
] }) : null,
|
|
2030
2116
|
state.attachError?.key === row.key ? /* @__PURE__ */ jsx7("em", { children: state.attachError.message }) : null
|
|
@@ -2035,6 +2121,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
2035
2121
|
const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
|
|
2036
2122
|
const [query, setQuery] = useState4(remembered.query);
|
|
2037
2123
|
const [loadingMore, setLoadingMore] = useState4(false);
|
|
2124
|
+
const [now, setNow] = useState4(() => Date.now());
|
|
2038
2125
|
const root = useRef5(null);
|
|
2039
2126
|
const rowScroller = useRef5(null);
|
|
2040
2127
|
const rows = filterSessions(state.sessions, query);
|
|
@@ -2050,6 +2137,10 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
2050
2137
|
useEffect6(() => {
|
|
2051
2138
|
if (loadingMore) setLoadingMore(false);
|
|
2052
2139
|
}, [state.error, state.history.hasMoreSessions, state.sessions.length]);
|
|
2140
|
+
useEffect6(() => {
|
|
2141
|
+
const timer = setInterval(() => setNow(Date.now()), 1e4);
|
|
2142
|
+
return () => clearInterval(timer);
|
|
2143
|
+
}, []);
|
|
2053
2144
|
const loadMore = () => {
|
|
2054
2145
|
if (loadingMore) return;
|
|
2055
2146
|
setLoadingMore(true);
|
|
@@ -2083,23 +2174,120 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
2083
2174
|
] }) : null,
|
|
2084
2175
|
/* @__PURE__ */ jsxs6("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
|
|
2085
2176
|
!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,
|
|
2086
|
-
rows.map((row) => /* @__PURE__ */ jsx7(Row, { value: row, row, state, adapter, onOpen }, row.key)),
|
|
2177
|
+
rows.map((row) => /* @__PURE__ */ jsx7(Row, { value: row, row, state, adapter, onOpen, now }, row.key)),
|
|
2087
2178
|
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
|
|
2088
2179
|
] })
|
|
2089
2180
|
] });
|
|
2090
2181
|
}
|
|
2091
2182
|
|
|
2092
|
-
// src/
|
|
2183
|
+
// src/settings.jsx
|
|
2184
|
+
import { useEffect as useEffect7, useId as useId2, useMemo as useMemo2, useRef as useRef6, useState as useState5 } from "preact/hooks";
|
|
2093
2185
|
import { jsx as jsx8, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
2186
|
+
function HarnessAdvisory({ state, onReview }) {
|
|
2187
|
+
const advisory = state.interopSettings?.advisories[0];
|
|
2188
|
+
if (!advisory && !state.interopSettingsError) return null;
|
|
2189
|
+
return /* @__PURE__ */ jsxs7("aside", { class: "scui-advisory", "data-severity": advisory?.severity ?? "error", role: advisory?.severity === "error" ? "alert" : "status", children: [
|
|
2190
|
+
/* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "!" }),
|
|
2191
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2192
|
+
/* @__PURE__ */ jsx8("strong", { children: advisory?.title ?? "Could not inspect harness settings" }),
|
|
2193
|
+
/* @__PURE__ */ jsx8("small", { children: advisory?.message ?? state.interopSettingsError })
|
|
2194
|
+
] }),
|
|
2195
|
+
advisory && state.canConfigureSettings ? /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => onReview(advisory.recommendation.change), children: "Review" }) : null
|
|
2196
|
+
] });
|
|
2197
|
+
}
|
|
2198
|
+
function initialValues(report, recommendedChange) {
|
|
2199
|
+
return Object.fromEntries(report.controls.map((control) => [
|
|
2200
|
+
control.key,
|
|
2201
|
+
recommendedChange?.key === control.key ? recommendedChange.value : control.configuredValue ?? control.effectiveValue ?? control.choices[0]?.value ?? null
|
|
2202
|
+
]));
|
|
2203
|
+
}
|
|
2204
|
+
function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = null }) {
|
|
2205
|
+
const report = state.interopSettings;
|
|
2206
|
+
const titleId = useId2();
|
|
2207
|
+
const panel = useRef6(null);
|
|
2208
|
+
const valuesKey = `${report?.revision ?? ""}:${recommendedChange?.key ?? ""}:${recommendedChange?.value ?? ""}`;
|
|
2209
|
+
const defaults = useMemo2(() => report ? initialValues(report, recommendedChange) : {}, [valuesKey]);
|
|
2210
|
+
const [values, setValues] = useState5(defaults);
|
|
2211
|
+
useEffect7(() => setValues(defaults), [defaults]);
|
|
2212
|
+
useEffect7(() => {
|
|
2213
|
+
panel.current?.querySelector("select, button")?.focus({ preventScroll: true });
|
|
2214
|
+
const dismiss = (event) => {
|
|
2215
|
+
if (event.key === "Escape") {
|
|
2216
|
+
event.preventDefault();
|
|
2217
|
+
onClose();
|
|
2218
|
+
}
|
|
2219
|
+
};
|
|
2220
|
+
document.addEventListener("keydown", dismiss);
|
|
2221
|
+
return () => document.removeEventListener("keydown", dismiss);
|
|
2222
|
+
}, [onClose]);
|
|
2223
|
+
if (!report) return /* @__PURE__ */ jsx8("section", { ref: panel, class: "scui-settings", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: /* @__PURE__ */ jsxs7("header", { children: [
|
|
2224
|
+
/* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close settings", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }),
|
|
2225
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2226
|
+
/* @__PURE__ */ jsx8("strong", { id: titleId, children: "Harness settings" }),
|
|
2227
|
+
/* @__PURE__ */ jsx8("small", { children: state.interopSettingsError ?? "No interoperability controls are available." })
|
|
2228
|
+
] })
|
|
2229
|
+
] }) });
|
|
2230
|
+
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 }] : []);
|
|
2231
|
+
const submit = (event) => {
|
|
2232
|
+
event.preventDefault();
|
|
2233
|
+
if (!changed.length || !state.canConfigureSettings) return;
|
|
2234
|
+
adapter.onIntent({ action: "configureHarness", harness: report.harness, changes: changed, expectedRevision: report.revision });
|
|
2235
|
+
};
|
|
2236
|
+
return /* @__PURE__ */ jsxs7("section", { ref: panel, class: "scui-settings", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [
|
|
2237
|
+
/* @__PURE__ */ jsxs7("header", { children: [
|
|
2238
|
+
/* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close settings", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }),
|
|
2239
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2240
|
+
/* @__PURE__ */ jsxs7("strong", { id: titleId, children: [
|
|
2241
|
+
harnessDisplayName(report.harness),
|
|
2242
|
+
" interoperability"
|
|
2243
|
+
] }),
|
|
2244
|
+
/* @__PURE__ */ jsx8("small", { children: "Native harness settings used by Supercode" })
|
|
2245
|
+
] })
|
|
2246
|
+
] }),
|
|
2247
|
+
/* @__PURE__ */ jsxs7("form", { onSubmit: submit, children: [
|
|
2248
|
+
report.controls.map((control) => {
|
|
2249
|
+
const choice = control.choices.find((item) => item.value === values[control.key]);
|
|
2250
|
+
const recommendation = report.advisories.map((advisory) => advisory.recommendation).find((item) => item.change.key === control.key && item.change.value === values[control.key]);
|
|
2251
|
+
const consequence = choice?.risk ?? recommendation?.consequence;
|
|
2252
|
+
return /* @__PURE__ */ jsxs7("fieldset", { disabled: !control.writable || Boolean(state.operation), children: [
|
|
2253
|
+
/* @__PURE__ */ jsxs7("label", { for: `scui-setting-${control.key}`, children: [
|
|
2254
|
+
/* @__PURE__ */ jsx8("strong", { children: control.label }),
|
|
2255
|
+
/* @__PURE__ */ jsx8("small", { children: control.description })
|
|
2256
|
+
] }),
|
|
2257
|
+
/* @__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: [
|
|
2258
|
+
control.resettable ? /* @__PURE__ */ jsx8("option", { value: "@default", children: "Use harness default" }) : null,
|
|
2259
|
+
control.choices.map((item) => /* @__PURE__ */ jsx8("option", { value: item.value, children: item.label }, item.value))
|
|
2260
|
+
] }),
|
|
2261
|
+
choice ? /* @__PURE__ */ jsx8("p", { children: choice.description }) : null,
|
|
2262
|
+
consequence ? /* @__PURE__ */ jsxs7("p", { class: "scui-setting-risk", children: [
|
|
2263
|
+
/* @__PURE__ */ jsx8("strong", { children: "Security consequence" }),
|
|
2264
|
+
consequence
|
|
2265
|
+
] }) : null,
|
|
2266
|
+
/* @__PURE__ */ jsxs7("small", { class: "scui-setting-source", children: [
|
|
2267
|
+
control.effectiveNote,
|
|
2268
|
+
control.sourcePath ? ` Source: ${control.sourcePath}` : ""
|
|
2269
|
+
] })
|
|
2270
|
+
] }, control.key);
|
|
2271
|
+
}),
|
|
2272
|
+
/* @__PURE__ */ jsxs7("footer", { children: [
|
|
2273
|
+
/* @__PURE__ */ jsx8("button", { type: "button", onClick: onClose, children: "Cancel" }),
|
|
2274
|
+
/* @__PURE__ */ jsx8("button", { type: "submit", disabled: !changed.length || !state.canConfigureSettings || Boolean(state.operation), children: state.operation === "configureHarness" ? "Applying\u2026" : "Apply changes" })
|
|
2275
|
+
] })
|
|
2276
|
+
] })
|
|
2277
|
+
] });
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
// src/messenger.jsx
|
|
2281
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "preact/jsx-runtime";
|
|
2094
2282
|
var pendingMessageMemory = /* @__PURE__ */ new Map();
|
|
2095
2283
|
var messengerViewMemory = /* @__PURE__ */ new Map();
|
|
2096
2284
|
var newChatMemory = /* @__PURE__ */ new Map();
|
|
2097
|
-
var TRACKED_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "detach", "branch", "reduce", "terminal", "export", "interrupt", "respond", "refresh", "loadEarlier", "loadSessions"]);
|
|
2285
|
+
var TRACKED_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "detach", "branch", "reduce", "terminal", "export", "interrupt", "respond", "configureHarness", "refresh", "loadEarlier", "loadSessions"]);
|
|
2098
2286
|
function Receipt({ state, adapter }) {
|
|
2099
2287
|
const receipt = state.reductionReceipt;
|
|
2100
|
-
if (receipt) return /* @__PURE__ */
|
|
2101
|
-
/* @__PURE__ */
|
|
2102
|
-
/* @__PURE__ */
|
|
2288
|
+
if (receipt) return /* @__PURE__ */ jsx9("div", { class: "scui-receipt", children: /* @__PURE__ */ jsxs8("span", { children: [
|
|
2289
|
+
/* @__PURE__ */ jsx9("strong", { children: "Reduced and verified" }),
|
|
2290
|
+
/* @__PURE__ */ jsxs8("small", { children: [
|
|
2103
2291
|
receipt.sourceTokens.toLocaleString(),
|
|
2104
2292
|
" \u2192 ",
|
|
2105
2293
|
receipt.reducedTokens.toLocaleString(),
|
|
@@ -2108,36 +2296,36 @@ function Receipt({ state, adapter }) {
|
|
|
2108
2296
|
"\xD7 \xB7 reversible"
|
|
2109
2297
|
] })
|
|
2110
2298
|
] }) });
|
|
2111
|
-
if (state.exportReceipt) return /* @__PURE__ */
|
|
2112
|
-
/* @__PURE__ */
|
|
2113
|
-
/* @__PURE__ */
|
|
2299
|
+
if (state.exportReceipt) return /* @__PURE__ */ jsxs8("div", { class: "scui-receipt", children: [
|
|
2300
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
2301
|
+
/* @__PURE__ */ jsxs8("strong", { children: [
|
|
2114
2302
|
"Lossless export ready \xB7 ",
|
|
2115
2303
|
harnessDisplayName(state.exportReceipt.targetHarness)
|
|
2116
2304
|
] }),
|
|
2117
|
-
/* @__PURE__ */
|
|
2305
|
+
/* @__PURE__ */ jsxs8("small", { children: [
|
|
2118
2306
|
state.exportReceipt.path,
|
|
2119
2307
|
" \xB7 ",
|
|
2120
2308
|
state.exportReceipt.files,
|
|
2121
2309
|
" files"
|
|
2122
2310
|
] })
|
|
2123
2311
|
] }),
|
|
2124
|
-
/* @__PURE__ */
|
|
2312
|
+
/* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter.copyText?.(state.exportReceipt.path), children: "Copy path" })
|
|
2125
2313
|
] });
|
|
2126
|
-
if (state.terminalHandoff) return /* @__PURE__ */
|
|
2127
|
-
/* @__PURE__ */
|
|
2128
|
-
/* @__PURE__ */
|
|
2129
|
-
/* @__PURE__ */
|
|
2314
|
+
if (state.terminalHandoff) return /* @__PURE__ */ jsxs8("div", { class: "scui-receipt", children: [
|
|
2315
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
2316
|
+
/* @__PURE__ */ jsx9("strong", { children: "Terminal handoff ready" }),
|
|
2317
|
+
/* @__PURE__ */ jsx9("small", { children: state.terminalHandoff.cwd })
|
|
2130
2318
|
] }),
|
|
2131
|
-
/* @__PURE__ */
|
|
2319
|
+
/* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter.copyText?.(terminalCommand(state.terminalHandoff)), children: "Copy command" })
|
|
2132
2320
|
] });
|
|
2133
2321
|
return null;
|
|
2134
2322
|
}
|
|
2135
|
-
function ConversationActions({ state, adapter, actionPending }) {
|
|
2136
|
-
const [open, setOpen] =
|
|
2137
|
-
const root =
|
|
2138
|
-
const panel =
|
|
2139
|
-
const trigger =
|
|
2140
|
-
const menuId =
|
|
2323
|
+
function ConversationActions({ state, adapter, actionPending, onSettings }) {
|
|
2324
|
+
const [open, setOpen] = useState6(false);
|
|
2325
|
+
const root = useRef7(null);
|
|
2326
|
+
const panel = useRef7(null);
|
|
2327
|
+
const trigger = useRef7(null);
|
|
2328
|
+
const menuId = useId3();
|
|
2141
2329
|
const targets = state.harnesses.filter((item) => item.startable);
|
|
2142
2330
|
const groups = [
|
|
2143
2331
|
{
|
|
@@ -2145,7 +2333,8 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2145
2333
|
items: [
|
|
2146
2334
|
state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
|
|
2147
2335
|
state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
|
|
2148
|
-
state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null
|
|
2336
|
+
state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null,
|
|
2337
|
+
state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null
|
|
2149
2338
|
].filter(Boolean)
|
|
2150
2339
|
},
|
|
2151
2340
|
{
|
|
@@ -2157,7 +2346,7 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2157
2346
|
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 } })) : []
|
|
2158
2347
|
}
|
|
2159
2348
|
].filter((group) => group.items.length);
|
|
2160
|
-
|
|
2349
|
+
useEffect8(() => {
|
|
2161
2350
|
if (!open) return;
|
|
2162
2351
|
panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
|
|
2163
2352
|
const dismiss = (event) => {
|
|
@@ -2176,9 +2365,10 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2176
2365
|
document.removeEventListener("pointerdown", dismiss, true);
|
|
2177
2366
|
};
|
|
2178
2367
|
}, [open]);
|
|
2179
|
-
const dispatch = (
|
|
2368
|
+
const dispatch = (item) => {
|
|
2180
2369
|
setOpen(false);
|
|
2181
|
-
|
|
2370
|
+
if (item.onSelect) item.onSelect();
|
|
2371
|
+
else adapter.onIntent(item.intent);
|
|
2182
2372
|
};
|
|
2183
2373
|
const navigate = (event) => {
|
|
2184
2374
|
if (event.key === "Escape") {
|
|
@@ -2196,62 +2386,63 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2196
2386
|
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;
|
|
2197
2387
|
items[index].focus({ preventScroll: true });
|
|
2198
2388
|
};
|
|
2199
|
-
return /* @__PURE__ */
|
|
2389
|
+
return /* @__PURE__ */ jsxs8("div", { class: "scui-menu", ref: root, onBlur: (event) => {
|
|
2200
2390
|
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
2201
2391
|
}, children: [
|
|
2202
|
-
/* @__PURE__ */
|
|
2203
|
-
open ? /* @__PURE__ */
|
|
2204
|
-
/* @__PURE__ */
|
|
2205
|
-
group.items.map((item) => /* @__PURE__ */
|
|
2392
|
+
/* @__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 }) }),
|
|
2393
|
+
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: [
|
|
2394
|
+
/* @__PURE__ */ jsx9("strong", { children: group.label }),
|
|
2395
|
+
group.items.map((item) => /* @__PURE__ */ jsx9("button", { role: "menuitem", type: "button", disabled: actionPending, onClick: () => dispatch(item), children: item.label }, item.key))
|
|
2206
2396
|
] }, group.label)) }) : null
|
|
2207
2397
|
] });
|
|
2208
2398
|
}
|
|
2209
|
-
function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
|
|
2210
|
-
const back =
|
|
2399
|
+
function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose, onSettings }) {
|
|
2400
|
+
const back = useRef7(null);
|
|
2211
2401
|
const harness = state.attached?.harness ?? state.harness;
|
|
2212
2402
|
const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
|
|
2213
2403
|
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";
|
|
2214
|
-
const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
|
|
2215
|
-
|
|
2404
|
+
const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce || state.interopSettings || state.interopSettingsError;
|
|
2405
|
+
useEffect8(() => {
|
|
2216
2406
|
if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
|
|
2217
2407
|
}, []);
|
|
2218
|
-
return /* @__PURE__ */
|
|
2219
|
-
/* @__PURE__ */
|
|
2220
|
-
/* @__PURE__ */
|
|
2221
|
-
/* @__PURE__ */
|
|
2222
|
-
/* @__PURE__ */
|
|
2223
|
-
/* @__PURE__ */
|
|
2408
|
+
return /* @__PURE__ */ jsxs8("header", { class: "scui-head scui-chat-head", children: [
|
|
2409
|
+
/* @__PURE__ */ jsx9("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 18 }) }),
|
|
2410
|
+
/* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 28 }),
|
|
2411
|
+
/* @__PURE__ */ jsxs8("span", { class: "scui-head-copy", children: [
|
|
2412
|
+
/* @__PURE__ */ jsx9("strong", { children: title }),
|
|
2413
|
+
/* @__PURE__ */ jsxs8("small", { children: [
|
|
2224
2414
|
harnessDisplayName(harness),
|
|
2225
2415
|
" \xB7 ",
|
|
2226
2416
|
status
|
|
2227
2417
|
] })
|
|
2228
2418
|
] }),
|
|
2229
|
-
menu ? /* @__PURE__ */
|
|
2230
|
-
/* @__PURE__ */
|
|
2231
|
-
onClose ? /* @__PURE__ */
|
|
2419
|
+
menu ? /* @__PURE__ */ jsx9(ConversationActions, { state, adapter, actionPending, onSettings }) : null,
|
|
2420
|
+
/* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx9(UiIcon, { name: "plus", size: 18 }) }),
|
|
2421
|
+
onClose ? /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx9(UiIcon, { name: "close", size: 18 }) }) : null
|
|
2232
2422
|
] });
|
|
2233
2423
|
}
|
|
2234
2424
|
function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
|
|
2235
2425
|
const Header = slots.header;
|
|
2236
2426
|
const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
|
|
2237
|
-
const [pending, setPendingState] =
|
|
2238
|
-
const [pendingAction, setPendingAction] =
|
|
2239
|
-
const [restoreDraft, setRestoreDraft] =
|
|
2240
|
-
const
|
|
2241
|
-
const
|
|
2242
|
-
const
|
|
2427
|
+
const [pending, setPendingState] = useState6(() => pendingMessageMemory.get(memoryKey) ?? null);
|
|
2428
|
+
const [pendingAction, setPendingAction] = useState6(null);
|
|
2429
|
+
const [restoreDraft, setRestoreDraft] = useState6(null);
|
|
2430
|
+
const [settings, setSettings] = useState6(null);
|
|
2431
|
+
const restoreSequence = useRef7(0);
|
|
2432
|
+
const actionSequence = useRef7(0);
|
|
2433
|
+
const acknowledged = useRef7(/* @__PURE__ */ new Set());
|
|
2243
2434
|
const setPending = (update) => setPendingState((current) => {
|
|
2244
2435
|
const next = typeof update === "function" ? update(current) : update;
|
|
2245
2436
|
if (next) boundedSet(pendingMessageMemory, memoryKey, next);
|
|
2246
2437
|
else pendingMessageMemory.delete(memoryKey);
|
|
2247
2438
|
return next;
|
|
2248
2439
|
});
|
|
2249
|
-
|
|
2440
|
+
useEffect8(() => {
|
|
2250
2441
|
setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
|
|
2251
2442
|
setPendingAction(null);
|
|
2252
2443
|
setRestoreDraft(null);
|
|
2253
2444
|
}, [memoryKey]);
|
|
2254
|
-
|
|
2445
|
+
useEffect8(() => {
|
|
2255
2446
|
if (!pending) return;
|
|
2256
2447
|
if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
|
|
2257
2448
|
setPending(null);
|
|
@@ -2280,7 +2471,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2280
2471
|
setRestoreDraft({ id: restoreSequence.current, text: pending.text, context: pending.context, images: pending.images });
|
|
2281
2472
|
setPending({ ...pending, status: "editing" });
|
|
2282
2473
|
};
|
|
2283
|
-
|
|
2474
|
+
useEffect8(() => {
|
|
2284
2475
|
const key = state.attached?.key;
|
|
2285
2476
|
if (!key || !state.attention.some((item) => item.key === key)) {
|
|
2286
2477
|
if (key) acknowledged.current.delete(key);
|
|
@@ -2291,7 +2482,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2291
2482
|
adapter.onIntent({ action: "ack", key });
|
|
2292
2483
|
}
|
|
2293
2484
|
}, [adapter, state.attached?.key, state.attention]);
|
|
2294
|
-
const trackedAdapter =
|
|
2485
|
+
const trackedAdapter = useMemo3(() => ({
|
|
2295
2486
|
...adapter,
|
|
2296
2487
|
onIntent(intent) {
|
|
2297
2488
|
if (!TRACKED_ACTIONS.has(intent.action)) return adapter.onIntent(intent);
|
|
@@ -2315,7 +2506,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2315
2506
|
return result;
|
|
2316
2507
|
}
|
|
2317
2508
|
}), [adapter, state.error]);
|
|
2318
|
-
|
|
2509
|
+
useEffect8(() => {
|
|
2319
2510
|
if (!pendingAction) return;
|
|
2320
2511
|
if (state.operation && !pendingAction.seenOperation) {
|
|
2321
2512
|
setPendingAction({ ...pendingAction, seenOperation: true });
|
|
@@ -2328,44 +2519,48 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2328
2519
|
const actionLabel = operationLabel(action);
|
|
2329
2520
|
const actionState = action ? { ...state, operation: action, canInterrupt: false, canRespond: false } : state;
|
|
2330
2521
|
const unreadAfterMessages = state.attention.find((item) => item.key === state.attached?.key)?.afterMessages ?? null;
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2522
|
+
const Advisory = components.HarnessAdvisory ?? HarnessAdvisory;
|
|
2523
|
+
const SettingsPanel = components.HarnessSettingsPanel ?? HarnessSettingsPanel;
|
|
2524
|
+
return /* @__PURE__ */ jsxs8("section", { class: "scui-chat", children: [
|
|
2525
|
+
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 }) }),
|
|
2526
|
+
actionLabel ? /* @__PURE__ */ jsxs8("div", { class: "scui-operation", role: "status", children: [
|
|
2527
|
+
/* @__PURE__ */ jsx9("i", {}),
|
|
2335
2528
|
actionLabel
|
|
2336
2529
|
] }) : null,
|
|
2337
|
-
state.error ? /* @__PURE__ */
|
|
2338
|
-
/* @__PURE__ */
|
|
2339
|
-
state.recoverable ? /* @__PURE__ */
|
|
2530
|
+
state.error ? /* @__PURE__ */ jsxs8("div", { class: "scui-error", role: "alert", children: [
|
|
2531
|
+
/* @__PURE__ */ jsx9("span", { children: state.error }),
|
|
2532
|
+
state.recoverable ? /* @__PURE__ */ jsx9("button", { type: "button", disabled: Boolean(action), onClick: () => trackedAdapter.onIntent({ action: "refresh" }), children: "Retry" }) : null
|
|
2340
2533
|
] }) : null,
|
|
2341
|
-
/* @__PURE__ */
|
|
2342
|
-
/* @__PURE__ */
|
|
2343
|
-
/* @__PURE__ */
|
|
2344
|
-
|
|
2534
|
+
/* @__PURE__ */ jsx9(Receipt, { state, adapter }),
|
|
2535
|
+
/* @__PURE__ */ jsx9(Conversation, { state: actionState, adapter: trackedAdapter, components, slots, memoryKey, pending: pendingMessage, unreadAfterMessages }, memoryKey),
|
|
2536
|
+
/* @__PURE__ */ jsx9(ContinuationBar, { state: actionState, adapter: trackedAdapter, labels }),
|
|
2537
|
+
/* @__PURE__ */ jsx9(Advisory, { state: actionState, adapter: trackedAdapter, value: state.interopSettings?.advisories[0] ?? null, onReview: (recommendedChange) => setSettings({ recommendedChange }) }),
|
|
2538
|
+
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,
|
|
2539
|
+
settings ? /* @__PURE__ */ jsx9(SettingsPanel, { state: actionState, adapter: trackedAdapter, value: state.interopSettings, recommendedChange: settings.recommendedChange, onClose: () => setSettings(null) }) : null
|
|
2345
2540
|
] });
|
|
2346
2541
|
}
|
|
2347
2542
|
function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey }) {
|
|
2348
2543
|
const startable = state.harnesses.filter((item) => item.startable);
|
|
2349
2544
|
const startableKey = startable.map((item) => item.id).join("\0");
|
|
2350
2545
|
const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: "", context: [], images: [] };
|
|
2351
|
-
const [harness, setHarness] =
|
|
2352
|
-
const [draft, setDraft] =
|
|
2353
|
-
const [context, setContext] =
|
|
2354
|
-
const [images, setImages] =
|
|
2355
|
-
const [starting, setStarting] =
|
|
2356
|
-
const [picking, setPicking] =
|
|
2357
|
-
const [dragging, setDragging] =
|
|
2358
|
-
const [pickerError, setPickerError] =
|
|
2359
|
-
const startSequence =
|
|
2360
|
-
const textarea =
|
|
2546
|
+
const [harness, setHarness] = useState6(remembered.harness);
|
|
2547
|
+
const [draft, setDraft] = useState6(remembered.draft);
|
|
2548
|
+
const [context, setContext] = useState6(remembered.context);
|
|
2549
|
+
const [images, setImages] = useState6(remembered.images ?? []);
|
|
2550
|
+
const [starting, setStarting] = useState6(null);
|
|
2551
|
+
const [picking, setPicking] = useState6(false);
|
|
2552
|
+
const [dragging, setDragging] = useState6(false);
|
|
2553
|
+
const [pickerError, setPickerError] = useState6(null);
|
|
2554
|
+
const startSequence = useRef7(0);
|
|
2555
|
+
const textarea = useRef7(null);
|
|
2361
2556
|
useAutosizeTextarea(textarea, draft);
|
|
2362
|
-
|
|
2557
|
+
useEffect8(() => {
|
|
2363
2558
|
if (startable.some((item) => item.id === harness)) return;
|
|
2364
2559
|
const next = startable[0]?.id ?? "";
|
|
2365
2560
|
setHarness(next);
|
|
2366
2561
|
boundedSet(newChatMemory, memoryKey, { harness: next, draft, context, images });
|
|
2367
2562
|
}, [harness, startableKey]);
|
|
2368
|
-
|
|
2563
|
+
useEffect8(() => {
|
|
2369
2564
|
if (!starting) return;
|
|
2370
2565
|
const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
|
|
2371
2566
|
const beganWorking = !starting.busy && state.busy;
|
|
@@ -2373,10 +2568,10 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2373
2568
|
newChatMemory.delete(memoryKey);
|
|
2374
2569
|
onStarted();
|
|
2375
2570
|
}, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
|
|
2376
|
-
|
|
2571
|
+
useEffect8(() => {
|
|
2377
2572
|
if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
|
|
2378
2573
|
}, [starting, state.busy, state.error, state.operation]);
|
|
2379
|
-
|
|
2574
|
+
useEffect8(() => {
|
|
2380
2575
|
textarea.current?.focus({ preventScroll: true });
|
|
2381
2576
|
}, []);
|
|
2382
2577
|
const pickContext = () => {
|
|
@@ -2438,49 +2633,49 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2438
2633
|
Promise.resolve(result).catch(() => setStarting((current) => current?.id === id ? null : current));
|
|
2439
2634
|
}
|
|
2440
2635
|
};
|
|
2441
|
-
return /* @__PURE__ */
|
|
2442
|
-
/* @__PURE__ */
|
|
2443
|
-
/* @__PURE__ */
|
|
2444
|
-
/* @__PURE__ */
|
|
2445
|
-
/* @__PURE__ */
|
|
2446
|
-
/* @__PURE__ */
|
|
2636
|
+
return /* @__PURE__ */ jsxs8("section", { class: "scui-chat", children: [
|
|
2637
|
+
/* @__PURE__ */ jsxs8("header", { class: "scui-head", children: [
|
|
2638
|
+
/* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Back", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 18 }) }),
|
|
2639
|
+
/* @__PURE__ */ jsxs8("span", { class: "scui-head-copy", children: [
|
|
2640
|
+
/* @__PURE__ */ jsx9("strong", { children: labels.newChat }),
|
|
2641
|
+
/* @__PURE__ */ jsx9("small", { children: "No session is created until you send" })
|
|
2447
2642
|
] }),
|
|
2448
|
-
onClose ? /* @__PURE__ */
|
|
2643
|
+
onClose ? /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx9(UiIcon, { name: "close", size: 18 }) }) : null
|
|
2449
2644
|
] }),
|
|
2450
|
-
starting ? /* @__PURE__ */
|
|
2451
|
-
/* @__PURE__ */
|
|
2645
|
+
starting ? /* @__PURE__ */ jsxs8("div", { class: "scui-operation", role: "status", children: [
|
|
2646
|
+
/* @__PURE__ */ jsx9("i", {}),
|
|
2452
2647
|
operationLabel("start")
|
|
2453
2648
|
] }) : null,
|
|
2454
|
-
/* @__PURE__ */
|
|
2455
|
-
/* @__PURE__ */
|
|
2456
|
-
/* @__PURE__ */
|
|
2457
|
-
/* @__PURE__ */
|
|
2649
|
+
/* @__PURE__ */ jsxs8("div", { class: "scui-new", children: [
|
|
2650
|
+
/* @__PURE__ */ jsx9("span", { "aria-hidden": "true", children: "\u2726" }),
|
|
2651
|
+
/* @__PURE__ */ jsx9("strong", { children: "What should the agent build or fix?" }),
|
|
2652
|
+
/* @__PURE__ */ jsx9("small", { children: "Choose a coding harness and send the first message." })
|
|
2458
2653
|
] }),
|
|
2459
|
-
/* @__PURE__ */
|
|
2460
|
-
/* @__PURE__ */
|
|
2461
|
-
/* @__PURE__ */
|
|
2462
|
-
/* @__PURE__ */
|
|
2463
|
-
/* @__PURE__ */
|
|
2654
|
+
/* @__PURE__ */ jsxs8("div", { class: "scui-compose", children: [
|
|
2655
|
+
/* @__PURE__ */ jsxs8("label", { class: "scui-harness-picker", children: [
|
|
2656
|
+
/* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 24 }),
|
|
2657
|
+
/* @__PURE__ */ jsx9("span", { children: "Coding harness" }),
|
|
2658
|
+
/* @__PURE__ */ jsx9("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
|
|
2464
2659
|
const value = event.currentTarget.value;
|
|
2465
2660
|
setHarness(value);
|
|
2466
2661
|
boundedSet(newChatMemory, memoryKey, { harness: value, draft, context, images });
|
|
2467
|
-
}, children: state.harnesses.map((item) => /* @__PURE__ */
|
|
2662
|
+
}, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs8("option", { value: item.id, disabled: !item.startable, children: [
|
|
2468
2663
|
item.label,
|
|
2469
2664
|
item.startable ? "" : " \xB7 unavailable"
|
|
2470
2665
|
] }, item.id)) })
|
|
2471
2666
|
] }),
|
|
2472
|
-
/* @__PURE__ */
|
|
2667
|
+
/* @__PURE__ */ jsx9(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
|
|
2473
2668
|
const next = items.filter((_, itemIndex) => itemIndex !== index);
|
|
2474
2669
|
boundedSet(newChatMemory, memoryKey, { harness, draft, context, images: next });
|
|
2475
2670
|
return next;
|
|
2476
2671
|
}) }),
|
|
2477
|
-
/* @__PURE__ */
|
|
2672
|
+
/* @__PURE__ */ jsx9(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
|
|
2478
2673
|
const next = items.filter((_, itemIndex) => itemIndex !== index);
|
|
2479
2674
|
boundedSet(newChatMemory, memoryKey, { harness, draft, context: next, images });
|
|
2480
2675
|
return next;
|
|
2481
2676
|
}) }),
|
|
2482
|
-
pickerError ? /* @__PURE__ */
|
|
2483
|
-
/* @__PURE__ */
|
|
2677
|
+
pickerError ? /* @__PURE__ */ jsx9("small", { class: "scui-context-error", role: "alert", children: pickerError }) : null,
|
|
2678
|
+
/* @__PURE__ */ jsxs8("div", { class: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
2484
2679
|
if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
|
|
2485
2680
|
event.preventDefault();
|
|
2486
2681
|
setDragging(true);
|
|
@@ -2490,8 +2685,8 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2490
2685
|
}, onDragLeave: (event) => {
|
|
2491
2686
|
if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
|
|
2492
2687
|
}, onDrop: dropImages, children: [
|
|
2493
|
-
adapter.pickContext ? /* @__PURE__ */
|
|
2494
|
-
/* @__PURE__ */
|
|
2688
|
+
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,
|
|
2689
|
+
/* @__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) => {
|
|
2495
2690
|
const value = event.currentTarget.value;
|
|
2496
2691
|
setDraft(value);
|
|
2497
2692
|
boundedSet(newChatMemory, memoryKey, { harness, draft: value, context, images });
|
|
@@ -2501,23 +2696,23 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2501
2696
|
send();
|
|
2502
2697
|
}
|
|
2503
2698
|
} }),
|
|
2504
|
-
/* @__PURE__ */
|
|
2699
|
+
/* @__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 }) }) })
|
|
2505
2700
|
] })
|
|
2506
2701
|
] })
|
|
2507
2702
|
] });
|
|
2508
2703
|
}
|
|
2509
2704
|
function SupercodeMessenger({ state: stateInput, adapter, class: className = "", initialView, labels, components = {}, slots = {} }) {
|
|
2510
|
-
const state =
|
|
2705
|
+
const state = useMemo3(() => normalizeUiState(stateInput), [stateInput]);
|
|
2511
2706
|
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
2512
2707
|
const memoryKey = state.workspace || "@default";
|
|
2513
|
-
const [view, setViewState] =
|
|
2514
|
-
const [opening, setOpening] =
|
|
2515
|
-
const [listFocus, setListFocus] =
|
|
2708
|
+
const [view, setViewState] = useState6(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
|
|
2709
|
+
const [opening, setOpening] = useState6(null);
|
|
2710
|
+
const [listFocus, setListFocus] = useState6(null);
|
|
2516
2711
|
const setView = (next) => {
|
|
2517
2712
|
boundedSet(messengerViewMemory, memoryKey, next);
|
|
2518
2713
|
setViewState(next);
|
|
2519
2714
|
};
|
|
2520
|
-
|
|
2715
|
+
useEffect8(() => {
|
|
2521
2716
|
if (!opening) return;
|
|
2522
2717
|
if (state.attached?.key === opening.key) {
|
|
2523
2718
|
setOpening(null);
|
|
@@ -2533,31 +2728,31 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
|
|
|
2533
2728
|
};
|
|
2534
2729
|
const close = () => adapter.onClose?.();
|
|
2535
2730
|
const Footer = slots.footer;
|
|
2536
|
-
return /* @__PURE__ */
|
|
2537
|
-
view === "list" ? /* @__PURE__ */
|
|
2731
|
+
return /* @__PURE__ */ jsxs8("main", { class: `scui-root ${className}`, "data-view": view, "data-mode": state.mode, "aria-label": "Supercode messenger", children: [
|
|
2732
|
+
view === "list" ? /* @__PURE__ */ jsx9(SessionList, { state, adapter, focusKey: listFocus, onOpen: open, onNew: () => {
|
|
2538
2733
|
setListFocus("@new");
|
|
2539
2734
|
setView("new");
|
|
2540
2735
|
}, onClose: adapter.onClose ? close : void 0, components, labels: copy, memoryKey }) : null,
|
|
2541
|
-
view === "new" ? /* @__PURE__ */
|
|
2542
|
-
view === "chat" ? /* @__PURE__ */
|
|
2736
|
+
view === "new" ? /* @__PURE__ */ jsx9(NewChat, { state, adapter, onBack: () => setView("list"), onClose: adapter.onClose ? close : void 0, onStarted: () => setView("chat"), labels: copy, memoryKey }) : null,
|
|
2737
|
+
view === "chat" ? /* @__PURE__ */ jsx9(Chat, { state, adapter, onBack: () => {
|
|
2543
2738
|
setListFocus(state.attached?.key ?? listFocus);
|
|
2544
2739
|
setView("list");
|
|
2545
2740
|
}, onNew: () => {
|
|
2546
2741
|
setListFocus("@new");
|
|
2547
2742
|
setView("new");
|
|
2548
2743
|
}, onClose: adapter.onClose ? close : void 0, components, slots, labels: copy }) : null,
|
|
2549
|
-
opening ? /* @__PURE__ */
|
|
2550
|
-
/* @__PURE__ */
|
|
2551
|
-
/* @__PURE__ */
|
|
2552
|
-
/* @__PURE__ */
|
|
2744
|
+
opening ? /* @__PURE__ */ jsxs8("div", { class: "scui-opening", role: "status", "aria-busy": "true", children: [
|
|
2745
|
+
/* @__PURE__ */ jsx9(HarnessLogo, { id: opening.harness, size: 34 }),
|
|
2746
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
2747
|
+
/* @__PURE__ */ jsxs8("strong", { children: [
|
|
2553
2748
|
"Opening ",
|
|
2554
2749
|
sessionDisplayName(opening)
|
|
2555
2750
|
] }),
|
|
2556
|
-
/* @__PURE__ */
|
|
2751
|
+
/* @__PURE__ */ jsx9("small", { children: "Loading the latest transcript window\u2026" })
|
|
2557
2752
|
] }),
|
|
2558
|
-
/* @__PURE__ */
|
|
2753
|
+
/* @__PURE__ */ jsx9("i", {})
|
|
2559
2754
|
] }) : null,
|
|
2560
|
-
Footer ? /* @__PURE__ */
|
|
2755
|
+
Footer ? /* @__PURE__ */ jsx9(Footer, { state, adapter, value: copy }) : null
|
|
2561
2756
|
] });
|
|
2562
2757
|
}
|
|
2563
2758
|
export {
|
|
@@ -2565,7 +2760,9 @@ export {
|
|
|
2565
2760
|
Composer,
|
|
2566
2761
|
ContinuationBar,
|
|
2567
2762
|
Conversation,
|
|
2763
|
+
HarnessAdvisory,
|
|
2568
2764
|
HarnessLogo,
|
|
2765
|
+
HarnessSettingsPanel,
|
|
2569
2766
|
ImageViewer,
|
|
2570
2767
|
LoadingStatus,
|
|
2571
2768
|
MessageImages,
|