Files
Aislo/A00_Common/app_settings_cad.ts
T
eomsangdonandClaude Opus 5.5 2e4316567c feat(설정 단축키): CAD 리본 묶음 · 찾기 · 다른 곳 = 실제 동작 등록부 표 · 키 바꾸기 · 겹침 막음 (PLAN 588-1~3 · 요구 445 C2 · C3)
- 새 key_registry.ts — 화면마다 받는 키 · 마우스 동작 한 벌 · keyIs(evt, id) · 바꾼 키만 input-prefs.keys
- ☰ [설정] CAD 탭 = 찾기 칸 + 「키 · 마우스」 + 리본 탭 › 패널 접이 묶음(기타 여섯) · 날 input → 부품 입력
- 다른 곳 탭 = 손글 안내 지우고 등록부 표(B05 방향키 · D03 Delete 등 빠진 것 포함) · 찾기
- 키 기록 칸 · 줄마다 [기본값] · [모두 기본값] · 같은 화면 · 모든 화면 겹침 · 브라우저 몫 · CAD 글자 단독은 저장 막음
- 배선: B05 되돌림 · 자유지정 Enter · Backspace · D03 되돌림 · 그리기 · 지움(한글 자판도 자판 자리) · CAD F3 · F7 · F8 · F10 · Ctrl 명령 · Delete
- 낡은 글 대조 시험 셋(215-1 · 345-3 · 346-4)은 새 배선 글로 고침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:50:41 +09:00

146 lines
5.5 KiB
TypeScript

/* =============================================================================
* 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<AliasError, Parameters<typeof L>[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<string, string[]>,
commands: CadCommandRow[] = CAD_COMMANDS,
): Record<string, AliasError> {
const ids = new Set(commands.map((c) => c.id));
const owner = new Map<string, string>();
const errors: Record<string, AliasError> = {};
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<string, string[]>,
keys: Record<string, string[]>,
onKeysChange: () => void,
): {
root: HTMLElement;
mark: (errors: Record<string, AliasError>) => void;
keys: KeyRows;
count: () => number;
} {
const message = createText("", "error");
message.hidden = true;
const inputs = new Map<string, HTMLInputElement>();
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<string, AliasError>): 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 };
}