refactor!: remove redundant compatibility layers

Use native browser and Tauri behavior instead of custom selector, dialog, and window-state implementations. Remove obsolete bridge, relay, asset, and dependency paths to keep the supported surface small.

BREAKING CHANGE: The Noctalia relay no longer accepts --agent, and the Tauri host no longer exposes the legacy load_agent command.
This commit is contained in:
2026-08-31 17:35:07 +02:00
parent a4ee3eb7a8
commit 0949c6f0d7
37 changed files with 510 additions and 1341 deletions
+41 -228
View File
@@ -1,240 +1,53 @@
import { useEffect, useId, useRef, useState } from "react";
import { reconcileThinkingLevel, thinkingLevelOptions } from "../model-catalog";
import type { Model } from "../workspace/types";
import "./ModelSelector.css";
import "./ThinkingSelector.css";
type ThinkingSelectorProps = {
model?: Model;
label?: string;
value: string;
disabled?: boolean;
placement?: "up" | "down";
className?: string;
controlClassName?: string;
onChange: (level: string) => void;
model?: Model;
label?: string;
value: string;
disabled?: boolean;
className?: string;
controlClassName?: string;
onChange: (level: string) => void;
};
export function ThinkingSelector({
model,
label = "Thinking",
value,
disabled = false,
placement = "up",
className,
controlClassName,
onChange,
}: ThinkingSelectorProps) {
const [open, setOpen] = useState(false);
const [activeIndex, setActiveIndex] = useState(0);
const openRef = useRef(false);
const disabledRef = useRef(disabled);
const focusFrameRef = useRef<number | undefined>(undefined);
const typeaheadRef = useRef({ query: "", time: 0 });
const rootRef = useRef<HTMLDivElement>(null);
const triggerRef = useRef<HTMLButtonElement>(null);
const optionRefs = useRef<(HTMLButtonElement | null)[]>([]);
disabledRef.current = disabled;
const labelId = useId();
const valueId = useId();
const menuId = useId();
const options = thinkingLevelOptions(model);
const selected = reconcileThinkingLevel(model, value);
const selectedIndex = options.findIndex(
(option) => option.value === selected,
);
const selectedOption = options[selectedIndex];
const classes = [
"thinking-selector",
`thinking-selector-${placement}`,
model,
label = "Thinking",
value,
disabled = false,
className,
]
.filter(Boolean)
.join(" ");
const closeMenu = (restoreFocus = false) => {
if (!openRef.current) return;
openRef.current = false;
setOpen(false);
typeaheadRef.current = { query: "", time: 0 };
if (restoreFocus) {
window.cancelAnimationFrame(focusFrameRef.current ?? 0);
focusFrameRef.current = window.requestAnimationFrame(() => {
focusFrameRef.current = undefined;
if (!disabledRef.current) triggerRef.current?.focus();
});
}
};
const openMenu = () => {
if (disabledRef.current || openRef.current) return;
setActiveIndex(selectedIndex >= 0 ? selectedIndex : 0);
openRef.current = true;
setOpen(true);
};
const selectOption = (index: number) => {
const option = options[index];
if (!option || disabledRef.current) return;
onChange(option.value);
closeMenu(true);
};
const moveActiveOption = (direction: 1 | -1) => {
setActiveIndex(
(index) => (index + direction + options.length) % options.length,
);
};
const typeahead = (key: string) => {
const now = Date.now();
const previous = typeaheadRef.current;
let query = now - previous.time < 500 ? `${previous.query}${key}` : key;
let matches = options
.map((option, index) => ({ option, index }))
.filter(({ option }) =>
option.label.toLocaleLowerCase().startsWith(query),
);
if (!matches.length && query.length > 1) {
query = key;
matches = options
.map((option, index) => ({ option, index }))
.filter(({ option }) =>
option.label.toLocaleLowerCase().startsWith(query),
);
}
typeaheadRef.current = { query, time: now };
if (!matches.length) return;
const next = matches.find(({ index }) => index > activeIndex) ?? matches[0];
setActiveIndex(next.index);
};
useEffect(() => {
if (!disabled) return;
window.cancelAnimationFrame(focusFrameRef.current ?? 0);
focusFrameRef.current = undefined;
closeMenu();
}, [disabled]);
useEffect(() => {
return () => {
openRef.current = false;
window.cancelAnimationFrame(focusFrameRef.current ?? 0);
};
}, []);
useEffect(() => {
if (!open) return;
const focusFrame = window.requestAnimationFrame(() =>
optionRefs.current[activeIndex]?.focus(),
);
return () => window.cancelAnimationFrame(focusFrame);
}, [activeIndex, open]);
useEffect(() => {
if (!open) return;
const closeOnEscape = (event: KeyboardEvent) => {
if (event.key !== "Escape") return;
event.preventDefault();
closeMenu(true);
};
const closeOutside = (event: PointerEvent) => {
if (!rootRef.current?.contains(event.target as Node)) closeMenu();
};
window.addEventListener("keydown", closeOnEscape);
window.addEventListener("pointerdown", closeOutside);
return () => {
window.removeEventListener("keydown", closeOnEscape);
window.removeEventListener("pointerdown", closeOutside);
};
}, [open]);
return (
<div className={classes}>
<span id={labelId}>{label}</span>
<div className="model-selector thinking-popup" ref={rootRef}>
<button
ref={triggerRef}
type="button"
className={["model-trigger", "thinking-trigger", controlClassName]
.filter(Boolean)
.join(" ")}
aria-expanded={open}
aria-haspopup="listbox"
aria-controls={open ? menuId : undefined}
aria-labelledby={`${labelId} ${valueId}`}
disabled={disabled}
onClick={() => {
if (disabledRef.current) return;
if (openRef.current) closeMenu();
else openMenu();
}}
controlClassName,
onChange,
}: ThinkingSelectorProps) {
const options = thinkingLevelOptions(model);
return (
<label
className={["thinking-selector", className]
.filter(Boolean)
.join(" ")}
>
<span id={valueId}>{selectedOption?.label ?? "Off"}</span>
</button>
{open && (
<div
id={menuId}
className="model-menu thinking-menu"
role="listbox"
aria-label={`${label} options`}
>
<div className="model-results thinking-options">
{options.map((option, index) => (
<button
ref={(element) => {
optionRefs.current[index] = element;
}}
type="button"
className="model-option thinking-option"
role="option"
aria-selected={option.value === selected}
tabIndex={index === activeIndex ? 0 : -1}
value={option.value}
key={option.value}
onClick={() => selectOption(index)}
onKeyDown={(event) => {
switch (event.key) {
case "ArrowDown":
event.preventDefault();
moveActiveOption(1);
break;
case "ArrowUp":
event.preventDefault();
moveActiveOption(-1);
break;
case "Home":
event.preventDefault();
setActiveIndex(0);
break;
case "End":
event.preventDefault();
setActiveIndex(options.length - 1);
break;
case "Enter":
case " ":
event.preventDefault();
selectOption(activeIndex);
break;
case "Escape":
event.preventDefault();
closeMenu(true);
break;
default:
if (
event.key.length === 1 &&
!event.altKey &&
!event.ctrlKey &&
!event.metaKey
) {
event.preventDefault();
typeahead(event.key.toLocaleLowerCase());
}
}
}}
>
{option.label}
</button>
))}
</div>
</div>
)}
</div>
</div>
);
<span>{label}</span>
<select
className={[
"model-trigger",
"thinking-trigger",
controlClassName,
]
.filter(Boolean)
.join(" ")}
value={reconcileThinkingLevel(model, value)}
disabled={disabled}
onChange={(event) => onChange(event.currentTarget.value)}
>
{options.map((option) => (
<option value={option.value} key={option.value}>
{option.label}
</option>
))}
</select>
</label>
);
}