You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(web): Keep chip shows its own icon when the composer is narrow - #30045
When the composer is narrower than 480px (for example with a file open in a wide right panel), the Compact chip hides its Compact/Keep label and leaves only the icon and token count. The icon was Minimize2 in both states, so clicking the chip to switch to Keep left it looking the same. The only remaining cue was the faint pressed background, and the hover background covers it while the pointer is still on the chip.
Reproduced on main at e2bb9e6 in the web dev server (1512×945 window, a stale Claude thread at 549k tokens). With the right panel closed, the composer was 736px and the chip read "Compact 549k". With Files open and a file shown, and the panel dragged to 896px, the composer was 264px and the chip showed only "549k". Clicking the chip changed aria-pressed from true to false and the send button label from "Compact and send" to "Submit message", but the chip's icon and text stayed the same. Closing the panel showed "Keep 549k".
Change
The chip's icon is now a MorphIcon that shows Minimize2 for Compact and Maximize2 for Keep. That is the same pair, and the same component, the right panel's maximize toggle uses. The label, colours, layout and toggle behaviour are unchanged. The icon is still decorative (aria-hidden), and the button keeps its aria-label and aria-pressed.
vp run check:changed passes on the final head: format, lint, knip, typecheck of 2 packages, react compiler, and the tests beside the change.
No new test. This is a purely visual bug, so the proof is the before/after captures below.
Evidence. Before is e2bb9e6 and After is a87b809, the fix commit. The later merge of main touches nothing the chip renders. Each side ran as the real web app from its own checkout, on a fresh database seeded by the same driver: one real Claude Haiku turn, then the server stopped and that turn's usage raised to 549k tokens with an expired cache. Every capture used a fresh browser context.
Rendering environment for every capture: Linux, Playwright 1.60, headless Chromium 148, no device emulation, DPR 1, 100% zoom and font scaling, clock frozen at 2026-11-03T07:00Z. Composer widths: 736px with the label shown, 386px narrow, 366px at phone width.
Sheet (Before | After | Diff0 | DiffT, SHA under each column)
Rows
Viewport, theme
light-narrow-F3-F4.png
F3 narrow Compact, F4 narrow Keep
1512×945, light
dark-narrow-F3-F4.png
F3 narrow Compact, F4 narrow Keep
1512×945, dark
light-labelled-F1-F2.png
F1 Compact with label, F2 Keep with label
1512×945, light
dark-labelled-F1-F2.png
F1 Compact with label, F2 Keep with label
1512×945, dark
light-phone-F7-F8.png
F7 Compact, F8 Keep, web composer expanded at phone width
390×844, light
Diff0 marks any nonzero channel delta, amplified ×255. DiffT marks a channel delta above 16/255. Both compare native PNGs at full viewport size. Changed pixels per pair (Diff0 / DiffT), with 0 outside the 14×14 icon area in every pair:
Theme
F1
F2
F3
F4
light
50 / 25
86 / 76
50 / 25
86 / 76
dark
52 / 11
88 / 75
52 / 11
88 / 75
Phone width, light: F7 50 / 25, F8 86 / 76.
Recordings (Before on the left, After on the right, step captions burned in):
P1-light-labelled-narrow.gif, 1512×945 light:
0.2s: Compact with label. 1.3s: hover tooltip. 2.4s: click to Keep, pointer stays on the chip. 3.6s: pointer away. 4.6s: keyboard focus. 5.4s: Space toggles back to Compact.
6.3s: Files and math.ts opened and the panel widened below 480px (a captioned cut removes the editor loading frames). 6.5s: narrow Compact. 8.7s: click to Keep with the pointer on the chip; Before is unchanged, After shows outward arrows. 11.8s: toggle back. 12.8s: rapid double toggle.
13.9s to 17.4s: Keep survives closing and reopening the panel.
P2-dark-labelled-narrow.gif, 1512×945 dark: the same forward and reverse sequence, wide and narrow, with the same captioned cut.
P3-light-reduced-motion-narrow.gif, 1512×945 light, prefers-reduced-motion: reduce, narrow: Compact to Keep and back. After switches straight to the end icon, with no animation and no blank frame.
P4-light-phone-web-expanded.gif, 390×844 light, web composer expanded at phone width: Compact to Keep and back. The editor keeps focus and the composer stays expanded.
What else changed, every visible difference:
Keep shows outward arrows in the wide layout too, where the label is visible, not only in the narrow one.
On After, toggling morphs between the two icons. Reduced motion switches straight to the end icon.
Compact still points inward, but the new renderer draws its strokes slightly differently: 50 changed pixels in light and 52 in dark, all inside the icon.
Nothing outside the 14×14 icon area changes in any still pair.
Seed driver (synthetic data only)
#!/usr/bin/env bash# Synthetic real-app capture seed. Server running for run; stopped for fixture.set -euo pipefail
checkout="$1"
project="$2"
phase="${3:-run}"cd"$checkout"if [ "$phase"= fixture ];then
python3 /tmp/issue-lead-29974-r2b/capture/scripts/normalize-fixture.py "$checkout"exitfi
mkdir -p "$project"
git -C "$project" init -q
printf'# Demo\n\nA small sample project.\n'>"$project/README.md"printf'export const add = (a: number, b: number) => a + b;\n'>"$project/math.ts"
git -C "$project" add README.md math.ts
git -C "$project" -c user.email=demo@example.com -c user.name=demo commit -qm init
node apps/server/src/bin.ts project add --base-dir .t3 "$project"
node apps/server/src/bin.ts thread start --base-dir .t3 --provider claudeAgent --model claude-haiku-5-5 --title 'Refactor math helpers' --wait --json "$project"'Reply with one short sentence describing math.ts. Do not edit files.'
#!/usr/bin/env python3"""Stopped-server, synthetic capture fixtures only. Run after lead's seed driver."""importsqlite3,json,sys,pathlibcheckout=pathlib.Path(sys.argv[1]); db=checkout/'.t3/userdata/statev2.sqlite'c=sqlite3.connect(db)
project=c.execute("SELECT project_id FROM projection_projects WHERE workspace_root LIKE '%/capture/demo-%'").fetchone()[0]
# Hide only the automatic, empty checkout project; the demo is the sole capture project.c.execute("UPDATE projection_projects SET deleted_at='2026-11-03T05:00:00.000Z' WHERE project_id!=?",(project,))
c.execute("UPDATE projection_projects SET title='Compact proof',created_at='2026-11-03T05:00:00.000Z',updated_at='2026-11-03T05:00:08.000Z' WHERE project_id=?",(project,))
defnormalized(v):
ifisinstance(v,list): return [normalized(x) forxinv]
ifnotisinstance(v,dict): returnvout={}
fork,xinv.items():
ifkin ['createdAt','startedAt','lastVisitedAt'] andisinstance(x,str): out[k]='2026-11-03T05:00:00.000Z'elifkin ['updatedAt','completedAt','settledAt'] andisinstance(x,str): out[k]='2026-11-03T05:00:08.000Z'else: out[k]=normalized(x)
returnoutfor (t,) inc.execute("SELECT name FROM sqlite_schema WHERE type='table' AND name LIKE 'orchestration_v2_projection_%'").fetchall():
cols=[r[1] forrinc.execute(f'PRAGMA table_info({t})')]
if'payload_json'notincols:continueforrowid,rawinc.execute(f'SELECT rowid,payload_json FROM {t}').fetchall():
p=normalized(json.loads(raw))
ift=='orchestration_v2_projection_provider_turns'andp.get('tokenUsage'):
p['tokenUsage'].update(usedTokens=549000,inputTokens=549000,updatedAt='2026-11-03T05:00:00.000Z',promptCacheTtlMs=3600000,promptCacheExpiresAt='2026-11-03T06:00:00.000Z')
ift=='orchestration_v2_projection_threads'andp.get('projectId')!=project:p['deletedAt']='2026-11-03T05:00:00.000Z'updates={'payload_json':json.dumps(p,separators=(',',':'))}
forcolin ['created_at','started_at','last_visited_at']:
ifcolincols:updates[col]='2026-11-03T05:00:00.000Z'forcolin ['updated_at','completed_at']:
ifcolincols:updates[col]='2026-11-03T05:00:08.000Z'c.execute(f"UPDATE {t} SET "+','.join(k+'=?'forkinupdates)+' WHERE rowid=?',(*updates.values(),rowid))
c.commit();c.close()
print('Normalized synthetic data:',checkout.name)
Not verified separately: the Electron desktop app. It renders this same web component. The mobile app has no Compact chip. Optional matrix rows (other token counts, neighbouring controls, the chip being absent, the cache-expiry boundary) were not captured; the diff does not change those.
Model and harness: Claude Opus 5.5 (lead, implementation, review B) and GPT-6.1-sol (review A, proof audits, capture) via Claude Code and Codex in T3 Code.
Below 480px the Compact chip hides its label, so Compact and Keep both
showed the same minimize icon. Keep now morphs to Maximize2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Macroscope's review found this PR approvable — The history chip now shows distinct Keep and Compact icons, including when narrow layouts hide its text. This is a presentation-only change; history handling and product defaults are unchanged.
You can add or adjust custom eligibility rules. Learn more.
No actionable comments were generated in the recent review.
Walkthrough
The compact-history toggle now uses MorphIcon to render Maximize2 when keeping full history and Minimize2 otherwise. The icons are imported from lucide, and the toggle no longer sets aria-hidden.
The toggle selects Maximize2 for keep-full-history and Minimize2 otherwise. It renders the selected icon through MorphIcon and removes the explicit aria-hidden prop.
The focused icon change is mergeable with no remaining material risk identified.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title clearly identifies the web fix: the Keep state now shows its own icon when the composer is narrow. It is concise and uses the repository's conventional commit format.
Description check
The description includes the required Problem, Change, Scope and approval, and Verification sections. It explains the reproduction, implementation, linked issue, validation results, visual evidence, a…
Linked Issues check
Coding requirement from #29974: the Compact chip must show a visible state difference between Compact and Keep when the label is hidden (composer under 480px). The head implements this in ComposerPrim…
Out of Scope Changes check
The whole-PR diff against the merge base (cefb544) changes one file, apps/web/src/components/chat/ComposerPrimaryActions.tsx, with +3/-3 lines. The changes are: the lucide-react import drops Minimize2…
Full details: Description check
Explanation
The description includes the required Problem, Change, Scope and approval, and Verification sections. It explains the reproduction, implementation, linked issue, validation results, visual evidence, and known verification limits.
Full details: Linked Issues check
Explanation
Coding requirement from #29974: the Compact chip must show a visible state difference between Compact and Keep when the label is hidden (composer under 480px). The head implements this in ComposerPrimaryActions.tsx: the chip icon is now <MorphIcon icon={keepFullHistory ? Maximize2 : Minimize2} />. Compact shows Minimize2 (inward arrows). Keep shows Maximize2 (outward arrows), the icon pair suggested in the issue. The label span, aria-pressed, aria-label and onClick are unchanged in the diff. The issue's suggested behaviour is therefore implemented in code. The PR's before/after captures were not inspected; this verdict relies on the source diff. The issue's documentation item (docs/user/providers-claude.md, 'Full' versus 'Keep') is not a coding requirement of this PR and is not assessed here. Unresolved point: the diff removes the explicit aria-hidden="true" from the chip icon. The PR says the icon stays decorative. That depends on MorphIcon's default behaviour in the morphicons/react package, which could not be read from the repository. The issue does not state an accessibility requirement, and the chip button keeps its aria-label, so this does not change the verdict.
Full details: Out of Scope Changes check
Explanation
The whole-PR diff against the merge base (cefb544) changes one file, apps/web/src/components/chat/ComposerPrimaryActions.tsx, with +3/-3 lines. The changes are: the lucide-react import drops Minimize2Icon; the lucide import adds Maximize2 and Minimize2; the chip icon switches from Minimize2Icon to MorphIcon with the state-dependent icon. Each change serves the chip icon requirement of #29974. No unrelated code, styling, label, or behaviour changes appear in the diff. The removed aria-hidden prop is part of the same icon change and is noted as an unresolved point in the linked-issue assessment. The PR-described extra effects (Keep shows outward arrows in the wide layout; morph animation) follow from the same icon change and do not add unrelated scope.
Finishing Touches
Generate docstrings
Commit to this branch
Create a new PR
Generate unit tests (beta)
Commit to this branch
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
CodeRabbit Verify
Run CodeRabbit Verify to test the changes in this pull request.
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
bugfixFix PR opened by an issue lead; enters the PR looppr:gate-failedA land gate failedsize:XS0-9 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.
1 participant
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When the composer is narrower than 480px (for example with a file open in a wide right panel), the Compact chip hides its Compact/Keep label and leaves only the icon and token count. The icon was Minimize2 in both states, so clicking the chip to switch to Keep left it looking the same. The only remaining cue was the faint pressed background, and the hover background covers it while the pointer is still on the chip.
Reproduced on main at e2bb9e6 in the web dev server (1512×945 window, a stale Claude thread at 549k tokens). With the right panel closed, the composer was 736px and the chip read "Compact 549k". With Files open and a file shown, and the panel dragged to 896px, the composer was 264px and the chip showed only "549k". Clicking the chip changed
aria-pressedfrom true to false and the send button label from "Compact and send" to "Submit message", but the chip's icon and text stayed the same. Closing the panel showed "Keep 549k".Change
The chip's icon is now a
MorphIconthat shows Minimize2 for Compact and Maximize2 for Keep. That is the same pair, and the same component, the right panel's maximize toggle uses. The label, colours, layout and toggle behaviour are unchanged. The icon is still decorative (aria-hidden), and the button keeps itsaria-labelandaria-pressed.Scope and approval
Fixes #29974
Triaged via the automated issue flow; GitCafe issue https://git.cafe/pingdotgg/t3code/issues/29974.
Verification
vp run check:changedpasses on the final head: format, lint, knip, typecheck of 2 packages, react compiler, and the tests beside the change.Evidence. Before is e2bb9e6 and After is a87b809, the fix commit. The later merge of main touches nothing the chip renders. Each side ran as the real web app from its own checkout, on a fresh database seeded by the same driver: one real Claude Haiku turn, then the server stopped and that turn's usage raised to 549k tokens with an expired cache. Every capture used a fresh browser context.
Rendering environment for every capture: Linux, Playwright 1.60, headless Chromium 148, no device emulation, DPR 1, 100% zoom and font scaling, clock frozen at 2026-11-03T07:00Z. Composer widths: 736px with the label shown, 386px narrow, 366px at phone width.
light-narrow-F3-F4.pngdark-narrow-F3-F4.pnglight-labelled-F1-F2.pngdark-labelled-F1-F2.pnglight-phone-F7-F8.pngDiff0 marks any nonzero channel delta, amplified ×255. DiffT marks a channel delta above 16/255. Both compare native PNGs at full viewport size. Changed pixels per pair (Diff0 / DiffT), with 0 outside the 14×14 icon area in every pair:
Phone width, light: F7 50 / 25, F8 86 / 76.
Recordings (Before on the left, After on the right, step captions burned in):
P1-light-labelled-narrow.gif, 1512×945 light:P2-dark-labelled-narrow.gif, 1512×945 dark: the same forward and reverse sequence, wide and narrow, with the same captioned cut.P3-light-reduced-motion-narrow.gif, 1512×945 light,prefers-reduced-motion: reduce, narrow: Compact to Keep and back. After switches straight to the end icon, with no animation and no blank frame.P4-light-phone-web-expanded.gif, 390×844 light, web composer expanded at phone width: Compact to Keep and back. The editor keeps focus and the composer stays expanded.What else changed, every visible difference:
Seed driver (synthetic data only)
Not verified separately: the Electron desktop app. It renders this same web component. The mobile app has no Compact chip. Optional matrix rows (other token counts, neighbouring controls, the chip being absent, the cache-expiry boundary) were not captured; the diff does not change those.
Model and harness: Claude Opus 5.5 (lead, implementation, review B) and GPT-6.1-sol (review A, proof audits, capture) via Claude Code and Codex in T3 Code.
🤖 Generated with Claude Code