/** 명령행 — AutoCAD 명령행처럼 기록·입력·자동완성을 담당한다. */ import { type FC, type FormEvent, useMemo, useRef, useState } from 'react'; import { describeCommand, matchCommandPrefixes } from '../commands/registry'; import { getCommandHistory, runCommandInput } from '../commands/run-command'; import { getInputController, getLastStateInstructions } from '../state'; export const CommandLine: FC = () => { const [text, setText] = useState(''); const [historyIndex, setHistoryIndex] = useState(null); const inputRef = useRef(null); const history = getCommandHistory(); const instruction = getLastStateInstructions() || '명령을 입력하거나 도구를 선택하십시오.'; const suggestions = useMemo(() => matchCommandPrefixes(text).slice(0, 8), [text]); const submit = (event: FormEvent) => { event.preventDefault(); const value = text.trim(); if (!value) { // 빈 ENTER는 활성 명령의 확정 신호다 (선택 끝내기·직전 명령 반복) getInputController().handleEnterKey(); return; } // 진행 중인 명령이 문자·숫자를 기다리면 그쪽으로, 아니면 명령으로 해석된다 getInputController().submitText(value); setText(''); setHistoryIndex(null); }; const recallHistory = (direction: -1 | 1) => { if (!history.length) return; const nextIndex = historyIndex === null ? history.length - 1 : Math.min(history.length - 1, Math.max(0, historyIndex + direction)); setHistoryIndex(nextIndex); setText(history[nextIndex].split(' ')[0]); }; return (
{history.slice(-3).map((line, index) => ( // biome-ignore lint/suspicious/noArrayIndexKey: 기록은 순서 자체가 식별자다 {line} ))}
setText(event.target.value)} onKeyDown={(event) => { if (event.nativeEvent.isComposing) return; // 한글 조합 중 키는 IME 몫 if (event.key === 'Escape') { event.preventDefault(); getInputController().handleEscapeKey(); } if (event.key === 'ArrowUp') { event.preventDefault(); recallHistory(-1); } if (event.key === 'ArrowDown') { event.preventDefault(); recallHistory(1); } if (event.key === 'Tab' && suggestions.length) { event.preventDefault(); setText(suggestions[0].id); } }} placeholder="명령 입력 (예: L, LINE, REC, MOVE)" autoComplete="off" spellCheck={false} /> {instruction}
{text.trim() && suggestions.length > 0 && (
{suggestions.map((command) => ( ))}
)}
); };