feat(B07-CAD): Space = Enter(오토캐드) · 빈 Enter · Space = 마지막 명령 반복(97-3)

- 캔버스 · 명령줄에서 Space(수정키 없음 · 길게 눌러 반복 아님) = Enter · 글 단계(TEXT · MTEXT · 표 칸 · 이름 등)는 띄어쓰기
- 글 단계 판정 = sequence-tool 단계 meta kind 'text'(activeToolWantsFreeText)
- 빈 입력 + 선택 도구 대기에서 키보드 Enter · Space = repeatLastCommand · 빈 우클릭은 확정만
- 한글 조합 중 Space · Shift/Ctrl+Space 무시
- 시험 ime-sink: 「L␣」 = LINE · TEXT 띄어쓰기 · 조합 중 · 수정키 · 반복 무시 · 빈 Space · Enter 반복 · 우클릭 반복 안 함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
This commit is contained in:
2026-10-08 14:02:30 +09:00
co-authored by Claude Opus 5.5
parent e020cf71aa
commit 456c11aa5d
5 changed files with 136 additions and 7 deletions
@@ -14,7 +14,7 @@ import {
matchCommandPrefixes,
resolveCommandInput,
} from '../commands/registry.ts';
import { runCommand } from '../commands/run-command.ts';
import { repeatLastCommand, runCommand } from '../commands/run-command.ts';
import { requestHostSave } from '../integration/aislo-drawing-bridge.ts';
import type { ScreenCanvasDrawController } from '../drawControllers/screenCanvas.drawController.ts';
import { calculateAngleGuidesAndSnapPoints } from '../helpers/calculate-angle-guides-and-snap-points.ts';
@@ -58,7 +58,13 @@ import {
type RelativePointInputEvent,
type TextInputEvent,
} from '../tools/tool.types.ts';
import { activeToolAcceptsText, ImeSink, isImeKey } from './ime-sink.ts';
import {
activeToolAcceptsText,
activeToolWantsFreeText,
ImeSink,
isImeKey,
isPlainSpace,
} from './ime-sink.ts';
import { drawInputField } from './input-field-draw.ts';
import {
ABSOLUTE_POINT_REGEXP,
@@ -437,8 +443,14 @@ export class InputController {
this.handleEscapeKey();
} else if (evt.key === 'Enter') {
// User wants to submit the input or submit the action
this.handleEnterKey();
this.handleEnterKey(true);
this.ime.sync(activeToolAcceptsText());
} else if (evt.key === ' ' && !activeToolWantsFreeText()) {
// Space = Enter(오토캐드) · 글 단계는 아래 띄어쓰기 · 수정키 · 길게 눌러 반복은 무시
if (isPlainSpace(evt)) {
this.handleEnterKey(true);
this.ime.sync(activeToolAcceptsText());
}
} else if (evt.key === 'ArrowDown') {
// Move the screen down
getScreenCanvasDrawController().setScreenOffset(this.getScreenPanStep('down', evt.shiftKey));
@@ -482,7 +494,8 @@ export class InputController {
return matchCommandPrefixes(this.text).slice(0, 6).map(describeCommand);
}
public handleEnterKey() {
/** @param repeatIfIdle 키보드 Enter · Space — 빈 입력 + 선택 도구 대기면 마지막 명령 반복(우클릭은 확정만) */
public handleEnterKey(repeatIfIdle = false) {
// submit the text as input to the active tool and clear the input field
const activeToolCanHandleTextInput = activeToolAcceptsText();
@@ -492,6 +505,9 @@ export class InputController {
activeTool: getActiveToolActor(),
});
const wasSelectTool = this.isSelectToolActive();
const selectIdle =
getActiveToolActor()?.getSnapshot()?.value === SelectState.WAITING_FOR_FIRST_SELECT_POINT;
if (repeatIfIdle && wasSelectTool && selectIdle && repeatLastCommand()) return;
// Send the ENTER event to the active tool
getActiveToolActor()?.send({
type: ActorEvent.ENTER,