- 새 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>
146 lines
5.5 KiB
TypeScript
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 };
|
|
}
|