Skip to content

fix(web): Keep chip shows its own icon when the composer is narrow - #30045

Open
juliusmarminge wants to merge 2 commits into
mainfrom
fix/issue-29974
Open

juliusmarminge wants to merge 2 commits into
mainfrom
fix/issue-29974

Conversation

@juliusmarminge

Copy link
Copy Markdown
Member

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-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.

Scope and approval

Fixes #29974

Triaged via the automated issue flow; GitCafe issue https://git.cafe/pingdotgg/t3code/issues/29974.

Verification

  • 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"
  exit
fi
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."""
import sqlite3,json,sys,pathlib
checkout=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,))
def normalized(v):
 if isinstance(v,list): return [normalized(x) for x in v]
 if not isinstance(v,dict): return v
 out={}
 for k,x in v.items():
  if k in ['createdAt','startedAt','lastVisitedAt'] and isinstance(x,str): out[k]='2026-11-03T05:00:00.000Z'
  elif k in ['updatedAt','completedAt','settledAt'] and isinstance(x,str): out[k]='2026-11-03T05:00:08.000Z'
  else: out[k]=normalized(x)
 return out
for (t,) in c.execute("SELECT name FROM sqlite_schema WHERE type='table' AND name LIKE 'orchestration_v2_projection_%'").fetchall():
 cols=[r[1] for r in c.execute(f'PRAGMA table_info({t})')]
 if 'payload_json' not in cols:continue
 for rowid,raw in c.execute(f'SELECT rowid,payload_json FROM {t}').fetchall():
  p=normalized(json.loads(raw))
  if t=='orchestration_v2_projection_provider_turns' and p.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')
  if t=='orchestration_v2_projection_threads' and p.get('projectId')!=project:p['deletedAt']='2026-11-03T05:00:00.000Z'
  updates={'payload_json':json.dumps(p,separators=(',',':'))}
  for col in ['created_at','started_at','last_visited_at']:
   if col in cols:updates[col]='2026-11-03T05:00:00.000Z'
  for col in ['updated_at','completed_at']:
   if col in cols:updates[col]='2026-11-03T05:00:08.000Z'
  c.execute(f"UPDATE {t} SET "+','.join(k+'=?' for k in updates)+' 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.

🤖 Generated with Claude Code

light-narrow-F3-F4.png: 1512x945 light, narrow composer 386px, F3 Compact and F4 Keep, Before | After | Diff0 | DiffT

dark-narrow-F3-F4.png: 1512x945 dark, narrow composer 386px, F3 Compact and F4 Keep, Before | After | Diff0 | DiffT

light-labelled-F1-F2.png: 1512x945 light, composer 736px with label, F1 Compact and F2 Keep, Before | After | Diff0 | DiffT

dark-labelled-F1-F2.png: 1512x945 dark, composer 736px with label, F1 Compact and F2 Keep, Before | After | Diff0 | DiffT

light-phone-F7-F8.png: 390x844 light, expanded web composer, F7 Compact and F8 Keep, Before | After | Diff0 | DiffT

P1-light-labelled-narrow.gif: recording, Before left and After right, 1512x945 light, wide then narrow toggles

P2-dark-labelled-narrow.gif: recording, Before left and After right, 1512x945 dark, wide then narrow toggles

P3-light-reduced-motion-narrow.gif: recording, Before left and After right, 1512x945 light, reduced motion, narrow

P4-light-phone-web-expanded.gif: recording, Before left and After right, 390x844 light, expanded web composer

juliusmarminge and others added 2 commits November 3, 2026 07:27
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>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Nov 3, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 611ef71

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.

@coderabbitai

coderabbitai Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

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.

Changes: Compact-history toggle

Layer / File(s) Summary
State-dependent toggle icons
apps/web/src/components/chat/ComposerPrimaryActions.tsx
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.

Estimated code review effort: 1 (Trivial) | ~3 minutes

Change: Bug fix · Severity of issue fixed: Low

Possibly related PRs

Suggested labels: bugfix

Suggested reviewers: flamboh

Merge Risk: Minimal · up to 611ef

The focused icon change is mergeable with no remaining material risk identified.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed 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 Passed 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 Passed 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 Passed 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 &lt;MorphIcon icon={keepFullHistory ? Maximize2 : Minimize2} /&gt;. 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.
Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between cefb544 and 611ef71.

Files selected for processing: 1
  • apps/web/src/components/chat/ComposerPrimaryActions.tsx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.

@juliusmarminge juliusmarminge added bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed labels Nov 3, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed size:XS 0-9 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compact chip looks the same after switching to Keep when the composer is narrow

1 participant