@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 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`, and
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
@@ -8,6 +8,7 @@ export {
8
8
  ContinuationBar,
9
9
  Conversation,
10
10
  HarnessLogo,
11
+ HarnessPicker,
11
12
  HarnessReadiness,
12
13
  ImageViewer,
13
14
  LoadingStatus,
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: !harnesses.some((item) => item.startable), children: [
2464
- /* @__PURE__ */ jsxs8("summary", { children: [
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__ */ 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 }) }) })
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: !harnesses.some((item) => item.startable), children: [
2384
- /* @__PURE__ */ jsxs7("summary", { children: [
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__ */ 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 }) }) })
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: !harnesses.some((item) => item.startable), children: [
2381
- /* @__PURE__ */ jsxs7("summary", { children: [
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__ */ 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 }) }) })
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter-ai-dev/supercode-ui",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "type": "module",
5
5
  "description": "Composable default UI kit for Supercode-powered coding-agent experiences",
6
6
  "exports": {
@@ -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: !harnesses.some((item) => item.startable), children: [
2464
- /* @__PURE__ */ jsxs8("summary", { children: [
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__ */ 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 }) }) })
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;
@@ -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: !harnesses.some((item) => item.startable), children: [
2381
- /* @__PURE__ */ jsxs7("summary", { children: [
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__ */ 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 }) }) })
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
  ] });
@@ -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';
@@ -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: !harnesses.some((item) => item.startable), children: [
294
- /* @__PURE__ */ jsxs3("summary", { children: [
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: !harnesses.some((item) => item.startable), children: [
294
- /* @__PURE__ */ jsxs3("summary", { children: [
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-harness-picker { display:flex; align-items:center; gap:7px; margin-bottom:7px }.scui-harness-picker select { margin-left:auto; max-width:55%; padding:4px; border:1px solid var(--scui-border); border-radius:6px; background:var(--scui-bg); color:var(--scui-fg) }
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
  }