feat(vscode): editor-theme design system, config hub, and host UX - #48
Conversation
Adopt the Cline-inspired design across the VS Code extension: - Webview theme derives every token from --vscode-* variables (dark/light/high-contrast); hardcoded palette classes removed. - Config hub: full-page view with overview, models, providers, MCP, raw config.toml viewer (new GetConfigInfo RPC), and settings. - Composer: single mode menu (permissions, plan, thinking effort); approval/question dialogs gain real keyboard shortcuts. - Chat: react-virtuoso list, token/context indicators, welcome screen with recent sessions; history grouped by date with search highlight. - Host: status bar item, view badge, code actions, terminal/editor context menus, walkthrough, real logout, cmd+alt+n keybinding. - Deps: drop @fontsource-variable/inter and react-scroll-to-bottom, add react-virtuoso, bump @vscode/test-electron for VS Code 1.132.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
💤 Files with no reviewable changes (1)
🚧 Files skipped from review as they are similar to previous changes (6)
📝 WalkthroughWalkthroughThe VS Code extension adds host commands, activity indicators, chat-context actions, logout handling, and a unified configuration hub. The webview adds composer controls, virtualized chat rendering, session history features, context usage details, and VS Code theme-aware styling. ChangesVS Code extension and host integrations
Configuration hub
Composer and chat experience
Theme-aware webview styling
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Comment |
commit: |
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/vscode/src/runtime/reverse-rpc.ts (1)
33-37: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse decision-neutral activity text for question requests.
requestQuestionincrementspendingApprovals. When only a question is pending, the status bar and sidebar say that an approval is needed. Rename the shared state to a decision-neutral term, or track questions separately.
apps/vscode/src/runtime/reverse-rpc.ts#L33-L37: Track questions with the decision-neutral state.apps/vscode/src/runtime/reverse-rpc.ts#L72-L78: Decrement the matching question state.apps/vscode/src/activity.ts#L8-L10: RenamependingApprovalsto match both request types.apps/vscode/src/extension.ts#L67-L84: Use decision-neutral status text and tooltips.apps/vscode/src/PythinkerWebviewProvider.ts#L72-L84: Use decision-neutral badge tooltips.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/vscode/src/runtime/reverse-rpc.ts` around lines 33 - 37, Rename the shared pending-approval state to a decision-neutral name and update all related usage: in apps/vscode/src/runtime/reverse-rpc.ts lines 33-37 increment it for requestQuestion and lines 72-78 decrement the matching question state; in apps/vscode/src/activity.ts lines 8-10 rename pendingApprovals; in apps/vscode/src/extension.ts lines 67-84 use decision-neutral status text and tooltips; and in apps/vscode/src/PythinkerWebviewProvider.ts lines 72-84 use decision-neutral badge tooltips.
🧹 Nitpick comments (6)
apps/vscode/webview-ui/src/components/PythinkerMascot.tsx (1)
3-3: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the repository-preferred import alias.
This import uses
@/hooks/useIsDark. Replace it with#/hooks/useIsDarkto follow the TypeScript import convention.As per coding guidelines, prefer
#/...over the equivalent@/...alias.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/vscode/webview-ui/src/components/PythinkerMascot.tsx` at line 3, Update the useIsDark import in PythinkerMascot to use the repository-preferred `#/hooks/useIsDark` alias instead of `@/hooks/useIsDark`, leaving its usage unchanged.Source: Coding guidelines
apps/vscode/webview-ui/src/components/confighub/sections.ts (1)
9-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
#/alias for new Config Hub imports.The new files use
@/imports. Use the preferred#/alias consistently.
apps/vscode/webview-ui/src/components/confighub/sections.ts#L9-L9: change the settings-store import to#/stores/settings.store.apps/vscode/webview-ui/src/components/confighub/ConfigFileSection.tsx#L3-L4: change component and service imports to#/.apps/vscode/webview-ui/src/components/confighub/ConfigHub.tsx#L2-L6: change component, store, service, and utility imports to#/.apps/vscode/webview-ui/src/components/confighub/OverviewSection.tsx#L1-L2: change component and store imports to#/.As per coding guidelines, “Use
import ... from '#/...'in preference to the equivalent@/...alias.”🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/vscode/webview-ui/src/components/confighub/sections.ts` at line 9, Replace the new Config Hub `@/` aliases with the preferred `#/` aliases: update the settings-store import in apps/vscode/webview-ui/src/components/confighub/sections.ts (line 9), component and service imports in ConfigFileSection.tsx (lines 3-4), component, store, service, and utility imports in ConfigHub.tsx (lines 2-6), and component and store imports in OverviewSection.tsx (lines 1-2); no other changes are needed.Source: Coding guidelines
apps/vscode/src/handlers/config.handler.ts (1)
12-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMove the type alias below the import list.
type SdkConfig = anyappears before this import. Oxlint reportsimport(first)for this file.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/vscode/src/handlers/config.handler.ts` at line 12, Move the SdkConfig type alias below the complete import list in config.handler.ts, keeping the existing alias unchanged and ensuring all imports remain first in the file to satisfy oxlint's import(first) rule.Source: Linters/SAST tools
apps/vscode/webview-ui/src/components/ComposerModeMenu.tsx (1)
3-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the
#/alias for internal imports.Replace changed
@/imports with the equivalent#/imports.
apps/vscode/webview-ui/src/components/ComposerModeMenu.tsx#L3-L5: replace the three internal@/imports.apps/vscode/webview-ui/src/components/DisplayBlocks.tsx#L7-L7: replace the@/hooks/useIsDarkimport.apps/vscode/webview-ui/src/components/SessionList.tsx#L4-L11: replace the internal@/imports and retain package imports unchanged.As per coding guidelines: “Use
import ... from '#/...'in preference to the equivalent@/...alias.”🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/vscode/webview-ui/src/components/ComposerModeMenu.tsx` around lines 3 - 5, Replace the internal `@/` imports with equivalent `#/` imports in ComposerModeMenu.tsx (lines 3-5), DisplayBlocks.tsx (line 7), and SessionList.tsx (lines 4-11); leave SessionList.tsx package imports unchanged.Source: Coding guidelines
apps/vscode/webview-ui/src/components/index.ts (1)
13-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse re-exports for the new barrel entries.
Replace the new named exports with
export * from .... This follows the non-rootindex.tsconvention.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/vscode/webview-ui/src/components/index.ts` around lines 13 - 16, Update the new barrel entries in the component index to use wildcard re-exports instead of named exports, specifically for ConfigHub, WorkDirModal, ThinkingBlock, and ComposerModeMenu. Follow the existing non-root index.ts convention while preserving the same module paths.Source: Coding guidelines
apps/vscode/webview-ui/src/components/confighub/ModelsSection.tsx (1)
2-4: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse the
#/alias for new internal imports.
apps/vscode/webview-ui/src/components/confighub/ModelsSection.tsx#L2-L4: replace each@/alias with its#/equivalent.apps/vscode/webview-ui/src/components/ChatArea.tsx#L6-L7: replace each@/alias with its#/equivalent.apps/vscode/webview-ui/src/components/WelcomeScreen.tsx#L3-L5,L9: replace each@/alias with its#/equivalent.As per coding guidelines, use
import ... from '#/...'in preference to the equivalent@/...alias.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/vscode/webview-ui/src/components/confighub/ModelsSection.tsx` around lines 2 - 4, Replace every new "`@/`..." internal import with the equivalent "`#/`..." alias in ModelsSection.tsx (lines 2-4), ChatArea.tsx (lines 6-7), and WelcomeScreen.tsx (lines 3-5 and 9); no other import changes are needed.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/vscode/src/integrations/chat-context.ts`:
- Around line 95-107: Update the terminal selection flow around copySelection to
write a generated no-selection sentinel to the clipboard before executing the
command. Treat the result as empty only when the sentinel remains, allowing
valid selections matching previousClipboard, and continue restoring
previousClipboard in the finally block.
In `@apps/vscode/webview-ui/src/components/ApprovalDialog.tsx`:
- Around line 43-62: Guard response submission in both ApprovalDialog.tsx (lines
43-62) and QuestionDialog.tsx (lines 59-88) with an in-flight flag so repeated
keyboard events cannot invoke respondApproval or respondQuestion again before
the first request settles; set the guard before calling the response handler and
clear it when the request completes, preserving the existing response behavior.
In `@apps/vscode/webview-ui/src/components/ChatArea.tsx`:
- Line 57: Update the increaseViewportBy configuration in ChatArea to replace
Number.MAX_SAFE_INTEGER with a bounded bottom overscan value, preserving the
existing top overscan and ensuring virtualization does not mount all later
messages during upward scrolling.
In `@apps/vscode/webview-ui/src/components/ChatStatus.tsx`:
- Around line 61-65: Update formatTokens so thousand-scale values that round to
1,000.0k are promoted to the million unit and formatted as 1.0m, avoiding
1000.0k at the boundary. Preserve the existing formatting for values below that
threshold and the current million-unit behavior.
In `@apps/vscode/webview-ui/src/components/confighub/ConfigFileSection.tsx`:
- Around line 51-53: Update the conditional rendering in ConfigFileSection so
that when error is set and info is null, it renders only the error state and
refresh control instead of the “No config file was found” message. Preserve the
missing-file result for cases where info exists and indicates a null path or
non-existent file.
In `@apps/vscode/webview-ui/src/components/Markdown.tsx`:
- Line 16: Update the useIsDark import in Markdown.tsx to use the
`#/hooks/useIsDark` alias instead of the `@/` alias, leaving the imported symbol and
surrounding component logic unchanged.
In `@apps/vscode/webview-ui/src/components/SessionList.tsx`:
- Around line 20-25: Update getGroupLabel to derive the yesterday and week
cutoff timestamps from local calendar dates using Date year/month/day values
with day offsets, rather than subtracting DAY_MS. Preserve the existing Today,
Yesterday, and This week labels while correctly handling daylight-saving
transitions.
In `@apps/vscode/webview-ui/src/components/WelcomeScreen.tsx`:
- Line 21: Remove the console.error statement from the WelcomeScreen
session-loading error path, while preserving the user-facing toast. Do not add
another console call; use the supported logger only if error logging remains
necessary.
In `@apps/vscode/webview-ui/src/styles/index.css`:
- Around line 90-93: Update the --font-sans and --font-mono declarations to
quote the non-generic family names BlinkMacSystemFont, Roboto, SFMono-Regular,
Menlo, Monaco, and Consolas, while leaving the generic family names and existing
fallback order unchanged.
---
Outside diff comments:
In `@apps/vscode/src/runtime/reverse-rpc.ts`:
- Around line 33-37: Rename the shared pending-approval state to a
decision-neutral name and update all related usage: in
apps/vscode/src/runtime/reverse-rpc.ts lines 33-37 increment it for
requestQuestion and lines 72-78 decrement the matching question state; in
apps/vscode/src/activity.ts lines 8-10 rename pendingApprovals; in
apps/vscode/src/extension.ts lines 67-84 use decision-neutral status text and
tooltips; and in apps/vscode/src/PythinkerWebviewProvider.ts lines 72-84 use
decision-neutral badge tooltips.
---
Nitpick comments:
In `@apps/vscode/src/handlers/config.handler.ts`:
- Line 12: Move the SdkConfig type alias below the complete import list in
config.handler.ts, keeping the existing alias unchanged and ensuring all imports
remain first in the file to satisfy oxlint's import(first) rule.
In `@apps/vscode/webview-ui/src/components/ComposerModeMenu.tsx`:
- Around line 3-5: Replace the internal `@/` imports with equivalent `#/` imports in
ComposerModeMenu.tsx (lines 3-5), DisplayBlocks.tsx (line 7), and
SessionList.tsx (lines 4-11); leave SessionList.tsx package imports unchanged.
In `@apps/vscode/webview-ui/src/components/confighub/ModelsSection.tsx`:
- Around line 2-4: Replace every new "`@/`..." internal import with the equivalent
"`#/`..." alias in ModelsSection.tsx (lines 2-4), ChatArea.tsx (lines 6-7), and
WelcomeScreen.tsx (lines 3-5 and 9); no other import changes are needed.
In `@apps/vscode/webview-ui/src/components/confighub/sections.ts`:
- Line 9: Replace the new Config Hub `@/` aliases with the preferred `#/`
aliases: update the settings-store import in
apps/vscode/webview-ui/src/components/confighub/sections.ts (line 9), component
and service imports in ConfigFileSection.tsx (lines 3-4), component, store,
service, and utility imports in ConfigHub.tsx (lines 2-6), and component and
store imports in OverviewSection.tsx (lines 1-2); no other changes are needed.
In `@apps/vscode/webview-ui/src/components/index.ts`:
- Around line 13-16: Update the new barrel entries in the component index to use
wildcard re-exports instead of named exports, specifically for ConfigHub,
WorkDirModal, ThinkingBlock, and ComposerModeMenu. Follow the existing non-root
index.ts convention while preserving the same module paths.
In `@apps/vscode/webview-ui/src/components/PythinkerMascot.tsx`:
- Line 3: Update the useIsDark import in PythinkerMascot to use the
repository-preferred `#/hooks/useIsDark` alias instead of `@/hooks/useIsDark`,
leaving its usage unchanged.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ce44839a-7425-44a0-a60e-65b026b319a6
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!**/pnpm-lock.yaml
📒 Files selected for processing (92)
.changeset/vscode-chat-fixes.md.changeset/vscode-composer-menu.md.changeset/vscode-config-hub.md.changeset/vscode-editor-theme.md.changeset/vscode-host-integrations.mdapps/vscode/package.jsonapps/vscode/shared/bridge.tsapps/vscode/shared/legacy-sdk.tsapps/vscode/shared/types.tsapps/vscode/src/PythinkerWebviewProvider.tsapps/vscode/src/activity.tsapps/vscode/src/config/vscode-settings.tsapps/vscode/src/extension.tsapps/vscode/src/handlers/auth.handler.tsapps/vscode/src/handlers/chat.handler.tsapps/vscode/src/handlers/config.handler.tsapps/vscode/src/handlers/workspace.handler.tsapps/vscode/src/integrations/chat-context.tsapps/vscode/src/runtime/reverse-rpc.tsapps/vscode/walkthrough/first-conversation.mdapps/vscode/walkthrough/open-view.mdapps/vscode/walkthrough/reference-code.mdapps/vscode/walkthrough/sign-in.mdapps/vscode/webview-ui/src/App.tsxapps/vscode/webview-ui/src/components/ActionMenu.tsxapps/vscode/webview-ui/src/components/ApprovalDialog.tsxapps/vscode/webview-ui/src/components/BottomToolbar.tsxapps/vscode/webview-ui/src/components/BrailleSpinner.tsxapps/vscode/webview-ui/src/components/ChatArea.tsxapps/vscode/webview-ui/src/components/ChatMessage.tsxapps/vscode/webview-ui/src/components/ChatStatus.tsxapps/vscode/webview-ui/src/components/CompactionCard.tsxapps/vscode/webview-ui/src/components/ComposerModeMenu.tsxapps/vscode/webview-ui/src/components/ConfigErrorScreen.tsxapps/vscode/webview-ui/src/components/CopyButton.tsxapps/vscode/webview-ui/src/components/DisplayBlocks.tsxapps/vscode/webview-ui/src/components/FileChangesPanel.tsxapps/vscode/webview-ui/src/components/Header.tsxapps/vscode/webview-ui/src/components/InlineError.tsxapps/vscode/webview-ui/src/components/LoginScreen.tsxapps/vscode/webview-ui/src/components/Markdown.tsxapps/vscode/webview-ui/src/components/MediaPreviewModal.tsxapps/vscode/webview-ui/src/components/MediaThumbnail.tsxapps/vscode/webview-ui/src/components/PermissionModeBadge.tsxapps/vscode/webview-ui/src/components/PlanCard.tsxapps/vscode/webview-ui/src/components/PlanModeButton.tsxapps/vscode/webview-ui/src/components/PythinkerMascot.tsxapps/vscode/webview-ui/src/components/QuestionDialog.tsxapps/vscode/webview-ui/src/components/QueuedMessagesPanel.tsxapps/vscode/webview-ui/src/components/SessionList.tsxapps/vscode/webview-ui/src/components/SilverSpinner.tsxapps/vscode/webview-ui/src/components/ThinkingBlock.tsxapps/vscode/webview-ui/src/components/ThinkingButton.tsxapps/vscode/webview-ui/src/components/ToolRenderers.tsxapps/vscode/webview-ui/src/components/WelcomeScreen.tsxapps/vscode/webview-ui/src/components/WorkDirModal.tsxapps/vscode/webview-ui/src/components/WorkflowCard.tsxapps/vscode/webview-ui/src/components/confighub/ConfigFileSection.tsxapps/vscode/webview-ui/src/components/confighub/ConfigHub.tsxapps/vscode/webview-ui/src/components/confighub/MCPServersSection.tsxapps/vscode/webview-ui/src/components/confighub/ModelsSection.tsxapps/vscode/webview-ui/src/components/confighub/OverviewSection.tsxapps/vscode/webview-ui/src/components/confighub/ProvidersSection.tsxapps/vscode/webview-ui/src/components/confighub/SettingsSection.tsxapps/vscode/webview-ui/src/components/confighub/sections.tsapps/vscode/webview-ui/src/components/index.tsapps/vscode/webview-ui/src/components/inputarea/InputArea.tsxapps/vscode/webview-ui/src/components/inputarea/hooks/useInputHistory.tsapps/vscode/webview-ui/src/components/ui/badge.tsxapps/vscode/webview-ui/src/components/ui/button.tsxapps/vscode/webview-ui/src/components/ui/checkbox.tsxapps/vscode/webview-ui/src/components/ui/combobox.tsxapps/vscode/webview-ui/src/components/ui/command.tsxapps/vscode/webview-ui/src/components/ui/context-menu.tsxapps/vscode/webview-ui/src/components/ui/dropdown-menu.tsxapps/vscode/webview-ui/src/components/ui/field.tsxapps/vscode/webview-ui/src/components/ui/input-group.tsxapps/vscode/webview-ui/src/components/ui/input.tsxapps/vscode/webview-ui/src/components/ui/select.tsxapps/vscode/webview-ui/src/components/ui/sonner.tsxapps/vscode/webview-ui/src/components/ui/switch.tsxapps/vscode/webview-ui/src/components/ui/tabs.tsxapps/vscode/webview-ui/src/components/ui/textarea.tsxapps/vscode/webview-ui/src/hooks/useAppInit.tsapps/vscode/webview-ui/src/hooks/useIsDark.tsapps/vscode/webview-ui/src/hooks/useWelcomeHint.tsxapps/vscode/webview-ui/src/lib/format.tsapps/vscode/webview-ui/src/main.tsxapps/vscode/webview-ui/src/services/bridge.tsapps/vscode/webview-ui/src/stores/chat.store.tsapps/vscode/webview-ui/src/stores/settings.store.tsapps/vscode/webview-ui/src/styles/index.css
💤 Files with no reviewable changes (6)
- apps/vscode/webview-ui/src/components/BrailleSpinner.tsx
- apps/vscode/webview-ui/src/hooks/useAppInit.ts
- apps/vscode/webview-ui/src/components/PermissionModeBadge.tsx
- apps/vscode/webview-ui/src/components/ThinkingButton.tsx
- apps/vscode/webview-ui/src/components/PlanModeButton.tsx
- apps/vscode/webview-ui/src/main.tsx
Guard approval and question dialogs against duplicate in-flight responses, use a clipboard sentinel to detect an empty terminal selection, promote token counts that round to one million into the m unit, stop claiming the config file is missing after a load error, group sessions by calendar-day boundaries so DST does not shift them, and drop a stray console statement.
Related Issue
No linked issue — the problem is explained below.
Problem
The VS Code extension UI did not follow the editor color theme: it shipped a fixed dark palette with a bundled font, so light and high-contrast themes rendered incorrectly. Composer controls (permission mode, plan mode, thinking effort) were separate buttons that overlapped at narrow sidebar widths. Configuration was fragmented across three separate modals with no way to inspect the local config file or MCP connections. Host integration was minimal: no status bar presence, no code actions, no terminal/editor context menus, and long conversations rendered every message at once.
What changed
--vscode-*variables via Tailwind@theme inline, with Dark+ fallbacks. Light, dark, and both high-contrast themes work with zero runtime theme detection; the only JS consumer is a singleuseIsDarkhook backed by the host-maintained body classes. The bundled Inter font and the hardcoded.darkpalette are removed.config.toml(with an open-in-editor action), and extension settings. Adapts to narrow sidebar widths.@fontsource-variable/interandreact-scroll-to-bottom; bumped@vscode/test-electronto ^3.1.0 (VS Code ≥ 1.132 renamed the macOS binary, which broke the extension-host smoke test).This approach fits the extension because VS Code already maintains the theme contract (
--vscode-*variables and body classes) — deriving from it removes an entire parallel theming system instead of maintaining one.Verified locally: typecheck (tsc + tsgo, both tsconfigs), build, all 338 existing tests, type-aware lint, VSIX packaging audit, and an extension-host smoke test that activates the extension and opens the webview.
Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update. (Extension is a private package; no docs affected.)Summary by CodeRabbit