CrownyAI PPM 터미널 사용성 v2 — 순간에코 + 스크롤백 선택/복사 (2026-07-11)
개요
크라우니 입력도구 v1(2026-07-11-크라우니-입력도구-v1.md) 이후 사용자가 실측한 두 가지
터미널 통증을 해소했다.
- A. 타이핑 순간 에코: 키 입력마다 전체 렌더 파이프라인(bash+crownyc 서브프로세스,
- B. 스크롤백 선택·복사: 출력 줄 클릭으로 선택(Shift+클릭=범위), ⌘C(입력 선택 없으면
A. 순간 에코 + 디바운스 렌더
설계
CrownyAppView에마지막키시각(NSDate)·디바운스타이머(NSTimer) 프로퍼티 추가.injectKey:/injectJamo:/조합 중 BS/캐럿 내비게이션(←/→/Home/End) 이 기존
[self renderAndReload] 즉시호출 대신 [self noteKeyTypedAndScheduleRender] 호출로
통일:
마지막키시각 = nowsetNeedsDisplay:YES (다음 프레임에 drawRect 오버드로우만 반영, 수 ms)scheduleDebouncedFullRender (0.4s 단발 NSTimer, 재호출 시 이전 타이머- Enter(실행 버튼 경로)·마우스 클릭 액션(버튼/포커스 등)은 기존 그대로 즉시
renderAndReload/reload — 디바운스 대상 아님.오버드로우(drawTypingOverdraw)
drawRect:에서self.rep(마지막 PPM)을 그린 위에,service=="터미널"이고
- 13pt 모노스페이스 시스템 폰트로 근사(정본 CIF 벡터 폰트와 완전히 동일하지는
- 순수 헬퍼로 분리(프로세스 스폰 없음, 파일 I/O만):
appTypingOverdrawActive · appFocusedInputName · appReadInputField ·
appInputRectForName · appCanvasRectToView — 모두 --selftest-inputv2
하네스로 검증.
- 현재 범위는 터미널의 다크 입력칸만(
어두운입력칸()). 다른 앱의 흰 입력칸(입력칸())
알려진 트레이드오프
디바운스 타이머가 0.4s에 발화한 뒤 실제 렌더 파이프라인이 도착하기까지
추가로 90~290ms가 걸린다. 그 사이(마지막 키 후 약 0.49~0.69s 구간의 마지막
0.09~0.29s)는 오버드로우 조건(<0.4s)이 이미 꺼졌고 self.rep도 아직 갱신되지
않아, 화면이 짧게 "타이핑 직전 상태"로 보일 수 있다. 스펙에 명시된 설계
그대로이며, 그 직후 렌더가 도착해 정상 표시된다.
B. 스크롤백 선택·복사
클릭 선택
mouseDown:에 새 분기 추가:service=="터미널"이고 클릭 캔버스좌표
cy >= 44(출력영역 시작, 터미널앱.한선 상수 미러 kTermOutputY)이며 어떤
히트영역(appAnyHitRect, 입력/버튼 등)에도 속하지 않으면 스크롤백 클릭으로
처리 — 기존 kAppEventToau 프로세스 스폰(VM 이벤트루프 조회) 전에 가로채
빈 영역 클릭마다 VM을 띄우지 않는 부수 이득도 있다.
표시행 = (cy - 44) / 19(라인높이,kTermLineHeight— 터미널앱.한선 :169~170
termSelectLineIndex:extend:가 /tmp/터미널선택.psv에
시작|<표시행>\n끝|<표시행>\n 기록. Shift+클릭은 기존 시작(앵커)을 유지한 채
끝만 갱신.
터미널앱.한선이_선택값("시작"/"끝")으로 읽어 스크롤백 루프에서 선택 범위에
박스(0, 출력y-2, CVW, 라인높이, 36,32,28) 배경을 텍스트보다 먼저
그린다.
- PPM 픽셀 검증: 선택 표시행(row2, y=80~99)은
(36,32,28), 비선택 행은
(10,9,8) 그대로 — bbox·색상 정확히 일치 확인.복사
copy:(⌘C) →cb_copyGeneric: 입력칸에 선택이 있으면(입력코어COPY응답의
SEL| 파싱) 기존대로 그 텍스트를 pasteboard로. 없으면(또는 포커스 자체가
없으면) service=="터미널"일 때 /tmp/터미널선택.psv를 읽어 스크롤백 선택
텍스트로 폴백.
cut:(⌘X)은 기존 입력-전용 동작 그대로(스크롤백은 읽기전용 로그라 폴백 없음).copyAllScrollback:(⌘⇧C, 신규 Edit 메뉴 항목 "Copy All Scrollback") — 선택
/tmp/터미널상태.psv의 "줄|" 전체를 pasteboard로.
- 스크롤백 텍스트 추출은 네이티브 순수 헬퍼(
appTerminalOutputLines·
appReadTermSelection · appTermScrollbackSelectionText)로 분리 —
프로세스 스폰 없이 파일만 읽어 즉시 복사(pasteboard 실제 쓰기는 빌드로만 확인,
텍스트 추출 로직은 하네스로 검증).
- "지우기" 액션(버튼/명령 둘 다) 시
/tmp/터미널선택.psv도 함께 비움
터미널상태.한선 _선택비움()) — 낡은 표시행 참조 방지.셀프테스트 하네스
CrownyBrowser --selftest-inputv2 — GUI 없이 실행, 위 순수 헬퍼 9종 + 인스턴스
메서드 2종(termSelectLineIndex:extend:, scheduleDebouncedFullRender 코얼레스)을
실제 프로덕션 코드로 직접 검증(19/19 PASS). /tmp/입력상태.psv·히트맵.psv·
터미널상태.psv·터미널선택.psv(라이브 세션과 공유하는 실제 경로)를 테스트
전후 백업/복원해 다른 세션과의 공존을 배려했다 — 실행 후 복원 확인 완료.
검증 요약
--selftest-inputv219/19 PASS (오버드로우 판정·입력필드 파싱 2종·히트맵
- 실제
입력코어.toauFOCUS→KEY×N→LEFT×2 시퀀스로/tmp/입력상태.psv가
이름|값|캐럿|선앞|선뒤 5필드로 정확히 갱신됨을 확인(오버드로우 파서 입력과
정합).
- 스크롤백 선택 하이라이트 PPM 픽셀 검증(선택행 36,32,28 / 비선택 10,9,8 정확
TERM_ACTION=지우기(자가치유 경유터미널액션.sh 지우기) →/tmp/터미널선택.psv
- 터미널 echo E2E:
FOCUS→KEY×N("echo 입력도구완성")→TERM_ACTION=실행→
/tmp/터미널상태.psv에 $ echo 입력도구완성 + 입력도구완성 정확히 기록,
입력값 초기화 확인(v1 대비 무회귀).
- 회귀 스모크: 계산기(
계산기렌더.sh)·메모(뷰어렌더.sh) 둘 다 exit0 + 유효
- 빌드:
make webexit0(신규 경고 0),make public universal crownyonly
otool -L WebKit 링크수: public=1·universal=1·
crownyonly(Crowny-Full)=0·main=1 — 기존과 동일(무회귀).
- 3곳 배치(scratch 경유 mv, 제자리 cp 금지 규칙 준수):
native/CrownyBrowser,
CrownyAI.app/Contents/MacOS/CrownyBrowser,
CrownyBrowser.app/Contents/MacOS/CrownyBrowser SHA1 전부 동일
98ea4ceecaabfaa317dc5ba599fb04c9b60d7d59.
터미널앱.한선·터미널상태.한선수정분hanseonc_high단독 컴파일 exit0
CROWNY_STD 지정).크라우니코드 보고
- lookup-first 조회:
crownycode-learn.sh search "터미널 스크롤백 선택 복사"/
"디바운스 렌더 오버드로우" / "스크롤백 선택 하이라이트" → 전부 MISS(0건).
- 생성: 오버드로우/디바운스/스크롤백선택 네이티브 헬퍼(ObjC, crowny-browser.m)
터미널앱.한선 _선택값()/선택 하이라이트 렌더 + 터미널상태.한선
_선택비움().
- 학습:
crownycode-learn.sh add 터미널스크롤백선택하이라이트 "<코드>"완료. - 크라우니코드: lookup HIT 0건(재사용) / MISS 3건(생성) / learn 추가 1건
수정 파일
/Users/ef/CrownyBrowser/native/crowny-browser.m— 순수 헬퍼 9종(오버드로우
CrownyAppView: 마지막키시각/디바운스타이머 프로퍼티,
noteKeyTypedAndScheduleRender/scheduleDebouncedFullRender/
drawTypingOverdraw, injectKey:/injectJamo:/캐럿내비/조합BS 디바운스
전환, mouseDown: 스크롤백 클릭 분기 + termSelectLineIndex:extend:,
copy:→cb_copyGeneric(스크롤백 폴백)+cbWriteStringToPasteboard:,
copyAllScrollback:(⌘⇧C 메뉴 항목 추가), dealloc에 디바운스 타이머 정리,
--selftest-inputv2 셀프테스트 하네스(main() 진입점 분기).
/Users/ef/CrownyBrowser/src/터미널앱.한선—_선택값()읽기 함수, 스크롤백
/Users/ef/CrownyBrowser/src/터미널상태.한선—_선택비움(), 두 "지우기"
지우기 명령)에서 호출.잔여 이슈 / 다음 단계
- GUI 실측(사용자 몫): 타이핑 시 순간 에코가 실제로 보이는지, 클릭 선택·
- 오버드로우 폰트 근사: 13pt 시스템 모노스페이스가 정본 CIF 벡터 폰트와
- 다른 앱으로 오버드로우 확장:
입력칸()(흰 배경) 팔레트 분기를 추가하면
- Shift+드래그(마우스 드래그로 실시간 범위 선택): 이번 구현은 클릭/
mouseDragged: 핸들러 추가로 확장 가능
(입력도구 v1의 "Shift+화살표 드래그 선택 미구현"과 같은 패턴의 잔여 항목).
- 분할뷰 동시 터미널 2개일 때 스크롤백선택.psv 전역 공유: 캐럿상태.psv와