feat(status-ui): improve concurrent session workflows
Add in-place session renewal, searchable model selection, clearer progress feedback, tool-result previews, and per-session presentation state. Keep commands, errors, loading, and lifecycle indicators attached to their owning runtime so parallel tabs cannot interfere.
This commit is contained in:
@@ -3,9 +3,8 @@ import type { RpcCommand, RpcField } from "../commands/rpc";
|
||||
|
||||
const controls = (container: HTMLElement | null) =>
|
||||
Array.from(
|
||||
container?.querySelectorAll<HTMLElement>(
|
||||
"button, input, select, textarea",
|
||||
) ?? [],
|
||||
container?.querySelectorAll<HTMLElement>("button, input, select, textarea") ??
|
||||
[],
|
||||
).filter((node) => !node.hasAttribute("disabled"));
|
||||
|
||||
function valueFor(field: RpcField, value: string | boolean): unknown {
|
||||
@@ -19,23 +18,25 @@ export function CommandFormDialog({
|
||||
command,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
pending = false,
|
||||
commandError,
|
||||
}: {
|
||||
command?: RpcCommand;
|
||||
onSubmit: (input: Record<string, unknown>) => void;
|
||||
onCancel: () => void;
|
||||
pending?: boolean;
|
||||
commandError?: string;
|
||||
}) {
|
||||
const ref = useRef<HTMLElement>(null);
|
||||
const opener = useRef<HTMLElement | null>(null);
|
||||
const [values, setValues] = useState<Record<string, string | boolean>>({});
|
||||
const [error, setError] = useState<string>();
|
||||
const [validationError, setValidationError] = useState<string>();
|
||||
useEffect(() => {
|
||||
if (!command) return;
|
||||
opener.current = document.activeElement as HTMLElement | null;
|
||||
setValues({});
|
||||
setError(undefined);
|
||||
const frame = requestAnimationFrame(() =>
|
||||
controls(ref.current)[0]?.focus(),
|
||||
);
|
||||
setValidationError(undefined);
|
||||
const frame = requestAnimationFrame(() => controls(ref.current)[0]?.focus());
|
||||
return () => {
|
||||
cancelAnimationFrame(frame);
|
||||
opener.current?.focus();
|
||||
@@ -44,6 +45,7 @@ export function CommandFormDialog({
|
||||
if (!command) return null;
|
||||
const fields = command.input ?? [];
|
||||
const submit = () => {
|
||||
if (pending) return;
|
||||
try {
|
||||
const input: Record<string, unknown> = {};
|
||||
for (const field of fields) {
|
||||
@@ -57,7 +59,7 @@ export function CommandFormDialog({
|
||||
}
|
||||
onSubmit(input);
|
||||
} catch (cause) {
|
||||
setError(cause instanceof Error ? cause.message : "Invalid input");
|
||||
setValidationError(cause instanceof Error ? cause.message : "Invalid input");
|
||||
}
|
||||
};
|
||||
return (
|
||||
@@ -68,6 +70,7 @@ export function CommandFormDialog({
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="command-form-title"
|
||||
aria-busy={pending}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
@@ -99,6 +102,7 @@ export function CommandFormDialog({
|
||||
aria-label={field.label}
|
||||
type="checkbox"
|
||||
checked={values[field.name] === true}
|
||||
disabled={pending}
|
||||
onChange={(event) =>
|
||||
setValues({
|
||||
...values,
|
||||
@@ -110,6 +114,7 @@ export function CommandFormDialog({
|
||||
<select
|
||||
aria-label={field.label}
|
||||
value={(values[field.name] as string) ?? ""}
|
||||
disabled={pending}
|
||||
onChange={(event) =>
|
||||
setValues({
|
||||
...values,
|
||||
@@ -126,6 +131,7 @@ export function CommandFormDialog({
|
||||
<textarea
|
||||
aria-label={field.label}
|
||||
value={(values[field.name] as string) ?? ""}
|
||||
disabled={pending}
|
||||
onChange={(event) =>
|
||||
setValues({
|
||||
...values,
|
||||
@@ -137,6 +143,7 @@ export function CommandFormDialog({
|
||||
<input
|
||||
aria-label={field.label}
|
||||
value={(values[field.name] as string) ?? ""}
|
||||
disabled={pending}
|
||||
onChange={(event) =>
|
||||
setValues({
|
||||
...values,
|
||||
@@ -147,10 +154,14 @@ export function CommandFormDialog({
|
||||
)}
|
||||
</label>
|
||||
))}
|
||||
{error && <p role="alert">{error}</p>}
|
||||
{(validationError || commandError) && (
|
||||
<p role="alert">{validationError || commandError}</p>
|
||||
)}
|
||||
<div className="extension-options">
|
||||
<button onClick={submit}>Run command</button>
|
||||
<button className="quiet" onClick={onCancel}>
|
||||
<button disabled={pending} onClick={submit}>
|
||||
{pending ? "Running…" : "Run command"}
|
||||
</button>
|
||||
<button className="quiet" disabled={pending} onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user