Polish chat UI and provider prompts#2451
Polish chat UI and provider prompts#2451sandersonstabo wants to merge 12 commits intopingdotgg:mainfrom
Conversation
… remove TODO.md - Model picker, provider settings, traits, badges, timestamps, branch/workspace pickers - Bundled fonts; context menu fallback reliability; DONE.md changelog - Delete TODO.md; server/provider contract and text-generation related updates Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value). Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c967b6d2ed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| onPromptChange(stripped); | ||
| } | ||
| updateDescriptors(replaceDescriptorCurrentValue(descriptors, descriptor.id, value)); | ||
| updateDescriptors(replaceDescriptorCurrentValue(visibleDescriptors, descriptor.id, value)); |
There was a problem hiding this comment.
Preserve hidden traits when updating one descriptor
When hiddenDescriptorIds is used, replaceDescriptorCurrentValue is called with visibleDescriptors only, so changing a single trait rebuilds modelOptions from a partial descriptor list and drops every hidden option. This is reachable in the new split-trigger flow (each popup hides sibling descriptors), and it can silently reset previously selected provider options (e.g., reasoning/context settings) on the next change.
Useful? React with 👍 / 👎.
| <TooltipTrigger | ||
| render={<p className="text-right text-muted-foreground text-sm" />} | ||
| > | ||
| {formatChatTimestamp(row.message.createdAt)} |
There was a problem hiding this comment.
Refresh relative timestamps after initial render
This row now renders relative time via formatChatTimestamp(...), but non-streaming message rows have no ticking state, so labels like 59s ago stop updating until an unrelated re-render happens. The new chat timestamp UX therefore becomes stale during idle sessions and can mislead users about message recency.
Useful? React with 👍 / 👎.
ApprovabilityVerdict: Needs human review Diff is too large for automated approval analysis. A human reviewer should evaluate this PR. You can customize Macroscope's approvability policy. Learn more. |
|
@juliusmarminge @t3dotgg I've applied to the fixes to the issues raised. I also added the option to answer to questionnaires with your own input, as it was low effort and probably a highly requested feature.
I dont have this issue. Is it a mac issue? It loads instantly for me without any repaints. I also didn't touch anything about the shortcut component yet, I'm not sure what todo about it so I'm leaving it as. Questionnaire
Added back the initials for coloursSomewhat reckless in hindsight to remove this. Requested by Julius
Limited the tool calls visible to 1 at a timeI made the tool calls show the latest tool call currently available. You can still expand it down.
Toned down the glow for when text area is focusedI also thought it was too much, but didn't want to lose the intent with the focus, so I just made it consistent with what other inputs have as focus ring, but now it's toned down.
Moved checkbox to the right
Debadged most of the UII agree that the badge usage was excessive and a checkmark is sufficient for most UI here. It now displays a blue checkmark. As requested by both
Fixed both icons under the text-area to not look weird.The issue was that it was not properly sized with the text. As requested by Theo Updated model picker againAdded an explanation for why models are missing by adding them back as disabled, with a tooltip on hover. Also added a slight 1 tw unit offset to give it room to breathe. as requested by Theo Made tool call summaries default to offIt uses inference as Theo suspected, using the same model as the text generation model. I would come back to this and suggest to create another model picker in settings allowing a small model like gpt-5-nano to generate summaries. Requested by Theo
Simplified visuals for the "worked for" dash
Removed the double highlight for buttons inside directory tab and per thread tab
Fixed revert button to be in the button group with copy prompt button
Preserved GitHub original colourDuring a pass to fix every icon to be consistently muted when it has a foreground text label, GPT accidentally overwrote GitHub's logo to be muted. I reverted this change to respect GitBub's branding guidelines.
Fixed dialogs under Connections to use correct vertical centeringThe dialogs for Connections used the vertical centering for a command menu (like search bar). I changed it to be middle to create a distinction between user expectations and what shadcn does.
Lowered blur tint for dialogsLowered from sm (8px) to xs (4px). Requested by Theo Refactored the changed files cardIt was using the old style and had too much visual noise. I simplified it while preserving all details, and added the +- diff to be a consistent 4ch. This should hopefully allow easier visual scanning. I also added rounding to closest power when it reaches 1000 LoC (very unlikely). Also fixed various padding issues around the card.
Fixed visual profile of when a tool call errors with runtime errrorWhen a tool call errored like this, its visual profile was all over the place. I made it fully red, to create a difference between a runtime error and normal error.
Added blue checkmark when a model selected
Fixed top padding for on the model rail for model pickerIt was off by a pixel, so I fixed it. I also did a lot of misc like fixing alignment and padding which was not important enough to write here, but I added my notes just in-case: Fixed inconsistent behaviour when Claude is unauthenticatedI noticed that Codex and Claude behave differently in T3 Code when unauthenticated, where Codex generates an alert banner. I made this the same behaviour for Claude also. |
|
Some more fixes |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 06e063f. Configure here.
| const option = activeQuestion.options[optionIndex]; | ||
| if (!option) return; | ||
| event.preventDefault(); | ||
| if (option.label === "Other") { |
There was a problem hiding this comment.
Case-sensitive "Other" check mismatches server-side case-insensitive filtering
Low Severity
The server-side withCustomUserInputOption uses case-insensitive matching (option.label.trim().toLowerCase() !== "other") to identify and reposition the "Other" option, but the client-side ComposerPendingUserInputPanel uses strict case-sensitive equality (option.label === "Other") to decide whether to render the inline text input. If a provider sends an option with label "other" or "OTHER", it would be recognized by the server as the custom-answer option and moved to the end, but the UI wouldn't render the inline text input for it — the user would see it as a regular clickable option with no way to type a custom answer.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 06e063f. Configure here.
…w assistant meta only on terminal message
|
|
as for the custom answer, you can do this today? and since we're using the same composer all features from it still works like file tagging etc: CleanShot.2026-05-03.at.12.45.02.mp4 |
…bar missing during answering - Always include totalProcessedTokens in context-window payload (was conditionally omitted when equal to usedTokens), fixing the status bar text disappearing during agent responses - Show provider display name in compaction text with bold formatting - Combine token usage and compaction into a single flowing paragraph - Add mt-2 spacing to status footer Fix lint warnings: unused var, map spread -> Object.assign, function scoping UI polish: preference picker layout, trait search, warning indicator styling, composer mode controls, empty state alignment, diff stat layout, work log failure markers
| return ( | ||
| <div | ||
| key={`${activeQuestion.id}:${option.label}`} | ||
| role="button" | ||
| tabIndex={isResponding ? -1 : 0} | ||
| aria-disabled={isResponding} | ||
| onClick={() => { | ||
| if (isResponding) return; | ||
| handleOptionSelection(activeQuestion.id, option.label); | ||
| }} | ||
| className={className} | ||
| > | ||
| {content} | ||
| </div> |
There was a problem hiding this comment.
🟠 High chat/ComposerPendingUserInputPanel.tsx:207
The change from <button> to <div role="button"> breaks keyboard activation. While the div has tabIndex={0} and role="button", it lacks an onKeyDown handler for Enter and Space keys. Users who tab to an option cannot activate it with the keyboard — the original <button> handled this natively.
- <div
+ <button
key={`${activeQuestion.id}:${option.label}`}
- role="button"
- tabIndex={isResponding ? -1 : 0}
- aria-disabled={isResponding}
+ type="button"
+ disabled={isResponding}
onClick={() => {
if (isResponding) return;
handleOptionSelection(activeQuestion.id, option.label);
}}
className={className}
>
{content}
- </div>
+ </button>🤖 Copy this AI Prompt to have your agent fix this:
In file apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx around lines 207-220:
The change from `<button>` to `<div role="button">` breaks keyboard activation. While the `div` has `tabIndex={0}` and `role="button"`, it lacks an `onKeyDown` handler for Enter and Space keys. Users who tab to an option cannot activate it with the keyboard — the original `<button>` handled this natively.
Evidence trail:
apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx lines 207-219 at REVIEWED_COMMIT: `<div role="button" tabIndex={...} onClick={...}>` with no `onKeyDown` handler. git_diff MERGE_BASE..REVIEWED_COMMIT shows original code used `<button type="button" onClick={...}>` which natively handles Enter/Space. WAI-ARIA Button Pattern: https://www.w3.org/WAI/ARIA/apg/patterns/button/
There was a problem hiding this comment.
🟡 Medium components/ChatView.tsx:2803
onSelectActivePendingUserInputOption clears the draft's customAnswer by calling togglePendingUserInputOptionSelection, but it no longer clears promptRef.current. When the user then sends a message, onSend reads promptRef.current directly as promptForSend and uses it for deriveComposerSendState and appendTerminalContextsToPrompt. This causes the stale custom answer text to be sent as a new message even though the composer visually appears empty.
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/web/src/components/ChatView.tsx around line 2803:
`onSelectActivePendingUserInputOption` clears the draft's `customAnswer` by calling `togglePendingUserInputOptionSelection`, but it no longer clears `promptRef.current`. When the user then sends a message, `onSend` reads `promptRef.current` directly as `promptForSend` and uses it for `deriveComposerSendState` and `appendTerminalContextsToPrompt`. This causes the stale custom answer text to be sent as a new message even though the composer visually appears empty.
Evidence trail:
ChatView.tsx:2780-2810 - `onSelectActivePendingUserInputOption` does not clear `promptRef.current`; ChatView.tsx:2822 - `onChangeActivePendingUserInputCustomAnswer` sets `promptRef.current = value`; pendingUserInput.ts:76-101 - `togglePendingUserInputOptionSelection` returns `{customAnswer: ""}` clearing the draft but not the ref; ChatComposer.tsx:1943-1948 - editor value is `activePendingProgress.customAnswer ?? ""` when pending, `prompt` otherwise; ChatComposer.tsx:1117-1120 - sync effect only runs when draft store `prompt` changes; ChatComposer.tsx:1310-1327 - `onPromptChange` routes to pending handler (doesn't call `setPrompt`) so draft store `prompt` never changes during pending input; ChatView.tsx:2409-2412 - `onSend` early-returns when `activePendingProgress` is truthy; ChatView.tsx:2424 - `promptForSend = promptRef.current` when `activePendingProgress` is null



























Summary
Checks
Note
Add AI-generated tool work log summaries and polish chat UI with status indicators
generateToolWorkLogSummaryRPC backed by Claude/Codex/Cursor/OpenCode text generation, IndexedDB caching intoolWorkLogSummaryCache.ts, and a toggle in General Settings (toolCallSummaries, default off).SimpleWorkEntryRowin the messages timeline to show success/failure/neutral status icons with tooltips, expandable tool call details, and friendly summaries via the newuseToolWorkLogFriendlyLinehook.ComposerPendingUserInputCardsupports free-text entry with optimistic selection and Enter-to-submit.TraitsPickerto render one trigger button per descriptor (with icons) instead of a single dropdown;ModelPickerContentadds Go/Zen tab filtering for OpenCode providers and disables incompatible providers in locked mode.Tabs,Table,Shortcut,SelectedModelBadge, andColorSelectorUI components; switches app fonts to DM Sans Variable and JetBrains Mono loaded from NPM packages.activeTurnIdinProviderRuntimeIngestionby force-clearing it when lifecycle guard blocks turn completion or a runtime error occurs.ClaudeProvidernow detects unauthenticated CLI state viaclaude auth statusand surfaces an error with a sign-in prompt instead of proceeding.Macroscope summarized f4eff53. (Automatic summaries will resume when PR exits draft mode or review begins).
Note
Medium Risk
Adds a new LLM-backed
generateToolWorkLogSummarypath and Claude auth probing, plus changes provider runtime lifecycle handling; these touch provider integration and session state, so regressions could affect chat/runtime UX.Overview
Adds LLM-generated tool/work-log summary lines by extending
TextGenerationwithgenerateToolWorkLogSummaryacross Claude/Codex/Cursor/OpenCode, wiring it to a new websocket RPC (gitSummarizeToolWorkLog), and sanitizing/normalizing the resulting single-line output.Unifies provider user-input question options by appending a trailing "Other" option via a shared helper, and updates the pending user-input UI to support inline custom answers with improved selection/auto-advance behavior.
Improves provider robustness by probing
claude auth statusto surface an explicit unauthenticated error state, and hardens provider runtime ingestion to force-clear stuckactiveTurnIdwhen lifecycle guards rejectturn.completed/runtime.errorevents.Polishes multiple web UI surfaces (composer controls/badges, branch picker, command palette shortcuts, timeline metadata/durations, changed-files panel, icons/styles), bundles variable fonts (removing Google Fonts), and makes small desktop/server script tweaks (context menu header skipping, Windows shell flag removal, test updates, TODO removal).
Reviewed by Cursor Bugbot for commit f4eff53. Bugbot is set up for automated code reviews on this repo. Configure here.