import React, { useEffect, useRef } from "react"; import { Text } from "ink"; import { useTerminalTheme } from "../hooks/useTerminalTheme"; import { InteractionCancelledError } from "../../core/interaction/interaction-service"; import type { TInteractionContext } from "../../core/interaction/interaction-service"; import type { InkInteractionService } from "../services/ink-interaction-service"; /** * Generic native screen for a command whose business logic ALREADY accepts * `ctx: TInteractionContext` and calls `ctx.interaction.*`/`ctx.interaction.notify` * exclusively (e.g. the `cf region *` commands) — generalizes the pattern * `CfOrgScreen.tsx`/`GitMoveCodeScreen.tsx` hand-wrote. No re-implementation * of prompts needed: `` (mounted alongside this screen by * SmdgTerminalApp) renders whatever the function's own `ctx.interaction.*` * calls request. */ export function makeCtxWorkflowScreen( run: (ctx: TInteractionContext) => Promise, title: string, ): React.ComponentType<{ service: InkInteractionService; onDone: (success: boolean) => void }> { return function CtxWorkflowScreen(props) { const theme = useTerminalTheme(); const startedRef = useRef(false); useEffect(() => { if (startedRef.current) return; startedRef.current = true; const ctx: TInteractionContext = { interaction: props.service, signal: props.service.signal }; void (async () => { try { await run(ctx); props.onDone(true); } catch (error) { if (!(error instanceof InteractionCancelledError) && !props.service.signal.aborted) { props.service.notify({ level: "error", message: error instanceof Error ? error.message : String(error) }); } props.onDone(false); } })(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( {title} ); }; }