Files
Aislo/B07_DesignDetail/openwebcad/src/commands/registry.ts
T
eomsangdonandClaude Opus 5.5 83e246deb4 feat(CAD): 337-1 가-1 옵션 틀 — 물음 꼴 · 친 글 사다리 한 곳 · 명령 끝 → 쉼 · 순서 엔진 v2 · 키보드 흠
- 새 src/commands/prompt.ts — PromptSpec · renderPrompt(키워드 물음은 「또는」 없음) · matchOption(initget 줄임) · 세션 기억 · OPT · SELECT_OBJECTS
- 새 inputController/prompt-input.ts — routeTyped 사다리(점 · 옵션 묻는 중 명령 이름 거절 · 식 · 단위 거절 · 투명 ') · 알림 줄 · 덧물음 · cancelActiveCommand · undoCommand
- 새 tools/factories/end-command.ts — final(END) 갈고리 → 같은 틱 선택 도구 · state.ts 갈고리 · LASTPOINT
- sequence-tool v2 — ROUTE · id · prompt · when · next · options · keyword · loopFrom · escCommits · pickfirst · 확정 뒤 기본 끝 · Esc 늘 끝
- 흠 — N1 ②③ · N5 · N6 · N11 · N18(앞글자 실행 없앰) · H1(Ctrl · Alt 글자 안 쌓음 · Ctrl+C · X · Shift+C · Shift+V · 1) · 우클릭 = 쉴 때 반복 · SE 뺌 · ED 더함
- 시험 — cad-drive 도우미 · prompt-frame · prompt-contract · 옛 시험 손질(keyboard-chain · ime-sink · command-catalog · input-prefs-cad · eraser 녹화)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QfHKQs7J3hQELKMWQMUJEy
2026-10-15 23:55:58 +09:00

151 lines
5.3 KiB
TypeScript

/**
* 명령 레지스트리 — 리본·명령행·단축키가 읽는 단일 소스.
* 새 명령은 commands.*.ts에 한 줄 추가하면 세 곳에 동시에 나타난다.
*/
import type { AnyToolMachine, CadCommand } from './command.types';
import type { Tool } from '../tools';
import {
DIMENSION_COMMANDS,
LEADER_COMMANDS,
MARKUP_COMMANDS,
TABLE_COMMANDS,
TEXT_COMMANDS,
} from './commands.annotate';
import { DRAW_COMMANDS } from './commands.draw';
import { FILE_COMMANDS } from './commands.file';
import { INSERT_COMMANDS } from './commands.insert';
import { LAYER_COMMANDS } from './commands.layer';
import {
CLIPBOARD_COMMANDS,
GROUP_COMMANDS,
INQUIRY_COMMANDS,
PROPERTY_COMMANDS,
} from './commands.utility';
import { ALIGN_COMMANDS, MODIFY_COMMANDS } from './commands.modify';
import { VIEW_COMMANDS } from './commands.view';
import { inputPrefs } from '../../../../A00_Common/input_prefs';
import '../tools/factories/end-command'; // 명령 끝 → 쉬는 상태 갈고리
const ALL_COMMANDS: CadCommand[] = [
...DRAW_COMMANDS,
...MODIFY_COMMANDS,
...ALIGN_COMMANDS,
...TEXT_COMMANDS,
...DIMENSION_COMMANDS,
...LEADER_COMMANDS,
...TABLE_COMMANDS,
...MARKUP_COMMANDS,
...LAYER_COMMANDS,
...PROPERTY_COMMANDS,
...GROUP_COMMANDS,
...INQUIRY_COMMANDS,
...CLIPBOARD_COMMANDS,
...INSERT_COMMANDS,
...VIEW_COMMANDS,
...FILE_COMMANDS,
];
const COMMANDS_BY_ID = new Map<string, CadCommand>(
ALL_COMMANDS.map((command) => [command.id, command])
);
const COMMANDS_BY_ALIAS = new Map<string, CadCommand>();
for (const command of ALL_COMMANDS) {
for (const alias of command.aliases ?? []) {
if (!COMMANDS_BY_ALIAS.has(alias)) {
COMMANDS_BY_ALIAS.set(alias, command);
}
}
}
/**
* 확정한 도면에서도 쓸 수 있는 명령 — **보는 일만 하는 것들**이다
* (2026-09-01 사용자 확정: 확정하면 수정이 안 되고, 풀려면 [수정]을 누른다).
*
* 허용을 명시하는 쪽으로 짠다. 새 편집 명령이 늘어도 자동으로 막히고, 반대로 짜면
* 새 명령마다 여기 적어야 해 언젠가 빠뜨린다.
*/
const VIEW_ONLY_COMMAND_IDS: ReadonlySet<string> = new Set([
// 보기 — 확대·이동·다시그리기·선택
...VIEW_COMMANDS.map((command) => command.id),
// 조회 — 거리·면적·각도처럼 재기만 하는 것
...INQUIRY_COMMANDS.map((command) => command.id),
// 도면층 보이기·잠금 — 객체를 건드리지 않는 것만 (LAYMCH·LAYCUR·LAYMRG·LAYDEL 제외)
'LAYER',
'LAYCURSET',
'LAYOFF',
'LAYON',
'LAYFRZ',
'LAYTHW',
'LAYLCK',
'LAYULK',
'LAYISO',
'LAYUNISO',
'LAYERP',
'LAYERSTATE',
'LAYERSTATERESTORE',
'LAYWALK',
// 파일 — 내보내기와 클립보드 복사는 도면을 바꾸지 않는다
'EXPORT',
'EXPORTSVG',
'EXPORTPNG',
'COPYCLIP',
'COPYBASE',
'TOAREA', // 고른 범위 → 면적산출 표(M02 83장)
]);
/** 확정한 도면에서 이 명령을 실행해도 되는가. */
export const isViewOnlyCommand = (command: CadCommand): boolean =>
VIEW_ONLY_COMMAND_IDS.has(command.id);
export const getAllCommands = (): CadCommand[] => ALL_COMMANDS;
export const getCommandById = (id: string): CadCommand | undefined => COMMANDS_BY_ID.get(id);
/** 활성 도구로 어떤 명령이 실행 중인지 되짚는다 (리본 강조·상태 표시용) */
export const getCommandByTool = (tool: Tool | null): CadCommand | undefined =>
tool ? ALL_COMMANDS.find((command) => command.tool === tool) : undefined;
/** 도구형 명령의 xstate 머신 표 — 도구 활성화 경로가 이 표만 본다 */
export const TOOL_STATE_MACHINES = Object.fromEntries(
ALL_COMMANDS.filter((command) => command.tool && command.machine).map((command) => [
command.tool,
command.machine,
])
) as Record<Tool, AnyToolMachine>;
/** 내 별칭(☰ [설정] · `cadAliases`) 이 가리키는 명령들 — 부를 때마다 읽어 저장 즉시 따름 */
function userAliasCommands(match: (alias: string) => boolean): CadCommand[] {
return Object.entries(inputPrefs().cadAliases).flatMap(([id, aliases]) => {
const command = COMMANDS_BY_ID.get(id);
return command && aliases.some(match) ? [command] : [];
});
}
/** 명령행 입력 해석 — 이름 > 내 별칭 > 기본 별칭(acad.pgp 식) 딱 맞음만(앞글자 실행 없음 · 후보는 목록) */
export function resolveCommandInput(input: string): CadCommand | undefined {
const text = input.trim().toUpperCase().replace(/^_/, '');
if (!text) return undefined;
return (
COMMANDS_BY_ID.get(text) ??
userAliasCommands((alias) => alias === text)[0] ??
COMMANDS_BY_ALIAS.get(text)
);
}
/** 접두사가 같은 명령 후보 (커서 옆 자동완성 목록에 쓴다) */
export function matchCommandPrefixes(input: string): CadCommand[] {
const text = input.trim().toUpperCase();
if (!text) return [];
const byAlias = ALL_COMMANDS.filter((command) =>
(command.aliases ?? []).some((alias) => alias.startsWith(text))
);
const byId = ALL_COMMANDS.filter((command) => command.id.startsWith(text));
const byUser = userAliasCommands((alias) => alias.startsWith(text));
return [...new Set([...byUser, ...byAlias, ...byId])];
}
/** 명령행 자동완성에 보여줄 문자열 (예: "LINE (L) 선") */
export const describeCommand = (command: CadCommand): string =>
`${command.id}${command.aliases?.length ? ` (${command.aliases[0]})` : ''} ${command.label}`;