feat(frontend): multiselect/artifact-gate/artifact widgets + linkage + no-limbo
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import { useState } from "react";
|
||||
import type { WidgetProps } from "./types";
|
||||
import type { UiResponse, WidgetDescriptor } from "../api/types";
|
||||
import { ReservedControls } from "./ReservedControls";
|
||||
import { LinkageHost } from "./LinkageHost";
|
||||
|
||||
export function ArtifactGateWidget({ descriptor, onRespond }: WidgetProps) {
|
||||
const [pendingLinkage, setPendingLinkage] = useState<{
|
||||
parentResponse: UiResponse;
|
||||
childDescriptor: WidgetDescriptor;
|
||||
} | null>(null);
|
||||
|
||||
function handleOption(optionId: string) {
|
||||
const option = descriptor.options?.find((o) => o.id === optionId);
|
||||
const parentResponse: UiResponse = { id: descriptor.id, kind: "artifact-gate", choices: [optionId] };
|
||||
|
||||
if (option?.opens) {
|
||||
// Route through LinkageHost to collect child text first
|
||||
setPendingLinkage({ parentResponse, childDescriptor: option.opens });
|
||||
} else {
|
||||
onRespond(parentResponse);
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingLinkage) {
|
||||
return (
|
||||
<LinkageHost
|
||||
parentResponse={pendingLinkage.parentResponse}
|
||||
childDescriptor={pendingLinkage.childDescriptor}
|
||||
onRespond={onRespond}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{descriptor.title && <p className="font-medium">{descriptor.title}</p>}
|
||||
{descriptor.artifact?.content && (
|
||||
<pre className="overflow-auto max-h-64 bg-muted p-3 text-sm rounded border">
|
||||
{descriptor.artifact.content}
|
||||
</pre>
|
||||
)}
|
||||
<div className="flex flex-col gap-2">
|
||||
{descriptor.options?.map((o) => (
|
||||
<button
|
||||
key={o.id}
|
||||
className="border rounded px-3 py-2 text-left hover:bg-accent"
|
||||
onClick={() => handleOption(o.id)}
|
||||
>
|
||||
{o.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<ReservedControls
|
||||
reserved={descriptor.reserved}
|
||||
onControl={(c) => onRespond({ id: descriptor.id, control: c })}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user