/* ============================================================================= * app_settings_cad.ts * ☰ [설정] ② CAD 단축키 탭(PLAN 332-2 · 588-1) — 찾기 · 키 · 마우스 묶음 · 리본 이름 묶음 명령 표(명령 · 한글 이름 · 기본 별칭 · 내 별칭) · 검사. * * - 명령 목록 = `openwebcad/src/commands/command-catalog.json`(registry 거울 · commands.*.ts 는 xstate 가 딸려 와서 JSON 한 벌) * - acad.pgp 식 덮어쓰기 — CAD 는 명령 이름 > 내 별칭 > 기본 별칭 순서로 찾음(배선은 332-3) * - 검사 = 영문 · 숫자만 · 내 별칭끼리 겹침 금지 · 명령 이름과 같은 별칭 금지 * ========================================================================== */ import catalog from "../B07_DesignDetail/openwebcad/src/commands/command-catalog.json"; import { el } from "@ui/ui_template_elements"; import { createInputField } from "@ui/ui_template_input"; import { createText } from "@ui/ui_template_text"; import { t as L } from "@ui/ui_template_locale"; import { cadCommandGroups } from "./app_settings_find"; import { findGroup, findPanel, keyRows, type FindRow, type KeyRows } from "./app_settings_keys"; export interface CadCommandRow { id: string; label: string; aliases: string[]; } export const CAD_COMMANDS = catalog as CadCommandRow[]; export type AliasError = "chars" | "dup" | "name"; const ERROR_TEXT: Record[0]> = { chars: "App_Settings_Cad_ErrChars", dup: "App_Settings_Cad_ErrDup", name: "App_Settings_Cad_ErrName", }; /** 칸 글 → 별칭 목록(쉼표 · 빈칸으로 가름 · 대문자) */ export const parseAliases = (text: string): string[] => text .toUpperCase() .split(/[\s,]+/) .filter(Boolean); /** 내 별칭 검사 — `{명령: 첫 잘못}` · 비면 통과 */ export function checkCadAliases( aliases: Record, commands: CadCommandRow[] = CAD_COMMANDS, ): Record { const ids = new Set(commands.map((c) => c.id)); const owner = new Map(); const errors: Record = {}; for (const [id, list] of Object.entries(aliases)) { for (const alias of list) { const first = owner.get(alias); const error: AliasError | null = !/^[A-Z0-9]+$/.test(alias) ? "chars" : ids.has(alias) ? "name" : first !== undefined ? "dup" : null; if (!error) owner.set(alias, id); else { errors[id] ??= error; if (first !== undefined) errors[first] ??= "dup"; } } } return errors; } /** 탭 몸(588-1) — 찾기 · 「키 · 마우스」 묶음(등록부 CAD 줄) · 리본 이름 묶음 · `aliases` · `keys` 를 칸 입력대로 고쳐 씀 */ export function buildCadAliasTab( aliases: Record, keys: Record, onKeysChange: () => void, ): { root: HTMLElement; mark: (errors: Record) => void; keys: KeyRows; count: () => number; } { const message = createText("", "error"); message.hidden = true; const inputs = new Map(); const byId = new Map(CAD_COMMANDS.map((command) => [command.id, command])); const row = (command: CadCommandRow): FindRow => { const field = createInputField({ size: 24 }); const input = field.input; input.setAttribute("aria-label", `${command.id} ${L("App_Settings_Cad_Mine")}`); input.value = (aliases[command.id] ?? []).join(", "); input.dataset.cadCommand = command.id; input.addEventListener("input", () => { const list = parseAliases(input.value); if (list.length) aliases[command.id] = list; else delete aliases[command.id]; }); inputs.set(command.id, input); return { cells: [command.id, command.label, command.aliases.join(", "), field.root], texts: () => [command.id, command.label, ...command.aliases, input.value], }; }; const columns = ( [ "App_Settings_Cad_Command", "App_Settings_Cad_Label", "App_Settings_Cad_Default", "App_Settings_Cad_Mine", ] as const ).map((key) => ({ label: L(key) })); const keyRowsCad = keyRows(["cad"], keys, onKeysChange, () => L("App_Settings_Cad_Keys")); const groups = [ ...keyRowsCad.groups, ...cadCommandGroups(CAD_COMMANDS.map((c) => c.id)).map((group) => findGroup( group.label || L("App_Settings_Cad_Other"), columns, group.ids.map((id) => row(byId.get(id)!)), ), ), ]; const mark = (errors: Record): void => { for (const [id, input] of inputs) { const error = errors[id]; if (error) input.setAttribute("aria-invalid", "true"); else input.removeAttribute("aria-invalid"); input.title = error ? L(ERROR_TEXT[error]) : ""; } const kinds = [...new Set(Object.values(errors))]; message.lastChild!.textContent = kinds.map((k) => L(ERROR_TEXT[k])).join(" · "); message.hidden = !kinds.length; }; /** [모두 기본값] — 내 별칭 · CAD 바꾼 키 비움 */ const resetAll = (): void => { for (const id of Object.keys(aliases)) delete aliases[id]; for (const input of inputs.values()) input.value = ""; for (const id of Object.keys(keys)) if (id.startsWith("cad.")) delete keys[id]; mark({}); onKeysChange(); }; const panel = findPanel( groups, [el("p", { text: L("App_Settings_Cad_Help") }), message], resetAll, ); return { root: panel.root, mark, keys: keyRowsCad, count: panel.count }; }