1/** @jsxImportSource @alchemy.run/sigil */
2import { useMemo, useState } from "@alchemy.run/sigil/react";
3import type { JSX } from "react";
4import type { Plan } from "../../../Plan.ts";
5import {
6 PromptFrame,
7 ChoiceGroup,
8 useKeyGlyphs,
9 useTerminalInput,
10} from "../ui/index.ts";
11import { Screen, type ScreenController } from "../../CliKit/index.ts";
12import { Plan as PlanComponent, PlanTree } from "./PlanView.tsx";
13import type { PlanTreeData } from "./PlanTree.ts";
14
15export interface PlanDecisionChoice<Value> {
16 readonly value: Value;
17 readonly label: string;
18}
19
20function PlanDecision<Value>(props: {
21 readonly plan: Plan | PlanTreeData;
22 readonly label?: string;
23 readonly message: string;
24 readonly choices: ReadonlyArray<PlanDecisionChoice<Value>>;
25 readonly initialValue: Value;
26 readonly controller: ScreenController<Value>;
27}): JSX.Element {
28 const {
29 plan,
30 label = "Plan",
31 message,
32 choices,
33 initialValue,
34 controller,
35 } = props;
36 const tree = useMemo(
37 () => new PlanTree(plan, { mode: "review", label }),
38 [plan, label],
39 );
40 const [selected, setSelected] = useState(initialValue);
41 const keys = useKeyGlyphs();
42
43 useTerminalInput((_input, key) => {
44 if (key.enter) controller.submit(selected);
45 else if (key.escape) controller.cancel();
46 });
47
48 return (
49 <PlanComponent
50 tree={tree}
51 footer={
52 <PromptFrame
53 message={message}
54 keys={[
55 [keys.upDown, "scroll plan"],
56 [keys.leftRight, "choose"],
57 [keys.enter, "confirm"],
58 [keys.escape, "cancel"],
59 ]}
60 >
61 <ChoiceGroup
62 choices={choices}
63 value={selected}
64 onChange={setSelected}
65 />
66 </PromptFrame>
67 }
68 />
69 );
70}
71
72export const planDecisionScreen = <Value,>(options: {
73 readonly plan: Plan | PlanTreeData;
74 readonly label?: string;
75 readonly message: string;
76 readonly choices: ReadonlyArray<PlanDecisionChoice<Value>>;
77 readonly initialValue: Value;
78}) =>
79 Screen.make<Value>("plan decision", (controller) => (
80 <PlanDecision {...options} controller={controller} />
81 ));