Files
Aislo/resources/tester/cad/helpers/cad-drive.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

132 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* CAD 시험 공용 조작(337 설계 6-2) — 빈 도면 · 쉬는 상태(선택 도구)에서 키 · 마우스를 사람처럼 넣음.
* 시험 쪽 flatten-js 는 다른 사본(「Illegal Parameters」) — 점은 앱 코드로만 만들고 숫자로 판정.
* 시험 캔버스는 축척 1 · y 위 + · 세계 (x, y) = 화면 (x, 높이 − y).
*/
import { vi } from "vitest";
import type { InputController } from "../../../../B07_DesignDetail/openwebcad/src/inputController/input-controller";
import {
currentNotice,
currentPrompt,
} from "../../../../B07_DesignDetail/openwebcad/src/inputController/prompt-input";
import {
getActiveToolActor,
getEntities,
setSelectedEntityIds,
} from "../../../../B07_DesignDetail/openwebcad/src/state";
import { ActorEvent } from "../../../../B07_DesignDetail/openwebcad/src/tools/tool.types";
import { initApplication } from "../../../../B07_DesignDetail/openwebcad/test/helpers/init-application";
import { CANVAS_HEIGHT } from "../../../../B07_DesignDetail/openwebcad/test/helpers/tests.consts";
export type Mods = Partial<Pick<KeyboardEvent, "shiftKey" | "ctrlKey" | "altKey" | "metaKey">> & {
code?: string;
repeat?: boolean;
};
let controller: InputController;
/** 빈 도면 · 고른 것 없음 · 선택 도구(쉼)로 시작 — 앱 도우미가 켠 LINE 첫 물음에서 Esc */
export function initIdle(): InputController {
controller = initApplication();
getActiveToolActor()?.send({ type: ActorEvent.ESC });
setSelectedEntityIds([]);
return controller;
}
export const driver = (): InputController => controller;
/** 키 하나 — `key` 는 KeyboardEvent.key 꼴(「a」 · 「Enter」 · 「Escape」 · 「 」) */
export function key(k: string, mods: Mods = {}) {
const preventDefault = vi.fn();
controller.handleKeyStroke({
key: k,
code: mods.code ?? "",
keyCode: 0,
isComposing: false,
shiftKey: false,
ctrlKey: false,
altKey: false,
metaKey: false,
repeat: false,
target: null,
preventDefault,
stopPropagation: vi.fn(),
...mods,
} as unknown as KeyboardEvent);
return preventDefault;
}
/** 글자만 침(제출 안 함) */
export const typed = (text: string) => {
for (const ch of text) key(ch);
};
/** 글자 + Space(= Enter) */
export const type = (text: string) => {
typed(text);
key(" ");
};
/** 글자 + Enter */
export const typeEnter = (text: string) => {
typed(text);
key("Enter");
};
const mouse = (x: number, y: number, button: number, mods: Mods = {}) =>
({
button,
clientX: x,
clientY: CANVAS_HEIGHT - y,
target: null,
shiftKey: false,
ctrlKey: false,
preventDefault: () => {},
stopPropagation: () => {},
...mods,
}) as unknown as MouseEvent;
/** 세계 좌표 (x, y) 로 커서 이동 */
export const move = (x: number, y: number) =>
controller.handleMouseMove({ clientX: x, clientY: CANVAS_HEIGHT - y } as MouseEvent);
export const press = (x: number, y: number, mods: Mods = {}) => {
move(x, y);
controller.handleMouseDown(mouse(x, y, 0, mods));
};
export const release = (x: number, y: number, mods: Mods = {}) => {
move(x, y);
controller.handleMouseUp(mouse(x, y, 0, mods));
};
/** 왼쪽 누름 한 번(누름 + 뗌 같은 자리) */
export const click = (x: number, y: number, mods: Mods = {}) => {
press(x, y, mods);
release(x, y, mods);
};
/** a 에서 눌러 b 에서 뗌 */
export const drag = (a: [number, number], b: [number, number], mods: Mods = {}) => {
press(a[0], a[1], mods);
move(b[0], b[1]);
release(b[0], b[1], mods);
};
export const rightClick = (shift = false) =>
controller.handleMouseUp(mouse(0, 0, 2, { shiftKey: shift }));
/** 가운데 단추 두 번 */
export const middleDouble = (x: number, y: number) => {
for (let i = 0; i < 2; i++) {
controller.handleMouseDown(mouse(x, y, 1));
controller.handleMouseUp(mouse(x, y, 1));
}
};
/** 도면 객체(유형 이름 거름 — 「Line」 · 「Circle」 …) */
export const entities = (type?: string) =>
getEntities().filter((entity) => !type || entity.getType() === type);
/** 커서 곁 첫 줄(지금 물음) */
export const promptLine = () => currentPrompt().line;
/** 커서 곁 둘째 줄(거절 · 안내) */
export const noticeLine = () => currentNotice();
/** 맨 위 도구 이름(Tool) */
export const activeTool = () => getActiveToolActor()?.getSnapshot()?.context?.type;
export const toolState = () => getActiveToolActor()?.getSnapshot()?.value;
/** 친 글(커서 곁 입력 칸) */
export const inputText = () => (controller as unknown as { text: string }).text;
export const tick = () => new Promise((resolve) => setTimeout(resolve, 0));