import { useEffect, useRef, useState } from "react"; import { Button, ChoiceDialog, ConfirmDialog, DialogFrame, TextField, type ChoiceDialogChoice } from "../../../../components"; import { t } from "../../../../i18n"; import { colors } from "../../../../theme/colors"; import { Box, Text, Textarea, type InputRenderable, type TextareaRenderable } from "../../../../ui"; import { type DialogApi, type PromptContext, useDialogKeyboard } from "../../../../ui/dialog"; /** * The questions a thesis flow asks, on the same dialog pattern as the rest * of the app: a DialogFrame, kit buttons, and an `Enter save · Esc cancel` * footer. Every prompt resolves `undefined` when the person backs out. */ interface TextDialogProps extends PromptContext { title: string; body?: string[]; defaultValue?: string; placeholder?: string; /** Sentences rather than a name: a taller field where Shift+Enter breaks a line. */ multiline?: boolean; /** Empty is a valid answer (clearing a field) rather than a cancel. */ allowEmpty?: boolean; width?: number; } const CANCEL = "\u0000cancel"; function TextDialog({ resolve, dialogId, title, body, defaultValue = "", placeholder, multiline = false, allowEmpty = false, width = 72 }: TextDialogProps) { const inputRef = useRef(null); const textareaRef = useRef(null); const [value, setValue] = useState(defaultValue); useEffect(() => { (multiline ? textareaRef.current : inputRef.current)?.focus?.(); }, [multiline]); const current = () => { if (!multiline) return value; try { return textareaRef.current?.editBuffer.getText() ?? value; } catch { return value; } }; const save = () => { const text = current().trim(); if (text || allowEmpty) resolve(text); }; const cancel = () => resolve(CANCEL); useDialogKeyboard((event) => { if (event.name === "escape") { event.stopPropagation(); cancel(); } }, { scope: dialogId, allowEditable: true }); return ( {body?.map((line, index) => ( {line ? t(line) : " "} ))} {body && body.length > 0 && } {multiline ? (