터미널 렌더링
터미널 화면은 cols × rows 셀 그리드입니다. 정확성은 VT에서 비롯되고, '아름다움'은 메트릭과 페인트에서 옵니다. 같은 에뮬레이터라도 다른 결과를 보여줄 수 있습니다.
네 가지 계층
| 계층 | 역할 | 문제 발생 시 |
|---|---|---|
| 1. VT | 바이트 → 셀 그리드 (커서, 모드, 스크롤백, 대체 화면) | 깨짐 / 잘못된 크기 / 커서 고정 |
| 2. 메트릭 | 글꼴의 셀 너비/높이; cols/rows 및 셀 px로 PTY 크기 조정 | 흔들리는 컬럼, 흐릿한 가장자리 |
| 3. 페인트 | 셀 그리기: 배경 실행, 글리프, 스프라이트, 대비 | 간격, 얇은 텍스트, 깨진 TUI 테두리 |
| 4. 세션 (선택 사항) | 지속/원격: 데몬이 VT를 소유, 클라이언트가 프레임 미러링 | 창이 닫히면 스크롤백 사라짐 |
요약: 정확성은 VT; 미관은 글꼴 + 메트릭 + 셀별 배치 + 기하학적 박스 드로잉; 속도는 더티 로우 캐시.
1. VT (그리드가 정확해야 함)
- 에이전트 TUI의 경우 직접 만든 ANSI 서브셋보다는 실제 에뮬레이터(예: libghostty-vt)를 선호하세요.
- 각 셀: 텍스트(자소), fg/bg, 볼드/이탤릭/희미하게, 밑줄/취소선, 와이드 플래그.
- 처리: 와이드 문자/이모지 너비, 대체 화면, 동기화된 출력(모드 2026), 다음 PTY 읽기 전에 기록되는 쿼리 응답.
- UI는 그리드의 스냅샷을 읽습니다. 페인트 경로에서 바이트 스트림을 다시 구문 분석하지 마세요.
2. 메트릭
- 컬럼 너비 ≈
'M'의 글리프 전진 값(선택적CellWidth배수). - 행 높이 ≈ 어센트 + 디센트, 이후 × 약 1.15–1.2; 장치 픽셀에 맞춤.
- 너비를 글꼴 전진 값으로 유지하고, 박스/배경 가장자리를 픽셀에 맞춰 이웃이 틈 없이 만나도록 합니다(글리프가 흔들리지 않도록 셀 너비를 반올림하지 마세요).
- 크기 조정 시 PTY에
cols,rows,cell_width_px,cell_height_px를 전송합니다(프로그램은 마우스/이미지에 픽셀 크기를 사용).
3. 페인트 (외관이 결정되는 곳)
기존 터미널을 개선할 때 우선 순위:
- 고정된 모노 + Nerd 심볼 폰트 제공(예: JetBrains Mono + Symbols Nerd Font). 시스템 기본 모노/누락된 Powerline 아이콘은 에이전트 TUI를 망칩니다.
- 박스 / 블록 / 브레일 / Powerline을 글꼴 글리프가 아닌 기하학적 도형으로 그리기 — 글꼴은 행 사이에 간격을 남깁니다. 전체 화면 테두리는 연결되어야 합니다.
- 그리드에 글리프 배치: 동일 스타일의 연속 텍스트는 강제 너비 셰이핑 또는 셀별 중앙 정렬. 좁은 셀을 넘는 대체 글리프(이모지)는 축소.
- 누락된 PUA / Nerd 아이콘을 심볼 폰트로 라우팅(또는 셀 내에 외곽선 맞춤).
- 선택적 개선:
최소-대비(스프라이트/의도적 그래픽 건너뜀), macOS 글꼴 두껍게 하기, 다중 스타일 밑줄, 패딩 색상 확장.
페인트 파이프라인 (프레임당)
- 창을 터미널 배경색으로 채웁니다.
- 시작점 = 패딩 + 수직 여백/2; 시작점을 장치 픽셀에 맞춥니다.
- 행의
Arc/id가 변경된 경우(또는 글꼴/셀 크기 변경/전체 더티)에만 행 연산을 다시 빌드합니다. - 모든 배경 범위, 그 다음 스프라이트, 그 다음 텍스트 실행, 마지막으로 커서 / IME / 오버레이를 그립니다.
행 연산
- 연속된 동일 배경 셀을 하나의 사각형으로 병합합니다.
- 하나의 스프라이트 셀 → 기하학적 그리기(herdr / Ghostty의 스프라이트 접근 방식 참조).
- 그 외에는 동일 스타일의 텍스트 실행을 셰이핑합니다. 좁은 셀의 경우 열이 고정되도록 전진 값을
cell_w로 강제합니다.
VT를 탓하지 말아야 할 경우
테두리에 간격이 생기거나, 아이콘이 네모 상자로 표시되거나, 텍스트가 얇거나 고르지 않지만 그리드 내용이 올바르다면 — 페인트/글꼴을 수정하고 에뮬레이터를 교체하지 마세요.
4. 세션 토폴로지
프로세스 내(단순): PTY → VT → 스냅샷 → UI 페인트.
데몬 + 클라이언트(kero 스타일): 데몬 헤드리스 VT + PTY; Mirror가 현재 화면의 VT 프레임을 인코딩; 클라이언트 VT가 Conn과 함께 셀 단위로 페인트; 창 닫기/원격 연결 시에도 스크롤백 유지.
참조 구현
| 프로젝트 | 스택 | 참고 |
|---|---|---|
| Ghostty | Zig + libghostty-vt | VT + 스프라이트 의미론의 원천 |
| herdr-app | GPUI + libghostty-vt | 셀 페인트, 스프라이트, 대비, 번들 폰트 |
| kero | MyGo + libghostty-vt | 미러 프레임, Ghostty 테마, GPU UI |
| kumu | GPUI-kit + libghostty-vt | 동일한 페인트 아이디어: 스프라이트 + JetBrains + Nerd |
체크리스트
터미널 렌더링을 추가하거나 수정할 때:
- VT가 그리드를 소유; UI는 스냅샷만 페인트
- 모노 폰트의 셀 메트릭; 크기 조정에 픽셀 셀 크기 포함
- 번들(또는 보장된) 모노 + Nerd 심볼 폰트
- 박스 / Powerline / 브레일을 도형으로 그림
- 더티 로우 페인트 캐시는 행 ID를 키로 사용
- 커서, IME 및 선택 항목이 셀 좌표에 위치
- 알려진 TUI(htop, Claude Code, statusline)를 Ghostty와 비교
추가 세부 사항
- 스프라이트 / 기하학적 범위 및 대비 규칙: paint.md