# 013 — 작성한 규칙으로 직접 조작하기 2026-09-23. 이번 변경은 입력 표현력과 실제 조작 경로의 구현이다. 속도·RAM 비교나 합성 대규모 벤치마크는 수행하지 않았다. ## 문제와 이번 결정 기존 작성물은 키 또는 버튼의 누름·놓음으로만 조작할 수 있었다. 화면에 보이는 대상을 잡고 이동시키려면 좌표 입력이 필요했다. 호스트에 별도 이동 모델을 만들면 작성한 상태와 화면 위치를 동기화해야 한다. 이번에는 좌표를 기존 사건 실행의 일시적인 피연산자로 전달하고, 이동·제한·취소·연결된 출력은 작성한 상태와 표현식으로 처리했다. ```text input drag = pointer(tile) on drag.move { x = clamp(event_x - grab_x, 20, 564) y = clamp(event_y - grab_y, 100, 276) } ``` 전체 [직접 조작 예제](../../prototypes/workbench/examples/direct.pure)는 `.down`에서 잡은 위치와 시작 위치를 기억하고, `.up`에서 마지막 위치를 반영한다. `.cancel`은 시작 위치로 복귀한다. 좌우 키도 같은 x 상태를 바꾼다. 오른쪽 영역에 들어갔는지는 같은 상태를 읽는 다른 출력으로 표시한다. 제한이나 복귀 정책을 바꿀 때 실행기·렌더러 수정은 필요 없다. ## 구현 경계 브라우저는 대상 찾기, 좌표 변환, 포인터 캡처와 해제를 담당한다. SVG 루트가 캡처를 유지하므로 대상 또는 장면 밖에서도 조작을 끝낼 수 있다. 클라이언트 좌표는 SVG 변환의 역행렬로 장면 좌표에 연결한다. 축소된 모바일 화면에서도 작성 코드는 같은 좌표계를 사용한다. 코어에는 좌표 읽기 명령 두 개와 `dispatch_point(event,x,y)`가 추가됐다. 좌표는 호출 동안만 존재하며 영구 상태 슬롯을 만들지 않는다. 기존 명령·키 입력 번호와 사건의 원자적 확정, 변경 기록, 출력 경로는 유지한다. 호스트의 소유권 기록은 활성 포인터 입력마다 출처와 마지막 좌표를 가진다. 조작 위치의 권위 있는 값은 작성한 상태에만 있다. 하나의 입력은 한 포인터가 소유하며 다른 대상에는 독립적인 접촉을 허용한다. 놓기는 마지막 좌표를 반영한다. 취소·캡처 상실·초점 상실은 별도 cancel 사건이다. 이미 종료된 포인터의 늦은 놓기나 이동이 두 번째 사건을 만들지 않는다. 새 큐, 프레임 단위 묶기, 최신 좌표만 남기는 정책은 추가하지 않았다. 브라우저가 전달한 move를 처리하며 하드웨어의 모든 샘플 수신을 보장하지는 않는다. 하나의 사건에서 뒤쪽 반응이 실패하면 앞쪽 상태 변경도 확정하지 않는다. 이는 전체 드래그를 자동 되돌리는 것과 다르다. 전체 복귀는 작성한 cancel 반응이다. 취소 반응 자체가 실패하더라도 호스트의 입력 소유권은 해제하고 오류를 보존한다. CLI의 `point NAME PHASE X Y`도 같은 호스트 API와 코어를 사용한다. ## 검증 목적과 결과 검증 질문은 **작성한 직접 조작이 입력의 시작·변경·종료·취소에서 일관되는가**다. 처리량이나 실행 시간을 연구 결과로 제시하지 않는다. 테스트 도구가 자동 출력하는 실행 시간도 성능 증거로 해석하지 않는다. - 네이티브 26개: 좌표 문맥, 유한 값과 사건 범위, 표시 계산에서 좌표 접근 거부, 여러 반응에 걸친 실패 시 원자성 및 기존 코어 동작. - JavaScript 59개: 컴파일러·런타임·출력·CLI 회귀, 입력 소유권, 중복/오래된 사건, 마지막 놓기 좌표, 좌표로 변경한 상태의 출력 갱신, CLI 재생 동등성. - 브라우저 직접 조작 26개: 실제 마우스로 장면 밖까지 끌기와 놓기, 캡처 해제, 초점 상실과 취소, 작성 코드의 제한 변경, 실패 후 정리, 축소 화면의 터치와 서로 다른 대상의 두 접촉, 동일 대상의 중복 접촉. 페이지 오류는 없었다. - 기존 작성 흐름 브라우저 44개: 빈 문서 시작, 편집·적용·초기화, 키 입력, 파일 저장·불러오기, 오류 표시와 모바일 레이아웃의 회귀 검증. 브라우저는 Linux AArch64 Chromium이다. 터치는 Chromium CDP 입력을 사용했다. 초점 상실과 단일 포인터 cancel은 사건 주입, 캡처 상실은 실제 캡처 해제 API, 마우스 조작은 Playwright 마우스 입력으로 확인했다. 실제 휴대폰·Safari·펜 압력·기울기 동작을 검증했다고 주장하지 않는다. 근거: [검증 로그](../../prototypes/workbench/results/2026-09-23-pointer/verification.json), [브라우저 검사](../../prototypes/workbench/results/2026-09-23-pointer/pointer-browser.json), [작성 흐름 회귀](../../prototypes/workbench/results/2026-09-23-pointer/authoring-browser.json), [재현 스크립트](../../prototypes/workbench/qa-pointer.js), [상세 계약](../../prototypes/workbench/CONTRACT.md). ## 이번 결과의 의미와 다음 작업 새 제스처 이론이나 대규모 성능 우위를 입증한 결과는 아니다. 화면의 대상에 좌표 입력을 연결하고, 작성 코드가 동작을 정하며, 기존 상태 확정과 출력 경로로 이어지는 기능을 확보했다. 별도 호스트 이동 상태 없이 이 경로가 동작하는 것이 이번 단계의 결과다. 현재 상태 32개·입력 8개·출력 24개라는 한도는 그대로다. 실제 컨테이너 소속 변경, 다중 선택, 확대·축소, 배열·가변 개체 집합은 구현하지 않았다. 두 배경 영역을 가로지르는 예제는 컨테이너 간 재배치 구현이 아니다. 저장 기능도 현재는 소스 저장이며 조작 후 배치 상태의 저장·복원은 아니다. 다음은 같은 작업물을 **여러 대상을 배치하고 결과를 저장·복원하는 도구**로 연결한다. 작업 중 발생하는 상태·정의·파일의 일관성과 현재 고정 개수 모델의 제약을 확인한다. 규모를 억지로 키운 숫자 대신, 실제 작업을 막는 제약이 확인될 때 구조 변경과 측정을 택한다. ## 사용자 피드백에 따른 놓기 수정 Codex 내장 브라우저에서 놓으면 되돌아가며 마지막 사건이 up 또는 cancel로 표시된다는 보고가 있었다. 기존 Chromium 검사가 내장 브라우저의 정상 동작까지 입증하지는 않는다. 놓기가 SVG 밖에 전달된 뒤 캡처를 잃는 순서를 주입하면 기존 코드에서 cancel과 원위치 복귀가 재현됐다. 내장 브라우저의 실제 사건 순서를 직접 확보한 것은 아니다. 놓기·취소를 창의 캡처 단계에서 받고, pointerup 없이 전달된 기본 mouseup도 활성 마우스 입력에 한해 처리한다. 종료 후 늦은 캡처 해제는 중복 종료를 만들지 않는다. 예제의 up은 위치를 다시 계산하지 않고 마지막으로 표시된 위치를 확정하도록 변경했다. 놓기 좌표가 누르기 좌표를 반복하더라도 이동 결과가 튀지 않는다. 좌표가 필요한 작성자는 여전히 up에서 event_x/event_y를 읽을 수 있다. 실제 cancel/초점 상실의 복귀는 유지한다. 이 수정의 재현 검사는 [qa-release.js](../../prototypes/workbench/qa-release.js)에 있다.