@volter-ai-dev/supercode-ui 0.1.29 → 0.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -2
- package/components.mjs +326 -130
- package/composer.mjs +3 -0
- package/controller.mjs +10 -11
- package/conversation.mjs +4 -1
- package/core.d.ts +1 -0
- package/core.mjs +94 -2
- package/embed.mjs +326 -132
- package/index.d.ts +80 -1
- package/messenger.mjs +324 -130
- package/package.json +8 -2
- package/sessions.mjs +30 -6
- package/settings.d.ts +2 -0
- package/settings.mjs +216 -0
- package/styles.css +12 -3
package/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,6 +558,7 @@ 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,
|
|
@@ -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) => {
|
|
@@ -750,7 +828,7 @@ function canContinueHere(state) {
|
|
|
750
828
|
}
|
|
751
829
|
function operationLabel(operation) {
|
|
752
830
|
if (!operation) return "";
|
|
753
|
-
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" };
|
|
754
832
|
return labels[operation] ?? `${operation.replaceAll("_", " ")}\u2026`;
|
|
755
833
|
}
|
|
756
834
|
function terminalCommand(handoff) {
|
|
@@ -1987,7 +2065,7 @@ function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
|
|
|
1987
2065
|
}
|
|
1988
2066
|
|
|
1989
2067
|
// src/messenger.jsx
|
|
1990
|
-
import { useEffect as
|
|
2068
|
+
import { useEffect as useEffect8, useId as useId3, useMemo as useMemo3, useRef as useRef7, useState as useState6 } from "preact/hooks";
|
|
1991
2069
|
|
|
1992
2070
|
// src/sessions.jsx
|
|
1993
2071
|
import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useRef as useRef5, useState as useState4 } from "preact/hooks";
|
|
@@ -2004,28 +2082,35 @@ function sessionPathParts(value) {
|
|
|
2004
2082
|
trailing: complete.slice(boundary + 1)
|
|
2005
2083
|
};
|
|
2006
2084
|
}
|
|
2007
|
-
function SessionRow({ row, state, onOpen }) {
|
|
2085
|
+
function SessionRow({ row, state, onOpen, now = Date.now() }) {
|
|
2008
2086
|
const activity = sessionActivity(state, row);
|
|
2087
|
+
const working = activity === "working";
|
|
2009
2088
|
const attention = state.attention.find((item) => item.key === row.key);
|
|
2010
2089
|
const title = sessionDisplayName(row);
|
|
2011
2090
|
const path = sessionPathParts(row.cwd);
|
|
2012
2091
|
const preview = row.preview || attention?.preview || "";
|
|
2013
2092
|
const unreadCount = attention?.unreadCount ?? 0;
|
|
2014
2093
|
const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
|
|
2015
|
-
|
|
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: [
|
|
2016
2096
|
/* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
|
|
2017
2097
|
/* @__PURE__ */ jsxs6("span", { class: "scui-session-copy", children: [
|
|
2018
2098
|
/* @__PURE__ */ jsxs6("span", { class: "scui-session-title", children: [
|
|
2019
2099
|
/* @__PURE__ */ jsx7("strong", { children: title }),
|
|
2020
|
-
|
|
2100
|
+
/* @__PURE__ */ jsx7("span", { class: "scui-session-meta", children: age ? /* @__PURE__ */ jsx7("time", { children: age }) : null })
|
|
2021
2101
|
] }),
|
|
2022
2102
|
path.complete ? /* @__PURE__ */ jsxs6("small", { class: "scui-session-path", title: row.cwd, children: [
|
|
2023
2103
|
/* @__PURE__ */ jsx7("span", { class: "scui-session-path-leading", children: path.leading }),
|
|
2024
2104
|
path.separator ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-separator", children: path.separator }) : null,
|
|
2025
2105
|
path.trailing ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-trailing", children: path.trailing }) : null
|
|
2026
2106
|
] }) : null,
|
|
2027
|
-
preview || unreadCount ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-preview", children: [
|
|
2028
|
-
|
|
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,
|
|
2029
2114
|
unreadCount ? /* @__PURE__ */ jsx7("b", { "aria-label": `${unreadCount} unread messages`, children: unreadLabel }) : null
|
|
2030
2115
|
] }) : null,
|
|
2031
2116
|
state.attachError?.key === row.key ? /* @__PURE__ */ jsx7("em", { children: state.attachError.message }) : null
|
|
@@ -2036,6 +2121,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
2036
2121
|
const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
|
|
2037
2122
|
const [query, setQuery] = useState4(remembered.query);
|
|
2038
2123
|
const [loadingMore, setLoadingMore] = useState4(false);
|
|
2124
|
+
const [now, setNow] = useState4(() => Date.now());
|
|
2039
2125
|
const root = useRef5(null);
|
|
2040
2126
|
const rowScroller = useRef5(null);
|
|
2041
2127
|
const rows = filterSessions(state.sessions, query);
|
|
@@ -2051,6 +2137,10 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
2051
2137
|
useEffect6(() => {
|
|
2052
2138
|
if (loadingMore) setLoadingMore(false);
|
|
2053
2139
|
}, [state.error, state.history.hasMoreSessions, state.sessions.length]);
|
|
2140
|
+
useEffect6(() => {
|
|
2141
|
+
const timer = setInterval(() => setNow(Date.now()), 1e4);
|
|
2142
|
+
return () => clearInterval(timer);
|
|
2143
|
+
}, []);
|
|
2054
2144
|
const loadMore = () => {
|
|
2055
2145
|
if (loadingMore) return;
|
|
2056
2146
|
setLoadingMore(true);
|
|
@@ -2084,23 +2174,120 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
2084
2174
|
] }) : null,
|
|
2085
2175
|
/* @__PURE__ */ jsxs6("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
|
|
2086
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,
|
|
2087
|
-
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)),
|
|
2088
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
|
|
2089
2179
|
] })
|
|
2090
2180
|
] });
|
|
2091
2181
|
}
|
|
2092
2182
|
|
|
2093
|
-
// 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";
|
|
2094
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";
|
|
2095
2282
|
var pendingMessageMemory = /* @__PURE__ */ new Map();
|
|
2096
2283
|
var messengerViewMemory = /* @__PURE__ */ new Map();
|
|
2097
2284
|
var newChatMemory = /* @__PURE__ */ new Map();
|
|
2098
|
-
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"]);
|
|
2099
2286
|
function Receipt({ state, adapter }) {
|
|
2100
2287
|
const receipt = state.reductionReceipt;
|
|
2101
|
-
if (receipt) return /* @__PURE__ */
|
|
2102
|
-
/* @__PURE__ */
|
|
2103
|
-
/* @__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: [
|
|
2104
2291
|
receipt.sourceTokens.toLocaleString(),
|
|
2105
2292
|
" \u2192 ",
|
|
2106
2293
|
receipt.reducedTokens.toLocaleString(),
|
|
@@ -2109,36 +2296,36 @@ function Receipt({ state, adapter }) {
|
|
|
2109
2296
|
"\xD7 \xB7 reversible"
|
|
2110
2297
|
] })
|
|
2111
2298
|
] }) });
|
|
2112
|
-
if (state.exportReceipt) return /* @__PURE__ */
|
|
2113
|
-
/* @__PURE__ */
|
|
2114
|
-
/* @__PURE__ */
|
|
2299
|
+
if (state.exportReceipt) return /* @__PURE__ */ jsxs8("div", { class: "scui-receipt", children: [
|
|
2300
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
2301
|
+
/* @__PURE__ */ jsxs8("strong", { children: [
|
|
2115
2302
|
"Lossless export ready \xB7 ",
|
|
2116
2303
|
harnessDisplayName(state.exportReceipt.targetHarness)
|
|
2117
2304
|
] }),
|
|
2118
|
-
/* @__PURE__ */
|
|
2305
|
+
/* @__PURE__ */ jsxs8("small", { children: [
|
|
2119
2306
|
state.exportReceipt.path,
|
|
2120
2307
|
" \xB7 ",
|
|
2121
2308
|
state.exportReceipt.files,
|
|
2122
2309
|
" files"
|
|
2123
2310
|
] })
|
|
2124
2311
|
] }),
|
|
2125
|
-
/* @__PURE__ */
|
|
2312
|
+
/* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter.copyText?.(state.exportReceipt.path), children: "Copy path" })
|
|
2126
2313
|
] });
|
|
2127
|
-
if (state.terminalHandoff) return /* @__PURE__ */
|
|
2128
|
-
/* @__PURE__ */
|
|
2129
|
-
/* @__PURE__ */
|
|
2130
|
-
/* @__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 })
|
|
2131
2318
|
] }),
|
|
2132
|
-
/* @__PURE__ */
|
|
2319
|
+
/* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter.copyText?.(terminalCommand(state.terminalHandoff)), children: "Copy command" })
|
|
2133
2320
|
] });
|
|
2134
2321
|
return null;
|
|
2135
2322
|
}
|
|
2136
|
-
function ConversationActions({ state, adapter, actionPending }) {
|
|
2137
|
-
const [open, setOpen] =
|
|
2138
|
-
const root =
|
|
2139
|
-
const panel =
|
|
2140
|
-
const trigger =
|
|
2141
|
-
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();
|
|
2142
2329
|
const targets = state.harnesses.filter((item) => item.startable);
|
|
2143
2330
|
const groups = [
|
|
2144
2331
|
{
|
|
@@ -2146,7 +2333,8 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2146
2333
|
items: [
|
|
2147
2334
|
state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
|
|
2148
2335
|
state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
|
|
2149
|
-
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
|
|
2150
2338
|
].filter(Boolean)
|
|
2151
2339
|
},
|
|
2152
2340
|
{
|
|
@@ -2158,7 +2346,7 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2158
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 } })) : []
|
|
2159
2347
|
}
|
|
2160
2348
|
].filter((group) => group.items.length);
|
|
2161
|
-
|
|
2349
|
+
useEffect8(() => {
|
|
2162
2350
|
if (!open) return;
|
|
2163
2351
|
panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
|
|
2164
2352
|
const dismiss = (event) => {
|
|
@@ -2177,9 +2365,10 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2177
2365
|
document.removeEventListener("pointerdown", dismiss, true);
|
|
2178
2366
|
};
|
|
2179
2367
|
}, [open]);
|
|
2180
|
-
const dispatch = (
|
|
2368
|
+
const dispatch = (item) => {
|
|
2181
2369
|
setOpen(false);
|
|
2182
|
-
|
|
2370
|
+
if (item.onSelect) item.onSelect();
|
|
2371
|
+
else adapter.onIntent(item.intent);
|
|
2183
2372
|
};
|
|
2184
2373
|
const navigate = (event) => {
|
|
2185
2374
|
if (event.key === "Escape") {
|
|
@@ -2197,62 +2386,63 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2197
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;
|
|
2198
2387
|
items[index].focus({ preventScroll: true });
|
|
2199
2388
|
};
|
|
2200
|
-
return /* @__PURE__ */
|
|
2389
|
+
return /* @__PURE__ */ jsxs8("div", { class: "scui-menu", ref: root, onBlur: (event) => {
|
|
2201
2390
|
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
2202
2391
|
}, children: [
|
|
2203
|
-
/* @__PURE__ */
|
|
2204
|
-
open ? /* @__PURE__ */
|
|
2205
|
-
/* @__PURE__ */
|
|
2206
|
-
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))
|
|
2207
2396
|
] }, group.label)) }) : null
|
|
2208
2397
|
] });
|
|
2209
2398
|
}
|
|
2210
|
-
function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
|
|
2211
|
-
const back =
|
|
2399
|
+
function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose, onSettings }) {
|
|
2400
|
+
const back = useRef7(null);
|
|
2212
2401
|
const harness = state.attached?.harness ?? state.harness;
|
|
2213
2402
|
const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
|
|
2214
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";
|
|
2215
|
-
const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
|
|
2216
|
-
|
|
2404
|
+
const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce || state.interopSettings || state.interopSettingsError;
|
|
2405
|
+
useEffect8(() => {
|
|
2217
2406
|
if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
|
|
2218
2407
|
}, []);
|
|
2219
|
-
return /* @__PURE__ */
|
|
2220
|
-
/* @__PURE__ */
|
|
2221
|
-
/* @__PURE__ */
|
|
2222
|
-
/* @__PURE__ */
|
|
2223
|
-
/* @__PURE__ */
|
|
2224
|
-
/* @__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: [
|
|
2225
2414
|
harnessDisplayName(harness),
|
|
2226
2415
|
" \xB7 ",
|
|
2227
2416
|
status
|
|
2228
2417
|
] })
|
|
2229
2418
|
] }),
|
|
2230
|
-
menu ? /* @__PURE__ */
|
|
2231
|
-
/* @__PURE__ */
|
|
2232
|
-
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
|
|
2233
2422
|
] });
|
|
2234
2423
|
}
|
|
2235
2424
|
function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
|
|
2236
2425
|
const Header = slots.header;
|
|
2237
2426
|
const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
|
|
2238
|
-
const [pending, setPendingState] =
|
|
2239
|
-
const [pendingAction, setPendingAction] =
|
|
2240
|
-
const [restoreDraft, setRestoreDraft] =
|
|
2241
|
-
const
|
|
2242
|
-
const
|
|
2243
|
-
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());
|
|
2244
2434
|
const setPending = (update) => setPendingState((current) => {
|
|
2245
2435
|
const next = typeof update === "function" ? update(current) : update;
|
|
2246
2436
|
if (next) boundedSet(pendingMessageMemory, memoryKey, next);
|
|
2247
2437
|
else pendingMessageMemory.delete(memoryKey);
|
|
2248
2438
|
return next;
|
|
2249
2439
|
});
|
|
2250
|
-
|
|
2440
|
+
useEffect8(() => {
|
|
2251
2441
|
setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
|
|
2252
2442
|
setPendingAction(null);
|
|
2253
2443
|
setRestoreDraft(null);
|
|
2254
2444
|
}, [memoryKey]);
|
|
2255
|
-
|
|
2445
|
+
useEffect8(() => {
|
|
2256
2446
|
if (!pending) return;
|
|
2257
2447
|
if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
|
|
2258
2448
|
setPending(null);
|
|
@@ -2281,7 +2471,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2281
2471
|
setRestoreDraft({ id: restoreSequence.current, text: pending.text, context: pending.context, images: pending.images });
|
|
2282
2472
|
setPending({ ...pending, status: "editing" });
|
|
2283
2473
|
};
|
|
2284
|
-
|
|
2474
|
+
useEffect8(() => {
|
|
2285
2475
|
const key = state.attached?.key;
|
|
2286
2476
|
if (!key || !state.attention.some((item) => item.key === key)) {
|
|
2287
2477
|
if (key) acknowledged.current.delete(key);
|
|
@@ -2292,7 +2482,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2292
2482
|
adapter.onIntent({ action: "ack", key });
|
|
2293
2483
|
}
|
|
2294
2484
|
}, [adapter, state.attached?.key, state.attention]);
|
|
2295
|
-
const trackedAdapter =
|
|
2485
|
+
const trackedAdapter = useMemo3(() => ({
|
|
2296
2486
|
...adapter,
|
|
2297
2487
|
onIntent(intent) {
|
|
2298
2488
|
if (!TRACKED_ACTIONS.has(intent.action)) return adapter.onIntent(intent);
|
|
@@ -2316,7 +2506,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2316
2506
|
return result;
|
|
2317
2507
|
}
|
|
2318
2508
|
}), [adapter, state.error]);
|
|
2319
|
-
|
|
2509
|
+
useEffect8(() => {
|
|
2320
2510
|
if (!pendingAction) return;
|
|
2321
2511
|
if (state.operation && !pendingAction.seenOperation) {
|
|
2322
2512
|
setPendingAction({ ...pendingAction, seenOperation: true });
|
|
@@ -2329,44 +2519,48 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2329
2519
|
const actionLabel = operationLabel(action);
|
|
2330
2520
|
const actionState = action ? { ...state, operation: action, canInterrupt: false, canRespond: false } : state;
|
|
2331
2521
|
const unreadAfterMessages = state.attention.find((item) => item.key === state.attached?.key)?.afterMessages ?? null;
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
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", {}),
|
|
2336
2528
|
actionLabel
|
|
2337
2529
|
] }) : null,
|
|
2338
|
-
state.error ? /* @__PURE__ */
|
|
2339
|
-
/* @__PURE__ */
|
|
2340
|
-
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
|
|
2341
2533
|
] }) : null,
|
|
2342
|
-
/* @__PURE__ */
|
|
2343
|
-
/* @__PURE__ */
|
|
2344
|
-
/* @__PURE__ */
|
|
2345
|
-
|
|
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
|
|
2346
2540
|
] });
|
|
2347
2541
|
}
|
|
2348
2542
|
function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey }) {
|
|
2349
2543
|
const startable = state.harnesses.filter((item) => item.startable);
|
|
2350
2544
|
const startableKey = startable.map((item) => item.id).join("\0");
|
|
2351
2545
|
const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: "", context: [], images: [] };
|
|
2352
|
-
const [harness, setHarness] =
|
|
2353
|
-
const [draft, setDraft] =
|
|
2354
|
-
const [context, setContext] =
|
|
2355
|
-
const [images, setImages] =
|
|
2356
|
-
const [starting, setStarting] =
|
|
2357
|
-
const [picking, setPicking] =
|
|
2358
|
-
const [dragging, setDragging] =
|
|
2359
|
-
const [pickerError, setPickerError] =
|
|
2360
|
-
const startSequence =
|
|
2361
|
-
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);
|
|
2362
2556
|
useAutosizeTextarea(textarea, draft);
|
|
2363
|
-
|
|
2557
|
+
useEffect8(() => {
|
|
2364
2558
|
if (startable.some((item) => item.id === harness)) return;
|
|
2365
2559
|
const next = startable[0]?.id ?? "";
|
|
2366
2560
|
setHarness(next);
|
|
2367
2561
|
boundedSet(newChatMemory, memoryKey, { harness: next, draft, context, images });
|
|
2368
2562
|
}, [harness, startableKey]);
|
|
2369
|
-
|
|
2563
|
+
useEffect8(() => {
|
|
2370
2564
|
if (!starting) return;
|
|
2371
2565
|
const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
|
|
2372
2566
|
const beganWorking = !starting.busy && state.busy;
|
|
@@ -2374,10 +2568,10 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2374
2568
|
newChatMemory.delete(memoryKey);
|
|
2375
2569
|
onStarted();
|
|
2376
2570
|
}, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
|
|
2377
|
-
|
|
2571
|
+
useEffect8(() => {
|
|
2378
2572
|
if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
|
|
2379
2573
|
}, [starting, state.busy, state.error, state.operation]);
|
|
2380
|
-
|
|
2574
|
+
useEffect8(() => {
|
|
2381
2575
|
textarea.current?.focus({ preventScroll: true });
|
|
2382
2576
|
}, []);
|
|
2383
2577
|
const pickContext = () => {
|
|
@@ -2439,49 +2633,49 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2439
2633
|
Promise.resolve(result).catch(() => setStarting((current) => current?.id === id ? null : current));
|
|
2440
2634
|
}
|
|
2441
2635
|
};
|
|
2442
|
-
return /* @__PURE__ */
|
|
2443
|
-
/* @__PURE__ */
|
|
2444
|
-
/* @__PURE__ */
|
|
2445
|
-
/* @__PURE__ */
|
|
2446
|
-
/* @__PURE__ */
|
|
2447
|
-
/* @__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" })
|
|
2448
2642
|
] }),
|
|
2449
|
-
onClose ? /* @__PURE__ */
|
|
2643
|
+
onClose ? /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx9(UiIcon, { name: "close", size: 18 }) }) : null
|
|
2450
2644
|
] }),
|
|
2451
|
-
starting ? /* @__PURE__ */
|
|
2452
|
-
/* @__PURE__ */
|
|
2645
|
+
starting ? /* @__PURE__ */ jsxs8("div", { class: "scui-operation", role: "status", children: [
|
|
2646
|
+
/* @__PURE__ */ jsx9("i", {}),
|
|
2453
2647
|
operationLabel("start")
|
|
2454
2648
|
] }) : null,
|
|
2455
|
-
/* @__PURE__ */
|
|
2456
|
-
/* @__PURE__ */
|
|
2457
|
-
/* @__PURE__ */
|
|
2458
|
-
/* @__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." })
|
|
2459
2653
|
] }),
|
|
2460
|
-
/* @__PURE__ */
|
|
2461
|
-
/* @__PURE__ */
|
|
2462
|
-
/* @__PURE__ */
|
|
2463
|
-
/* @__PURE__ */
|
|
2464
|
-
/* @__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) => {
|
|
2465
2659
|
const value = event.currentTarget.value;
|
|
2466
2660
|
setHarness(value);
|
|
2467
2661
|
boundedSet(newChatMemory, memoryKey, { harness: value, draft, context, images });
|
|
2468
|
-
}, children: state.harnesses.map((item) => /* @__PURE__ */
|
|
2662
|
+
}, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs8("option", { value: item.id, disabled: !item.startable, children: [
|
|
2469
2663
|
item.label,
|
|
2470
2664
|
item.startable ? "" : " \xB7 unavailable"
|
|
2471
2665
|
] }, item.id)) })
|
|
2472
2666
|
] }),
|
|
2473
|
-
/* @__PURE__ */
|
|
2667
|
+
/* @__PURE__ */ jsx9(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
|
|
2474
2668
|
const next = items.filter((_, itemIndex) => itemIndex !== index);
|
|
2475
2669
|
boundedSet(newChatMemory, memoryKey, { harness, draft, context, images: next });
|
|
2476
2670
|
return next;
|
|
2477
2671
|
}) }),
|
|
2478
|
-
/* @__PURE__ */
|
|
2672
|
+
/* @__PURE__ */ jsx9(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
|
|
2479
2673
|
const next = items.filter((_, itemIndex) => itemIndex !== index);
|
|
2480
2674
|
boundedSet(newChatMemory, memoryKey, { harness, draft, context: next, images });
|
|
2481
2675
|
return next;
|
|
2482
2676
|
}) }),
|
|
2483
|
-
pickerError ? /* @__PURE__ */
|
|
2484
|
-
/* @__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) => {
|
|
2485
2679
|
if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
|
|
2486
2680
|
event.preventDefault();
|
|
2487
2681
|
setDragging(true);
|
|
@@ -2491,8 +2685,8 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2491
2685
|
}, onDragLeave: (event) => {
|
|
2492
2686
|
if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
|
|
2493
2687
|
}, onDrop: dropImages, children: [
|
|
2494
|
-
adapter.pickContext ? /* @__PURE__ */
|
|
2495
|
-
/* @__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) => {
|
|
2496
2690
|
const value = event.currentTarget.value;
|
|
2497
2691
|
setDraft(value);
|
|
2498
2692
|
boundedSet(newChatMemory, memoryKey, { harness, draft: value, context, images });
|
|
@@ -2502,23 +2696,23 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2502
2696
|
send();
|
|
2503
2697
|
}
|
|
2504
2698
|
} }),
|
|
2505
|
-
/* @__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 }) }) })
|
|
2506
2700
|
] })
|
|
2507
2701
|
] })
|
|
2508
2702
|
] });
|
|
2509
2703
|
}
|
|
2510
2704
|
function SupercodeMessenger({ state: stateInput, adapter, class: className = "", initialView, labels, components = {}, slots = {} }) {
|
|
2511
|
-
const state =
|
|
2705
|
+
const state = useMemo3(() => normalizeUiState(stateInput), [stateInput]);
|
|
2512
2706
|
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
2513
2707
|
const memoryKey = state.workspace || "@default";
|
|
2514
|
-
const [view, setViewState] =
|
|
2515
|
-
const [opening, setOpening] =
|
|
2516
|
-
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);
|
|
2517
2711
|
const setView = (next) => {
|
|
2518
2712
|
boundedSet(messengerViewMemory, memoryKey, next);
|
|
2519
2713
|
setViewState(next);
|
|
2520
2714
|
};
|
|
2521
|
-
|
|
2715
|
+
useEffect8(() => {
|
|
2522
2716
|
if (!opening) return;
|
|
2523
2717
|
if (state.attached?.key === opening.key) {
|
|
2524
2718
|
setOpening(null);
|
|
@@ -2534,31 +2728,31 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
|
|
|
2534
2728
|
};
|
|
2535
2729
|
const close = () => adapter.onClose?.();
|
|
2536
2730
|
const Footer = slots.footer;
|
|
2537
|
-
return /* @__PURE__ */
|
|
2538
|
-
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: () => {
|
|
2539
2733
|
setListFocus("@new");
|
|
2540
2734
|
setView("new");
|
|
2541
2735
|
}, onClose: adapter.onClose ? close : void 0, components, labels: copy, memoryKey }) : null,
|
|
2542
|
-
view === "new" ? /* @__PURE__ */
|
|
2543
|
-
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: () => {
|
|
2544
2738
|
setListFocus(state.attached?.key ?? listFocus);
|
|
2545
2739
|
setView("list");
|
|
2546
2740
|
}, onNew: () => {
|
|
2547
2741
|
setListFocus("@new");
|
|
2548
2742
|
setView("new");
|
|
2549
2743
|
}, onClose: adapter.onClose ? close : void 0, components, slots, labels: copy }) : null,
|
|
2550
|
-
opening ? /* @__PURE__ */
|
|
2551
|
-
/* @__PURE__ */
|
|
2552
|
-
/* @__PURE__ */
|
|
2553
|
-
/* @__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: [
|
|
2554
2748
|
"Opening ",
|
|
2555
2749
|
sessionDisplayName(opening)
|
|
2556
2750
|
] }),
|
|
2557
|
-
/* @__PURE__ */
|
|
2751
|
+
/* @__PURE__ */ jsx9("small", { children: "Loading the latest transcript window\u2026" })
|
|
2558
2752
|
] }),
|
|
2559
|
-
/* @__PURE__ */
|
|
2753
|
+
/* @__PURE__ */ jsx9("i", {})
|
|
2560
2754
|
] }) : null,
|
|
2561
|
-
Footer ? /* @__PURE__ */
|
|
2755
|
+
Footer ? /* @__PURE__ */ jsx9(Footer, { state, adapter, value: copy }) : null
|
|
2562
2756
|
] });
|
|
2563
2757
|
}
|
|
2564
2758
|
export {
|
|
@@ -2566,7 +2760,9 @@ export {
|
|
|
2566
2760
|
Composer,
|
|
2567
2761
|
ContinuationBar,
|
|
2568
2762
|
Conversation,
|
|
2763
|
+
HarnessAdvisory,
|
|
2569
2764
|
HarnessLogo,
|
|
2765
|
+
HarnessSettingsPanel,
|
|
2570
2766
|
ImageViewer,
|
|
2571
2767
|
LoadingStatus,
|
|
2572
2768
|
MessageImages,
|