Repository navigation
refactor(web): composer clears scroll-collapse in render, not after paint - #25909
Conversation
…aint ChatComposer cleared its scroll-collapsed flag in a passive effect whenever the composer stopped being eligible for scroll-collapse (multiline prompt, expanded chrome, mobile viewport, non-server route, or the setting turned off). That committed the 3.8k-line component once with the stale flag and then a second time after paint. The flag lives in useState inside this component (useComposerFocusState), so reset it during render with a guard, the same pattern this file already uses for the model picker. The flag is still cleared rather than masked, so the composer does not re-collapse on its own when it becomes eligible again. Effects: ChatComposer.tsx 17 -> 16 useEffect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a narrowly scoped refactor in ChatComposer that moves an existing state reset before paint, removing a transient stale collapsed frame and an extra commit. It does not change product defaults or add any static-analysis overrides. You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Reset collapsed state when eligibility is lost apps/web/src/components/chat/ChatComposer.tsx |
When scroll-collapse eligibility becomes false, ChatComposer clears the collapsed state during rendering instead of in an effect. |
Priority:
Estimated code review effort: 2 (Simple) | ~5 minutes
Change: Refactor
Suggested labels: size:M
Suggested reviewers: macroscopeapp
Merge Risk: 
Minimal · up to 325d8
-
The change preserves scroll-collapse reset behavior without introducing an identified merge-blocking risk.
Pre-merge checks: 
3 · 
1
-
Failed checks: 1 warning Passed checks: 3 passedFull details: Description check
-
Explanation
The description clearly explains the problem, implementation, unchanged behavior, and verification results. It does not include the required Scope and approval section or explain why this focused refactor qualifies without prior approval. It also does not provide the required UI screenshots or state why they could not be provided.
Resolution
Add a Scope and approval section with the linked issue or maintainer approval, or explain why this focused refactor is an obvious bug-free change that qualifies without prior approval. Add before/after UI screenshots, or state why screenshots are not meaningful for this timing-only change.
-
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
Comment @coderabbitai help to get the list of available commands.
## What's Changed * refactor(mobile): subagent timers share the visible second clock by @juliusmarminge in pingdotgg/t3code#25854 * refactor(mobile): app update checks start with the app by @juliusmarminge in pingdotgg/t3code#25856 * refactor(mobile): workspace tabs, home filters and connection status derive instead of syncing by @juliusmarminge in pingdotgg/t3code#25813 * refactor(mobile): settings permission and update checks without sync effects by @juliusmarminge in pingdotgg/t3code#25895 * fix(desktop): run the packaged bundle self-check on macOS artifacts by @stbenjam in pingdotgg/t3code#25900 * fix(web): the Add ChatGPT account dialog animates closed once the account connects by @juliusmarminge in pingdotgg/t3code#25868 * refactor(web): composer clears scroll-collapse in render, not after paint by @juliusmarminge in pingdotgg/t3code#25909 * refactor(web): composer overlay re-measures from one layout effect by @juliusmarminge in pingdotgg/t3code#25915 * refactor(web): file browser records the right-click position on its own panel by @juliusmarminge in pingdotgg/t3code#25908 * refactor(web): preview element picker drops its unmount guard by @juliusmarminge in pingdotgg/t3code#25920 * fix(web): PR Code tab no longer requests the diff of a commit the PR dropped by @juliusmarminge in pingdotgg/t3code#25916 * perf(mobile): worktree setup updates commit the thread screen once by @juliusmarminge in pingdotgg/t3code#25910 * fix(desktop): a broken release opens an error window instead of hanging by @t3dotgg in pingdotgg/t3code#25903 * ci(web): releases fail when the built web app cannot boot by @t3dotgg in pingdotgg/t3code#25929 * ci(release): desktop builds that cannot open no longer publish by @t3dotgg in pingdotgg/t3code#25928 * refactor(web): file tree drag mentions use React drag handlers by @juliusmarminge in pingdotgg/t3code#25913 * refactor(mobile): git action results buzz from where they are reported by @juliusmarminge in pingdotgg/t3code#25912 * fix(mobile): the Add environment form shows only its own pairing errors by @juliusmarminge in pingdotgg/t3code#25859 * refactor(mobile): showcase capture runs as an app service, not a chain of effects by @juliusmarminge in pingdotgg/t3code#25857 * fix(server): bound hung delegated stop effects so later runs can start by @juliusmarminge in pingdotgg/t3code#25675 * feat(mobile): add One UI navigation preference on Android by @PixPMusic in pingdotgg/t3code#25758 **Full Changelog**: pingdotgg/t3code@v0.1.4-nightly.20261027.3770...v0.1.4-nightly.20261027.3783 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.1.4-nightly.20261027.3783
Problem
ChatComposercleared its scroll-collapsed flag in a passiveuseEffectwhenever the composer stopped being eligible for scroll-collapse (multiline prompt, expanded chrome, mobile viewport, non-server route, or the "collapse on scroll" setting turned off). The value it reacted to is already computed in render, so the effect only mirrored derived state into local state, and each eligibility drop while collapsed committed the component twice: once with the stale flag, then again after paint.Change
The flag is plain
useStateinsideuseComposerFocusState, called byChatComposeritself, so it is reset during render with a guard (!canScrollCollapseComposer && isComposerScrollCollapsed), the same pattern the file already uses for the model picker. The guard stops once the flag is false, andcanScrollCollapseComposerdoes not read the flag, so it cannot loop. The effect is deleted.Behavior: none intended. The flag is still cleared rather than masked, so the composer still does not re-collapse by itself when it becomes eligible again. The only difference is timing: the stale collapsed render is discarded before commit instead of being painted for one frame.
What this improves
ChatComposer(and its layout effects) on each eligibility flip while collapsed.Effects
apps/web/src/components/chat/ChatComposer.tsx: 17 -> 16useEffectVerification
vp run check:changed: format, lint, knip 1/2, knip 2/2, typecheck (3 packages), test all pass.Model: Claude Opus 5.5, Claude Code harness.
🤖 Generated with Claude Code