Repository navigation
fix(chat): the Default chat width's ceiling is 60rem, never wider than Wide at another root font size - #474
Conversation
…n Wide at another root font size The Default step's ceiling was a fixed 960px while Wide is 72rem, so at a 13px root Wide (936px) was narrower than Default on a large pane. chatLaneWidth now takes the root font size, which PaneTerminal reads with the pane's width, and resolves a 60rem ceiling with it. The 820px floor stays in px (--content-w) and wins where 60rem is under it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…is described Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a font size changed in an open tab applies at once PaneTerminal read the root font size only when the pane was measured, so a browser font size changed while the tab was open left Default at the old ceiling (960px at a former 16px root) until the pane was next resized, wider than Wide's 72rem in the meantime. --chat-w is now written as min(max(820px, 60rem), <the pane's share>px): still one length with no percentage, and the rem part follows the root font as Wide's does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review. 📝 SummarySummary by CodeRabbit
WalkthroughThe default chat lane now uses a pane-relative CSS length with an 820px floor and a 60rem ceiling. Numeric width resolution accepts a root font size. PaneTerminal writes the CSS length for the default width. ChangesDefault chat lane sizing
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~12 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to Default chat width now uses a pane-relative share with an 820px floor and a font-scaled ceiling; the transcript remains constrained to available width. No material merge-blocking risk is evident. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Under an 11.39px root the 820px floor wins on purpose and is wider than Wide's 72rem; the changelog, DESIGN.md, a comment and a test title promised it without that limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… message box on a coarse-pointer device too (#475) ## Change 머지된 #468(입력 카드 위에 붙는 승인 카드)의 리뷰에서 나온 후속 수정입니다. #468은 아직 릴리스되지 않았습니다. ### 무엇이 잘못되었나 카드에서 답을 누르면 카드가 사라지므로, 탭이 아닌 경우 포커스를 메시지 상자로 넘깁니다. 카드는 클릭의 `pointerType`으로 탭을 가려냈지만, `PaneTerminal`이 기기가 `(pointer: coarse)`이면 넘기지 않는 조건을 하나 더 걸고 있었습니다. 원래는 클릭이 무엇으로 눌렸는지 알려 주지 않는 브라우저를 위한 대비책이었는데, 모든 누름에 적용되었습니다. 재현: 키보드를 붙인 태블릿(또는 터치 화면이 주 포인터인 노트북, 키보드를 연결한 폰)에서 Tab으로 선택지에 가서 Enter를 누릅니다. 답은 전달되고 카드가 사라지는데, 포커스는 메시지 상자로 가지 않고 `document.body`로 떨어집니다. 키보드 사용자는 자리를 잃고 Tab을 처음부터 다시 눌러야 합니다. 같은 기기에서 마우스로 눌러도 같았습니다. ### 무엇을 바꿨나 - 누름의 출처를 `keyboard` / `mouse` / `touch` / `pen` / `unknown` 다섯 가지로 구분합니다(`pressOrigin`, `src/lib/promptAnswer.ts`). - 규칙(`focusFollowsAnswer`): 키보드와 마우스는 어떤 기기에서든 포커스를 넘깁니다. 터치와 펜은 넘기지 않습니다. `(pointer: coarse)`는 `unknown`일 때만 봅니다. 답이 돌아왔을 때 카드가 아직 있고 포커스가 카드 안이나 페이지에 있어야 한다는 기존 조건은 그대로입니다. - `PaneTerminal`의 `!coarseRef.current` 조건을 뺐습니다. 판단은 이제 순수 함수 한 곳에 있습니다. - 답 경로는 바뀌지 않았습니다. 누르지 않으면 아무것도 보내지 않고, 탭은 화면 키보드를 올리지 않습니다. CSS 변경은 없습니다. ### 누름의 출처를 알아내는 방법 클릭의 `pointerType`과, 그 버튼에서 직전에 있었던 pointerdown의 `pointerType`을 함께 봅니다. 둘 중 하나라도 `touch`나 `pen`이면 그것이고, 아니면 `mouse`입니다. pointerdown을 함께 보는 이유는 iOS Safari가 탭의 클릭을 `mouse`로 보고한 적이 있기 때문입니다(WebKit bug 282988, CodeRabbit 지적). coarse 기기에서도 마우스가 포커스를 넘기게 되었으므로, 이를 믿으면 폰에서 탭이 화면 키보드를 올릴 수 있습니다. 클릭에 `pointerType`이 없는 브라우저도 pointerdown으로 구분됩니다. 포인터가 없으면 `detail === 0`이고 **같은 버튼에서 Enter나 Space의 keydown이 먼저 있었을 때만** 키보드로 봅니다. 카드가 keydown을 기억해 두고, 포인터가 눌리거나 답이 나가면 지웁니다. `detail === 0`만 쓰지 않은 이유: 스크립트의 `click()`과 보조 기술의 활성화도 `detail`이 0입니다. 폰의 화면 낭독기로 선택지를 활성화했을 때 키보드로 판정하면 메시지 상자에 포커스가 가서 화면 키보드가 올라옵니다. keydown까지 요구하면 이런 클릭은 `unknown`이 되어 정밀 포인터에서는 넘기고 coarse에서는 넘기지 않습니다. `pointerType === ""`만 보지 않은 이유: 클릭이 `MouseEvent`인 브라우저에는 이 값이 아예 없어서 탭과 키를 구분할 수 없습니다. 카드의 입력란에서 Enter로 보낸 답은 클릭이 없으므로 그대로 키보드입니다. ### 고치지 않은 것: 카드가 사라진 뒤 돌아온 실패 답이 돌아왔을 때 카드가 이미 없으면 성공이든 실패든 아무것도 알리지 않습니다(`PromptCard.tsx`의 `if (!shown.current) return false`). 실패에 대해서는 알리지 않는 채로 두었습니다. 이유는 다음과 같습니다. - 메시지 상자의 알림 줄(`composer-note`)은 `Composer` 안의 상태라서, 밖에서 쓰려면 `PromptCard` → `ChatView` → `PaneTerminal` → `Composer`로 새 prop을 이어야 하고 pane별 범위와 지우는 시점도 정해야 합니다. 문구는 기존 "Not sent: {message}"로 되지만 배선이 이 PR의 크기를 넘습니다. - 카드가 사라지는 경우는 둘입니다. (1) 같은 pane에서 프롬프트가 바뀌었거나 없어진 경우: 서버는 대개 409 `prompt_changed`로 답하고, 화면에는 이미 지금의 프롬프트가 있습니다. 그 밖의 실패(네트워크 등)는 키가 전달되었는지 알 수 없어서 "보내지 못했다"는 말이 틀릴 수 있습니다. (2) 다른 pane으로 옮긴 경우: 지금 보는 메시지 상자에 알림을 띄우면 다른 pane의 이야기가 됩니다. 원래 pane은 답이 전달되지 않았다면 사이드바에 INPUT으로 남고, 돌아가면 카드가 다시 보입니다. 알려진 한계: (1)에서 409가 아닌 실패는 사용자에게 아무 말도 가지 않습니다. ### 그 밖의 한계 - 답이 가는 동안 카드가 다른 프롬프트로 바뀌면 키보드 사용자의 포커스는 여전히 페이지로 떨어집니다. 새 카드로 옮길지는 따로 정해야 해서 손대지 않았습니다. ## Validation 검증 환경: Linux, Bun 1.4.2, Chrome(`/usr/bin/google-chrome`). `origin/main`(#474, ac5b066) 위에 있습니다. - `bun run typecheck`: 통과 - `bun run test:unit`: 1,302건 통과, 실패 없음. `src/lib/promptAnswer.test.ts`에 `pressOrigin` 5건과, 다섯 출처 각각을 정밀 포인터와 coarse 포인터에서 본 10건을 넣었습니다. - `bun run build`: 통과 - `scripts/prompt-dock-demo-regression.ts`: PASS 23개, 종료 코드 0(두 번). 새 블록은 `(pointer: coarse)`인 1024x768 터치 페이지에서 다음을 봅니다. 선택지에서 Enter와 Space, 카드 입력란에서 Enter, 400ms 걸리는 답, 마우스 클릭이 모두 메시지 상자로 포커스를 넘깁니다. 카드가 dock에서 빠지는 순간마다 포커스가 `document.body`가 아니었습니다. 탭, keydown 없는 `click()`, pointerdown은 `touch`인데 클릭은 `mouse`라고 하는 누름은 넘기지 않습니다(마지막 것은 pointerdown을 보지 않으면 실패합니다). `PaneTerminal`의 예전 조건을 되돌리면 이 블록은 `actual: "page"`로 실패합니다. - 같은 스크립트의 기존 폰 검사 하나는 Send를 Playwright의 `click()`(마우스)으로 누르고 포커스가 가지 않기를 기대했습니다. 새 규칙에서는 마우스가 포커스를 넘기므로 의도대로 `tap()`으로 바꿨습니다. - `scripts/held-rows-demo-regression.ts`: PASS 10개, 종료 코드 0. `scripts/composer-fit-demo-regression.ts`: PASS 8개, 종료 코드 0. - `DESIGN.md`의 Prompt card 문장을 새 규칙으로 고쳤고, 변경 이력은 #468 항목의 문장에 덧붙였습니다(릴리스되지 않은 기능의 수정). 돌리지 않은 것: `bun run test:ui`, `bun run test:integration`, `*.contract` 테스트(CI에 맡김). 확인하지 못한 것: 실제 태블릿과 폰, Safari와 Firefox, 실제 화면 낭독기(TalkBack, VoiceOver)의 활성화가 어떤 클릭을 만드는지. Chromium에서만 실행했습니다. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Bumps package.json and herdr-plugin.toml to 0.3.50 and moves the Unreleased notes under [0.3.50] - 2026-10-06, with the compare link. Adds the PR link and credit the #471 entry was missing; every other entry already carries them. Raises the remote PC bundle to 17: #470 changes the prompt answer route on the bridge, so a remote PC on bundle 16 keeps the old behaviour until its bridge is updated. Push the `remote-v17` tag and let the bundle workflow publish before running release.yml. Known limits carried into the notes: Japanese and Chinese text uses the device's fonts beside Pretendard; #469 stays open for what #470 does not cover (answers after the first committing key, identical twin prompts, screens no reader knows). Carries #422 #423 #424 #425 #426 #435 #437 #439 #446 #447 #448 #452 #454 #455 #456 #457 #458 #459 #460 #461 #462 #463 #465 #466 #467 #468 #471 #473 #474 #475 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Resolves the conflicts with devswha#473 and devswha#474: the Markdown preview takes --chat-fs-body too, and highlighted chat code blocks take main's padding, size and line height. Review follow-ups: - Quotes nest at most 32 deep (MAX_QUOTE_DEPTH): 20 000 nested quotes overflowed the stack and blanked the app from the file viewer. The viewer's text view sits in a RenderBoundary with a Show source fallback. - The text's size is read from the response (Content-Range, or the body of a 200); an unknown size counts as cut short. An empty file (416 bytes */0) opens empty again instead of as an error. - The Default chat lane is written on the document, so the Default Markdown preview is as wide as the chat.
Change
승인된 채팅 모드 재설계의 한 조각입니다(앞서 #456 ~ #468이 머지되었고, 이번이 마지막 묶음입니다). 이 PR에 해당하는 목업 규칙은 없습니다. #463(960px 채팅 레인과 Chat width 설정) 리뷰에서 남은 후속 수정입니다.
Chat width가 Default일 때 레인의 최대 폭이 고정
960px이었습니다. Wide는72rem이라서, 브라우저 글꼴 크기가 16px이 아니면 Default가 Wide보다 넓어질 수 있었습니다(13px 루트에서 Default 960px, Wide 936px).이제 Default의 최대 폭은
60rem입니다.구현:
PaneTerminal이--chat-w를 px 숫자가 아니라 CSS 길이 하나로 씁니다.min(max(820px, 60rem), <pane 몫>px)형태이고chatLaneLength가 만듭니다. 루트 글꼴 크기를 JS에서 읽지 않습니다.CHAT_LANE_MAX(960)는CHAT_LANE_MAX_REM(60)으로 바뀌었고,chatLaneWidth(pane, root)는 같은 규칙의 숫자 형태로 남아 테스트에서만 씁니다.DESIGN.md와src/styles.css의 주석을 함께 고쳤습니다. 새 문구, HTTP/WS 계약, 클래스 이름 변경은 없습니다.목업과 다른 점
60rem이 820px보다 작아지는데, 이때는 바닥값 820px이 이깁니다. Default가 Narrow보다 좁아지지 않게 하기 위해서입니다. 그래서 "Default ≤ Wide"는 루트 11.39px 이상에서 성립합니다. 그보다 작으면 Narrow의 820px 자체가 Wide의 72rem보다 넓은데, 이 PR은 그 부분을 바꾸지 않습니다.chatLaneWidth는 rem 최대 폭을 정수 px로 반올림하지 않습니다(CSS가 계산하는 값과 같게). 루트가 소수 px이면 레인도 소수 px 폭이 될 수 있습니다.위험과 리뷰할 곳
.terminal-stack의--chat-w가 px 값이 아니라min()/max()식입니다.ChatView.css와Composer.css(calc()안에서 쓰는 곳 포함)에서 Chrome은 올바르게 계산했습니다. Chrome에서만 확인했습니다.src와scripts에 이 인라인 값을 숫자로 파싱하는 코드는 없습니다.src/lib/settings.test.ts가PaneTerminal이setProperty("--chat-w", chatLaneLength(를 쓰고chatLaneWidth(를 호출하지 않는지 소스 문자열로 확인합니다. JS에서 px로 계산한 최대 폭이 되돌아오는 것을 막기 위한 것입니다.Validation
검증 환경: Linux, Bun 1.4.2, Chrome.
origin/main(8378a95, #468 포함)을 머지한 뒤 실행했습니다.bun run typecheck: 통과bun run test:unit: 1,284건 통과, 4건 건너뜀, 실패 없음bun run build: 통과(test:ui안에서)bun run test:ui(별도 세션herdr-web-ui-test): PASS 36개 뒤checkAlertSound의.droplet-card대기(scripts/alert-sound-regression.ts:79)에서 실패했습니다. 이 실패는 이 장비에서origin/main도 전체 순서로 돌릴 때 똑같이 나는 것으로 알려져 있고, CI는 통과합니다. 그 앞에서 실패한 검사는 없습니다.checkAlertSound와checkUpdateNotice두 호출만 뺀 임시 사본(커밋하지 않음)으로 다시 실행: PASS 64개, 종료 코드 0. 1920 창에서 레인이 960px, 1280 창에서 820px인 기존 검사가 여기에 들어 있고 그대로 통과합니다(검사 값은 바꾸지 않았고 주석만 고쳤습니다).composer-fit-demo-regression(8),held-rows-demo-regression(10),prompt-dock-demo-regression(22),chat-greeting-demo-regression(10) 모두 통과.src/lib/settings.test.ts): 루트 13, 14, 16, 17.6, 20, 24px에서 Default가 820px 이상 72rem 이하인지, 쓸 수 없는 루트 값(0, 음수, NaN, Infinity)이 16px로 처리되는지,chatLaneLength가 쓰는 문자열 형태, 그리고 그 길이가 각 루트에서chatLaneWidth와 같은 값으로 계산되는지. 16px 기대값은 그대로입니다.하지 않은 것:
test:integration, 계약 테스트,chat-history-browser-qa,file-viewer-regression,keyboard-viewport-regression,file-viewer-mobile-regression,droplet-demo-regression은 실행하지 않았습니다(CI가 실행합니다). Chrome 외 브라우저는 확인하지 않았습니다. 이번 단계에서 스크린샷을 새로 찍지 않았습니다.Codex 적대적 리뷰: 작은 조각이라 실행하지 않았습니다. 자체 리뷰에서 막는 지적은 없었습니다.
열린 외부 PR과의 충돌
#436, #409, #438, #441, #421은 살펴보지 않았습니다. 이 PR은
settings.ts의 채팅 레인 함수,PaneTerminal.tsx의 effect 하나, 주석,DESIGN.md,CHANGELOG.md만 건드립니다. 겹칠 만한 곳은CHANGELOG.md의 Unreleased → Fixed입니다.🤖 Generated with Claude Code