fix(B07-CAD): 한글 IME 조합 입력 — 문자 단계 숨은 받개 · 조합 중 단축키 건너뜀
- 문자를 기다리는 단계(TEXT · MTEXT · 표 칸 · 지시선)에서 숨은 input 에 초점 · compositionupdate/end 로 글자 받음 - 조합 중 키(isComposing · 229 · Process)는 단축키 처리에서 건너뜀 · 명령줄도 같음 - 명령 단계는 초점을 풀어 한글 자판이어도 영문 명령 그대로 - 입력 해석 패턴을 input-patterns.ts 로 나눔(700줄 상한) - 검증 창구 entities 에 label · 시나리오 ui_checks/44-8_CAD_한글입력.js Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UoSr2sqvYtBf1dtQdus88s
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
/* 44-8 CAD 한글 입력 — orca eval 한 번에 도는 시나리오.
|
||||
* 시작 화면: http://localhost:5183/b07-cad/index.html (CAD 앱 단독 · 빈 도면 · 저장 안 함)
|
||||
* 또는 CAD iframe 을 품은 화면(B07 · M02 양식).
|
||||
* 하는 일: 키보드로 TEXT 명령 → 캔버스 한 점 클릭 → 숨은 받개(.cad-ime-sink) 초점 →
|
||||
* 조합 사건(keydown Process · compositionstart/update/end)으로 「가나다」 → Enter → 글자 객체 → Ctrl+Z 되돌림.
|
||||
* 기대: sinkFocused true · imeKeyPrevented 0 · label "가나다" · 명령 끝 뒤 sinkReleased true ·
|
||||
* 되돌린 뒤 textCount = 시작 값.
|
||||
*/
|
||||
(async () => {
|
||||
const W = (t) => new Promise((r) => setTimeout(r, t));
|
||||
const w = document.querySelector("iframe")?.contentWindow ?? window;
|
||||
const d = w.document;
|
||||
const cad = w.__aisloCad;
|
||||
const texts = () => cad.entities().filter((e) => e.type === "Text");
|
||||
const before = texts().length;
|
||||
const key = (el, opts) => {
|
||||
const e = new w.KeyboardEvent("keydown", { bubbles: true, cancelable: true, ...opts });
|
||||
el.dispatchEvent(e);
|
||||
return e.defaultPrevented;
|
||||
};
|
||||
for (const k of ["T", "E", "X", "T", "Enter"]) key(d.body, { key: k });
|
||||
const canvas = d.querySelector("canvas");
|
||||
const r = canvas.getBoundingClientRect();
|
||||
const at = { clientX: r.left + r.width / 2, clientY: r.top + r.height / 2, bubbles: true };
|
||||
canvas.dispatchEvent(new w.MouseEvent("mouseenter", at));
|
||||
canvas.dispatchEvent(new w.MouseEvent("mousemove", at));
|
||||
canvas.dispatchEvent(new w.MouseEvent("mousedown", { ...at, button: 0 }));
|
||||
canvas.dispatchEvent(new w.MouseEvent("mouseup", { ...at, button: 0 }));
|
||||
canvas.dispatchEvent(new w.MouseEvent("mousemove", { ...at, clientX: at.clientX + 3 }));
|
||||
await W(300);
|
||||
const sink = d.querySelector(".cad-ime-sink");
|
||||
const sinkFocused = !!sink && d.activeElement === sink;
|
||||
let imeKeyPrevented = 0;
|
||||
const comp = (type, data) =>
|
||||
sink.dispatchEvent(new w.CompositionEvent(type, { data, bubbles: true }));
|
||||
for (const [jamo, syllable] of [
|
||||
["ㄱ", "가"],
|
||||
["ㄴ", "나"],
|
||||
["ㄷ", "다"],
|
||||
]) {
|
||||
imeKeyPrevented += key(sink, { key: "Process", keyCode: 229 });
|
||||
comp("compositionstart", "");
|
||||
comp("compositionupdate", jamo);
|
||||
imeKeyPrevented += key(sink, { key: "Process", keyCode: 229, isComposing: true });
|
||||
comp("compositionupdate", syllable);
|
||||
comp("compositionend", syllable);
|
||||
}
|
||||
key(sink, { key: "Enter" });
|
||||
await W(100);
|
||||
const made = texts().slice(before);
|
||||
const label = made.length ? made[made.length - 1].label : null;
|
||||
canvas.dispatchEvent(new w.MouseEvent("mousemove", { ...at, clientX: at.clientX + 6 }));
|
||||
await W(300);
|
||||
const sinkReleased = d.activeElement !== sink;
|
||||
key(d.body, { key: "Escape" });
|
||||
if (made.length) key(d.body, { key: "z", ctrlKey: true });
|
||||
await W(100);
|
||||
return JSON.stringify({
|
||||
sinkFocused,
|
||||
imeKeyPrevented,
|
||||
label,
|
||||
sinkReleased,
|
||||
textBefore: before,
|
||||
textAfterUndo: texts().length,
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user