"masc TUI 채팅 화면이 무엇을 어떤 기호로 그리는지 정리한 표와 규칙이다. 말한 사람 마크, 턴 레일, 왼쪽 여백의 순서, 시계 칸, Ctrl-F 밀도 단계, 밝기 위계와 끝난 일의 접힘, 줄 문구를 바꾸거나 새 기호를 넣기 전에 쓴다. 바꾼 화면을 실제로 띄워 확인하는 방법은 tui-pty-scenario 에 있다."
Install
npx skillscat add jeong-sik/masc/tui-chat-design Install via the SkillsCat registry.
masc 채팅 화면의 기호와 규칙
이 화면에는 이미 정해진 기호 체계가 있다. 새 기호를 만들기 전에 있는 기호로 말할 수 있는지 먼저 본다.
아래 값의 출처는 bin/masc_tui_message_layout.ml 과 bin/masc_tui_render_chat.ml 이다.
이 문서와 코드가 다르면 코드가 맞다. 그 자리에서 이 문서를 고친다.
말한 사람 — speaker_mark
| 기호 | 스타일 | 뜻 |
|---|---|---|
▶ |
User |
운영자가 보냈다 |
◀ |
Inbound |
바깥의 누군가가 여기로 보냈다 |
● |
Keeper |
키퍼가 말한다 |
○ |
Status |
그 턴에 대한 화면의 메모. 키퍼가 한 말이 아니다 |
› |
Local |
입력줄 프롬프트와 같은 기호. 방금 친 것에 화면이 답한다 |
◈ |
Journal |
나란히 도는 Memory 저널 레인 |
■ |
Tool |
도구 |
◇ ◆ △ ✗ |
Skill _ |
스킬: 도는 중 / 썼음 / 볼 것 / 실패 |
· |
Thinking |
생각 중 |
✗ |
Error |
오류 |
같은 ✗ 는 Overview 의 TUI Session Events 패널에서도 오류를 뜻한다. event_type = "error" 인 행만
시계 뒤에 ✗ 를 달고, 나머지 레벨은 표식 없이 그린다(Masc_tui_types.overview_event_mark).
이 표를 만든 규칙 둘
- 기호는 도형이다.
?같은 문장 부호는 쓰지 않는다. 끝난 줄에 물음표가 남으면 아무도 답하지
않은 것처럼 읽힌다. 화면의 메모가○인 이유다. - 채운 것과 빈 것이 짝이다.
●/○,◆/◇에서 채운 쪽은 그 일 자체이고, 빈 쪽은 그 일에
대한 화면의 메모다. 새 상태가 필요하면 이 짝으로 말할 수 있는지 먼저 본다.
색은 구분의 근거가 못 된다
NO_COLOR 가 켜지면 색이 사라진다. 그래서 누가 말했는지는 색이 아니라 마크가 답해야 한다.
색으로만 구분되는 상태를 만들면 색 없는 터미널에서는 그 상태가 안 보인다.
밝기는 위계다
모양이 무엇인가를, 색이 누구인가·어떤 상태인가를 말한다면, 밝기는 위계만 말한다.
밝은 자리는 화면에 몇 개 없어야 한다.
- 말만 전경이다.
User·Inbound·Keeper본문은 터미널 전경을 유지한다.Local은
명령에 대한 답이라 읽는 글이라 누르지 않는다. - 일·소식·생각은 한 단계 아래다.
Tool·Skill·Journal·Thinking본문은 dim 이다
(Chat_theme.body,bin/masc_tui_ansi.ml). dim 본문 안의 bold·링크가 닫힐 때 dim 을 다시
연다 — 스팬 하나가 그 뒤의 줄을 전부 밝히지 못한다. - 크롬은 가장 조용하다. 날짜 레일, 시계, 곁줄, 여백 라벨, 브레드크럼은
Theme.recede ()
단계다. 스크롤 방향타로는 남지만 밝은 자리를 쓰지 않는다. - 실패는 이 규칙의 예외다.
✗와failed절은 reset→bold→bright red 로 서고, 실패 줄은
접히지 않는다. 성공이 가라앉는 만큼 실패가 도드라져야 한다.
턴 레일 — turn_rail_glyph
레일은 "이 줄이 어느 턴에 속하나" 한 가지에만 답한다. 누가 말했나는 마크가 답한다.
한 기호에 두 질문을 싣지 않는다.
| 기호 | 값 | 뜻 |
|---|---|---|
╭ |
Rail_opens |
턴이 이 줄에서 시작한다 |
│ |
Rail_says |
턴 안의 말(운영자·키퍼·상태 등) |
├ |
Rail_does |
턴 안의 작업(도구·스킬·생각) |
╶ |
Rail_stands |
한 줄짜리 턴인데 그 줄이 작업이다 |
╰ |
Rail_closes |
턴이 이 줄에서 끝났다 |
┤ |
Rail_joins |
어느 턴에도 속하지 않은 줄이 선에 닿는다 |
| 빈칸 | Rail_none |
한 줄짜리 말, 또는 턴 밖의 줄 |
┤앞에는 세 칸짜리 곁줄이 붙는다. 저널은 점선╌╌╌, 다른 사람이 보낸 줄은 실선───이다.
종류는 기호가 아니라 선 모양으로 말한다. 마크와 라벨이 이미 누구인지 말하기 때문이다.- 괄호(
╭…╰)는 붙어 있는 줄에만 친다. 턴 밖의 줄(저널 기록, 남의 broadcast)은 괄호를 끊지
않고 지나간다. 다른 턴의 줄이 끼어들어 한 턴이 둘로 갈라지면, 괄호 대신 외톨이 줄 두 개로 그린다.
한 칸짜리 세로줄로는 떨어진 줄을 한 턴이라고 말할 수 없다. - 한 줄짜리 말은 레일을 그리지 않는다. 대화의 거의 모든 줄에 레일이 붙으면 아무도 레일을 안 본다.
왼쪽 여백의 순서는 고정이다
한 줄은 왼쪽부터 이 순서다. 순서를 바꾸면 세로줄이 어긋난다.
- 레일 여백 —
turn_rail_cells = 5. 곁줄 3칸 + 기호 1칸 + 띄움 1칸.
곁줄이 없는 줄도 5칸을 다 쓴다. 폭이 줄마다 달라지면 스크롤할 때 본문 줄바꿈이 바뀐다. - 시계 —
chat_clock_column = 5칸 + 띄움 1칸.Origin_inline에서만 그린다.
모든 줄의 시계 칸은 같은 5칸이다. 칸 폭이 줄마다 다르면 본문 줄바꿈도 줄마다 달라진다.
시계는 recede 단계로 눌려 있고, 마크만 말한 사람의 색을 지킨다
(시계 칸의 경계는gutter_clock_cells). - 마크 + 띄움 + 라벨 — 라벨 칸은
chat_role_label_column = 10. 마크 칸은 라벨 칸 안에서 뺀다.
이름이 넘치면 가운데를 줄인다(fit_middle). 마크는 줄이는 범위 밖에 있어서 긴 이름에서도 남는다.
도구·스킬·생각 줄은 라벨 단어를 쓰지 않는다 — 마크가 이미 레인을 말한다.
칸은 그대로 두고 마크와 띄움만 그린다. - 인용 막대 — 남이 쓴 글(
Tool·Skill·Status·Local)은 흐린│,Journal은┊,
말(User·Inbound·Keeper·Error·Thinking)은 빈칸 두 칸이다. - 본문
폭이 모자라면 레일을 먼저 버린다. 그래도 이름이 안 들어가면 시계와 마크를 버리고 이름을 줄여 남긴다.
누가 말했는지가 언제 말했는지보다 중요하다.
같은 값을 반복해서 그리지 않는다
- 시계는 분이 바뀐 줄에만 그린다. 같은 분에 여섯 줄이 오면 한 줄만 시각을 달고 나머지는 빈칸이다.
칸은 그대로 비워 둬서 아무것도 밀리지 않는다. - 같은 사람이 이어서 말하면 이름을 지운다. 마크도 가장 조용한 기호로 바뀐다
(continued_mark:User·Inbound·Keeper·Tool은│,Thinking은·, 나머지는 원래 마크).
밀도는 세 단계이고 Ctrl-F 로 돈다
| 단계 | 모양 |
|---|---|
Origin_inline (기본) |
출처를 여백에 접고, 분이 바뀐 줄에 짧은 시계를 단다 |
Origin_row |
헤더 한 줄: 이름 전체, 오른쪽 끝 recede 시계, 사이 rule |
Origin_bare |
시계를 떼고 마크와 이름만 남긴다 |
Origin_row 헤더의 시각 칸이 비면(timeline_bucket = None) 시계를 안 그리고 --:--:--
자리표도 쓰지 않는다. 같은 화자가 이어지는 행은 이름 없이 rule 과 시계만 그린다
(metadata_row, bin/masc_tui_render_chat.ml 의 origin_heading).
Ctrl-F 는 Origin_inline → Origin_row → Origin_bare → Origin_inline 으로 돈다
(Masc_tui_types.next_origin_display). 기본값은 Masc_tui_types 의 상태 생성에서 정한다.
기본값 하나만으로 읽을 수 있어야 한다. "Ctrl-F 누르면 보인다" 는 기본값이 모자란다는 뜻이다.
끝난 일은 접힌다
끝난 일은 화면의 주인공이 아니다. 무엇이 남고 무엇이 접히는지는 정해져 있다.
- 끝난 스킬은 요약 한 줄이다.
전달됨, 도구 씀 · 이름 · N action
(Keeper_chat_transcript.skill_rows). 전달만 됐는지 실제로 쓰였는지는 기본 밀도에서
읽혀야 한다 — 이름만 남기는 접힘은 금지다. ↳호출 줄,proof · turn=… · use=…, detail 줄은 Ctrl-D 뒤다.msg_tool_visibility = Tools_full일 때만 그린다. 새 키를 만들지 않고 기존 도구 펼침 축에
탄다.- 도구 블록은 기본
Tools_compact다. 반환된 호출은 inventory 헤더 한 줄로 접히고,
실패·미반환·진행 중 호출은 헤더의 절이 아니라 자기 마크의 독립 줄로 남는다 — 단 셋
이상 모였을 때만. 둘 이하면 쪼개도 Full 이 그리는 두 줄이라 접지 않고, 실패도 헤더가1 failed: web_fetch처럼 이름을 불러 한 줄이다 (project_tool_blockCompact 팔,bin/masc_tui_keeper_chat_transcript.ml). - 돌아가는 동안에도 같은 토글로 그린다. 라이브 블록과 확정된 블록은 같은
project_tool_block/skill_rows를 지난다. 줄이 접히는 시점은 완료가 아니라
Ctrl-D 토글이다.
줄의 문구
- 게이트 줄은 가운뎃점으로 소식과 대상을 잇는다. 앞이 소식, 뒤가 대상이다
(bin/masc_tui_gate_text.ml:승인됨 · 적용 예정,판정 중 · 이 호출은 미뤄짐). - 가운뎃점은 서로 다른 칸을 가르는 데만 쓴다. 한 상태에 사실이 둘이면 쉼표로 잇는다
(bin/masc_tui_keeper_chat_transcript.ml:읽음, 전달 확인 중,전달됨, 도구 안 씀).읽음 · 전달 기록 없음이라고 쓰면 상태 하나인지 둘인지 읽는 사람이 알 수 없다. - 상태 이름은 모델이 무엇을 했는지로 쓴다(
읽음,전달됨,도구 씀). 서버가 보내고 받은 동사로
쓰면 누가 누구에게 보냈는지 운영자가 알 수 없다. - 한국어는 한국 사람이 실제로 쓰는 말로 쓴다. 영어 라벨을 품사째 옮기지 않는다.
- 한글 문구는 소스에 리터럴로 쓴다. 바이트 이스케이프(
\xeb\x83\xa8)로 손으로 적지 않는다.
손으로 맞추다 틀리기 쉽다.
고치고 나면
- 화면은 코드를 읽어서 판정하지 않는다.
tui-pty-scenario스킬로 실제로 띄워 본다. - 문구·기호·폭을 바꿨으면
test/*.py가 기다리는 글자열(needle)에서 옛 문구와 옛 기호의 바이트열을
찾아 같이 고친다. 렌더 파일만 고친 PR 은 PR CI 가 키보드 산책을 돌리지 않는다. - 하네스가 못 보는 것은 사람에게 묻는다: 색, 사용자 글꼴이
╶ ╌ ┊ ◈ ○를 그리는지, 실제 폭에서 읽히는지.