import type { RefObject } from "react"; import { useEffect } from "react"; import { t } from "../../../i18n"; import { Box, ScrollBox, Text, useUiHost, type InputRenderable, type ScrollBoxRenderable, type TextareaRenderable, } from "../../../ui"; import { formatPrimaryShortcut, getShortcutDisplayMode } from "../../../utils/shortcut-labels"; import { Button, Spinner } from "../../ui"; import type { SelectFieldHandle } from "../../ui/select-field"; import { getVisibleWorkflowFields, } from "../helpers"; import { useCommandBarPalette } from "../panel/palette"; import { truncateText } from "../view-model"; import { CommandBarWorkflowFieldRow } from "./field-row"; import type { CommandBarFieldValue, CommandBarWorkflowField, CommandBarWorkflowRoute, } from "./types"; interface CommandBarWorkflowBodyProps { route: CommandBarWorkflowRoute; bodyHeight: number; contentPadding: number; nativePaneChrome: boolean; queryDisplayWidth: number; workflowScrollRef: RefObject; getWorkflowInputRef: (fieldId: string) => RefObject; onActiveTextareaSync: (route: CommandBarWorkflowRoute) => void; onFieldFocus: (fieldId: string) => void; onFieldPickerOpen: (route: CommandBarWorkflowRoute, field: CommandBarWorkflowField) => void; onFieldValueChange: (fieldId: string, value: CommandBarFieldValue) => void; onMoveFieldFocus: (delta: number) => void; onSelectFieldRef: (fieldId: string, element: SelectFieldHandle | null) => void; onSubmit: (route: CommandBarWorkflowRoute) => void | Promise; } export function CommandBarWorkflowBody({ route, bodyHeight, contentPadding, nativePaneChrome, queryDisplayWidth, workflowScrollRef, getWorkflowInputRef, onActiveTextareaSync, onFieldFocus, onFieldPickerOpen, onFieldValueChange, onMoveFieldFocus, onSelectFieldRef, onSubmit, }: CommandBarWorkflowBodyProps) { const palette = useCommandBarPalette(nativePaneChrome); const visibleFields = getVisibleWorkflowFields(route.fields, route.values); // Submits from any field (keyboard-handlers.ts), so it sits on the button. const submitShortcut = formatPrimaryShortcut("S", undefined, getShortcutDisplayMode(useUiHost().kind)); useEffect(() => { if (!nativePaneChrome) return; const scrollBox = workflowScrollRef.current; if (!scrollBox) return; const activeIndex = visibleFields.findIndex((field) => field.id === route.activeFieldId); if (activeIndex < 0) return; const estimatedFieldHeight = 4; const fieldTop = activeIndex * estimatedFieldHeight; const fieldBottom = fieldTop + estimatedFieldHeight; const viewportHeight = Math.max(1, scrollBox.viewport?.height ?? bodyHeight); if (fieldTop < scrollBox.scrollTop) { scrollBox.scrollTo(fieldTop); } else if (fieldBottom > scrollBox.scrollTop + viewportHeight) { scrollBox.scrollTo(fieldBottom - viewportHeight); } }, [bodyHeight, nativePaneChrome, route.activeFieldId, visibleFields, workflowScrollRef]); const workflowContent = ( <> {route.subtitle && ( {truncateText(t(route.subtitle), queryDisplayWidth)} )} {route.description?.map((line, index) => ( {truncateText(t(line), queryDisplayWidth)} ))} {route.subtitle || (route.description?.length ?? 0) > 0 ? : null} {visibleFields.map((field, fieldIndex) => { return ( ); })} {route.error && ( {truncateText(route.error, queryDisplayWidth)} )} {route.pendingLabel && route.pending && ( )} {!nativePaneChrome && } field.type === "textarea") ? "flex-end" : "flex-start"}>