사용자 편의성 점검 15건 중 반영 확정분 12건. 편집 손실 방지 - CAD에 미저장 표시(drawingDirty)를 두고, 도면을 바꾸기 전에 묻는다. 전에는 그은 선이 경고 없이 사라졌다(실측: 캔버스 서명이 원본과 동일). - 도면 전환을 겹쳐 눌러도 늦게 온 응답이 화면을 덮지 않게 가드를 뒀다. - 도면층 삭제·객체 이동을 되돌리기에 실어 Ctrl+Z로 돌아오게 했다. 확정한 도면은 읽기 전용 - runCommand 한 곳에서 보는 명령만 통과시킨다(허용 명시 방식). - 도면을 실을 때 앞 도면의 그리기 도구를 내린다 — 켜 둔 도구가 확정본에도 계속 그렸다. - 확정 버튼 자리를 [현재 도면 확정] / [수정]으로 가른다. 확정 해제는 [수정] 한 곳뿐 — 되돌리기·색 고르기로 확정이 풀리던 경로를 없앴다. 그 밖 - 잠금 도면층(도각·등고선·계류)은 마우스가 스쳐도 하이라이트하지 않는다. - 준비 중 도면 7종도 빈 도각으로 열린다(눌리지 않는 회색 버튼 제거). - 도면층 행이 잘리지 않게 패널을 300px로 넓히고 이름을 줄여 담는다. - 기본 그리기 색을 검정으로 — 흰 종이에 흰 선이라 안 보였다. - 색 고르는 동안 변경 통지가 연발하지 않는다. - 수량표는 앞 단계 산출물이라 도면에서 고치지 못하게 막고 안내한다. - 자동백업 칸을 도면별로 나눈다 — 되살리면 다른 도면에 붙었다. - 횡단 장 id를 시작 측점 기준으로 바꿔 구간이 달라져도 옛 확정이 안 붙는다. - 회사 도각 저장 전에 좌표를 검사하고, 기본 도각으로 되돌리기를 연다. - 횡단 파일이 없어도 나머지 도면 목록은 남는다. - 불러오는 중·실패를 화면에 표시한다. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
135 lines
4.5 KiB
TypeScript
135 lines
4.5 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';
|
|
|
|
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',
|
|
]);
|
|
|
|
/** 확정한 도면에서 이 명령을 실행해도 되는가. */
|
|
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>;
|
|
|
|
/** 명령행 입력 해석 — 정확한 이름·별칭이 먼저, 없으면 접두사 후보의 첫 항목 */
|
|
export function resolveCommandInput(input: string): CadCommand | undefined {
|
|
const text = input.trim().toUpperCase();
|
|
if (!text) return undefined;
|
|
return COMMANDS_BY_ID.get(text) ?? COMMANDS_BY_ALIAS.get(text) ?? matchCommandPrefixes(text)[0];
|
|
}
|
|
|
|
/** 접두사가 같은 명령 후보 (커서 옆 자동완성 목록에 쓴다) */
|
|
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));
|
|
return [...new Set([...byAlias, ...byId])];
|
|
}
|
|
|
|
/** 명령행 자동완성에 보여줄 문자열 (예: "LINE (L) 선") */
|
|
export const describeCommand = (command: CadCommand): string =>
|
|
`${command.id}${command.aliases?.length ? ` (${command.aliases[0]})` : ''} ${command.label}`;
|