@volter-ai-dev/supercode-ui 0.1.40 → 0.1.42
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 +3 -3
- package/components.d.ts +1 -0
- package/components.mjs +87 -23
- package/embed.mjs +86 -23
- package/index.d.ts +11 -0
- package/messenger.mjs +86 -23
- package/package.json +1 -1
- package/react/components.mjs +87 -23
- package/react/index.d.ts +4 -0
- package/react/messenger.mjs +86 -23
- package/react/settings.d.ts +2 -2
- package/react/settings.mjs +77 -8
- package/settings.d.ts +2 -2
- package/settings.mjs +77 -8
- package/styles.css +6 -1
package/README.md
CHANGED
|
@@ -60,14 +60,14 @@ import {
|
|
|
60
60
|
SessionList,
|
|
61
61
|
} from '@volter-ai-dev/supercode-ui/preact';
|
|
62
62
|
import { HarnessLogo, harnessLogoDataUrl } from '@volter-ai-dev/supercode-ui/preact/logo';
|
|
63
|
-
import { HarnessAdvisory, HarnessSettingsPanel } from '@volter-ai-dev/supercode-ui/preact/settings';
|
|
63
|
+
import { HarnessAdvisory, HarnessPicker, HarnessSettingsPanel } from '@volter-ai-dev/supercode-ui/preact/settings';
|
|
64
64
|
import { groupConversation } from '@volter-ai-dev/supercode-ui/core';
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
The public components are `SupercodeMessenger`, `AgentActivityLauncher`, `Conversation`, `TranscriptEntry`,
|
|
68
68
|
`ActivityGroup`, `RequestCard`, `SessionList`, `SessionRow`, `Composer`, `ContinuationBar`,
|
|
69
|
-
`ContextCandidate`, `ContextCandidates`, `LoadingStatus`, `TaskPlan`, `SessionDetails`, `HarnessLogo`, `HarnessAdvisory`,
|
|
70
|
-
`HarnessSettingsPanel`. Pure state readers, selectors,
|
|
69
|
+
`ContextCandidate`, `ContextCandidates`, `LoadingStatus`, `TaskPlan`, `SessionDetails`, `HarnessLogo`, `HarnessAdvisory`,
|
|
70
|
+
`HarnessPicker`, and `HarnessSettingsPanel`. Pure state readers, selectors,
|
|
71
71
|
formatters, and intent constructors live at `supercode-ui/core` and have no DOM or Preact imports.
|
|
72
72
|
For genuine partial delivery, the `preact/logo`, `preact/icon`, `preact/conversation`, `preact/sessions`,
|
|
73
73
|
`preact/composer`, `preact/settings`, and `preact/messenger` subpaths are independently tree-shaken artifacts; taking
|
package/components.d.ts
CHANGED
package/components.mjs
CHANGED
|
@@ -2459,14 +2459,9 @@ function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = nul
|
|
|
2459
2459
|
}
|
|
2460
2460
|
function HarnessReadiness({ harnesses, adapter }) {
|
|
2461
2461
|
const blocked = harnesses.filter((item) => !item.startable);
|
|
2462
|
-
if (!blocked.length) return null;
|
|
2463
|
-
return /* @__PURE__ */ jsxs8("details", { className: "scui-readiness", open:
|
|
2464
|
-
/* @__PURE__ */
|
|
2465
|
-
blocked.length,
|
|
2466
|
-
" ",
|
|
2467
|
-
blocked.length === 1 ? "harness needs" : "harnesses need",
|
|
2468
|
-
" setup"
|
|
2469
|
-
] }),
|
|
2462
|
+
if (!blocked.length || harnesses.some((item) => item.startable)) return null;
|
|
2463
|
+
return /* @__PURE__ */ jsxs8("details", { className: "scui-readiness", open: true, children: [
|
|
2464
|
+
/* @__PURE__ */ jsx9("summary", { children: "No coding harness is ready" }),
|
|
2470
2465
|
/* @__PURE__ */ jsx9("div", { children: blocked.map((item) => /* @__PURE__ */ jsxs8("section", { children: [
|
|
2471
2466
|
/* @__PURE__ */ jsx9(HarnessLogo, { id: item.id, size: 25 }),
|
|
2472
2467
|
/* @__PURE__ */ jsxs8("span", { children: [
|
|
@@ -2482,6 +2477,79 @@ function HarnessReadiness({ harnesses, adapter }) {
|
|
|
2482
2477
|
] }, item.id)) })
|
|
2483
2478
|
] });
|
|
2484
2479
|
}
|
|
2480
|
+
function HarnessPicker({ harnesses, value, disabled = false, adapter, onChange, logo: Logo = HarnessLogo }) {
|
|
2481
|
+
const [open, setOpen] = useState5(false);
|
|
2482
|
+
const menuId = useId2();
|
|
2483
|
+
const root = useRef6(null);
|
|
2484
|
+
const trigger = useRef6(null);
|
|
2485
|
+
const panel = useRef6(null);
|
|
2486
|
+
const available = harnesses.filter((item) => item.startable);
|
|
2487
|
+
const unavailable = harnesses.filter((item) => !item.startable);
|
|
2488
|
+
const selected = available.find((item) => item.id === value) ?? available[0] ?? null;
|
|
2489
|
+
const close = () => {
|
|
2490
|
+
setOpen(false);
|
|
2491
|
+
trigger.current?.focus({ preventScroll: true });
|
|
2492
|
+
};
|
|
2493
|
+
useEffect7(() => {
|
|
2494
|
+
if (!open) return;
|
|
2495
|
+
panel.current?.querySelector('[aria-selected="true"], [role="option"]')?.focus({ preventScroll: true });
|
|
2496
|
+
const dismiss = (event) => {
|
|
2497
|
+
if (event.type === "keydown" && event.key === "Escape") {
|
|
2498
|
+
event.preventDefault();
|
|
2499
|
+
close();
|
|
2500
|
+
} else if (event.type === "pointerdown" && !root.current?.contains(event.target)) {
|
|
2501
|
+
setOpen(false);
|
|
2502
|
+
}
|
|
2503
|
+
};
|
|
2504
|
+
document.addEventListener("keydown", dismiss);
|
|
2505
|
+
document.addEventListener("pointerdown", dismiss, true);
|
|
2506
|
+
return () => {
|
|
2507
|
+
document.removeEventListener("keydown", dismiss);
|
|
2508
|
+
document.removeEventListener("pointerdown", dismiss, true);
|
|
2509
|
+
};
|
|
2510
|
+
}, [open]);
|
|
2511
|
+
const navigate = (event) => {
|
|
2512
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
|
|
2513
|
+
const items = [...panel.current.querySelectorAll('[role="option"]')];
|
|
2514
|
+
if (!items.length) return;
|
|
2515
|
+
event.preventDefault();
|
|
2516
|
+
const current = items.indexOf(document.activeElement);
|
|
2517
|
+
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;
|
|
2518
|
+
items[index].focus({ preventScroll: true });
|
|
2519
|
+
};
|
|
2520
|
+
const select = (id) => {
|
|
2521
|
+
onChange(id);
|
|
2522
|
+
close();
|
|
2523
|
+
};
|
|
2524
|
+
return /* @__PURE__ */ jsxs8("div", { className: "scui-harness-menu", ref: root, onBlur: (event) => {
|
|
2525
|
+
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
2526
|
+
}, children: [
|
|
2527
|
+
/* @__PURE__ */ jsxs8("button", { ref: trigger, className: "scui-harness-trigger", type: "button", disabled: disabled || !selected, "aria-label": selected ? `Coding harness: ${selected.label}` : "No coding harness available", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": open ? menuId : void 0, onClick: () => setOpen((current) => !current), children: [
|
|
2528
|
+
selected ? /* @__PURE__ */ jsx9(Logo, { id: selected.id, size: 20 }) : null,
|
|
2529
|
+
/* @__PURE__ */ jsx9("strong", { children: selected?.label ?? "No harness available" }),
|
|
2530
|
+
/* @__PURE__ */ jsx9(UiIcon, { name: "chevron", size: 13 })
|
|
2531
|
+
] }),
|
|
2532
|
+
open ? /* @__PURE__ */ jsxs8("div", { ref: panel, id: menuId, className: "scui-harness-popover", role: "dialog", "aria-label": "Choose coding harness", onKeyDown: navigate, children: [
|
|
2533
|
+
/* @__PURE__ */ jsx9("strong", { className: "scui-harness-popover-title", children: "Choose coding harness" }),
|
|
2534
|
+
/* @__PURE__ */ jsx9("div", { className: "scui-harness-options", role: "listbox", "aria-label": "Available coding harnesses", children: available.map((item) => /* @__PURE__ */ jsxs8("button", { type: "button", role: "option", "aria-selected": item.id === selected?.id, onClick: () => select(item.id), children: [
|
|
2535
|
+
/* @__PURE__ */ jsx9(Logo, { id: item.id, size: 24 }),
|
|
2536
|
+
/* @__PURE__ */ jsx9("strong", { children: item.label }),
|
|
2537
|
+
/* @__PURE__ */ jsx9("span", { children: item.id === selected?.id ? /* @__PURE__ */ jsx9(UiIcon, { name: "check", size: 15 }) : null })
|
|
2538
|
+
] }, item.id)) }),
|
|
2539
|
+
unavailable.length ? /* @__PURE__ */ jsxs8("details", { className: "scui-harness-manage", children: [
|
|
2540
|
+
/* @__PURE__ */ jsx9("summary", { children: "Manage additional harnesses" }),
|
|
2541
|
+
/* @__PURE__ */ jsx9("div", { children: unavailable.map((item) => /* @__PURE__ */ jsxs8("section", { children: [
|
|
2542
|
+
/* @__PURE__ */ jsx9(Logo, { id: item.id, size: 24 }),
|
|
2543
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
2544
|
+
/* @__PURE__ */ jsx9("strong", { children: item.label }),
|
|
2545
|
+
/* @__PURE__ */ jsx9("small", { children: item.reason || (item.auth === "required" ? "Authentication required" : "Unavailable") })
|
|
2546
|
+
] }),
|
|
2547
|
+
item.repair ? /* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter?.copyText?.(item.repair), children: "Copy fix" }) : null
|
|
2548
|
+
] }, item.id)) })
|
|
2549
|
+
] }) : null
|
|
2550
|
+
] }) : null
|
|
2551
|
+
] });
|
|
2552
|
+
}
|
|
2485
2553
|
|
|
2486
2554
|
// src/messenger.jsx
|
|
2487
2555
|
import { jsx as jsx10, jsxs as jsxs9 } from "preact/jsx-runtime";
|
|
@@ -2775,6 +2843,7 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2775
2843
|
const startSequence = useRef7(0);
|
|
2776
2844
|
const textarea = useRef7(null);
|
|
2777
2845
|
const selectedHarness = startable.find((item) => item.id === harness) ?? null;
|
|
2846
|
+
const Picker = components.HarnessPicker ?? HarnessPicker;
|
|
2778
2847
|
const Readiness = components.HarnessReadiness ?? HarnessReadiness;
|
|
2779
2848
|
const launchModes = selectedHarness?.launchModes?.length ? selectedHarness.launchModes : ["headless"];
|
|
2780
2849
|
const rememberedMode = modes[harness];
|
|
@@ -2912,18 +2981,6 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2912
2981
|
/* @__PURE__ */ jsx10("small", { children: "Choose a coding harness and send the first message." })
|
|
2913
2982
|
] }),
|
|
2914
2983
|
/* @__PURE__ */ jsxs9("div", { className: "scui-compose", children: [
|
|
2915
|
-
/* @__PURE__ */ jsxs9("label", { className: "scui-harness-picker", children: [
|
|
2916
|
-
/* @__PURE__ */ jsx10(HarnessLogo, { id: harness, size: 24 }),
|
|
2917
|
-
/* @__PURE__ */ jsx10("span", { children: "Coding harness" }),
|
|
2918
|
-
/* @__PURE__ */ jsx10("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
|
|
2919
|
-
const value = event.currentTarget.value;
|
|
2920
|
-
setHarness(value);
|
|
2921
|
-
remember({ harness: value });
|
|
2922
|
-
}, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs9("option", { value: item.id, disabled: !item.startable, children: [
|
|
2923
|
-
item.label,
|
|
2924
|
-
item.startable ? "" : " \xB7 unavailable"
|
|
2925
|
-
] }, item.id)) })
|
|
2926
|
-
] }),
|
|
2927
2984
|
/* @__PURE__ */ jsx10(Readiness, { harnesses: state.harnesses, state, adapter }),
|
|
2928
2985
|
launchModes.length > 1 ? /* @__PURE__ */ jsxs9("fieldset", { className: "scui-launch-modes", disabled: Boolean(starting), children: [
|
|
2929
2986
|
/* @__PURE__ */ jsx10("legend", { children: "Run as" }),
|
|
@@ -2952,7 +3009,7 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2952
3009
|
return next;
|
|
2953
3010
|
}) }),
|
|
2954
3011
|
terminalAttachments ? /* @__PURE__ */ jsx10("small", { className: "scui-context-error", role: "alert", children: "Terminal starts currently accept text only. Remove attachments or choose Chat." }) : pickerError ? /* @__PURE__ */ jsx10("small", { className: "scui-context-error", role: "alert", children: pickerError }) : null,
|
|
2955
|
-
/* @__PURE__ */ jsxs9("div", { className: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
3012
|
+
/* @__PURE__ */ jsxs9("div", { className: `scui-envelope scui-new-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
2956
3013
|
if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
|
|
2957
3014
|
event.preventDefault();
|
|
2958
3015
|
setDragging(true);
|
|
@@ -2962,7 +3019,6 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2962
3019
|
}, onDragLeave: (event) => {
|
|
2963
3020
|
if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
|
|
2964
3021
|
}, onDrop: dropImages, children: [
|
|
2965
|
-
adapter.pickContext ? /* @__PURE__ */ jsx10("button", { className: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: mode === "terminal" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx10("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx10(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
2966
3022
|
/* @__PURE__ */ jsx10("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) => {
|
|
2967
3023
|
const value = event.currentTarget.value;
|
|
2968
3024
|
setDraft(value);
|
|
@@ -2973,7 +3029,14 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2973
3029
|
send();
|
|
2974
3030
|
}
|
|
2975
3031
|
} }),
|
|
2976
|
-
/* @__PURE__ */
|
|
3032
|
+
/* @__PURE__ */ jsxs9("footer", { className: "scui-new-envelope-controls", children: [
|
|
3033
|
+
adapter.pickContext ? /* @__PURE__ */ jsx10("button", { className: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: mode === "terminal" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx10("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx10(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
3034
|
+
/* @__PURE__ */ jsx10(Picker, { harnesses: state.harnesses, value: harness, disabled: Boolean(starting), adapter, logo: components.HarnessLogo, onChange: (value) => {
|
|
3035
|
+
setHarness(value);
|
|
3036
|
+
remember({ harness: value });
|
|
3037
|
+
} }),
|
|
3038
|
+
/* @__PURE__ */ jsx10("span", { children: /* @__PURE__ */ jsx10("button", { type: "button", className: "scui-send", "aria-label": mode === "terminal" ? "Start terminal session" : "Start chat", disabled: !draft.trim() && !images.length || !harness || Boolean(starting) || terminalAttachments || mode === "terminal" && !draft.trim(), onClick: send, children: starting ? /* @__PURE__ */ jsx10("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx10(UiIcon, { name: "send", size: 17 }) }) })
|
|
3039
|
+
] })
|
|
2977
3040
|
] })
|
|
2978
3041
|
] })
|
|
2979
3042
|
] });
|
|
@@ -3083,6 +3146,7 @@ export {
|
|
|
3083
3146
|
Conversation,
|
|
3084
3147
|
HarnessAdvisory,
|
|
3085
3148
|
HarnessLogo,
|
|
3149
|
+
HarnessPicker,
|
|
3086
3150
|
HarnessReadiness,
|
|
3087
3151
|
HarnessSettingsPanel,
|
|
3088
3152
|
ImageViewer,
|
package/embed.mjs
CHANGED
|
@@ -2379,14 +2379,9 @@ function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = nul
|
|
|
2379
2379
|
}
|
|
2380
2380
|
function HarnessReadiness({ harnesses, adapter }) {
|
|
2381
2381
|
const blocked = harnesses.filter((item) => !item.startable);
|
|
2382
|
-
if (!blocked.length) return null;
|
|
2383
|
-
return /* @__PURE__ */ jsxs7("details", { className: "scui-readiness", open:
|
|
2384
|
-
/* @__PURE__ */
|
|
2385
|
-
blocked.length,
|
|
2386
|
-
" ",
|
|
2387
|
-
blocked.length === 1 ? "harness needs" : "harnesses need",
|
|
2388
|
-
" setup"
|
|
2389
|
-
] }),
|
|
2382
|
+
if (!blocked.length || harnesses.some((item) => item.startable)) return null;
|
|
2383
|
+
return /* @__PURE__ */ jsxs7("details", { className: "scui-readiness", open: true, children: [
|
|
2384
|
+
/* @__PURE__ */ jsx8("summary", { children: "No coding harness is ready" }),
|
|
2390
2385
|
/* @__PURE__ */ jsx8("div", { children: blocked.map((item) => /* @__PURE__ */ jsxs7("section", { children: [
|
|
2391
2386
|
/* @__PURE__ */ jsx8(HarnessLogo, { id: item.id, size: 25 }),
|
|
2392
2387
|
/* @__PURE__ */ jsxs7("span", { children: [
|
|
@@ -2402,6 +2397,79 @@ function HarnessReadiness({ harnesses, adapter }) {
|
|
|
2402
2397
|
] }, item.id)) })
|
|
2403
2398
|
] });
|
|
2404
2399
|
}
|
|
2400
|
+
function HarnessPicker({ harnesses, value, disabled = false, adapter, onChange, logo: Logo = HarnessLogo }) {
|
|
2401
|
+
const [open, setOpen] = useState5(false);
|
|
2402
|
+
const menuId = useId2();
|
|
2403
|
+
const root = useRef6(null);
|
|
2404
|
+
const trigger = useRef6(null);
|
|
2405
|
+
const panel = useRef6(null);
|
|
2406
|
+
const available = harnesses.filter((item) => item.startable);
|
|
2407
|
+
const unavailable = harnesses.filter((item) => !item.startable);
|
|
2408
|
+
const selected = available.find((item) => item.id === value) ?? available[0] ?? null;
|
|
2409
|
+
const close = () => {
|
|
2410
|
+
setOpen(false);
|
|
2411
|
+
trigger.current?.focus({ preventScroll: true });
|
|
2412
|
+
};
|
|
2413
|
+
useEffect7(() => {
|
|
2414
|
+
if (!open) return;
|
|
2415
|
+
panel.current?.querySelector('[aria-selected="true"], [role="option"]')?.focus({ preventScroll: true });
|
|
2416
|
+
const dismiss = (event) => {
|
|
2417
|
+
if (event.type === "keydown" && event.key === "Escape") {
|
|
2418
|
+
event.preventDefault();
|
|
2419
|
+
close();
|
|
2420
|
+
} else if (event.type === "pointerdown" && !root.current?.contains(event.target)) {
|
|
2421
|
+
setOpen(false);
|
|
2422
|
+
}
|
|
2423
|
+
};
|
|
2424
|
+
document.addEventListener("keydown", dismiss);
|
|
2425
|
+
document.addEventListener("pointerdown", dismiss, true);
|
|
2426
|
+
return () => {
|
|
2427
|
+
document.removeEventListener("keydown", dismiss);
|
|
2428
|
+
document.removeEventListener("pointerdown", dismiss, true);
|
|
2429
|
+
};
|
|
2430
|
+
}, [open]);
|
|
2431
|
+
const navigate = (event) => {
|
|
2432
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
|
|
2433
|
+
const items = [...panel.current.querySelectorAll('[role="option"]')];
|
|
2434
|
+
if (!items.length) return;
|
|
2435
|
+
event.preventDefault();
|
|
2436
|
+
const current = items.indexOf(document.activeElement);
|
|
2437
|
+
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;
|
|
2438
|
+
items[index].focus({ preventScroll: true });
|
|
2439
|
+
};
|
|
2440
|
+
const select = (id) => {
|
|
2441
|
+
onChange(id);
|
|
2442
|
+
close();
|
|
2443
|
+
};
|
|
2444
|
+
return /* @__PURE__ */ jsxs7("div", { className: "scui-harness-menu", ref: root, onBlur: (event) => {
|
|
2445
|
+
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
2446
|
+
}, children: [
|
|
2447
|
+
/* @__PURE__ */ jsxs7("button", { ref: trigger, className: "scui-harness-trigger", type: "button", disabled: disabled || !selected, "aria-label": selected ? `Coding harness: ${selected.label}` : "No coding harness available", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": open ? menuId : void 0, onClick: () => setOpen((current) => !current), children: [
|
|
2448
|
+
selected ? /* @__PURE__ */ jsx8(Logo, { id: selected.id, size: 20 }) : null,
|
|
2449
|
+
/* @__PURE__ */ jsx8("strong", { children: selected?.label ?? "No harness available" }),
|
|
2450
|
+
/* @__PURE__ */ jsx8(UiIcon, { name: "chevron", size: 13 })
|
|
2451
|
+
] }),
|
|
2452
|
+
open ? /* @__PURE__ */ jsxs7("div", { ref: panel, id: menuId, className: "scui-harness-popover", role: "dialog", "aria-label": "Choose coding harness", onKeyDown: navigate, children: [
|
|
2453
|
+
/* @__PURE__ */ jsx8("strong", { className: "scui-harness-popover-title", children: "Choose coding harness" }),
|
|
2454
|
+
/* @__PURE__ */ jsx8("div", { className: "scui-harness-options", role: "listbox", "aria-label": "Available coding harnesses", children: available.map((item) => /* @__PURE__ */ jsxs7("button", { type: "button", role: "option", "aria-selected": item.id === selected?.id, onClick: () => select(item.id), children: [
|
|
2455
|
+
/* @__PURE__ */ jsx8(Logo, { id: item.id, size: 24 }),
|
|
2456
|
+
/* @__PURE__ */ jsx8("strong", { children: item.label }),
|
|
2457
|
+
/* @__PURE__ */ jsx8("span", { children: item.id === selected?.id ? /* @__PURE__ */ jsx8(UiIcon, { name: "check", size: 15 }) : null })
|
|
2458
|
+
] }, item.id)) }),
|
|
2459
|
+
unavailable.length ? /* @__PURE__ */ jsxs7("details", { className: "scui-harness-manage", children: [
|
|
2460
|
+
/* @__PURE__ */ jsx8("summary", { children: "Manage additional harnesses" }),
|
|
2461
|
+
/* @__PURE__ */ jsx8("div", { children: unavailable.map((item) => /* @__PURE__ */ jsxs7("section", { children: [
|
|
2462
|
+
/* @__PURE__ */ jsx8(Logo, { id: item.id, size: 24 }),
|
|
2463
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2464
|
+
/* @__PURE__ */ jsx8("strong", { children: item.label }),
|
|
2465
|
+
/* @__PURE__ */ jsx8("small", { children: item.reason || (item.auth === "required" ? "Authentication required" : "Unavailable") })
|
|
2466
|
+
] }),
|
|
2467
|
+
item.repair ? /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => adapter?.copyText?.(item.repair), children: "Copy fix" }) : null
|
|
2468
|
+
] }, item.id)) })
|
|
2469
|
+
] }) : null
|
|
2470
|
+
] }) : null
|
|
2471
|
+
] });
|
|
2472
|
+
}
|
|
2405
2473
|
|
|
2406
2474
|
// src/messenger.jsx
|
|
2407
2475
|
import { jsx as jsx9, jsxs as jsxs8 } from "preact/jsx-runtime";
|
|
@@ -2695,6 +2763,7 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2695
2763
|
const startSequence = useRef7(0);
|
|
2696
2764
|
const textarea = useRef7(null);
|
|
2697
2765
|
const selectedHarness = startable.find((item) => item.id === harness) ?? null;
|
|
2766
|
+
const Picker = components.HarnessPicker ?? HarnessPicker;
|
|
2698
2767
|
const Readiness = components.HarnessReadiness ?? HarnessReadiness;
|
|
2699
2768
|
const launchModes = selectedHarness?.launchModes?.length ? selectedHarness.launchModes : ["headless"];
|
|
2700
2769
|
const rememberedMode = modes[harness];
|
|
@@ -2832,18 +2901,6 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2832
2901
|
/* @__PURE__ */ jsx9("small", { children: "Choose a coding harness and send the first message." })
|
|
2833
2902
|
] }),
|
|
2834
2903
|
/* @__PURE__ */ jsxs8("div", { className: "scui-compose", children: [
|
|
2835
|
-
/* @__PURE__ */ jsxs8("label", { className: "scui-harness-picker", children: [
|
|
2836
|
-
/* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 24 }),
|
|
2837
|
-
/* @__PURE__ */ jsx9("span", { children: "Coding harness" }),
|
|
2838
|
-
/* @__PURE__ */ jsx9("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
|
|
2839
|
-
const value = event.currentTarget.value;
|
|
2840
|
-
setHarness(value);
|
|
2841
|
-
remember({ harness: value });
|
|
2842
|
-
}, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs8("option", { value: item.id, disabled: !item.startable, children: [
|
|
2843
|
-
item.label,
|
|
2844
|
-
item.startable ? "" : " \xB7 unavailable"
|
|
2845
|
-
] }, item.id)) })
|
|
2846
|
-
] }),
|
|
2847
2904
|
/* @__PURE__ */ jsx9(Readiness, { harnesses: state.harnesses, state, adapter }),
|
|
2848
2905
|
launchModes.length > 1 ? /* @__PURE__ */ jsxs8("fieldset", { className: "scui-launch-modes", disabled: Boolean(starting), children: [
|
|
2849
2906
|
/* @__PURE__ */ jsx9("legend", { children: "Run as" }),
|
|
@@ -2872,7 +2929,7 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2872
2929
|
return next;
|
|
2873
2930
|
}) }),
|
|
2874
2931
|
terminalAttachments ? /* @__PURE__ */ jsx9("small", { className: "scui-context-error", role: "alert", children: "Terminal starts currently accept text only. Remove attachments or choose Chat." }) : pickerError ? /* @__PURE__ */ jsx9("small", { className: "scui-context-error", role: "alert", children: pickerError }) : null,
|
|
2875
|
-
/* @__PURE__ */ jsxs8("div", { className: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
2932
|
+
/* @__PURE__ */ jsxs8("div", { className: `scui-envelope scui-new-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
2876
2933
|
if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
|
|
2877
2934
|
event.preventDefault();
|
|
2878
2935
|
setDragging(true);
|
|
@@ -2882,7 +2939,6 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2882
2939
|
}, onDragLeave: (event) => {
|
|
2883
2940
|
if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
|
|
2884
2941
|
}, onDrop: dropImages, children: [
|
|
2885
|
-
adapter.pickContext ? /* @__PURE__ */ jsx9("button", { className: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: mode === "terminal" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx9("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
2886
2942
|
/* @__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) => {
|
|
2887
2943
|
const value = event.currentTarget.value;
|
|
2888
2944
|
setDraft(value);
|
|
@@ -2893,7 +2949,14 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2893
2949
|
send();
|
|
2894
2950
|
}
|
|
2895
2951
|
} }),
|
|
2896
|
-
/* @__PURE__ */
|
|
2952
|
+
/* @__PURE__ */ jsxs8("footer", { className: "scui-new-envelope-controls", children: [
|
|
2953
|
+
adapter.pickContext ? /* @__PURE__ */ jsx9("button", { className: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: mode === "terminal" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx9("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
2954
|
+
/* @__PURE__ */ jsx9(Picker, { harnesses: state.harnesses, value: harness, disabled: Boolean(starting), adapter, logo: components.HarnessLogo, onChange: (value) => {
|
|
2955
|
+
setHarness(value);
|
|
2956
|
+
remember({ harness: value });
|
|
2957
|
+
} }),
|
|
2958
|
+
/* @__PURE__ */ jsx9("span", { children: /* @__PURE__ */ jsx9("button", { type: "button", className: "scui-send", "aria-label": mode === "terminal" ? "Start terminal session" : "Start chat", disabled: !draft.trim() && !images.length || !harness || Boolean(starting) || terminalAttachments || mode === "terminal" && !draft.trim(), onClick: send, children: starting ? /* @__PURE__ */ jsx9("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "send", size: 17 }) }) })
|
|
2959
|
+
] })
|
|
2897
2960
|
] })
|
|
2898
2961
|
] })
|
|
2899
2962
|
] });
|
package/index.d.ts
CHANGED
|
@@ -433,6 +433,15 @@ export interface HarnessSettingsPanelProps {
|
|
|
433
433
|
onClose(): void;
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
+
export interface HarnessPickerProps {
|
|
437
|
+
harnesses: HarnessOption[];
|
|
438
|
+
value: HarnessId;
|
|
439
|
+
disabled?: boolean;
|
|
440
|
+
adapter?: Pick<UiAdapter, 'copyText'>;
|
|
441
|
+
onChange(harness: HarnessId): void;
|
|
442
|
+
logo?: ComponentType<{ id: HarnessId; activity?: SessionActivity; size?: number }>;
|
|
443
|
+
}
|
|
444
|
+
|
|
436
445
|
export interface MessengerComponents {
|
|
437
446
|
SessionRow?: ComponentType<SessionRowProps>;
|
|
438
447
|
TranscriptEntry?: ComponentType<TranscriptEntryProps>;
|
|
@@ -441,6 +450,7 @@ export interface MessengerComponents {
|
|
|
441
450
|
HarnessLogo?: ComponentType<{ id: HarnessId; activity?: SessionActivity; size?: number }>;
|
|
442
451
|
HarnessAdvisory?: ComponentType<HarnessAdvisoryProps>;
|
|
443
452
|
HarnessSettingsPanel?: ComponentType<HarnessSettingsPanelProps>;
|
|
453
|
+
HarnessPicker?: ComponentType<HarnessPickerProps>;
|
|
444
454
|
ContextCandidate?: ComponentType<ContextCandidateProps>;
|
|
445
455
|
HarnessReadiness?: ComponentType<{ harnesses: HarnessOption[]; state: SupercodeUiState; adapter: UiAdapter }>;
|
|
446
456
|
}
|
|
@@ -532,6 +542,7 @@ export function ToolRow(props: ToolRowProps): VNode;
|
|
|
532
542
|
export function TaskPlan(props: { plan: TaskPlanModel }): VNode | null;
|
|
533
543
|
export function HarnessAdvisory(props: HarnessAdvisoryProps): VNode | null;
|
|
534
544
|
export function HarnessSettingsPanel(props: HarnessSettingsPanelProps): VNode;
|
|
545
|
+
export function HarnessPicker(props: HarnessPickerProps): VNode;
|
|
535
546
|
export function HarnessReadiness(props: { harnesses: HarnessOption[]; state: SupercodeUiState; adapter: UiAdapter }): VNode | null;
|
|
536
547
|
export function SessionDetails(props: { semantics: SessionSemanticsModel }): VNode | null;
|
|
537
548
|
export function Conversation(props: { state: SupercodeUiState; adapter: UiAdapter; components?: MessengerComponents; slots?: MessengerSlots; memoryKey?: string; pending?: string | PendingMessageModel | null; unreadAfterMessages?: number | null }): VNode;
|
package/messenger.mjs
CHANGED
|
@@ -2376,14 +2376,9 @@ function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = nul
|
|
|
2376
2376
|
}
|
|
2377
2377
|
function HarnessReadiness({ harnesses, adapter }) {
|
|
2378
2378
|
const blocked = harnesses.filter((item) => !item.startable);
|
|
2379
|
-
if (!blocked.length) return null;
|
|
2380
|
-
return /* @__PURE__ */ jsxs7("details", { className: "scui-readiness", open:
|
|
2381
|
-
/* @__PURE__ */
|
|
2382
|
-
blocked.length,
|
|
2383
|
-
" ",
|
|
2384
|
-
blocked.length === 1 ? "harness needs" : "harnesses need",
|
|
2385
|
-
" setup"
|
|
2386
|
-
] }),
|
|
2379
|
+
if (!blocked.length || harnesses.some((item) => item.startable)) return null;
|
|
2380
|
+
return /* @__PURE__ */ jsxs7("details", { className: "scui-readiness", open: true, children: [
|
|
2381
|
+
/* @__PURE__ */ jsx8("summary", { children: "No coding harness is ready" }),
|
|
2387
2382
|
/* @__PURE__ */ jsx8("div", { children: blocked.map((item) => /* @__PURE__ */ jsxs7("section", { children: [
|
|
2388
2383
|
/* @__PURE__ */ jsx8(HarnessLogo, { id: item.id, size: 25 }),
|
|
2389
2384
|
/* @__PURE__ */ jsxs7("span", { children: [
|
|
@@ -2399,6 +2394,79 @@ function HarnessReadiness({ harnesses, adapter }) {
|
|
|
2399
2394
|
] }, item.id)) })
|
|
2400
2395
|
] });
|
|
2401
2396
|
}
|
|
2397
|
+
function HarnessPicker({ harnesses, value, disabled = false, adapter, onChange, logo: Logo = HarnessLogo }) {
|
|
2398
|
+
const [open, setOpen] = useState5(false);
|
|
2399
|
+
const menuId = useId2();
|
|
2400
|
+
const root = useRef6(null);
|
|
2401
|
+
const trigger = useRef6(null);
|
|
2402
|
+
const panel = useRef6(null);
|
|
2403
|
+
const available = harnesses.filter((item) => item.startable);
|
|
2404
|
+
const unavailable = harnesses.filter((item) => !item.startable);
|
|
2405
|
+
const selected = available.find((item) => item.id === value) ?? available[0] ?? null;
|
|
2406
|
+
const close = () => {
|
|
2407
|
+
setOpen(false);
|
|
2408
|
+
trigger.current?.focus({ preventScroll: true });
|
|
2409
|
+
};
|
|
2410
|
+
useEffect7(() => {
|
|
2411
|
+
if (!open) return;
|
|
2412
|
+
panel.current?.querySelector('[aria-selected="true"], [role="option"]')?.focus({ preventScroll: true });
|
|
2413
|
+
const dismiss = (event) => {
|
|
2414
|
+
if (event.type === "keydown" && event.key === "Escape") {
|
|
2415
|
+
event.preventDefault();
|
|
2416
|
+
close();
|
|
2417
|
+
} else if (event.type === "pointerdown" && !root.current?.contains(event.target)) {
|
|
2418
|
+
setOpen(false);
|
|
2419
|
+
}
|
|
2420
|
+
};
|
|
2421
|
+
document.addEventListener("keydown", dismiss);
|
|
2422
|
+
document.addEventListener("pointerdown", dismiss, true);
|
|
2423
|
+
return () => {
|
|
2424
|
+
document.removeEventListener("keydown", dismiss);
|
|
2425
|
+
document.removeEventListener("pointerdown", dismiss, true);
|
|
2426
|
+
};
|
|
2427
|
+
}, [open]);
|
|
2428
|
+
const navigate = (event) => {
|
|
2429
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
|
|
2430
|
+
const items = [...panel.current.querySelectorAll('[role="option"]')];
|
|
2431
|
+
if (!items.length) return;
|
|
2432
|
+
event.preventDefault();
|
|
2433
|
+
const current = items.indexOf(document.activeElement);
|
|
2434
|
+
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;
|
|
2435
|
+
items[index].focus({ preventScroll: true });
|
|
2436
|
+
};
|
|
2437
|
+
const select = (id) => {
|
|
2438
|
+
onChange(id);
|
|
2439
|
+
close();
|
|
2440
|
+
};
|
|
2441
|
+
return /* @__PURE__ */ jsxs7("div", { className: "scui-harness-menu", ref: root, onBlur: (event) => {
|
|
2442
|
+
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
2443
|
+
}, children: [
|
|
2444
|
+
/* @__PURE__ */ jsxs7("button", { ref: trigger, className: "scui-harness-trigger", type: "button", disabled: disabled || !selected, "aria-label": selected ? `Coding harness: ${selected.label}` : "No coding harness available", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": open ? menuId : void 0, onClick: () => setOpen((current) => !current), children: [
|
|
2445
|
+
selected ? /* @__PURE__ */ jsx8(Logo, { id: selected.id, size: 20 }) : null,
|
|
2446
|
+
/* @__PURE__ */ jsx8("strong", { children: selected?.label ?? "No harness available" }),
|
|
2447
|
+
/* @__PURE__ */ jsx8(UiIcon, { name: "chevron", size: 13 })
|
|
2448
|
+
] }),
|
|
2449
|
+
open ? /* @__PURE__ */ jsxs7("div", { ref: panel, id: menuId, className: "scui-harness-popover", role: "dialog", "aria-label": "Choose coding harness", onKeyDown: navigate, children: [
|
|
2450
|
+
/* @__PURE__ */ jsx8("strong", { className: "scui-harness-popover-title", children: "Choose coding harness" }),
|
|
2451
|
+
/* @__PURE__ */ jsx8("div", { className: "scui-harness-options", role: "listbox", "aria-label": "Available coding harnesses", children: available.map((item) => /* @__PURE__ */ jsxs7("button", { type: "button", role: "option", "aria-selected": item.id === selected?.id, onClick: () => select(item.id), children: [
|
|
2452
|
+
/* @__PURE__ */ jsx8(Logo, { id: item.id, size: 24 }),
|
|
2453
|
+
/* @__PURE__ */ jsx8("strong", { children: item.label }),
|
|
2454
|
+
/* @__PURE__ */ jsx8("span", { children: item.id === selected?.id ? /* @__PURE__ */ jsx8(UiIcon, { name: "check", size: 15 }) : null })
|
|
2455
|
+
] }, item.id)) }),
|
|
2456
|
+
unavailable.length ? /* @__PURE__ */ jsxs7("details", { className: "scui-harness-manage", children: [
|
|
2457
|
+
/* @__PURE__ */ jsx8("summary", { children: "Manage additional harnesses" }),
|
|
2458
|
+
/* @__PURE__ */ jsx8("div", { children: unavailable.map((item) => /* @__PURE__ */ jsxs7("section", { children: [
|
|
2459
|
+
/* @__PURE__ */ jsx8(Logo, { id: item.id, size: 24 }),
|
|
2460
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2461
|
+
/* @__PURE__ */ jsx8("strong", { children: item.label }),
|
|
2462
|
+
/* @__PURE__ */ jsx8("small", { children: item.reason || (item.auth === "required" ? "Authentication required" : "Unavailable") })
|
|
2463
|
+
] }),
|
|
2464
|
+
item.repair ? /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => adapter?.copyText?.(item.repair), children: "Copy fix" }) : null
|
|
2465
|
+
] }, item.id)) })
|
|
2466
|
+
] }) : null
|
|
2467
|
+
] }) : null
|
|
2468
|
+
] });
|
|
2469
|
+
}
|
|
2402
2470
|
|
|
2403
2471
|
// src/messenger.jsx
|
|
2404
2472
|
import { jsx as jsx9, jsxs as jsxs8 } from "preact/jsx-runtime";
|
|
@@ -2692,6 +2760,7 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2692
2760
|
const startSequence = useRef7(0);
|
|
2693
2761
|
const textarea = useRef7(null);
|
|
2694
2762
|
const selectedHarness = startable.find((item) => item.id === harness) ?? null;
|
|
2763
|
+
const Picker = components.HarnessPicker ?? HarnessPicker;
|
|
2695
2764
|
const Readiness = components.HarnessReadiness ?? HarnessReadiness;
|
|
2696
2765
|
const launchModes = selectedHarness?.launchModes?.length ? selectedHarness.launchModes : ["headless"];
|
|
2697
2766
|
const rememberedMode = modes[harness];
|
|
@@ -2829,18 +2898,6 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2829
2898
|
/* @__PURE__ */ jsx9("small", { children: "Choose a coding harness and send the first message." })
|
|
2830
2899
|
] }),
|
|
2831
2900
|
/* @__PURE__ */ jsxs8("div", { className: "scui-compose", children: [
|
|
2832
|
-
/* @__PURE__ */ jsxs8("label", { className: "scui-harness-picker", children: [
|
|
2833
|
-
/* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 24 }),
|
|
2834
|
-
/* @__PURE__ */ jsx9("span", { children: "Coding harness" }),
|
|
2835
|
-
/* @__PURE__ */ jsx9("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
|
|
2836
|
-
const value = event.currentTarget.value;
|
|
2837
|
-
setHarness(value);
|
|
2838
|
-
remember({ harness: value });
|
|
2839
|
-
}, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs8("option", { value: item.id, disabled: !item.startable, children: [
|
|
2840
|
-
item.label,
|
|
2841
|
-
item.startable ? "" : " \xB7 unavailable"
|
|
2842
|
-
] }, item.id)) })
|
|
2843
|
-
] }),
|
|
2844
2901
|
/* @__PURE__ */ jsx9(Readiness, { harnesses: state.harnesses, state, adapter }),
|
|
2845
2902
|
launchModes.length > 1 ? /* @__PURE__ */ jsxs8("fieldset", { className: "scui-launch-modes", disabled: Boolean(starting), children: [
|
|
2846
2903
|
/* @__PURE__ */ jsx9("legend", { children: "Run as" }),
|
|
@@ -2869,7 +2926,7 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2869
2926
|
return next;
|
|
2870
2927
|
}) }),
|
|
2871
2928
|
terminalAttachments ? /* @__PURE__ */ jsx9("small", { className: "scui-context-error", role: "alert", children: "Terminal starts currently accept text only. Remove attachments or choose Chat." }) : pickerError ? /* @__PURE__ */ jsx9("small", { className: "scui-context-error", role: "alert", children: pickerError }) : null,
|
|
2872
|
-
/* @__PURE__ */ jsxs8("div", { className: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
2929
|
+
/* @__PURE__ */ jsxs8("div", { className: `scui-envelope scui-new-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
2873
2930
|
if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
|
|
2874
2931
|
event.preventDefault();
|
|
2875
2932
|
setDragging(true);
|
|
@@ -2879,7 +2936,6 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2879
2936
|
}, onDragLeave: (event) => {
|
|
2880
2937
|
if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
|
|
2881
2938
|
}, onDrop: dropImages, children: [
|
|
2882
|
-
adapter.pickContext ? /* @__PURE__ */ jsx9("button", { className: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: mode === "terminal" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx9("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
2883
2939
|
/* @__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) => {
|
|
2884
2940
|
const value = event.currentTarget.value;
|
|
2885
2941
|
setDraft(value);
|
|
@@ -2890,7 +2946,14 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2890
2946
|
send();
|
|
2891
2947
|
}
|
|
2892
2948
|
} }),
|
|
2893
|
-
/* @__PURE__ */
|
|
2949
|
+
/* @__PURE__ */ jsxs8("footer", { className: "scui-new-envelope-controls", children: [
|
|
2950
|
+
adapter.pickContext ? /* @__PURE__ */ jsx9("button", { className: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: mode === "terminal" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx9("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
2951
|
+
/* @__PURE__ */ jsx9(Picker, { harnesses: state.harnesses, value: harness, disabled: Boolean(starting), adapter, logo: components.HarnessLogo, onChange: (value) => {
|
|
2952
|
+
setHarness(value);
|
|
2953
|
+
remember({ harness: value });
|
|
2954
|
+
} }),
|
|
2955
|
+
/* @__PURE__ */ jsx9("span", { children: /* @__PURE__ */ jsx9("button", { type: "button", className: "scui-send", "aria-label": mode === "terminal" ? "Start terminal session" : "Start chat", disabled: !draft.trim() && !images.length || !harness || Boolean(starting) || terminalAttachments || mode === "terminal" && !draft.trim(), onClick: send, children: starting ? /* @__PURE__ */ jsx9("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "send", size: 17 }) }) })
|
|
2956
|
+
] })
|
|
2894
2957
|
] })
|
|
2895
2958
|
] })
|
|
2896
2959
|
] });
|
package/package.json
CHANGED
package/react/components.mjs
CHANGED
|
@@ -2459,14 +2459,9 @@ function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = nul
|
|
|
2459
2459
|
}
|
|
2460
2460
|
function HarnessReadiness({ harnesses, adapter }) {
|
|
2461
2461
|
const blocked = harnesses.filter((item) => !item.startable);
|
|
2462
|
-
if (!blocked.length) return null;
|
|
2463
|
-
return /* @__PURE__ */ jsxs8("details", { className: "scui-readiness", open:
|
|
2464
|
-
/* @__PURE__ */
|
|
2465
|
-
blocked.length,
|
|
2466
|
-
" ",
|
|
2467
|
-
blocked.length === 1 ? "harness needs" : "harnesses need",
|
|
2468
|
-
" setup"
|
|
2469
|
-
] }),
|
|
2462
|
+
if (!blocked.length || harnesses.some((item) => item.startable)) return null;
|
|
2463
|
+
return /* @__PURE__ */ jsxs8("details", { className: "scui-readiness", open: true, children: [
|
|
2464
|
+
/* @__PURE__ */ jsx9("summary", { children: "No coding harness is ready" }),
|
|
2470
2465
|
/* @__PURE__ */ jsx9("div", { children: blocked.map((item) => /* @__PURE__ */ jsxs8("section", { children: [
|
|
2471
2466
|
/* @__PURE__ */ jsx9(HarnessLogo, { id: item.id, size: 25 }),
|
|
2472
2467
|
/* @__PURE__ */ jsxs8("span", { children: [
|
|
@@ -2482,6 +2477,79 @@ function HarnessReadiness({ harnesses, adapter }) {
|
|
|
2482
2477
|
] }, item.id)) })
|
|
2483
2478
|
] });
|
|
2484
2479
|
}
|
|
2480
|
+
function HarnessPicker({ harnesses, value, disabled = false, adapter, onChange, logo: Logo = HarnessLogo }) {
|
|
2481
|
+
const [open, setOpen] = useState5(false);
|
|
2482
|
+
const menuId = useId2();
|
|
2483
|
+
const root = useRef6(null);
|
|
2484
|
+
const trigger = useRef6(null);
|
|
2485
|
+
const panel = useRef6(null);
|
|
2486
|
+
const available = harnesses.filter((item) => item.startable);
|
|
2487
|
+
const unavailable = harnesses.filter((item) => !item.startable);
|
|
2488
|
+
const selected = available.find((item) => item.id === value) ?? available[0] ?? null;
|
|
2489
|
+
const close = () => {
|
|
2490
|
+
setOpen(false);
|
|
2491
|
+
trigger.current?.focus({ preventScroll: true });
|
|
2492
|
+
};
|
|
2493
|
+
useEffect7(() => {
|
|
2494
|
+
if (!open) return;
|
|
2495
|
+
panel.current?.querySelector('[aria-selected="true"], [role="option"]')?.focus({ preventScroll: true });
|
|
2496
|
+
const dismiss = (event) => {
|
|
2497
|
+
if (event.type === "keydown" && event.key === "Escape") {
|
|
2498
|
+
event.preventDefault();
|
|
2499
|
+
close();
|
|
2500
|
+
} else if (event.type === "pointerdown" && !root.current?.contains(event.target)) {
|
|
2501
|
+
setOpen(false);
|
|
2502
|
+
}
|
|
2503
|
+
};
|
|
2504
|
+
document.addEventListener("keydown", dismiss);
|
|
2505
|
+
document.addEventListener("pointerdown", dismiss, true);
|
|
2506
|
+
return () => {
|
|
2507
|
+
document.removeEventListener("keydown", dismiss);
|
|
2508
|
+
document.removeEventListener("pointerdown", dismiss, true);
|
|
2509
|
+
};
|
|
2510
|
+
}, [open]);
|
|
2511
|
+
const navigate = (event) => {
|
|
2512
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
|
|
2513
|
+
const items = [...panel.current.querySelectorAll('[role="option"]')];
|
|
2514
|
+
if (!items.length) return;
|
|
2515
|
+
event.preventDefault();
|
|
2516
|
+
const current = items.indexOf(document.activeElement);
|
|
2517
|
+
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;
|
|
2518
|
+
items[index].focus({ preventScroll: true });
|
|
2519
|
+
};
|
|
2520
|
+
const select = (id) => {
|
|
2521
|
+
onChange(id);
|
|
2522
|
+
close();
|
|
2523
|
+
};
|
|
2524
|
+
return /* @__PURE__ */ jsxs8("div", { className: "scui-harness-menu", ref: root, onBlur: (event) => {
|
|
2525
|
+
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
2526
|
+
}, children: [
|
|
2527
|
+
/* @__PURE__ */ jsxs8("button", { ref: trigger, className: "scui-harness-trigger", type: "button", disabled: disabled || !selected, "aria-label": selected ? `Coding harness: ${selected.label}` : "No coding harness available", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": open ? menuId : void 0, onClick: () => setOpen((current) => !current), children: [
|
|
2528
|
+
selected ? /* @__PURE__ */ jsx9(Logo, { id: selected.id, size: 20 }) : null,
|
|
2529
|
+
/* @__PURE__ */ jsx9("strong", { children: selected?.label ?? "No harness available" }),
|
|
2530
|
+
/* @__PURE__ */ jsx9(UiIcon, { name: "chevron", size: 13 })
|
|
2531
|
+
] }),
|
|
2532
|
+
open ? /* @__PURE__ */ jsxs8("div", { ref: panel, id: menuId, className: "scui-harness-popover", role: "dialog", "aria-label": "Choose coding harness", onKeyDown: navigate, children: [
|
|
2533
|
+
/* @__PURE__ */ jsx9("strong", { className: "scui-harness-popover-title", children: "Choose coding harness" }),
|
|
2534
|
+
/* @__PURE__ */ jsx9("div", { className: "scui-harness-options", role: "listbox", "aria-label": "Available coding harnesses", children: available.map((item) => /* @__PURE__ */ jsxs8("button", { type: "button", role: "option", "aria-selected": item.id === selected?.id, onClick: () => select(item.id), children: [
|
|
2535
|
+
/* @__PURE__ */ jsx9(Logo, { id: item.id, size: 24 }),
|
|
2536
|
+
/* @__PURE__ */ jsx9("strong", { children: item.label }),
|
|
2537
|
+
/* @__PURE__ */ jsx9("span", { children: item.id === selected?.id ? /* @__PURE__ */ jsx9(UiIcon, { name: "check", size: 15 }) : null })
|
|
2538
|
+
] }, item.id)) }),
|
|
2539
|
+
unavailable.length ? /* @__PURE__ */ jsxs8("details", { className: "scui-harness-manage", children: [
|
|
2540
|
+
/* @__PURE__ */ jsx9("summary", { children: "Manage additional harnesses" }),
|
|
2541
|
+
/* @__PURE__ */ jsx9("div", { children: unavailable.map((item) => /* @__PURE__ */ jsxs8("section", { children: [
|
|
2542
|
+
/* @__PURE__ */ jsx9(Logo, { id: item.id, size: 24 }),
|
|
2543
|
+
/* @__PURE__ */ jsxs8("span", { children: [
|
|
2544
|
+
/* @__PURE__ */ jsx9("strong", { children: item.label }),
|
|
2545
|
+
/* @__PURE__ */ jsx9("small", { children: item.reason || (item.auth === "required" ? "Authentication required" : "Unavailable") })
|
|
2546
|
+
] }),
|
|
2547
|
+
item.repair ? /* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter?.copyText?.(item.repair), children: "Copy fix" }) : null
|
|
2548
|
+
] }, item.id)) })
|
|
2549
|
+
] }) : null
|
|
2550
|
+
] }) : null
|
|
2551
|
+
] });
|
|
2552
|
+
}
|
|
2485
2553
|
|
|
2486
2554
|
// src/messenger.jsx
|
|
2487
2555
|
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
@@ -2775,6 +2843,7 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2775
2843
|
const startSequence = useRef7(0);
|
|
2776
2844
|
const textarea = useRef7(null);
|
|
2777
2845
|
const selectedHarness = startable.find((item) => item.id === harness) ?? null;
|
|
2846
|
+
const Picker = components.HarnessPicker ?? HarnessPicker;
|
|
2778
2847
|
const Readiness = components.HarnessReadiness ?? HarnessReadiness;
|
|
2779
2848
|
const launchModes = selectedHarness?.launchModes?.length ? selectedHarness.launchModes : ["headless"];
|
|
2780
2849
|
const rememberedMode = modes[harness];
|
|
@@ -2912,18 +2981,6 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2912
2981
|
/* @__PURE__ */ jsx10("small", { children: "Choose a coding harness and send the first message." })
|
|
2913
2982
|
] }),
|
|
2914
2983
|
/* @__PURE__ */ jsxs9("div", { className: "scui-compose", children: [
|
|
2915
|
-
/* @__PURE__ */ jsxs9("label", { className: "scui-harness-picker", children: [
|
|
2916
|
-
/* @__PURE__ */ jsx10(HarnessLogo, { id: harness, size: 24 }),
|
|
2917
|
-
/* @__PURE__ */ jsx10("span", { children: "Coding harness" }),
|
|
2918
|
-
/* @__PURE__ */ jsx10("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
|
|
2919
|
-
const value = event.currentTarget.value;
|
|
2920
|
-
setHarness(value);
|
|
2921
|
-
remember({ harness: value });
|
|
2922
|
-
}, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs9("option", { value: item.id, disabled: !item.startable, children: [
|
|
2923
|
-
item.label,
|
|
2924
|
-
item.startable ? "" : " \xB7 unavailable"
|
|
2925
|
-
] }, item.id)) })
|
|
2926
|
-
] }),
|
|
2927
2984
|
/* @__PURE__ */ jsx10(Readiness, { harnesses: state.harnesses, state, adapter }),
|
|
2928
2985
|
launchModes.length > 1 ? /* @__PURE__ */ jsxs9("fieldset", { className: "scui-launch-modes", disabled: Boolean(starting), children: [
|
|
2929
2986
|
/* @__PURE__ */ jsx10("legend", { children: "Run as" }),
|
|
@@ -2952,7 +3009,7 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2952
3009
|
return next;
|
|
2953
3010
|
}) }),
|
|
2954
3011
|
terminalAttachments ? /* @__PURE__ */ jsx10("small", { className: "scui-context-error", role: "alert", children: "Terminal starts currently accept text only. Remove attachments or choose Chat." }) : pickerError ? /* @__PURE__ */ jsx10("small", { className: "scui-context-error", role: "alert", children: pickerError }) : null,
|
|
2955
|
-
/* @__PURE__ */ jsxs9("div", { className: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
3012
|
+
/* @__PURE__ */ jsxs9("div", { className: `scui-envelope scui-new-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
2956
3013
|
if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
|
|
2957
3014
|
event.preventDefault();
|
|
2958
3015
|
setDragging(true);
|
|
@@ -2962,7 +3019,6 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2962
3019
|
}, onDragLeave: (event) => {
|
|
2963
3020
|
if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
|
|
2964
3021
|
}, onDrop: dropImages, children: [
|
|
2965
|
-
adapter.pickContext ? /* @__PURE__ */ jsx10("button", { className: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: mode === "terminal" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx10("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx10(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
2966
3022
|
/* @__PURE__ */ jsx10("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) => {
|
|
2967
3023
|
const value = event.currentTarget.value;
|
|
2968
3024
|
setDraft(value);
|
|
@@ -2973,7 +3029,14 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2973
3029
|
send();
|
|
2974
3030
|
}
|
|
2975
3031
|
} }),
|
|
2976
|
-
/* @__PURE__ */
|
|
3032
|
+
/* @__PURE__ */ jsxs9("footer", { className: "scui-new-envelope-controls", children: [
|
|
3033
|
+
adapter.pickContext ? /* @__PURE__ */ jsx10("button", { className: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: mode === "terminal" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx10("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx10(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
3034
|
+
/* @__PURE__ */ jsx10(Picker, { harnesses: state.harnesses, value: harness, disabled: Boolean(starting), adapter, logo: components.HarnessLogo, onChange: (value) => {
|
|
3035
|
+
setHarness(value);
|
|
3036
|
+
remember({ harness: value });
|
|
3037
|
+
} }),
|
|
3038
|
+
/* @__PURE__ */ jsx10("span", { children: /* @__PURE__ */ jsx10("button", { type: "button", className: "scui-send", "aria-label": mode === "terminal" ? "Start terminal session" : "Start chat", disabled: !draft.trim() && !images.length || !harness || Boolean(starting) || terminalAttachments || mode === "terminal" && !draft.trim(), onClick: send, children: starting ? /* @__PURE__ */ jsx10("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx10(UiIcon, { name: "send", size: 17 }) }) })
|
|
3039
|
+
] })
|
|
2977
3040
|
] })
|
|
2978
3041
|
] })
|
|
2979
3042
|
] });
|
|
@@ -3083,6 +3146,7 @@ export {
|
|
|
3083
3146
|
Conversation,
|
|
3084
3147
|
HarnessAdvisory,
|
|
3085
3148
|
HarnessLogo,
|
|
3149
|
+
HarnessPicker,
|
|
3086
3150
|
HarnessReadiness,
|
|
3087
3151
|
HarnessSettingsPanel,
|
|
3088
3152
|
ImageViewer,
|
package/react/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
ContextCandidateProps,
|
|
8
8
|
HarnessAdvisoryProps,
|
|
9
9
|
HarnessId,
|
|
10
|
+
HarnessPickerProps,
|
|
10
11
|
HarnessSettingsPanelProps,
|
|
11
12
|
HarnessOption,
|
|
12
13
|
MessengerLabels,
|
|
@@ -36,6 +37,7 @@ export type {
|
|
|
36
37
|
ContextCandidateProps,
|
|
37
38
|
HarnessAdvisoryProps,
|
|
38
39
|
HarnessId,
|
|
40
|
+
HarnessPickerProps,
|
|
39
41
|
HarnessSettingsPanelProps,
|
|
40
42
|
HarnessOption,
|
|
41
43
|
MessengerLabels,
|
|
@@ -66,6 +68,7 @@ export interface MessengerComponents {
|
|
|
66
68
|
HarnessLogo?: ComponentType<{ id: HarnessId; activity?: SessionActivity; size?: number }>;
|
|
67
69
|
HarnessAdvisory?: ComponentType<HarnessAdvisoryProps>;
|
|
68
70
|
HarnessSettingsPanel?: ComponentType<HarnessSettingsPanelProps>;
|
|
71
|
+
HarnessPicker?: ComponentType<HarnessPickerProps>;
|
|
69
72
|
ContextCandidate?: ComponentType<ContextCandidateProps>;
|
|
70
73
|
HarnessReadiness?: ComponentType<{ harnesses: HarnessOption[]; state: SupercodeUiState; adapter: UiAdapter }>;
|
|
71
74
|
}
|
|
@@ -112,6 +115,7 @@ export function ToolRow(props: ToolRowProps): ReactElement;
|
|
|
112
115
|
export function TaskPlan(props: { plan: TaskPlanModel }): ReactElement | null;
|
|
113
116
|
export function HarnessAdvisory(props: HarnessAdvisoryProps): ReactElement | null;
|
|
114
117
|
export function HarnessSettingsPanel(props: HarnessSettingsPanelProps): ReactElement;
|
|
118
|
+
export function HarnessPicker(props: HarnessPickerProps): ReactElement;
|
|
115
119
|
export function HarnessReadiness(props: { harnesses: HarnessOption[]; state: SupercodeUiState; adapter: UiAdapter }): ReactElement | null;
|
|
116
120
|
export function SessionDetails(props: { semantics: SupercodeUiState['semantics'] }): ReactElement | null;
|
|
117
121
|
export function Conversation(props: { state: SupercodeUiState; adapter: UiAdapter; components?: MessengerComponents; slots?: MessengerSlots; memoryKey?: string; pending?: string | PendingMessageModel | null; unreadAfterMessages?: number | null }): ReactElement;
|
package/react/messenger.mjs
CHANGED
|
@@ -2376,14 +2376,9 @@ function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = nul
|
|
|
2376
2376
|
}
|
|
2377
2377
|
function HarnessReadiness({ harnesses, adapter }) {
|
|
2378
2378
|
const blocked = harnesses.filter((item) => !item.startable);
|
|
2379
|
-
if (!blocked.length) return null;
|
|
2380
|
-
return /* @__PURE__ */ jsxs7("details", { className: "scui-readiness", open:
|
|
2381
|
-
/* @__PURE__ */
|
|
2382
|
-
blocked.length,
|
|
2383
|
-
" ",
|
|
2384
|
-
blocked.length === 1 ? "harness needs" : "harnesses need",
|
|
2385
|
-
" setup"
|
|
2386
|
-
] }),
|
|
2379
|
+
if (!blocked.length || harnesses.some((item) => item.startable)) return null;
|
|
2380
|
+
return /* @__PURE__ */ jsxs7("details", { className: "scui-readiness", open: true, children: [
|
|
2381
|
+
/* @__PURE__ */ jsx8("summary", { children: "No coding harness is ready" }),
|
|
2387
2382
|
/* @__PURE__ */ jsx8("div", { children: blocked.map((item) => /* @__PURE__ */ jsxs7("section", { children: [
|
|
2388
2383
|
/* @__PURE__ */ jsx8(HarnessLogo, { id: item.id, size: 25 }),
|
|
2389
2384
|
/* @__PURE__ */ jsxs7("span", { children: [
|
|
@@ -2399,6 +2394,79 @@ function HarnessReadiness({ harnesses, adapter }) {
|
|
|
2399
2394
|
] }, item.id)) })
|
|
2400
2395
|
] });
|
|
2401
2396
|
}
|
|
2397
|
+
function HarnessPicker({ harnesses, value, disabled = false, adapter, onChange, logo: Logo = HarnessLogo }) {
|
|
2398
|
+
const [open, setOpen] = useState5(false);
|
|
2399
|
+
const menuId = useId2();
|
|
2400
|
+
const root = useRef6(null);
|
|
2401
|
+
const trigger = useRef6(null);
|
|
2402
|
+
const panel = useRef6(null);
|
|
2403
|
+
const available = harnesses.filter((item) => item.startable);
|
|
2404
|
+
const unavailable = harnesses.filter((item) => !item.startable);
|
|
2405
|
+
const selected = available.find((item) => item.id === value) ?? available[0] ?? null;
|
|
2406
|
+
const close = () => {
|
|
2407
|
+
setOpen(false);
|
|
2408
|
+
trigger.current?.focus({ preventScroll: true });
|
|
2409
|
+
};
|
|
2410
|
+
useEffect7(() => {
|
|
2411
|
+
if (!open) return;
|
|
2412
|
+
panel.current?.querySelector('[aria-selected="true"], [role="option"]')?.focus({ preventScroll: true });
|
|
2413
|
+
const dismiss = (event) => {
|
|
2414
|
+
if (event.type === "keydown" && event.key === "Escape") {
|
|
2415
|
+
event.preventDefault();
|
|
2416
|
+
close();
|
|
2417
|
+
} else if (event.type === "pointerdown" && !root.current?.contains(event.target)) {
|
|
2418
|
+
setOpen(false);
|
|
2419
|
+
}
|
|
2420
|
+
};
|
|
2421
|
+
document.addEventListener("keydown", dismiss);
|
|
2422
|
+
document.addEventListener("pointerdown", dismiss, true);
|
|
2423
|
+
return () => {
|
|
2424
|
+
document.removeEventListener("keydown", dismiss);
|
|
2425
|
+
document.removeEventListener("pointerdown", dismiss, true);
|
|
2426
|
+
};
|
|
2427
|
+
}, [open]);
|
|
2428
|
+
const navigate = (event) => {
|
|
2429
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
|
|
2430
|
+
const items = [...panel.current.querySelectorAll('[role="option"]')];
|
|
2431
|
+
if (!items.length) return;
|
|
2432
|
+
event.preventDefault();
|
|
2433
|
+
const current = items.indexOf(document.activeElement);
|
|
2434
|
+
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;
|
|
2435
|
+
items[index].focus({ preventScroll: true });
|
|
2436
|
+
};
|
|
2437
|
+
const select = (id) => {
|
|
2438
|
+
onChange(id);
|
|
2439
|
+
close();
|
|
2440
|
+
};
|
|
2441
|
+
return /* @__PURE__ */ jsxs7("div", { className: "scui-harness-menu", ref: root, onBlur: (event) => {
|
|
2442
|
+
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
2443
|
+
}, children: [
|
|
2444
|
+
/* @__PURE__ */ jsxs7("button", { ref: trigger, className: "scui-harness-trigger", type: "button", disabled: disabled || !selected, "aria-label": selected ? `Coding harness: ${selected.label}` : "No coding harness available", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": open ? menuId : void 0, onClick: () => setOpen((current) => !current), children: [
|
|
2445
|
+
selected ? /* @__PURE__ */ jsx8(Logo, { id: selected.id, size: 20 }) : null,
|
|
2446
|
+
/* @__PURE__ */ jsx8("strong", { children: selected?.label ?? "No harness available" }),
|
|
2447
|
+
/* @__PURE__ */ jsx8(UiIcon, { name: "chevron", size: 13 })
|
|
2448
|
+
] }),
|
|
2449
|
+
open ? /* @__PURE__ */ jsxs7("div", { ref: panel, id: menuId, className: "scui-harness-popover", role: "dialog", "aria-label": "Choose coding harness", onKeyDown: navigate, children: [
|
|
2450
|
+
/* @__PURE__ */ jsx8("strong", { className: "scui-harness-popover-title", children: "Choose coding harness" }),
|
|
2451
|
+
/* @__PURE__ */ jsx8("div", { className: "scui-harness-options", role: "listbox", "aria-label": "Available coding harnesses", children: available.map((item) => /* @__PURE__ */ jsxs7("button", { type: "button", role: "option", "aria-selected": item.id === selected?.id, onClick: () => select(item.id), children: [
|
|
2452
|
+
/* @__PURE__ */ jsx8(Logo, { id: item.id, size: 24 }),
|
|
2453
|
+
/* @__PURE__ */ jsx8("strong", { children: item.label }),
|
|
2454
|
+
/* @__PURE__ */ jsx8("span", { children: item.id === selected?.id ? /* @__PURE__ */ jsx8(UiIcon, { name: "check", size: 15 }) : null })
|
|
2455
|
+
] }, item.id)) }),
|
|
2456
|
+
unavailable.length ? /* @__PURE__ */ jsxs7("details", { className: "scui-harness-manage", children: [
|
|
2457
|
+
/* @__PURE__ */ jsx8("summary", { children: "Manage additional harnesses" }),
|
|
2458
|
+
/* @__PURE__ */ jsx8("div", { children: unavailable.map((item) => /* @__PURE__ */ jsxs7("section", { children: [
|
|
2459
|
+
/* @__PURE__ */ jsx8(Logo, { id: item.id, size: 24 }),
|
|
2460
|
+
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2461
|
+
/* @__PURE__ */ jsx8("strong", { children: item.label }),
|
|
2462
|
+
/* @__PURE__ */ jsx8("small", { children: item.reason || (item.auth === "required" ? "Authentication required" : "Unavailable") })
|
|
2463
|
+
] }),
|
|
2464
|
+
item.repair ? /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => adapter?.copyText?.(item.repair), children: "Copy fix" }) : null
|
|
2465
|
+
] }, item.id)) })
|
|
2466
|
+
] }) : null
|
|
2467
|
+
] }) : null
|
|
2468
|
+
] });
|
|
2469
|
+
}
|
|
2402
2470
|
|
|
2403
2471
|
// src/messenger.jsx
|
|
2404
2472
|
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
@@ -2692,6 +2760,7 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2692
2760
|
const startSequence = useRef7(0);
|
|
2693
2761
|
const textarea = useRef7(null);
|
|
2694
2762
|
const selectedHarness = startable.find((item) => item.id === harness) ?? null;
|
|
2763
|
+
const Picker = components.HarnessPicker ?? HarnessPicker;
|
|
2695
2764
|
const Readiness = components.HarnessReadiness ?? HarnessReadiness;
|
|
2696
2765
|
const launchModes = selectedHarness?.launchModes?.length ? selectedHarness.launchModes : ["headless"];
|
|
2697
2766
|
const rememberedMode = modes[harness];
|
|
@@ -2829,18 +2898,6 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2829
2898
|
/* @__PURE__ */ jsx9("small", { children: "Choose a coding harness and send the first message." })
|
|
2830
2899
|
] }),
|
|
2831
2900
|
/* @__PURE__ */ jsxs8("div", { className: "scui-compose", children: [
|
|
2832
|
-
/* @__PURE__ */ jsxs8("label", { className: "scui-harness-picker", children: [
|
|
2833
|
-
/* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 24 }),
|
|
2834
|
-
/* @__PURE__ */ jsx9("span", { children: "Coding harness" }),
|
|
2835
|
-
/* @__PURE__ */ jsx9("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
|
|
2836
|
-
const value = event.currentTarget.value;
|
|
2837
|
-
setHarness(value);
|
|
2838
|
-
remember({ harness: value });
|
|
2839
|
-
}, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs8("option", { value: item.id, disabled: !item.startable, children: [
|
|
2840
|
-
item.label,
|
|
2841
|
-
item.startable ? "" : " \xB7 unavailable"
|
|
2842
|
-
] }, item.id)) })
|
|
2843
|
-
] }),
|
|
2844
2901
|
/* @__PURE__ */ jsx9(Readiness, { harnesses: state.harnesses, state, adapter }),
|
|
2845
2902
|
launchModes.length > 1 ? /* @__PURE__ */ jsxs8("fieldset", { className: "scui-launch-modes", disabled: Boolean(starting), children: [
|
|
2846
2903
|
/* @__PURE__ */ jsx9("legend", { children: "Run as" }),
|
|
@@ -2869,7 +2926,7 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2869
2926
|
return next;
|
|
2870
2927
|
}) }),
|
|
2871
2928
|
terminalAttachments ? /* @__PURE__ */ jsx9("small", { className: "scui-context-error", role: "alert", children: "Terminal starts currently accept text only. Remove attachments or choose Chat." }) : pickerError ? /* @__PURE__ */ jsx9("small", { className: "scui-context-error", role: "alert", children: pickerError }) : null,
|
|
2872
|
-
/* @__PURE__ */ jsxs8("div", { className: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
2929
|
+
/* @__PURE__ */ jsxs8("div", { className: `scui-envelope scui-new-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
2873
2930
|
if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
|
|
2874
2931
|
event.preventDefault();
|
|
2875
2932
|
setDragging(true);
|
|
@@ -2879,7 +2936,6 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2879
2936
|
}, onDragLeave: (event) => {
|
|
2880
2937
|
if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
|
|
2881
2938
|
}, onDrop: dropImages, children: [
|
|
2882
|
-
adapter.pickContext ? /* @__PURE__ */ jsx9("button", { className: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: mode === "terminal" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx9("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
2883
2939
|
/* @__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) => {
|
|
2884
2940
|
const value = event.currentTarget.value;
|
|
2885
2941
|
setDraft(value);
|
|
@@ -2890,7 +2946,14 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2890
2946
|
send();
|
|
2891
2947
|
}
|
|
2892
2948
|
} }),
|
|
2893
|
-
/* @__PURE__ */
|
|
2949
|
+
/* @__PURE__ */ jsxs8("footer", { className: "scui-new-envelope-controls", children: [
|
|
2950
|
+
adapter.pickContext ? /* @__PURE__ */ jsx9("button", { className: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: mode === "terminal" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx9("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
2951
|
+
/* @__PURE__ */ jsx9(Picker, { harnesses: state.harnesses, value: harness, disabled: Boolean(starting), adapter, logo: components.HarnessLogo, onChange: (value) => {
|
|
2952
|
+
setHarness(value);
|
|
2953
|
+
remember({ harness: value });
|
|
2954
|
+
} }),
|
|
2955
|
+
/* @__PURE__ */ jsx9("span", { children: /* @__PURE__ */ jsx9("button", { type: "button", className: "scui-send", "aria-label": mode === "terminal" ? "Start terminal session" : "Start chat", disabled: !draft.trim() && !images.length || !harness || Boolean(starting) || terminalAttachments || mode === "terminal" && !draft.trim(), onClick: send, children: starting ? /* @__PURE__ */ jsx9("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "send", size: 17 }) }) })
|
|
2956
|
+
] })
|
|
2894
2957
|
] })
|
|
2895
2958
|
] })
|
|
2896
2959
|
] });
|
package/react/settings.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { HarnessAdvisoryProps, HarnessSettingsPanelProps } from './index.js';
|
|
2
|
-
export { HarnessAdvisory, HarnessSettingsPanel } from './index.js';
|
|
1
|
+
export type { HarnessAdvisoryProps, HarnessPickerProps, HarnessSettingsPanelProps } from './index.js';
|
|
2
|
+
export { HarnessAdvisory, HarnessPicker, HarnessSettingsPanel } from './index.js';
|
package/react/settings.mjs
CHANGED
|
@@ -289,14 +289,9 @@ function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = nul
|
|
|
289
289
|
}
|
|
290
290
|
function HarnessReadiness({ harnesses, adapter }) {
|
|
291
291
|
const blocked = harnesses.filter((item) => !item.startable);
|
|
292
|
-
if (!blocked.length) return null;
|
|
293
|
-
return /* @__PURE__ */ jsxs3("details", { className: "scui-readiness", open:
|
|
294
|
-
/* @__PURE__ */
|
|
295
|
-
blocked.length,
|
|
296
|
-
" ",
|
|
297
|
-
blocked.length === 1 ? "harness needs" : "harnesses need",
|
|
298
|
-
" setup"
|
|
299
|
-
] }),
|
|
292
|
+
if (!blocked.length || harnesses.some((item) => item.startable)) return null;
|
|
293
|
+
return /* @__PURE__ */ jsxs3("details", { className: "scui-readiness", open: true, children: [
|
|
294
|
+
/* @__PURE__ */ jsx3("summary", { children: "No coding harness is ready" }),
|
|
300
295
|
/* @__PURE__ */ jsx3("div", { children: blocked.map((item) => /* @__PURE__ */ jsxs3("section", { children: [
|
|
301
296
|
/* @__PURE__ */ jsx3(HarnessLogo, { id: item.id, size: 25 }),
|
|
302
297
|
/* @__PURE__ */ jsxs3("span", { children: [
|
|
@@ -312,8 +307,82 @@ function HarnessReadiness({ harnesses, adapter }) {
|
|
|
312
307
|
] }, item.id)) })
|
|
313
308
|
] });
|
|
314
309
|
}
|
|
310
|
+
function HarnessPicker({ harnesses, value, disabled = false, adapter, onChange, logo: Logo = HarnessLogo }) {
|
|
311
|
+
const [open, setOpen] = useState(false);
|
|
312
|
+
const menuId = useId();
|
|
313
|
+
const root = useRef(null);
|
|
314
|
+
const trigger = useRef(null);
|
|
315
|
+
const panel = useRef(null);
|
|
316
|
+
const available = harnesses.filter((item) => item.startable);
|
|
317
|
+
const unavailable = harnesses.filter((item) => !item.startable);
|
|
318
|
+
const selected = available.find((item) => item.id === value) ?? available[0] ?? null;
|
|
319
|
+
const close = () => {
|
|
320
|
+
setOpen(false);
|
|
321
|
+
trigger.current?.focus({ preventScroll: true });
|
|
322
|
+
};
|
|
323
|
+
useEffect2(() => {
|
|
324
|
+
if (!open) return;
|
|
325
|
+
panel.current?.querySelector('[aria-selected="true"], [role="option"]')?.focus({ preventScroll: true });
|
|
326
|
+
const dismiss = (event) => {
|
|
327
|
+
if (event.type === "keydown" && event.key === "Escape") {
|
|
328
|
+
event.preventDefault();
|
|
329
|
+
close();
|
|
330
|
+
} else if (event.type === "pointerdown" && !root.current?.contains(event.target)) {
|
|
331
|
+
setOpen(false);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
document.addEventListener("keydown", dismiss);
|
|
335
|
+
document.addEventListener("pointerdown", dismiss, true);
|
|
336
|
+
return () => {
|
|
337
|
+
document.removeEventListener("keydown", dismiss);
|
|
338
|
+
document.removeEventListener("pointerdown", dismiss, true);
|
|
339
|
+
};
|
|
340
|
+
}, [open]);
|
|
341
|
+
const navigate = (event) => {
|
|
342
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
|
|
343
|
+
const items = [...panel.current.querySelectorAll('[role="option"]')];
|
|
344
|
+
if (!items.length) return;
|
|
345
|
+
event.preventDefault();
|
|
346
|
+
const current = items.indexOf(document.activeElement);
|
|
347
|
+
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;
|
|
348
|
+
items[index].focus({ preventScroll: true });
|
|
349
|
+
};
|
|
350
|
+
const select = (id) => {
|
|
351
|
+
onChange(id);
|
|
352
|
+
close();
|
|
353
|
+
};
|
|
354
|
+
return /* @__PURE__ */ jsxs3("div", { className: "scui-harness-menu", ref: root, onBlur: (event) => {
|
|
355
|
+
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
356
|
+
}, children: [
|
|
357
|
+
/* @__PURE__ */ jsxs3("button", { ref: trigger, className: "scui-harness-trigger", type: "button", disabled: disabled || !selected, "aria-label": selected ? `Coding harness: ${selected.label}` : "No coding harness available", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": open ? menuId : void 0, onClick: () => setOpen((current) => !current), children: [
|
|
358
|
+
selected ? /* @__PURE__ */ jsx3(Logo, { id: selected.id, size: 20 }) : null,
|
|
359
|
+
/* @__PURE__ */ jsx3("strong", { children: selected?.label ?? "No harness available" }),
|
|
360
|
+
/* @__PURE__ */ jsx3(UiIcon, { name: "chevron", size: 13 })
|
|
361
|
+
] }),
|
|
362
|
+
open ? /* @__PURE__ */ jsxs3("div", { ref: panel, id: menuId, className: "scui-harness-popover", role: "dialog", "aria-label": "Choose coding harness", onKeyDown: navigate, children: [
|
|
363
|
+
/* @__PURE__ */ jsx3("strong", { className: "scui-harness-popover-title", children: "Choose coding harness" }),
|
|
364
|
+
/* @__PURE__ */ jsx3("div", { className: "scui-harness-options", role: "listbox", "aria-label": "Available coding harnesses", children: available.map((item) => /* @__PURE__ */ jsxs3("button", { type: "button", role: "option", "aria-selected": item.id === selected?.id, onClick: () => select(item.id), children: [
|
|
365
|
+
/* @__PURE__ */ jsx3(Logo, { id: item.id, size: 24 }),
|
|
366
|
+
/* @__PURE__ */ jsx3("strong", { children: item.label }),
|
|
367
|
+
/* @__PURE__ */ jsx3("span", { children: item.id === selected?.id ? /* @__PURE__ */ jsx3(UiIcon, { name: "check", size: 15 }) : null })
|
|
368
|
+
] }, item.id)) }),
|
|
369
|
+
unavailable.length ? /* @__PURE__ */ jsxs3("details", { className: "scui-harness-manage", children: [
|
|
370
|
+
/* @__PURE__ */ jsx3("summary", { children: "Manage additional harnesses" }),
|
|
371
|
+
/* @__PURE__ */ jsx3("div", { children: unavailable.map((item) => /* @__PURE__ */ jsxs3("section", { children: [
|
|
372
|
+
/* @__PURE__ */ jsx3(Logo, { id: item.id, size: 24 }),
|
|
373
|
+
/* @__PURE__ */ jsxs3("span", { children: [
|
|
374
|
+
/* @__PURE__ */ jsx3("strong", { children: item.label }),
|
|
375
|
+
/* @__PURE__ */ jsx3("small", { children: item.reason || (item.auth === "required" ? "Authentication required" : "Unavailable") })
|
|
376
|
+
] }),
|
|
377
|
+
item.repair ? /* @__PURE__ */ jsx3("button", { type: "button", onClick: () => adapter?.copyText?.(item.repair), children: "Copy fix" }) : null
|
|
378
|
+
] }, item.id)) })
|
|
379
|
+
] }) : null
|
|
380
|
+
] }) : null
|
|
381
|
+
] });
|
|
382
|
+
}
|
|
315
383
|
export {
|
|
316
384
|
HarnessAdvisory,
|
|
385
|
+
HarnessPicker,
|
|
317
386
|
HarnessReadiness,
|
|
318
387
|
HarnessSettingsPanel
|
|
319
388
|
};
|
package/settings.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { HarnessAdvisory, HarnessSettingsPanel } from './index.js';
|
|
2
|
-
export type { HarnessAdvisoryProps, HarnessSettingsPanelProps } from './index.js';
|
|
1
|
+
export { HarnessAdvisory, HarnessPicker, HarnessSettingsPanel } from './index.js';
|
|
2
|
+
export type { HarnessAdvisoryProps, HarnessPickerProps, HarnessSettingsPanelProps } from './index.js';
|
package/settings.mjs
CHANGED
|
@@ -289,14 +289,9 @@ function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = nul
|
|
|
289
289
|
}
|
|
290
290
|
function HarnessReadiness({ harnesses, adapter }) {
|
|
291
291
|
const blocked = harnesses.filter((item) => !item.startable);
|
|
292
|
-
if (!blocked.length) return null;
|
|
293
|
-
return /* @__PURE__ */ jsxs3("details", { className: "scui-readiness", open:
|
|
294
|
-
/* @__PURE__ */
|
|
295
|
-
blocked.length,
|
|
296
|
-
" ",
|
|
297
|
-
blocked.length === 1 ? "harness needs" : "harnesses need",
|
|
298
|
-
" setup"
|
|
299
|
-
] }),
|
|
292
|
+
if (!blocked.length || harnesses.some((item) => item.startable)) return null;
|
|
293
|
+
return /* @__PURE__ */ jsxs3("details", { className: "scui-readiness", open: true, children: [
|
|
294
|
+
/* @__PURE__ */ jsx3("summary", { children: "No coding harness is ready" }),
|
|
300
295
|
/* @__PURE__ */ jsx3("div", { children: blocked.map((item) => /* @__PURE__ */ jsxs3("section", { children: [
|
|
301
296
|
/* @__PURE__ */ jsx3(HarnessLogo, { id: item.id, size: 25 }),
|
|
302
297
|
/* @__PURE__ */ jsxs3("span", { children: [
|
|
@@ -312,8 +307,82 @@ function HarnessReadiness({ harnesses, adapter }) {
|
|
|
312
307
|
] }, item.id)) })
|
|
313
308
|
] });
|
|
314
309
|
}
|
|
310
|
+
function HarnessPicker({ harnesses, value, disabled = false, adapter, onChange, logo: Logo = HarnessLogo }) {
|
|
311
|
+
const [open, setOpen] = useState(false);
|
|
312
|
+
const menuId = useId();
|
|
313
|
+
const root = useRef(null);
|
|
314
|
+
const trigger = useRef(null);
|
|
315
|
+
const panel = useRef(null);
|
|
316
|
+
const available = harnesses.filter((item) => item.startable);
|
|
317
|
+
const unavailable = harnesses.filter((item) => !item.startable);
|
|
318
|
+
const selected = available.find((item) => item.id === value) ?? available[0] ?? null;
|
|
319
|
+
const close = () => {
|
|
320
|
+
setOpen(false);
|
|
321
|
+
trigger.current?.focus({ preventScroll: true });
|
|
322
|
+
};
|
|
323
|
+
useEffect2(() => {
|
|
324
|
+
if (!open) return;
|
|
325
|
+
panel.current?.querySelector('[aria-selected="true"], [role="option"]')?.focus({ preventScroll: true });
|
|
326
|
+
const dismiss = (event) => {
|
|
327
|
+
if (event.type === "keydown" && event.key === "Escape") {
|
|
328
|
+
event.preventDefault();
|
|
329
|
+
close();
|
|
330
|
+
} else if (event.type === "pointerdown" && !root.current?.contains(event.target)) {
|
|
331
|
+
setOpen(false);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
document.addEventListener("keydown", dismiss);
|
|
335
|
+
document.addEventListener("pointerdown", dismiss, true);
|
|
336
|
+
return () => {
|
|
337
|
+
document.removeEventListener("keydown", dismiss);
|
|
338
|
+
document.removeEventListener("pointerdown", dismiss, true);
|
|
339
|
+
};
|
|
340
|
+
}, [open]);
|
|
341
|
+
const navigate = (event) => {
|
|
342
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
|
|
343
|
+
const items = [...panel.current.querySelectorAll('[role="option"]')];
|
|
344
|
+
if (!items.length) return;
|
|
345
|
+
event.preventDefault();
|
|
346
|
+
const current = items.indexOf(document.activeElement);
|
|
347
|
+
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;
|
|
348
|
+
items[index].focus({ preventScroll: true });
|
|
349
|
+
};
|
|
350
|
+
const select = (id) => {
|
|
351
|
+
onChange(id);
|
|
352
|
+
close();
|
|
353
|
+
};
|
|
354
|
+
return /* @__PURE__ */ jsxs3("div", { className: "scui-harness-menu", ref: root, onBlur: (event) => {
|
|
355
|
+
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
356
|
+
}, children: [
|
|
357
|
+
/* @__PURE__ */ jsxs3("button", { ref: trigger, className: "scui-harness-trigger", type: "button", disabled: disabled || !selected, "aria-label": selected ? `Coding harness: ${selected.label}` : "No coding harness available", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": open ? menuId : void 0, onClick: () => setOpen((current) => !current), children: [
|
|
358
|
+
selected ? /* @__PURE__ */ jsx3(Logo, { id: selected.id, size: 20 }) : null,
|
|
359
|
+
/* @__PURE__ */ jsx3("strong", { children: selected?.label ?? "No harness available" }),
|
|
360
|
+
/* @__PURE__ */ jsx3(UiIcon, { name: "chevron", size: 13 })
|
|
361
|
+
] }),
|
|
362
|
+
open ? /* @__PURE__ */ jsxs3("div", { ref: panel, id: menuId, className: "scui-harness-popover", role: "dialog", "aria-label": "Choose coding harness", onKeyDown: navigate, children: [
|
|
363
|
+
/* @__PURE__ */ jsx3("strong", { className: "scui-harness-popover-title", children: "Choose coding harness" }),
|
|
364
|
+
/* @__PURE__ */ jsx3("div", { className: "scui-harness-options", role: "listbox", "aria-label": "Available coding harnesses", children: available.map((item) => /* @__PURE__ */ jsxs3("button", { type: "button", role: "option", "aria-selected": item.id === selected?.id, onClick: () => select(item.id), children: [
|
|
365
|
+
/* @__PURE__ */ jsx3(Logo, { id: item.id, size: 24 }),
|
|
366
|
+
/* @__PURE__ */ jsx3("strong", { children: item.label }),
|
|
367
|
+
/* @__PURE__ */ jsx3("span", { children: item.id === selected?.id ? /* @__PURE__ */ jsx3(UiIcon, { name: "check", size: 15 }) : null })
|
|
368
|
+
] }, item.id)) }),
|
|
369
|
+
unavailable.length ? /* @__PURE__ */ jsxs3("details", { className: "scui-harness-manage", children: [
|
|
370
|
+
/* @__PURE__ */ jsx3("summary", { children: "Manage additional harnesses" }),
|
|
371
|
+
/* @__PURE__ */ jsx3("div", { children: unavailable.map((item) => /* @__PURE__ */ jsxs3("section", { children: [
|
|
372
|
+
/* @__PURE__ */ jsx3(Logo, { id: item.id, size: 24 }),
|
|
373
|
+
/* @__PURE__ */ jsxs3("span", { children: [
|
|
374
|
+
/* @__PURE__ */ jsx3("strong", { children: item.label }),
|
|
375
|
+
/* @__PURE__ */ jsx3("small", { children: item.reason || (item.auth === "required" ? "Authentication required" : "Unavailable") })
|
|
376
|
+
] }),
|
|
377
|
+
item.repair ? /* @__PURE__ */ jsx3("button", { type: "button", onClick: () => adapter?.copyText?.(item.repair), children: "Copy fix" }) : null
|
|
378
|
+
] }, item.id)) })
|
|
379
|
+
] }) : null
|
|
380
|
+
] }) : null
|
|
381
|
+
] });
|
|
382
|
+
}
|
|
315
383
|
export {
|
|
316
384
|
HarnessAdvisory,
|
|
385
|
+
HarnessPicker,
|
|
317
386
|
HarnessReadiness,
|
|
318
387
|
HarnessSettingsPanel
|
|
319
388
|
};
|
package/styles.css
CHANGED
|
@@ -198,7 +198,10 @@
|
|
|
198
198
|
.scui-send,.scui-stop { display:grid; width:29px; height:29px; padding:0; place-items:center; border:0; border-radius:8px; background:var(--scui-accent); color:#fff; cursor:pointer }.scui-stop { background:var(--scui-danger) }.scui-send:disabled,.scui-stop:disabled { opacity:.4; cursor:default }.scui-control-spinner { box-sizing:border-box; width:13px; height:13px; border:1.5px solid currentColor; border-right-color:transparent; border-radius:50%; animation:scui-spin .75s linear infinite }
|
|
199
199
|
.scui-queue-send { display:grid; width:29px; height:29px; padding:0; place-items:center; border:0; border-radius:8px; background:transparent; color:var(--scui-muted); cursor:pointer }.scui-queue-send:hover { background:var(--scui-fill); color:var(--scui-fg) }
|
|
200
200
|
.scui-queue { display:grid; gap:4px; max-height:85px; margin-bottom:6px; overflow:auto; font-size:10.5px }.scui-queue > span { display:flex; gap:6px; padding:4px 6px; border-radius:5px; background:var(--scui-fill) }.scui-queue > span > span { display:grid; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }.scui-queue small { color:var(--scui-muted) }.scui-queue > span button { margin-left:auto; border:0; background:transparent }
|
|
201
|
-
.scui-
|
|
201
|
+
.scui-new-envelope { display:grid; align-items:stretch; gap:3px; padding:7px }.scui-new-envelope textarea { box-sizing:border-box; width:100%; min-height:54px; padding:2px 3px }.scui-new-envelope-controls { display:flex; min-width:0; align-items:center; gap:3px }.scui-new-envelope-controls > span { display:flex; margin-left:auto }.scui-new-envelope-controls .scui-attach { flex-basis:29px; width:29px; height:29px }
|
|
202
|
+
.scui-harness-menu { position:relative; min-width:0 }.scui-harness-trigger { display:flex; max-width:170px; height:29px; align-items:center; gap:6px; padding:3px 7px 3px 4px; border:0; border-radius:8px; background:transparent; color:var(--scui-fg); cursor:pointer }.scui-harness-trigger:hover:not(:disabled),.scui-harness-trigger:focus-visible,.scui-harness-trigger[aria-expanded="true"] { background:var(--scui-fill) }.scui-harness-trigger:disabled { cursor:default; opacity:.55 }.scui-harness-trigger > strong { min-width:0; overflow:hidden; font-size:10.5px; font-weight:600; text-overflow:ellipsis; white-space:nowrap }.scui-harness-trigger > .scui-icon { flex:none; color:var(--scui-muted); transform:rotate(90deg); transition:transform 120ms }.scui-harness-trigger[aria-expanded="true"] > .scui-icon { transform:rotate(-90deg) }
|
|
203
|
+
.scui-harness-popover { position:absolute; z-index:25; bottom:calc(100% + 7px); left:0; display:grid; box-sizing:border-box; width:min(250px,calc(100vw - 24px)); max-height:min(330px,calc(100dvh - 90px)); overflow:auto; padding:5px; border:1px solid var(--scui-border-strong); border-radius:10px; background:var(--scui-bg-raised); box-shadow:0 12px 34px rgba(0,0,0,.2) }.scui-harness-popover-title { padding:5px 7px; color:var(--scui-muted); font-size:8.5px; font-weight:650; letter-spacing:.055em; text-transform:uppercase }.scui-harness-options { display:grid; gap:1px }.scui-harness-options > button { display:grid; grid-template-columns:auto minmax(0,1fr) 18px; width:100%; min-height:36px; align-items:center; gap:8px; padding:5px 7px; border:0; border-radius:7px; background:transparent; color:var(--scui-fg); text-align:left; cursor:pointer }.scui-harness-options > button:hover,.scui-harness-options > button:focus-visible { background:var(--scui-fill) }.scui-harness-options > button[aria-selected="true"] { background:color-mix(in srgb,var(--scui-accent) 8%,var(--scui-bg-raised)) }.scui-harness-options > button > strong { min-width:0; overflow:hidden; font-size:11px; font-weight:600; text-overflow:ellipsis; white-space:nowrap }.scui-harness-options > button > span { display:grid; color:var(--scui-accent); place-items:center }
|
|
204
|
+
.scui-harness-manage { margin-top:4px; padding-top:4px; border-top:1px solid var(--scui-border) }.scui-harness-manage > summary { padding:6px 7px; color:var(--scui-muted); cursor:pointer; font-size:9.5px }.scui-harness-manage > div { display:grid; gap:1px }.scui-harness-manage section { display:grid; grid-template-columns:auto minmax(0,1fr) auto; min-height:36px; align-items:center; gap:8px; padding:5px 7px }.scui-harness-manage section > span { display:grid; min-width:0 }.scui-harness-manage section strong,.scui-harness-manage section small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }.scui-harness-manage section strong { font-size:10.5px; font-weight:600 }.scui-harness-manage section small { color:var(--scui-muted); font-size:8.5px }.scui-harness-manage section > button { padding:4px 6px; border:1px solid var(--scui-border); border-radius:6px; background:transparent; color:var(--scui-muted); cursor:pointer; font-size:8.5px }.scui-harness-manage section > button:hover { border-color:var(--scui-border-strong); color:var(--scui-fg) }
|
|
202
205
|
.scui-readiness { margin:0 0 7px; border:1px solid var(--scui-border); border-radius:7px; background:var(--scui-bg) }.scui-readiness > summary { padding:6px 8px; color:var(--scui-muted); cursor:pointer; font-size:10px }.scui-readiness > div { display:grid; border-top:1px solid var(--scui-border) }.scui-readiness section { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:7px; padding:7px 8px }.scui-readiness section + section { border-top:1px solid var(--scui-border) }.scui-readiness section > span { display:grid; min-width:0 }.scui-readiness section strong,.scui-readiness section small,.scui-readiness section em { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }.scui-readiness section small { color:var(--scui-muted); font-size:9px }.scui-readiness section em { color:var(--scui-muted); font-size:8px; font-style:normal }.scui-readiness section button { padding:4px 6px; border:1px solid var(--scui-border); border-radius:6px; background:transparent; cursor:pointer; font-size:9px }
|
|
203
206
|
.scui-launch-modes { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:5px; margin:0 0 7px; padding:0; border:0 }.scui-launch-modes legend { position:absolute; overflow:hidden; width:1px; height:1px; clip:rect(0 0 0 0); white-space:nowrap }.scui-launch-modes label { position:relative; min-width:0; cursor:pointer }.scui-launch-modes input { position:absolute; opacity:0; pointer-events:none }.scui-launch-modes label > span { display:flex; min-height:42px; flex-direction:column; justify-content:center; padding:6px 9px; border:1px solid var(--scui-border); border-radius:7px; background:var(--scui-bg); transition:border-color .14s ease,background .14s ease }.scui-launch-modes strong { font-size:11px; font-weight:650 }.scui-launch-modes small { overflow:hidden; color:var(--scui-muted); font-size:9px; text-overflow:ellipsis; white-space:nowrap }.scui-launch-modes input:checked + span { border-color:color-mix(in srgb,var(--scui-accent) 58%,var(--scui-border)); background:color-mix(in srgb,var(--scui-accent) 8%,var(--scui-bg)) }.scui-launch-modes input:focus-visible + span { outline:2px solid var(--scui-accent); outline-offset:1px }.scui-launch-modes:disabled label { cursor:default; opacity:.62 }
|
|
204
207
|
.scui-new { display:grid; justify-items:center; gap:5px; margin:auto; padding:20px; color:var(--scui-muted); text-align:center }.scui-new > span { color:var(--scui-accent); font-size:28px }.scui-new strong { color:var(--scui-fg) }
|
|
@@ -215,6 +218,8 @@
|
|
|
215
218
|
.scui-head { min-height:52px }
|
|
216
219
|
.scui-menu-panel button,.scui-request-actions button,.scui-load { min-height:40px }
|
|
217
220
|
.scui-send,.scui-stop { width:40px; height:40px }
|
|
221
|
+
.scui-new-envelope-controls .scui-attach,.scui-harness-trigger { min-height:40px }
|
|
222
|
+
.scui-harness-options > button,.scui-harness-manage section { min-height:44px }
|
|
218
223
|
.scui-message-meta button,.scui-code-copy { min-width:36px; min-height:36px }
|
|
219
224
|
.scui-latest { min-height:36px; padding-inline:12px }
|
|
220
225
|
}
|