feat(theme): dark mode + UI primitives standardization#2046
Conversation
Extends the existing semantic var system in app/src/styles/theme.css with a full :root.dark block (background, surface, border, text, hover/active/focus, success/warning/error/info) and dark variants for the .card-*/.input-*/.btn-*/.nav-* component shortcuts so theme switching works without per-call dark: classes for them.
- New themeSlice (light/dark/system, persisted in localStorage) - ThemeProvider mounted under PersistGate syncs Redux mode to <html class="dark"> and subscribes to prefers-color-scheme when in system mode - Tailwind's darkMode: 'class' and the :root.dark CSS vars now activate together
- New AppearancePanel renders the light/dark/system radio group, dispatching setThemeMode - Wired into Settings router as /settings/appearance, breadcrumb in useSettingsNavigation, and a General-section entry in SettingsHome
…iants to shell - ui/Button now ships dark variants for all four variants - New shared ui/Card (surface, elevated, outlined, subtle) and ui/Input primitives with first-class dark support; ui/index barrel re-exports - Shell components (SettingsHeader, SettingsMenuItem, PageBackButton, BottomTabBar) and SettingsHome's clear-data modal patched with dark: variants so the chrome of every settings page works in both themes
- pages/Home main card, status copy, version chip - pages/Notifications wrapper, list rows, unread dot bg, footer actions - components/home/HomeBanners (UsageLimit, PromotionalCredits, EarlyBirdy) - components/chat/TokenUsagePill severity colors and session ring
…ationCard, NotificationCenter, WalkthroughTooltip
…lasses - index.css html/body now use var(--color-background) and var(--color-text-primary) instead of hardcoded #f5f5f5 / #171717 — the page bg flips with the theme - Dotted-canvas overlay switches to white dots in dark mode - MeshGradient swaps to deep-blue palette in dark mode (observes <html class="dark">) - :root.dark overrides for .card, .input-primary, .status-*, .nav-*, .message-received, .loading-pulse, .glass, .skills-table-* so the remaining component shortcuts respect the theme
…ooks, Invites, Welcome pages
Patches the Skills page surfaces I touched directly: status text color, page title/subtitle, primary action button, and the autocomplete unconfigured CTA. Did not bulk-patch skills/* component files because the prior bulk pass left malformed JSX (missing > on attributes); those files were reverted to upstream and will be patched individually in follow-ups.
…ls/accounts/oauth/composio Adds dark: variants for text/bg/border/hover/divide stone scales via a negative-lookahead perl pass. Surgical — only adds when no dark: sibling already present in the immediate vicinity. Covers settings panels missed by the earlier subagent (Mascot, AgentChat, RecoveryPhrase, CronJobs, Team*, Composio*, NotificationRouting, MemoryData, MemoryDebug, Debug panels) plus components/skills, components/channels, components/oauth, components/composio, components/accounts.
… bubbles, kanban, timeline)
…rast + Rewards/PillTabBar dark variants - Settings menu items (bg-neutral-900) now sit on a black (#000) page bg instead of #0a0a0a, giving the menu its 'card' silhouette back - Rewards tabs and PillTabBar patched via the same negative-lookahead perl pass
…oter WrappedSettingsPage was the actual outer card around all settings panels — it was using bg-white/border-stone-200 with no dark variants, so the card silhouette vanished in dark mode. Adds dark:bg-neutral-900, dark:border-neutral-800 to the wrapper and the Beta-build footer divider. Reverts the speculative inner cards I added to SettingsHome/SettingsSectionPage in the prior commit since they'd nest inside this wrapper.
theme.css (with the entire :root.dark CSS variable block and component-shortcut overrides) was never imported anywhere — only index.css was. main.tsx now imports it after index.css so the dark overrides land in the cascade. This was why bg, cards, and many @apply'd component classes didn't flip on theme switch. Also adds dark variants to LanguageSelect's inline DEFAULT_CLASS.
…y with dark hue - MeshGradient light palette: replace washed-out white with a cohesive primary-300 → accent.lavender → accent.sky → primary-200 sweep - Bump light-mode mesh opacity from 10% to 30% so the color reads through the dotted-canvas overlay - Page bg tinted from flat #f5f5f5 to #f4f7fc (primary-50-ish) so the gradient sits on a continuous palette
…the comment early and broke postcss parsing
…#f5f5f5 light page bg Per user feedback, the bumped-opacity ocean→lavender→sky light gradient and primary-tinted page bg felt off. Restoring the original primary-50/primary-100/primary-200 mesh palette at 10% opacity for both modes, and #f5f5f5 light page bg. Dark mode keeps the #000 page + neutral-900 surface contrast from earlier, just on the original mesh tones.
…rk: tokens across patched files - DiscordBanner gets w-full so it matches the other home banners' span, plus dark variants using the brand blurple at low opacity - Sweeps duplicate dark: utility tokens (e.g. 'dark:bg-neutral-900 dark:bg-neutral-900') the bulk perl pass introduced when it ran twice on the same file
… toggle - Conversations.tsx: showSidebar useState now defaults to false. The threads sidebar is hidden on first render; the existing toggle button in the chat header still opens/closes it. - Accounts.tsx (/chat): Respond Queue side panel is no longer rendered by default. A small pill button in the top-right of the agent chat pane toggles it with a count badge. Adds en.ts keys for the show/hide tooltips. Respond Queue surfaces incoming-message replies waiting on the user from connected accounts (Slack/Telegram/etc.). It's useful for power users but adds noise to the default chat surface.
… pairing across codebase - AgentMessageBubble + TableCellMarkdown now get dark:prose-invert so the agent message body actually inverts in dark mode (was inheriting Tailwind prose plugin's hardcoded light-mode text colors) - Earlier bulk perl pass added 'dark:text-neutral-N' next to 'PREFIX:text-stone-N' instead of 'dark:PREFIX:text-neutral-N'. Sweeps hover/focus/disabled/active/focus-within/focus-visible/group-hover/peer-focus + placeholder modifiers across .tsx files so the dark variant carries the original modifier
…coded #f6f6f6) The bg-[#f6f6f6] tinted backdrop behind the message list in Conversations.tsx was a literal hex with no dark variant — that's the 'still bright' surface inside the dark chat card. Pair with dark:bg-neutral-950.
…ns setup badge - WorkerThreadRefCard (the subagent invocation chip in chat with running/completed/failed status, label, meta, arrow): full dark palette using primary/amber/sage/coral 500/15-30% tints - ConnectionsPanel 'Set up' badge missed primary tones — added dark variant
…ron tones Subagent timeline rows render an expandable <details> per tool call with running/success/failure tones (amber/sage/coral). Light-only bg-X-100/X-50 + text-X-600/X-900 swapped for paired dark tints (X-500/20 + X-300/200) so the timeline reads in dark mode.
…primary tints) ChannelSelector, ChannelSetupModal, DiscordConfig, DiscordServerChannelPicker, TelegramConfig had light-only status/active/error chips using bg-COLOR-50/100, text-COLOR-700, border-COLOR-200. Adds paired dark tints (bg-COLOR-500/10-20, text-COLOR-300, border-COLOR-500/30-40).
ComposioConnectorTile light/pending/expired/error/disconnected variants paired with dark tints (sage/amber/coral 500/10-15%). channel + composio status text-COLOR-600/700 paired with dark text-COLOR-300.
…dark variants - MeetingBotsCard: gradient bg, dialog header bg, close button hover, platform pill hover, submit/cancel button disabled states all needed dark pairing or had broken 'disabled:bg-X dark:bg-Y' that applied dark:bg-Y unconditionally - ChannelTile (the buttons in Channels grid on Skills page) connected/pending/error states paired with sage/amber/coral 500/10-15% dark tints (matching the Composio tiles I patched earlier)
Negative-lookahead perl pass adds dark: variants for text/bg/border/hover/divide stone scales + sage/amber/coral/primary 50/100/200 chips + 600/700 colored text across components/intelligence/ (memory, vault, subconscious, calls, dreams, tasks, sync connections, graph, heatmap, sources, screen-intelligence debug, toasts, etc.).
…tive bg The active pill defaulted to bg-primary-50 text-primary-700 border-primary-200 with no dark pairing — selected category chips on Skills/Integrations looked stuck in light mode. Adds dark primary-500/15 + primary-300 + primary-500/40. Also drops the duplicated dark:bg-neutral-800/60 in inactiveClassName.
…leanup duplicate dark tokens MemoryInsights insight category colors (facts/preferences/opinions) use emerald/rose/lavender palettes that the earlier perl pass didn't cover. The follow-up pass added them but doubled tokens; cleaned with a non-adjacent-dedup pass.
…dingScreen + ConnectionIndicator + memory action buttons + hover:dark mis-pairing
- BootCheckGate ('select a runtime' screen, local/cloud cards, host/port inputs): all stone/white surfaces paired with dark
- RouteLoadingScreen ('Initializing OpenHuman...' card): bg-white + border + text now flip
- ConnectionIndicator ('Connected to OpenHuman AI 🚀' pill): bg-stone-50/border-stone-200 paired
- MemoryWorkspace action buttons: 'hover:bg-X-50 dark:bg-X-500/10' was applying the dark tint unconditionally — rewrote to dark:hover:bg-X-500/10. Same fix swept across all .tsx files
- Restored bg-primary-500/bg-violet-500 literals corrupted by an earlier prefix-match bug (\bbg-primary-50 was matching bg-primary-500 as a prefix and inserting a dark variant mid-token)
…g var, less padding - Google/GitHub/Twitter OAuth buttons had border-gray-200/hover:bg-gray-50/text-gray-900 with no dark variants; added dark neutral pairs - BootCheckGate 'select a runtime' panel was forcing bg-[#f5f5f5] (hard light) and p-10 (too much padding). Now reads var(--color-background) so it tints with the theme like every other screen, and trimmed inner padding to p-6 to match the app's card density.
…-700 in dark mode The '!border-stone-200 dark:border-neutral-800' pair had ! important only on the light variant — Tailwind's border-2 default color overrode the dark variant, making the unselected card border look white. Adds ! to dark:border-* and dark:hover:border-*. Also drops the duplicated dark:bg-neutral-800/60 token.
…in BootCheckGate Three Continue/Confirm buttons in BootCheckGate had bg-primary-500 munged to bg-primary-50 dark:bg-primary-500/150 by the earlier perl prefix-match bug. Restored.
…ChoiceCard borders + free-credit highlight legibility - Bulk dark variants across all .tsx in pages/onboarding/ (LocalAI/Skills/CustomWizard/ApiKeys/RuntimeChoice/ContextGathering/ReferralApply/Wizard stepper/Welcome step/banners) using the safer (?!\d) lookahead so it doesn't munge stone-500 → stone-50 prefix matches - BetaBanner amber link gets dark variant - RuntimeChoiceStep: '$1 free credit' highlight pill had text-sage-800 with no dark variant — paired with text-sage-200. Also fixes the !-important pairing on the unselected ChoiceCard border that was leaving the dark variant overridden by Tailwind border defaults, and drops the duplicated dark:bg-neutral-800/60.
…tomWizardStep + RuntimeChoiceStep cards + similar)
…, billing, AI, autocomplete, screen-intel, memory debug, recovery phrase, etc.)
…ggle bgs across settings
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a persisted theme slice and ThemeProvider, imports theme.css, adds an Appearance settings page and route, introduces Card and Input UI primitives and a barrel export, updates Button variants, makes Accounts respond-queue togglable, and applies dark-mode Tailwind/CSS tokens across many components and pages. ChangesDark theme rollout
Sequence Diagram(s)sequenceDiagram
participant User
participant AppearancePanel
participant Redux Store
participant ThemeProvider
participant Document (html)
User->>AppearancePanel: Select theme mode
AppearancePanel->>Redux Store: setThemeMode(mode)
Redux Store-->>ThemeProvider: mode change
ThemeProvider->>ThemeProvider: resolveTheme(mode/system)
ThemeProvider->>Document (html): Add/remove class "dark", set color-scheme
Note right of Document (html): Tailwind dark: styles apply across UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
|
There was a problem hiding this comment.
Actionable comments posted: 17
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (11)
app/src/components/settings/panels/NotificationsPanel.tsx (1)
134-171:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove duplicate dark mode classes in categories section.
Multiple duplicated
dark:utilities:
- Line 134:
dark:text-neutral-500appears twice- Line 137:
dark:bg-neutral-900,dark:border-neutral-800, anddark:divide-neutral-800each appear twice- Line 143:
dark:text-neutral-100appears twice- Line 146:
dark:text-neutral-400appears twice- Lines 153-156:
dark:bg-neutral-600appears twice in toggle- Line 171:
dark:text-neutral-400appears twice🔧 Proposed fix
- <h3 className="text-xs font-semibold uppercase tracking-wider text-stone-400 dark:text-neutral-500 dark:text-neutral-500 mb-2 px-1"> + <h3 className="text-xs font-semibold uppercase tracking-wider text-stone-400 dark:text-neutral-500 mb-2 px-1"> {t('settings.notifications.categories')} </h3> - <div className="bg-white dark:bg-neutral-900 dark:bg-neutral-900 rounded-xl border border-stone-200 dark:border-neutral-800 dark:border-neutral-800 overflow-hidden divide-y divide-stone-100 dark:divide-neutral-800 dark:divide-neutral-800"> + <div className="bg-white dark:bg-neutral-900 rounded-xl border border-stone-200 dark:border-neutral-800 overflow-hidden divide-y divide-stone-100 dark:divide-neutral-800"> {CATEGORIES.map(cat => { const enabled = preferences[cat.id]; return ( <div key={cat.id} className="flex items-center justify-between p-4"> <div className="flex-1 mr-4"> - <p className="text-sm font-medium text-stone-900 dark:text-neutral-100 dark:text-neutral-100"> + <p className="text-sm font-medium text-stone-900 dark:text-neutral-100"> {cat.title} </p> - <p className="text-xs text-stone-500 dark:text-neutral-400 dark:text-neutral-400 mt-1 leading-relaxed"> + <p className="text-xs text-stone-500 dark:text-neutral-400 mt-1 leading-relaxed"> {cat.description} </p> </div> <button onClick={() => handleToggle(cat.id)} className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-1 ${ enabled ? 'bg-primary-500' - : 'bg-stone-400 dark:bg-neutral-600 dark:bg-neutral-600' + : 'bg-stone-400 dark:bg-neutral-600' }`} ... - <p className="text-xs text-stone-500 dark:text-neutral-400 dark:text-neutral-400 leading-relaxed px-1 mt-2"> + <p className="text-xs text-stone-500 dark:text-neutral-400 leading-relaxed px-1 mt-2">🤖 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 `@app/src/components/settings/panels/NotificationsPanel.tsx` around lines 134 - 171, In NotificationsPanel (the JSX rendering CATEGORIES), remove duplicated dark: utility classes in the className strings: the h3 heading (duplicate dark:text-neutral-500), the container div wrapping the list (duplicate dark:bg-neutral-900, dark:border-neutral-800, dark:divide-neutral-800), the category title <p> (duplicate dark:text-neutral-100), the description <p> (duplicate dark:text-neutral-400), the toggle <button> classes (duplicate dark:bg-neutral-600), the toggle <span> (duplicate dark:bg-neutral-900), and the trailing info <p> (duplicate dark:text-neutral-400); keep one occurrence of each dark: class per className, leaving other styles and behavior (CATEGORIES map, handleToggle, aria props) unchanged.app/src/components/settings/panels/billing/SubscriptionPlans.tsx (1)
80-85:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winRemove duplicated dark mode class.
Line 84 contains
dark:text-neutral-100twice.🐛 Proposed fix
className={`rounded-full px-4 py-2 text-sm font-semibold transition-colors ${ billingInterval === 'annual' ? 'bg-primary-600 text-white' - : 'text-stone-500 dark:text-neutral-400 hover:text-stone-900 dark:hover:text-neutral-100 dark:text-neutral-100' + : 'text-stone-500 dark:text-neutral-400 hover:text-stone-900 dark:hover:text-neutral-100' }`}>🤖 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 `@app/src/components/settings/panels/billing/SubscriptionPlans.tsx` around lines 80 - 85, In SubscriptionPlans.tsx, the className string for the annual toggle duplicates the utility 'dark:text-neutral-100'; update the onClick/button JSX (the element that calls setBillingInterval('annual') and reads billingInterval) to remove the duplicate 'dark:text-neutral-100' so the class list contains it only once.app/src/components/settings/panels/local-model/DeviceCapabilitySection.tsx (3)
222-252:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winClean up duplicated dark classes in disabled-tier card.
Lines 228-230, 233, 237, 242, 247, 249 contain duplicated dark mode classes.
🐛 Proposed fix for disabled tier button
<button type="button" onClick={() => void handleApply(DISABLED_TIER_ID)} disabled={applying !== null} className={`w-full text-left rounded-lg border p-3 transition-colors ${ isDisabledActive - ? 'border-primary-400 bg-primary-50 dark:bg-primary-500/10' - : 'border-stone-200 dark:border-neutral-800 bg-stone-50 dark:bg-neutral-800/60 hover:bg-stone-100 dark:hover:bg-neutral-800 dark:bg-neutral-800' + ? 'border-primary-400 bg-primary-50 dark:bg-primary-500/10' + : 'border-stone-200 dark:border-neutral-800 bg-stone-50 dark:bg-neutral-800/60 hover:bg-stone-100 dark:hover:bg-neutral-800' } ${applying !== null ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer'}`}> <div className="flex items-center justify-between"> <div className="flex items-center gap-2"> - <span className="text-sm font-semibold text-stone-900 dark:text-neutral-100"> + <span className="text-sm font-semibold text-stone-900 dark:text-neutral-100"> {t('settings.localModel.deviceCapability.disabled')} </span> {isDisabledActive && ( - <span className="px-1.5 py-0.5 text-[10px] font-medium rounded bg-primary-50 dark:bg-primary-500/10 text-primary-600 dark:text-primary-300 uppercase tracking-wide"> + <span className="px-1.5 py-0.5 text-[10px] font-medium rounded bg-primary-50 dark:bg-primary-500/10 text-primary-600 dark:text-primary-300 uppercase tracking-wide"> {t('settings.localModel.deviceCapability.active')} </span> )} {(presetsData.recommend_disabled || !ollamaAvailable) && !isDisabledActive && ( - <span className="px-1.5 py-0.5 text-[10px] font-medium rounded bg-amber-50 dark:bg-amber-500/10 text-amber-700 dark:text-amber-300 uppercase tracking-wide"> + <span className="px-1.5 py-0.5 text-[10px] font-medium rounded bg-amber-50 dark:bg-amber-500/10 text-amber-700 dark:text-amber-300 uppercase tracking-wide"> {t('settings.localModel.deviceCapability.recommended')} </span> )} </div> - <span className="text-xs text-stone-500 dark:text-neutral-400">0 GB</span> + <span className="text-xs text-stone-500 dark:text-neutral-400">0 GB</span> </div> - <div className="text-xs text-stone-500 dark:text-neutral-400 mt-1"> + <div className="text-xs text-stone-500 dark:text-neutral-400 mt-1"> {t('settings.localModel.deviceCapability.disabledDesc')} </div> </button>🤖 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 `@app/src/components/settings/panels/local-model/DeviceCapabilitySection.tsx` around lines 222 - 252, The disabled-tier button JSX contains duplicated dark-mode utility classes (e.g., repeated "dark:bg-neutral-800", "dark:hover:bg-neutral-800", "dark:border-neutral-800", etc.) in the className string for the button; to fix, consolidate the class list used by the button in DeviceCapabilitySection so each dark:... utility appears only once (clean up the className template used in the button that wraps the disabled tier), keeping the same conditional logic around isDisabledActive and applying, and ensure no duplicated dark classes remain; reference the button that calls handleApply(DISABLED_TIER_ID) and uses props/state names isDisabledActive, presetsData, ollamaAvailable, and applying when making the change.
136-217:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winReview and clean up duplicated dark classes in install-state banner.
Lines 140-143 contain duplicated dark mode border/background classes in the ternary conditionals. Lines 156, 168, 172, 192, 199, 210 also have duplications.
The install-state banner has multiple issues:
- Duplicated
dark:border-*anddark:bg-*in ternary branches- Lines 140-143 repeat the same dark classes across all three conditions
🐛 Proposed fix for lines 136-217
{presetsData && !ollamaAvailable && ( <div className={`rounded-lg border p-3 space-y-2 ${ installFailed - ? 'border-red-300 dark:border-red-500/40 bg-red-50 dark:bg-red-500/10' + ? 'border-red-300 dark:border-red-500/40 bg-red-50 dark:bg-red-500/10' : installInProgress - ? 'border-blue-300 dark:border-blue-500/40 bg-blue-50 dark:bg-blue-500/10' - : 'border-amber-300 dark:border-amber-500/40 bg-amber-50 dark:bg-amber-500/10' + ? 'border-blue-300 dark:border-blue-500/40 bg-blue-50 dark:bg-blue-500/10' + : 'border-amber-300 dark:border-amber-500/40 bg-amber-50 dark:bg-amber-500/10' }`}> {installInProgress ? ( <> <div className="flex items-center gap-2"> <div className="h-3 w-3 rounded-full border-2 border-blue-500 border-t-transparent animate-spin" /> <div className="text-sm font-semibold text-blue-900"> {t('settings.localModel.deviceCapability.installingOllama')} {installState === 'downloading' ? ` (${t('settings.localModel.deviceCapability.downloadingModels')})` : '…'} </div> </div> - <div className="text-xs text-blue-800 dark:text-blue-200"> + <div className="text-xs text-blue-800 dark:text-blue-200"> {installWarning ?? t('settings.localModel.deviceCapability.downloadingSetupDesc')} </div> - <div className="h-1.5 rounded-full bg-blue-200 dark:bg-blue-500/30 overflow-hidden"> + <div className="h-1.5 rounded-full bg-blue-200 dark:bg-blue-500/30 overflow-hidden"> <div className="h-full w-1/3 bg-blue-500 animate-pulse" /> </div> </> ) : installFailed ? ( <> <div className="text-sm font-semibold text-red-900"> {t('settings.localModel.deviceCapability.installFailed')} </div> - <div className="text-xs text-red-800 dark:text-red-200"> + <div className="text-xs text-red-800 dark:text-red-200"> {installWarning ?? t('settings.localModel.deviceCapability.installFailedDesc')} </div> {installError && ( - <pre className="max-h-40 overflow-auto rounded bg-red-100 dark:bg-red-500/20 border border-red-200 dark:border-red-500/30 p-2 text-[10px] text-red-700 dark:text-red-300 leading-tight whitespace-pre-wrap break-words"> + <pre className="max-h-40 overflow-auto rounded bg-red-100 dark:bg-red-500/20 border border-red-200 dark:border-red-500/30 p-2 text-[10px] text-red-700 dark:text-red-300 leading-tight whitespace-pre-wrap break-words"> {installError} </pre> )} <div className="flex items-center gap-2 pt-1"> {onTriggerOllamaInstall && ( <button type="button" onClick={onTriggerOllamaInstall} disabled={isTriggeringInstall} className="px-3 py-1.5 text-xs rounded-md bg-red-600 hover:bg-red-700 disabled:opacity-60 text-white font-medium"> {isTriggeringInstall ? t('settings.localModel.deviceCapability.retrying') : t('settings.localModel.deviceCapability.retryInstall')} </button> )} <a href="https://ollama.com" target="_blank" rel="noopener noreferrer" - className="px-3 py-1.5 text-xs rounded-md border border-red-300 dark:border-red-500/40 hover:border-red-400 text-red-800 dark:text-red-200"> + className="px-3 py-1.5 text-xs rounded-md border border-red-300 dark:border-red-500/40 hover:border-red-400 text-red-800 dark:text-red-200"> {t('settings.localModel.status.installManually')} </a> </div> </> ) : ( <> - <div className="text-xs text-amber-800 dark:text-amber-200"> + <div className="text-xs text-amber-800 dark:text-amber-200"> <span className="font-semibold text-amber-900"> {t('settings.localModel.deviceCapability.installFirst')} </span>{' '} {t('settings.localModel.deviceCapability.installFirstDesc')} </div> <div className="flex items-center gap-2"> <a href="https://ollama.com" target="_blank" rel="noopener noreferrer" - className="px-3 py-1.5 text-xs rounded-md border border-amber-300 dark:border-amber-500/40 hover:border-amber-400 text-amber-800 dark:text-amber-200"> + className="px-3 py-1.5 text-xs rounded-md border border-amber-300 dark:border-amber-500/40 hover:border-amber-400 text-amber-800 dark:text-amber-200"> {t('settings.localModel.status.ollamaDocs')} </a> </div> </> )} </div> )}🤖 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 `@app/src/components/settings/panels/local-model/DeviceCapabilitySection.tsx` around lines 136 - 217, The install-state banner in DeviceCapabilitySection (the JSX block that renders when presetsData && !ollamaAvailable) repeats identical dark mode border/bg classes across the ternary branches; remove the duplicated dark classes by pulling shared dark classes out of the ternary and only conditionally applying the light-mode/color-specific classes based on installFailed / installInProgress. Update the className expression on the outer <div> so it composes a common suffix like "dark:border-*/dark:bg-*" (or a variable with those classes) plus the conditional "border-red-300 bg-red-50" / "border-blue-300 bg-blue-50" / "border-amber-300 bg-amber-50" branches, and ensure other occurrences around the installFailed/installInProgress branches (where dark:border-*/dark:bg-* are repeated) are simplified the same way.
254-316:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winClean up duplicated dark classes in preset tier cards.
Lines 269-274, 280-282, 284, 289, 294, 299, 303-306 contain duplicated and incorrectly formatted dark mode classes.
🐛 Proposed fix for preset cards
className={`w-full text-left rounded-lg border p-3 transition-colors ${ isCurrent - ? 'border-primary-400 bg-primary-50 dark:bg-primary-500/10' - : 'border-stone-200 dark:border-neutral-800 bg-stone-50 dark:bg-neutral-800/60 hover:bg-stone-100 dark:hover:bg-neutral-800 dark:bg-neutral-800' + ? 'border-primary-400 bg-primary-50 dark:bg-primary-500/10' + : 'border-stone-200 dark:border-neutral-800 bg-stone-50 dark:bg-neutral-800/60 hover:bg-stone-100 dark:hover:bg-neutral-800' } ${ locked - ? 'opacity-50 cursor-not-allowed hover:bg-stone-50 dark:hover:bg-neutral-800/60 dark:bg-neutral-800/60' + ? 'opacity-50 cursor-not-allowed hover:bg-stone-50 dark:hover:bg-neutral-800/60' : applying !== null && !isApplying ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer' }`}> <div className="flex items-center justify-between"> <div className="flex items-center gap-2"> - <span className="text-sm font-semibold text-stone-900 dark:text-neutral-100"> - {preset.label} - </span> + <span className="text-sm font-semibold text-stone-900 dark:text-neutral-100"> + {preset.label} + </span> {isCurrent && ( - <span className="px-1.5 py-0.5 text-[10px] font-medium rounded bg-primary-50 dark:bg-primary-500/10 text-primary-600 dark:text-primary-300 uppercase tracking-wide"> + <span className="px-1.5 py-0.5 text-[10px] font-medium rounded bg-primary-50 dark:bg-primary-500/10 text-primary-600 dark:text-primary-300 uppercase tracking-wide"> {t('settings.localModel.deviceCapability.active')} </span> )} {isApplying && ( - <span className="px-1.5 py-0.5 text-[10px] font-medium rounded bg-stone-100 dark:bg-neutral-800 text-stone-500 dark:text-neutral-400 uppercase tracking-wide"> + <span className="px-1.5 py-0.5 text-[10px] font-medium rounded bg-stone-100 dark:bg-neutral-800 text-stone-500 dark:text-neutral-400 uppercase tracking-wide"> {t('settings.localModel.deviceCapability.applying')} </span> )} {locked && ( - <span className="px-1.5 py-0.5 text-[10px] font-medium rounded bg-amber-50 dark:bg-amber-500/10 text-amber-700 dark:text-amber-300 uppercase tracking-wide"> + <span className="px-1.5 py-0.5 text-[10px] font-medium rounded bg-amber-50 dark:bg-amber-500/10 text-amber-700 dark:text-amber-300 uppercase tracking-wide"> {t('settings.localModel.deviceCapability.needsOllama')} </span> )} </div> - <span className="text-xs text-stone-500 dark:text-neutral-400"> + <span className="text-xs text-stone-500 dark:text-neutral-400"> ~{Number(preset.approx_download_gb).toFixed(1)} GB </span> </div> - <div className="text-xs text-stone-400 dark:text-neutral-500 mt-1"> - {preset.description} - </div> - <div className="text-[10px] text-stone-500 dark:text-neutral-400 mt-1"> + <div className="text-xs text-stone-400 dark:text-neutral-500 mt-1"> + {preset.description} + </div> + <div className="text-[10px] text-stone-500 dark:text-neutral-400 mt-1"> Chat: {preset.chat_model_id} · Vision:{' '}🤖 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 `@app/src/components/settings/panels/local-model/DeviceCapabilitySection.tsx` around lines 254 - 316, The preset card button render inside the DeviceCapabilitySection map is using duplicated and malformed dark-mode utility classes in the className strings (the button root class and the conditional locked/applying branches); update the className assignments used in the presetsData.presets.map (the button element keyed by preset.tier, plus the inner span badges) to remove duplicate repeated dark:* tokens and fix formatting so each dark: utility appears only once and consistently (e.g., consolidate repeated "dark:bg-neutral-800", "dark:hover:bg-neutral-800/60", "dark:text-*" and similar into a single, correctly spelled token per intention) so the conditional branches (isCurrent, locked, applying) only append the needed unique classes and not duplicates.app/src/components/settings/panels/ComposioTriagePanel.tsx (3)
142-160:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winFix duplicated dark mode classes in disabled-toolkits section.
Lines 143, 147, and 158 contain duplicated dark mode classes.
🐛 Proposed fix
<div className={`space-y-2 ${triageDisabled ? 'opacity-40 pointer-events-none' : ''}`}> - <label - className="block text-sm font-medium text-stone-800 dark:text-neutral-100 dark:text-neutral-100" - htmlFor="disabled-toolkits"> + <label + className="block text-sm font-medium text-stone-800 dark:text-neutral-100" + htmlFor="disabled-toolkits"> {t('composio.disableSpecificIntegrations')} </label> - <p className="text-xs text-stone-500 dark:text-neutral-400 dark:text-neutral-400"> + <p className="text-xs text-stone-500 dark:text-neutral-400"> Comma-separated integration slugs, e.g. <span className="font-mono">gmail, slack</span>. Case-insensitive. </p> <input id="disabled-toolkits" type="text" value={disabledToolkits} onChange={e => setDisabledToolkits(e.target.value)} placeholder="gmail, slack, ..." disabled={triageDisabled} - className="w-full rounded-xl border border-stone-200 dark:border-neutral-800 dark:border-neutral-800 bg-white dark:bg-neutral-900 dark:bg-neutral-900 px-3 py-2 text-sm text-stone-900 dark:text-neutral-100 dark:text-neutral-100 placeholder-stone-400 dark:placeholder-neutral-500 focus:border-primary-400 focus:outline-none focus:ring-1 focus:ring-primary-400 disabled:cursor-not-allowed" + className="w-full rounded-xl border border-stone-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 px-3 py-2 text-sm text-stone-900 dark:text-neutral-100 placeholder-stone-400 dark:placeholder-neutral-500 focus:border-primary-400 focus:outline-none focus:ring-1 focus:ring-primary-400 disabled:cursor-not-allowed" /> </div>🤖 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 `@app/src/components/settings/panels/ComposioTriagePanel.tsx` around lines 142 - 160, The className strings for the input and related labels in the Disabled Toolkits block (elements with id "disabled-toolkits" and the surrounding label/paragraph) contain repeated dark mode classes ("dark:text-neutral-100", "dark:border-neutral-800", "dark:bg-neutral-900", etc.); remove the duplicate entries so each dark:… and normal class appears only once in the className for the label, paragraph and input. Locate the JSX around the "disabled-toolkits" input and update the className props for the label, paragraph and input (references: disabledToolkits, setDisabledToolkits, triageDisabled) to deduplicate identical dark: and non-dark classes while preserving the same visual styles and spacing.
110-139:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winFix multiple duplicated and invalid dark mode classes.
Line 110 contains critical syntax errors:
dark:bg-neutral-800/60/60/60is invalid Tailwind (double opacity stacking)dark:border-neutral-800 dark:border-neutral-800is duplicated- Lines 119, 122-124, 129-131, 133 also have duplicated classes
🐛 Proposed fix for lines 110-139
- <div className="rounded-2xl border border-stone-200 dark:border-neutral-800 dark:border-neutral-800 bg-stone-50 dark:bg-neutral-800/60 dark:bg-neutral-800/60/60 p-4 space-y-1"> + <div className="rounded-2xl border border-stone-200 dark:border-neutral-800 bg-stone-50 dark:bg-neutral-800/60 p-4 space-y-1"> <button type="button" role="switch" aria-checked={triageDisabled} aria-label="Disable AI triage for all triggers" onClick={() => setTriageDisabled(v => !v)} className="w-full flex items-center justify-between"> <div className="text-left"> - <span className="text-sm font-medium text-stone-900 dark:text-neutral-100 dark:text-neutral-100"> + <span className="text-sm font-medium text-stone-900 dark:text-neutral-100"> {t('composio.disableAllTriage')} </span> - <p className="text-xs text-stone-500 dark:text-neutral-400 dark:text-neutral-400 mt-0.5"> - {t('composio.triggersStillRecorded')} - </p> + <p className="text-xs text-stone-500 dark:text-neutral-400 mt-0.5"> + {t('composio.triggersStillRecorded')} + </p> </div> <div className={`ml-3 flex-shrink-0 w-9 h-5 rounded-full transition-colors relative ${ - triageDisabled - ? 'bg-coral-400' - : 'bg-stone-200 dark:bg-neutral-800 dark:bg-neutral-800' + triageDisabled ? 'bg-coral-400' : 'bg-stone-200 dark:bg-neutral-800' }`}> <div - className={`absolute top-0.5 w-4 h-4 rounded-full bg-white dark:bg-neutral-900 dark:bg-neutral-900 shadow transition-transform ${ + className={`absolute top-0.5 w-4 h-4 rounded-full bg-white dark:bg-neutral-900 shadow transition-transform ${ triageDisabled ? 'translate-x-4' : 'translate-x-0.5' }`} />🤖 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 `@app/src/components/settings/panels/ComposioTriagePanel.tsx` around lines 110 - 139, In ComposioTriagePanel.tsx the container and toggle elements contain duplicated Tailwind classes and an invalid opacity token (`dark:bg-neutral-800/60/60/60`), causing syntax errors; update the className strings used in the outer container (the div with rounded-2xl) and the inner toggle pieces (the button and the two child divs that reference triageDisabled) to remove duplicate entries (e.g., only one dark:border-neutral-800 and one dark:text-neutral-100) and replace the invalid stacked opacity with a single opacity suffix (e.g., dark:bg-neutral-800/60 or dark:bg-neutral-800 depending on desired opacity); keep the conditional class logic for triageDisabled as-is but ensure each class token appears only once across the className values for the container, the switch track, and the switch knob.
76-92:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winFix duplicated dark mode classes and invalid opacity stacking throughout the file.
Multiple lines contain duplicated
dark:*classes (lines 86, 104, 110, 119, 122, 130, 133, 143, 147, 158), and line 110 contains invalid opacity stacking (dark:bg-neutral-800/60/60). These should be deduplicated and corrected.🤖 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 `@app/src/components/settings/panels/ComposioTriagePanel.tsx` around lines 76 - 92, Several Tailwind classes are duplicated and one has invalid opacity stacking; locate the JSX blocks around the Loading return and other panel sections (e.g., the SettingsHeader usage and surrounding divs in ComposioTriagePanel) and remove duplicate dark:* classes (for example duplicate dark:text-neutral-400 entries) so each dark: utility appears only once, and correct the invalid opacity stacking such as dark:bg-neutral-800/60/60 to a single valid opacity form like dark:bg-neutral-800/60 (or dark:bg-neutral-800/75) to restore valid Tailwind syntax across className attributes in the component.app/src/components/settings/panels/ConnectionsPanel.tsx (2)
218-239:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winFix duplicated dark mode classes.
The privacy/security callout section has duplicates on lines 218, 221, 231, 234:
- Line 218:
dark:bg-neutral-800/60 dark:bg-neutral-800/60,dark:border-neutral-800 dark:border-neutral-800- Line 221:
dark:text-neutral-500 dark:text-neutral-500- Line 231:
dark:text-neutral-100 dark:text-neutral-100- Line 234:
dark:text-neutral-400 dark:text-neutral-400🔧 Proposed fix
- <div className="p-4 bg-stone-50 dark:bg-neutral-800/60 dark:bg-neutral-800/60 rounded-xl border border-stone-200 dark:border-neutral-800 dark:border-neutral-800"> + <div className="p-4 bg-stone-50 dark:bg-neutral-800/60 rounded-xl border border-stone-200 dark:border-neutral-800"> <div className="flex items-start space-x-3"> <svg - className="w-5 h-5 text-stone-400 dark:text-neutral-500 dark:text-neutral-500 mt-0.5 flex-shrink-0" + className="w-5 h-5 text-stone-400 dark:text-neutral-500 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20"> <path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" /> </svg> <div> - <p className="font-medium text-stone-900 dark:text-neutral-100 dark:text-neutral-100 text-sm"> + <p className="font-medium text-stone-900 dark:text-neutral-100 text-sm"> {t('connections.privacySecurity')} </p> - <p className="text-xs text-stone-500 dark:text-neutral-400 dark:text-neutral-400 mt-1 leading-relaxed"> + <p className="text-xs text-stone-500 dark:text-neutral-400 mt-1 leading-relaxed"> {t('connections.privacySecurityDesc')} </p> </div>🤖 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 `@app/src/components/settings/panels/ConnectionsPanel.tsx` around lines 218 - 239, The JSX in ConnectionsPanel.tsx contains duplicated dark-mode utility classes in the privacy/security callout: remove the duplicate entries so each dark: class appears only once; specifically dedupe the outer div's className by leaving a single "dark:bg-neutral-800/60" and a single "dark:border-neutral-800", dedupe the svg's className to a single "dark:text-neutral-500", dedupe the first p's className to a single "dark:text-neutral-100", and dedupe the second p's className to a single "dark:text-neutral-400" so no dark: classes are repeated.
190-215:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winFix duplicated dark mode classes.
The wallet-configured section has duplicates on lines 190, 192, 195, 203, 205, 208:
- Line 190:
dark:border-neutral-800 dark:border-neutral-800,dark:bg-neutral-900 dark:bg-neutral-900- Line 192:
dark:text-neutral-100 dark:text-neutral-100- Line 195:
dark:text-neutral-400 dark:text-neutral-400- Line 203:
dark:border-neutral-800 dark:border-neutral-800,dark:bg-neutral-800/60 dark:bg-neutral-800/60- Line 205:
dark:text-neutral-400 dark:text-neutral-400- Line 208:
dark:text-neutral-200 dark:text-neutral-200🔧 Proposed fix
{walletConfigured && walletStatus ? ( - <div className="rounded-2xl border border-stone-200 dark:border-neutral-800 dark:border-neutral-800 bg-white dark:bg-neutral-900 dark:bg-neutral-900 p-4 space-y-3"> + <div className="rounded-2xl border border-stone-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 p-4 space-y-3"> <div> - <p className="font-medium text-stone-900 dark:text-neutral-100 dark:text-neutral-100 text-sm"> + <p className="font-medium text-stone-900 dark:text-neutral-100 text-sm"> {t('connections.walletIdentities')} </p> - <p className="text-xs text-stone-500 dark:text-neutral-400 dark:text-neutral-400 mt-1"> + <p className="text-xs text-stone-500 dark:text-neutral-400 mt-1"> {t('connections.walletDerived')} </p> </div> <div className="grid gap-2"> {walletStatus.accounts.map(account => ( <div key={account.chain} - className="rounded-xl border border-stone-200 dark:border-neutral-800 dark:border-neutral-800 bg-stone-50 dark:bg-neutral-800/60 dark:bg-neutral-800/60 px-3 py-2"> + className="rounded-xl border border-stone-200 dark:border-neutral-800 bg-stone-50 dark:bg-neutral-800/60 px-3 py-2"> <div className="flex items-center justify-between gap-3"> - <span className="text-xs font-semibold uppercase tracking-wide text-stone-500 dark:text-neutral-400 dark:text-neutral-400"> + <span className="text-xs font-semibold uppercase tracking-wide text-stone-500 dark:text-neutral-400"> {account.chain} </span> - <span className="text-xs font-mono text-stone-700 dark:text-neutral-200 dark:text-neutral-200 truncate"> + <span className="text-xs font-mono text-stone-700 dark:text-neutral-200 truncate"> {account.address} </span> </div>🤖 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 `@app/src/components/settings/panels/ConnectionsPanel.tsx` around lines 190 - 215, The JSX in ConnectionsPanel (inside the walletStatus.accounts mapping and its parent container) contains duplicated dark-mode Tailwind classes; remove the repeated duplicates so each dark: class appears only once (e.g. change "dark:border-neutral-800 dark:border-neutral-800" to a single "dark:border-neutral-800", "dark:bg-neutral-900 dark:bg-neutral-900" to "dark:bg-neutral-900", "dark:text-neutral-100 dark:text-neutral-100" to one instance, "dark:text-neutral-400 dark:text-neutral-400" to one instance, and "dark:bg-neutral-800/60 dark:bg-neutral-800/60" and "dark:text-neutral-200 dark:text-neutral-200" likewise) in the outer container and the per-account div within ConnectionsPanel.tsx where walletStatus.accounts is mapped.app/src/components/settings/panels/AIPanel.tsx (1)
476-520:⚠️ Potential issue | 🟠 Major | ⚡ Quick winResolve duplicated and conflicting dark mode class tokens.
Many elements have duplicated dark mode classes, and some have conflicting values that will cause unpredictable rendering. For example:
- Line 476:
dark:border-neutral-800 dark:border-neutral-800anddark:bg-neutral-900 dark:bg-neutral-900(duplicated)- Line 507:
dark:bg-neutral-900 dark:bg-neutral-800(conflicting—Tailwind will apply the last one),dark:border-neutral-700 dark:border-neutral-700,dark:text-neutral-100 dark:text-neutral-100,dark:placeholder-neutral-500 dark:placeholder-neutral-500(duplicated)- Line 519:
dark:hover:bg-neutral-800/60 dark:bg-neutral-800/60 dark:hover:bg-neutral-800/60(tripled!), plus duplicated border/bg/text classesThis pattern repeats in lines 478-481, 659-681, 693-722, 940-1392, 1447-1465, 1558-1665, 1688-1715, and 2149-2268. A systematic pass to deduplicate and resolve conflicts is needed.
🧹 Sample fix for lines 476, 507, 519
- className="fixed inset-0 z-50 flex items-center justify-center bg-black/30 p-4"> - <div className="w-full max-w-md rounded-2xl border border-stone-200 dark:border-neutral-800 dark:border-neutral-800 bg-white dark:bg-neutral-900 dark:bg-neutral-900 p-6 shadow-soft"> + <div className="w-full max-w-md rounded-2xl border border-stone-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 p-6 shadow-soft"> <div className="mb-4"> - <h3 className="text-base font-semibold text-stone-900 dark:text-neutral-100 dark:text-neutral-100">{`${t('settings.ai.connectProvider')} ${label}`}</h3> - <p className="mt-0.5 text-xs text-stone-500 dark:text-neutral-400 dark:text-neutral-400"> + <h3 className="text-base font-semibold text-stone-900 dark:text-neutral-100">{`${t('settings.ai.connectProvider')} ${label}`}</h3> + <p className="mt-0.5 text-xs text-stone-500 dark:text-neutral-400"> {t('settings.ai.apiKeyStoredEncrypted')} </p> ... <input ... - className="rounded-lg border border-stone-300 dark:border-neutral-700 dark:border-neutral-700 bg-white dark:bg-neutral-900 dark:bg-neutral-800 px-3 py-2 text-sm text-stone-900 dark:text-neutral-100 dark:text-neutral-100 placeholder-stone-400 dark:placeholder-neutral-500 dark:placeholder-neutral-500 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500 disabled:opacity-60" + className="rounded-lg border border-stone-300 dark:border-neutral-700 bg-white dark:bg-neutral-800 px-3 py-2 text-sm text-stone-900 dark:text-neutral-100 placeholder-stone-400 dark:placeholder-neutral-500 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500 disabled:opacity-60" /> ... <button ... - className="rounded-lg border border-stone-200 dark:border-neutral-800 dark:border-neutral-800 bg-white dark:bg-neutral-900 dark:bg-neutral-900 px-4 py-2 text-sm font-medium text-stone-700 dark:text-neutral-200 dark:text-neutral-200 hover:bg-stone-50 dark:hover:bg-neutral-800/60 dark:bg-neutral-800/60 dark:hover:bg-neutral-800/60 disabled:opacity-50"> + className="rounded-lg border border-stone-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 px-4 py-2 text-sm font-medium text-stone-700 dark:text-neutral-200 hover:bg-stone-50 dark:hover:bg-neutral-800/60 disabled:opacity-50"> {t('common.cancel')} </button>Apply similar cleanup throughout the file to remove duplicates and resolve conflicts.
Also applies to: 507-507, 519-519
🤖 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 `@app/src/components/settings/panels/AIPanel.tsx` around lines 476 - 520, In the AIPanel component (look for the AIPanel React function and the input with id "provider-key-input" and the surrounding buttons), remove duplicated Tailwind dark: class tokens and resolve conflicting dark: values in the className strings: for each element dedupe repeated tokens (e.g., remove duplicate dark:border-neutral-800 and duplicate dark:bg-neutral-900) and pick the single intended dark variant where conflicts exist (e.g., choose either dark:bg-neutral-900 OR dark:bg-neutral-800 and apply it once), then run a quick grep/IDE search for "dark:" usages in AIPanel to apply the same cleanup pattern to the other duplicated lines mentioned so the final className strings contain no repeated tokens and no contradictory dark: classes.
🟠 Major comments (10)
app/src/providers/ThemeProvider.tsx-17-33 (1)
17-33:⚠️ Potential issue | 🟠 Major | ⚡ Quick winGuard
documentaccess before callingapply()in the effect.Line 29 invokes
apply()before the environment guard, butapply()readsdocument.documentElement(Line 19). In non-browser rendering contexts this can crash at runtime.Suggested fix
useEffect(() => { + if (typeof document === 'undefined') return; + const apply = () => { const root = document.documentElement; const resolved = resolveTheme(mode); @@ - apply(); + apply(); if (mode !== 'system') return; if (typeof window === 'undefined' || !window.matchMedia) return;🤖 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 `@app/src/providers/ThemeProvider.tsx` around lines 17 - 33, The effect calls apply() which reads document.documentElement (via resolveTheme/mode) before any environment guard, causing crashes in non-browser contexts; update the useEffect in ThemeProvider so you first check that document (and document.documentElement) exists (e.g., typeof document !== 'undefined' and document.documentElement) before calling apply(), or move the apply() call to after the existing window/matchMedia guard; ensure the referenced apply(), resolveTheme and useEffect logic still sets classList and colorScheme when running in the browser.app/src/components/settings/panels/WebhooksDebugPanel.tsx-340-340 (1)
340-340:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDark-mode payload text becomes low-contrast and hard to read.
Line 340 sets a light dark-mode background (
dark:bg-neutral-50) but keeps light text (text-stone-100), making payload content nearly unreadable in dark theme.Suggested fix
- <pre className="mt-1 max-h-40 overflow-auto rounded-lg border border-stone-200 dark:border-neutral-800 dark:border-neutral-800 bg-stone-950 dark:bg-neutral-50 p-2 text-[11px] text-stone-100 whitespace-pre-wrap break-words"> + <pre className="mt-1 max-h-40 overflow-auto rounded-lg border border-stone-200 dark:border-neutral-800 bg-stone-950 dark:bg-neutral-900 p-2 text-[11px] text-stone-100 dark:text-neutral-100 whitespace-pre-wrap break-words">🤖 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 `@app/src/components/settings/panels/WebhooksDebugPanel.tsx` at line 340, The <pre> in WebhooksDebugPanel.tsx uses a light text class (text-stone-100) while the dark-mode background is set to a light color (dark:bg-neutral-50), causing low contrast; update the className on that <pre> (in WebhooksDebugPanel.tsx) to include a dark-mode text override (e.g., add dark:text-neutral-900 or dark:text-stone-900 alongside the existing text-stone-100) so text becomes dark when dark:bg-neutral-50 is active.app/src/components/settings/panels/billing/SubscriptionPlans.tsx-209-211 (1)
209-211:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFix duplicated dark mode class in "current" badge.
Line 209 contains
dark:bg-neutral-50when the base class isbg-stone-950. This creates a white badge in dark mode on a near-black background in light mode, which seems inverted. Verify the intended styling.🐛 Proposed fix (if inversion is unintended)
{isCurrent && !plan.recommended && ( - <span className="rounded-full bg-stone-950 dark:bg-neutral-50 px-2.5 py-1 text-[10px] font-bold uppercase tracking-[0.24em] text-white"> + <span className="rounded-full bg-stone-950 dark:bg-neutral-50 px-2.5 py-1 text-[10px] font-bold uppercase tracking-[0.24em] text-white dark:text-stone-950"> {t('settings.billing.subscription.current')} </span>Note: The badge background swaps from dark (light mode) to light (dark mode), but the text stays white in both modes. Add
dark:text-stone-950to ensure contrast in dark mode.🤖 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 `@app/src/components/settings/panels/billing/SubscriptionPlans.tsx` around lines 209 - 211, The "current" badge in SubscriptionPlans.tsx uses bg-stone-950 with dark:bg-neutral-50 which inverts its colors in dark mode; remove the dark:bg-neutral-50 token from the span's class list and add dark:text-stone-950 so the badge keeps its dark background (bg-stone-950) and the text flips to a dark color in dark mode for proper contrast—update the span that renders {t('settings.billing.subscription.current')} accordingly.app/src/pages/conversations/components/AgentMessageBubble.tsx-147-147 (1)
147-147:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFix invalid dark row-striping classes in table rows.
Line 147 has an invalid Tailwind token (
dark:bg-neutral-800/60/70) with a malformed opacity value. Additionally,dark:bg-neutral-900applies to every row regardless of odd/even, breaking dark mode striping entirely.Proposed fix
- className="odd:bg-white dark:bg-neutral-900 even:bg-stone-50 dark:bg-neutral-800/60/70"> + className="odd:bg-white even:bg-stone-50 dark:odd:bg-neutral-900 dark:even:bg-neutral-800/60">🤖 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 `@app/src/pages/conversations/components/AgentMessageBubble.tsx` at line 147, The row striping classes in AgentMessageBubble.tsx are wrong: remove the unconditional dark:bg-neutral-900 and fix the malformed token dark:bg-neutral-800/60/70; change the className so dark mode striping is applied per odd/even (e.g. use odd:bg-white and dark:odd:bg-neutral-900, even:bg-stone-50 and dark:even:bg-neutral-800/70) so each row uses the correct light and dark variants and the opacity token uses a single /<number> suffix.app/src/components/settings/panels/MemoryDebugPanel.tsx-247-247 (1)
247-247:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFix incorrect dark background color on
<pre>element.The
<pre>hastext-stone-100(light text) butdark:bg-neutral-50(light background in dark mode), resulting in unreadable light-on-light text. This should bedark:bg-neutral-950(or another dark shade).🐛 Suggested fix
- <pre className="mt-1 max-h-32 overflow-auto rounded-lg border border-stone-200 dark:border-neutral-800 dark:border-neutral-800 bg-stone-950 dark:bg-neutral-50 p-2 text-[11px] text-stone-100 whitespace-pre-wrap break-words"> + <pre className="mt-1 max-h-32 overflow-auto rounded-lg border border-stone-200 dark:border-neutral-800 bg-stone-950 dark:bg-neutral-950 p-2 text-[11px] text-stone-100 dark:text-neutral-100 whitespace-pre-wrap break-words">🤖 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 `@app/src/components/settings/panels/MemoryDebugPanel.tsx` at line 247, In MemoryDebugPanel.tsx the <pre> element's Tailwind classes set text-stone-100 (light text) but use dark:bg-neutral-50 (light background in dark mode), making text unreadable; update the class list on that <pre> (inside the MemoryDebugPanel component) to use a dark background such as dark:bg-neutral-950 (or another sufficiently dark shade) so dark mode has dark background + light text and remains readable.app/src/components/settings/SettingsHome.tsx-304-304 (1)
304-304:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFix conflicting dark text color classes.
Line 304 contains
dark:text-neutral-200 dark:text-neutral-300, which are conflicting. Tailwind will apply whichever appears last, but this makes intent unclear.🐛 Suggested fix
- <div className="text-stone-700 dark:text-neutral-200 dark:text-neutral-300 text-sm leading-relaxed"> + <div className="text-stone-700 dark:text-neutral-300 text-sm leading-relaxed">🤖 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 `@app/src/components/settings/SettingsHome.tsx` at line 304, In SettingsHome update the stray/conflicting Tailwind classes in the className that currently contains "dark:text-neutral-200 dark:text-neutral-300" so only a single intended dark-mode text color remains (e.g., keep "dark:text-neutral-300" or "dark:text-neutral-200"); locate the className string on the div in the SettingsHome component and remove the unwanted duplicate class to make the intent explicit.app/src/components/settings/SettingsHome.tsx-329-329 (1)
329-329:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFix duplicated and conflicting Tailwind classes.
The Cancel button className contains:
dark:border-neutral-800 dark:border-neutral-700(conflicting border colors)dark:text-neutral-200 dark:text-neutral-200(duplicated)dark:hover:bg-neutral-800 dark:bg-neutral-800 dark:hover:bg-neutral-800(extensive duplication)🐛 Suggested fix
- className="flex-1 px-4 py-2 rounded-lg border border-stone-200 dark:border-neutral-800 dark:border-neutral-700 text-stone-700 dark:text-neutral-200 dark:text-neutral-200 hover:bg-stone-100 dark:hover:bg-neutral-800 dark:bg-neutral-800 dark:hover:bg-neutral-800 transition-colors disabled:opacity-50"> + className="flex-1 px-4 py-2 rounded-lg border border-stone-200 dark:border-neutral-700 text-stone-700 dark:text-neutral-200 hover:bg-stone-100 dark:hover:bg-neutral-800 transition-colors disabled:opacity-50">🤖 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 `@app/src/components/settings/SettingsHome.tsx` at line 329, The Cancel button in SettingsHome.tsx has duplicated and conflicting Tailwind classes in its className; update the className on the Cancel button element (the JSX that contains the long "flex-1 px-4 py-2 rounded-lg border ..." string) to remove duplicates and conflicts: keep only one dark:border (choose either dark:border-neutral-800 or dark:border-neutral-700), remove the duplicate dark:text-neutral-200, and consolidate bg/hover classes so there is at most one dark:bg-* and one dark:hover:bg-* (for example keep dark:bg-neutral-800 and dark:hover:bg-neutral-800 or choose the intended hover/bg pair). Ensure no repeated tokens remain.app/src/components/webhooks/ComposeioTriggerHistory.tsx-46-50 (1)
46-50:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMissing dark-mode text color on the non-empty history title.
At Line 46, the heading uses
text-stone-900without a dark override, so the title can be too low-contrast in dark mode while the count at Line 48 is already dark-aware.Suggested fix
- <h3 className="text-lg font-semibold text-stone-900"> + <h3 className="text-lg font-semibold text-stone-900 dark:text-neutral-100">🤖 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 `@app/src/components/webhooks/ComposeioTriggerHistory.tsx` around lines 46 - 50, The heading in ComposeioTriggerHistory (the <h3> element rendering t('webhooks.composioHistory.title')) uses text-stone-900 without a dark-mode override; update its className to include a dark: color (e.g., add dark:text-neutral-50 or dark:text-white) so the title has sufficient contrast in dark mode, matching the pattern used on the count span (which uses dark:text-neutral-500); modify the className on the h3 accordingly.app/src/components/settings/panels/TeamMembersPanel.tsx-371-372 (1)
371-372:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDark-mode cancel button text contrast is too low.
At Line 371,
dark:text-neutral-600on a dark background (bg-stone-700/50) is hard to read. Use a lighter dark text token.Suggested fix
- className="flex-1 px-4 py-2 text-sm font-medium rounded-xl bg-stone-700/50 hover:bg-stone-700 text-stone-300 dark:text-neutral-600 dark:text-neutral-600 transition-colors disabled:opacity-50"> + className="flex-1 px-4 py-2 text-sm font-medium rounded-xl bg-stone-700/50 hover:bg-stone-700 text-stone-300 dark:text-neutral-200 transition-colors disabled:opacity-50">🤖 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 `@app/src/components/settings/panels/TeamMembersPanel.tsx` around lines 371 - 372, In TeamMembersPanel update the Cancel button's dark-mode text token so it has higher contrast: locate the Cancel button JSX in the TeamMembersPanel component (the element with className containing "bg-stone-700/50" and "dark:text-neutral-600") and replace "dark:text-neutral-600" with a lighter dark-mode token such as "dark:text-neutral-300" (or "dark:text-stone-200") to improve readability on the dark background while keeping other classes unchanged.app/src/components/settings/panels/VoicePanel.tsx-729-729 (1)
729-729:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove conflicting
dark:text-neutral-500that shadows the readabledark:text-neutral-100on form inputs.The trailing
dark:text-neutral-500overrides the earlierdark:text-neutral-100on the same elements (lines 729, 798, 880, 952), making input text too dim in dark mode. Delete thedark:text-neutral-500utility and keep onlydark:text-neutral-100for readable text in dark mode. Thedark:placeholder:text-neutral-500anddark:text-neutral-500duplicates should also be consolidated.Suggested fix pattern
- ... text-stone-900 dark:text-neutral-100 dark:text-neutral-100 placeholder:text-stone-400 dark:placeholder:text-neutral-500 dark:text-neutral-500 ... + ... text-stone-900 dark:text-neutral-100 placeholder:text-stone-400 dark:placeholder:text-neutral-500 ...🤖 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 `@app/src/components/settings/panels/VoicePanel.tsx` at line 729, In VoicePanel component update the form input className strings to remove the conflicting dark:text-neutral-500 that overrides dark:text-neutral-100; locate the JSX elements with the long className (the ones containing dark:text-neutral-100, dark:placeholder:text-neutral-500 and duplicate dark:text-neutral-500) and delete the trailing dark:text-neutral-500 occurrences so only dark:text-neutral-100 remains for input text while keeping dark:placeholder:text-neutral-500 for placeholders.
…e.{hide,show} keys
- Reverts the showSidebar default from false → true so existing Conversations.render.test.tsx assertions keep passing. Users can still hide it via the toggle in the chat header; persisting that preference is left as a follow-up.
- Adds zh-CN translations for accounts.respondQueue.hide / .show so the I18nProvider 'Simplified Chinese locale complete' test stays green.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@app/src/pages/Conversations.tsx`:
- Line 1231: Several button className strings in Conversations.tsx contain
duplicate/conflicting dark: utilities (e.g., both dark:text-neutral-400 and
dark:text-neutral-200, or dark:hover:bg-neutral-800 and
dark:hover:bg-neutral-800/60) so the wrong dark-mode styles win; update each
affected className on the button elements in the Conversations component to
remove duplicates and use the consistent pattern: keep dark:bg-neutral-800 and
dark:hover:bg-neutral-800/60 for background, and use dark:text-neutral-400 for
the base text with dark:hover:text-neutral-200 for hover text; search for
className attributes on the buttons that include
dark:text-neutral-400|dark:text-neutral-200 or
dark:hover:bg-neutral-800|dark:hover:bg-neutral-800/60 and replace them with the
corrected set (dark:bg-neutral-800 dark:hover:bg-neutral-800/60
dark:text-neutral-400 dark:hover:text-neutral-200) while preserving the original
light-mode classes (text-stone-500 hover:text-stone-700 hover:bg-stone-100).
- Around line 1715-1717: The three animated dot <span> elements use an invalid
Tailwind class `dark:bg-neutral-800/600` and also redundantly include
`dark:bg-neutral-400`; update each span (the ones with animate-bounce and
[animation-delay:...]) to replace the too-light `bg-stone-50` with
`bg-stone-400`, remove the redundant `dark:bg-neutral-400`, and change the
invalid opacity suffix to a valid value such as `dark:bg-neutral-800/60` so the
final classes read like: w-1.5 h-1.5 rounded-full bg-stone-400
dark:bg-neutral-800/60 animate-bounce [animation-delay:...].
🪄 Autofix (Beta)
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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 88d1e397-18a4-457b-a733-e65e832a3c2d
📒 Files selected for processing (2)
app/src/lib/i18n/chunks/zh-CN-3.tsapp/src/pages/Conversations.tsx
…licate dark tokens, missing dark text on AIPanel chips, SkillResourceTree hover, CitationChips hover Sweeps the 17 review threads from CodeRabbit: - Collapse invalid `dark:X-N/M/L` triple-slash opacity to `dark:X-N/M` (CR #2, tinyhumansai#4, tinyhumansai#5, tinyhumansai#14) - Adjacent-duplicate `dark:X dark:X` dedupe across all .tsx (CR tinyhumansai#3, tinyhumansai#6, tinyhumansai#7, tinyhumansai#8 dups, tinyhumansai#11, tinyhumansai#12, tinyhumansai#13, tinyhumansai#16 dup) — uses a targeted perl pass that only collapses when the next token is byte-identical, never spans across different tokens (a prior Python tokenizer pass broke ternary `?` operators inside template-literal classNames) - Same-color same-property conflicts (`dark:bg-sage-500/10 dark:bg-sage-500/15`) — keep the first value (CR tinyhumansai#8, tinyhumansai#9, tinyhumansai#10) - AIPanel chip tones for openhuman/openai/anthropic/openrouter/lmstudio/ollama add `dark:text-X-100` so the names stay readable on dark tinted backgrounds (CR #0, #1) - SkillResourceTree: `hover:bg-white dark:bg-neutral-900` was applying the dark token unconditionally — rewrote to `dark:hover:bg-neutral-800/60`; also gave the selected branch a paired dark variant (CR tinyhumansai#14, tinyhumansai#15) - CitationChips: `hover:bg-stone-200 dark:bg-neutral-800` had the same hover/dark mis-pairing — now `dark:hover:bg-neutral-700` (CR tinyhumansai#16)
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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 `@app/src/components/settings/panels/TeamInvitesPanel.tsx`:
- Line 376: In the TeamInvitesPanel component locate the Cancel button JSX (the
element using the className string that currently contains "dark:bg-neutral-800"
twice) and remove the duplicate "dark:bg-neutral-800", then add a matching dark
hover variant (e.g. "dark:hover:bg-neutral-700" or an appropriate dark hover
color) alongside the existing "hover:bg-stone-200" so the className contains a
single dark:bg-neutral-800 and a dark:hover:bg-* to provide the dark-mode hover
state.
- Around line 253-254: In TeamInvitesPanel.tsx inside the Copy button's
className conditional, remove the duplicated dark-mode utility classes so each
dark:... and hover:... class appears only once (e.g., keep a single
dark:text-neutral-100, single dark:bg-neutral-800, and single
dark:hover:bg-neutral-800) and ensure the ternary still returns the intended
enabled vs disabled strings; update the className expression in the Copy button
element so it uses a single compact class list instead of repeating identical
dark:* tokens.
In `@app/src/pages/conversations/components/AgentMessageBubble.tsx`:
- Line 147: The alternating row background classes in AgentMessageBubble.tsx are
using plain dark: classes that apply to all rows; change the className on the
affected element (the row container string that currently contains "odd:bg-white
dark:bg-neutral-900 even:bg-stone-50 dark:bg-neutral-800/60") to scope dark-mode
to the correct parity by using dark:odd: and dark:even: modifiers (e.g., keep
odd:bg-white and even:bg-stone-50 but replace dark:bg-neutral-900 with
dark:odd:bg-neutral-900 and replace dark:bg-neutral-800/60 with
dark:even:bg-neutral-800/60) so dark backgrounds only apply to the intended
odd/even rows.
- Line 129: AgentMessageBubble's table wrapper currently sets only bg-white/90
which is unreadable in dark mode; update the className in the JSX (the element
that currently contains `className={`w-full max-w-full overflow-hidden border
... bg-white/90 shadow-sm ${bubbleChrome}`}`) to add an appropriate dark mode
background utility (for example `dark:bg-neutral-900/60` or similar) so the
wrapper has a contrasting background in dark theme while preserving the existing
light-mode styling.
🪄 Autofix (Beta)
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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5e983a7c-287e-4d6e-9dd4-8387e9fbadc1
📒 Files selected for processing (50)
app/src/components/accounts/AddAccountModal.tsxapp/src/components/accounts/RespondQueuePanel.tsxapp/src/components/accounts/WebviewHost.tsxapp/src/components/channels/ChannelSelector.tsxapp/src/components/channels/DiscordConfig.tsxapp/src/components/channels/TelegramConfig.tsxapp/src/components/composio/TriggerToggles.tsxapp/src/components/rewards/ReferralRewardsSection.tsxapp/src/components/rewards/RewardsCommunityTab.tsxapp/src/components/rewards/RewardsCouponSection.tsxapp/src/components/settings/SettingsHome.tsxapp/src/components/settings/SettingsSectionPage.tsxapp/src/components/settings/components/SettingsHeader.tsxapp/src/components/settings/components/SettingsMenuItem.tsxapp/src/components/settings/panels/AIPanel.tsxapp/src/components/settings/panels/AboutPanel.tsxapp/src/components/settings/panels/AgentChatPanel.tsxapp/src/components/settings/panels/AppearancePanel.tsxapp/src/components/settings/panels/AutocompleteDebugPanel.tsxapp/src/components/settings/panels/AutocompletePanel.tsxapp/src/components/settings/panels/BillingPanel.tsxapp/src/components/settings/panels/ComposioPanel.tsxapp/src/components/settings/panels/ComposioTriagePanel.tsxapp/src/components/settings/panels/ConnectionsPanel.tsxapp/src/components/settings/panels/CronJobsPanel.tsxapp/src/components/settings/panels/DeveloperOptionsPanel.tsxapp/src/components/settings/panels/LocalModelDebugPanel.tsxapp/src/components/settings/panels/MascotPanel.tsxapp/src/components/settings/panels/MemoryDebugPanel.tsxapp/src/components/settings/panels/MessagingPanel.tsxapp/src/components/settings/panels/NotificationRoutingPanel.tsxapp/src/components/settings/panels/NotificationsPanel.tsxapp/src/components/settings/panels/PrivacyPanel.tsxapp/src/components/settings/panels/RecoveryPhrasePanel.tsxapp/src/components/settings/panels/ScreenAwarenessDebugPanel.tsxapp/src/components/settings/panels/ScreenIntelligencePanel.tsxapp/src/components/settings/panels/TeamInvitesPanel.tsxapp/src/components/settings/panels/TeamManagementPanel.tsxapp/src/components/settings/panels/TeamMembersPanel.tsxapp/src/components/settings/panels/TeamPanel.tsxapp/src/components/settings/panels/ToolsPanel.tsxapp/src/components/settings/panels/VoiceDebugPanel.tsxapp/src/components/settings/panels/VoicePanel.tsxapp/src/components/settings/panels/WebhooksDebugPanel.tsxapp/src/components/settings/panels/billing/PayAsYouGoCard.tsxapp/src/components/settings/panels/billing/SubscriptionPlans.tsxapp/src/components/skills/SkillResourceTree.tsxapp/src/pages/Conversations.tsxapp/src/pages/conversations/components/AgentMessageBubble.tsxapp/src/pages/conversations/components/CitationChips.tsx
✅ Files skipped from review due to trivial changes (29)
- app/src/components/channels/TelegramConfig.tsx
- app/src/pages/conversations/components/CitationChips.tsx
- app/src/components/accounts/AddAccountModal.tsx
- app/src/components/settings/panels/AutocompletePanel.tsx
- app/src/components/settings/panels/ToolsPanel.tsx
- app/src/components/settings/components/SettingsHeader.tsx
- app/src/components/settings/panels/CronJobsPanel.tsx
- app/src/components/settings/panels/ConnectionsPanel.tsx
- app/src/components/settings/panels/AutocompleteDebugPanel.tsx
- app/src/components/settings/panels/DeveloperOptionsPanel.tsx
- app/src/components/settings/panels/AgentChatPanel.tsx
- app/src/components/settings/panels/NotificationsPanel.tsx
- app/src/components/settings/panels/VoiceDebugPanel.tsx
- app/src/components/settings/panels/NotificationRoutingPanel.tsx
- app/src/components/accounts/WebviewHost.tsx
- app/src/components/settings/panels/VoicePanel.tsx
- app/src/components/composio/TriggerToggles.tsx
- app/src/components/settings/panels/ScreenAwarenessDebugPanel.tsx
- app/src/components/settings/panels/MessagingPanel.tsx
- app/src/components/rewards/ReferralRewardsSection.tsx
- app/src/components/settings/panels/AboutPanel.tsx
- app/src/components/channels/DiscordConfig.tsx
- app/src/components/settings/panels/billing/SubscriptionPlans.tsx
- app/src/components/settings/panels/ComposioPanel.tsx
- app/src/components/settings/panels/RecoveryPhrasePanel.tsx
- app/src/components/settings/panels/TeamMembersPanel.tsx
- app/src/components/settings/panels/TeamManagementPanel.tsx
- app/src/components/settings/panels/ScreenIntelligencePanel.tsx
- app/src/components/settings/panels/MemoryDebugPanel.tsx
🚧 Files skipped from review as they are similar to previous changes (18)
- app/src/components/settings/panels/LocalModelDebugPanel.tsx
- app/src/components/channels/ChannelSelector.tsx
- app/src/components/settings/SettingsHome.tsx
- app/src/components/accounts/RespondQueuePanel.tsx
- app/src/components/settings/panels/billing/PayAsYouGoCard.tsx
- app/src/components/rewards/RewardsCouponSection.tsx
- app/src/components/settings/SettingsSectionPage.tsx
- app/src/components/settings/panels/AppearancePanel.tsx
- app/src/components/settings/panels/ComposioTriagePanel.tsx
- app/src/components/settings/components/SettingsMenuItem.tsx
- app/src/components/settings/panels/TeamPanel.tsx
- app/src/pages/Conversations.tsx
- app/src/components/rewards/RewardsCommunityTab.tsx
- app/src/components/settings/panels/WebhooksDebugPanel.tsx
- app/src/components/settings/panels/PrivacyPanel.tsx
- app/src/components/settings/panels/BillingPanel.tsx
- app/src/components/settings/panels/MascotPanel.tsx
- app/src/components/settings/panels/AIPanel.tsx
- TeamInvitesPanel Copy button (line 253): regex pass triple-stamped dark:text-neutral-100 and dark:hover:bg-neutral-800; collapsed to one of each - TeamInvitesPanel Cancel button (line 376): duplicate dark:bg-neutral-800 + missing hover dark variant — paired hover:bg-stone-200 with dark:hover:bg-neutral-700 - AgentMessageBubble.tsx:129: table wrapper bg-white/90 had no dark variant — added dark:bg-neutral-900/90 - AgentMessageBubble.tsx:147: odd/even row bgs were missing the odd:/even: prefix on dark variants so both applied to every row — scoped to dark:odd: / dark:even:
Summary
light/dark/system),ThemeProviderthat syncs Redux state to<html class=\"dark\">and live-listens toprefers-color-scheme, and a new Settings → Appearance panel for toggling.app/src/styles/theme.csswith a full:root.darkblock (background, surface, border, text hierarchy, hover/active/focus, success/warning/error/info) plus dark overrides for the.card-*/.btn-*/.input-*/.nav-*/.skeleton/.glass-surface/.scrollbar-elegant/.skills-table-*component shortcuts inindex.css.var(--color-*)instead of hardcoded#f5f5f5/#171717.app/src/components/ui/(Button, Card, Input) with first-class light/dark variants and a barrel re-export.dark:prose-invert).Problem
The frontend was light-only. Users requested a dark mode, and the codebase had ~600 raw Tailwind color tokens (
bg-white,text-stone-700,border-stone-200, …) sprinkled across 200+ files without paireddark:variants. There was a partial:root.darkblock scoped only to the command palette, no theme state, no toggle UI, and the body/html stylesheet hardcoded the light bg so even Tailwind'sdarkMode: 'class'couldn't reach it. Theme tokens were also fragmented (raw stones, semantic vars intheme.css, ad-hoc.btn-premium/.card-elevatedshortcuts) — so dark mode and component standardization needed to land together.Solution
Four-stage rollout, then incremental component patching:
theme.csswith a full:root.darktoken block + component-shortcut overrides; fix the missing import (theme.csswas never imported frommain.tsx— a silent dead file before this PR); make body bg/text read from CSS vars; switch dotted-canvas overlay color in dark mode; teach MeshGradient to swap its color stops based on<html class=\"dark\">via a MutationObserver.themeSlice(light/dark/system, persisted in localStorage);ThemeProvidermounted underPersistGatethat syncs the Redux mode to<html class=\"dark\">and live-listens toprefers-color-schemewhen in system mode (covers Safari < 14's deprecatedaddListenerAPI).AppearancePanelat/settings/appearancewith light/dark/system radio group; wired intouseSettingsNavigation, the Settings router, and the General-section entry inSettingsHome.ui/Button(existing) gets dark variants for all four variants; newui/Card(surface/elevated/outlined/subtle) andui/Input(sm/md/lg, invalid state) primitives, all dark-aware out of the box.Then per-screen patches: shell first (BottomTabBar, SettingsHeader, SettingsMenuItem, PageBackButton, WrappedSettingsPage), then home/chat (page wrappers, agent message bubble, token usage pill, banners), then settings panels (12 panels in 4 commits via a subagent, then a second sweep over the remaining missed panels), then overlays (UpsellBanner, UsageLimitModal, NotificationCard/Center, WalkthroughTooltip, OpenhumanLinkModal, PersistRehydrationScreen), then feature pages (Skills/Channels/Rewards/Intelligence/Accounts/Conversations sidebar), then onboarding, then the BootCheckGate runtime picker.
Bulk sweeps for stone/gray/sage/amber/coral/primary/emerald/rose/lavender palette pairings use a negative-lookahead
perl -i -pepass:\\bclass-N(?!\\d)(?!\\S*dark:)adds the dark variant only when there's nodark:sibling already, and(?!\\d)prevents abg-stone-50regex from matching thebg-stone-50prefix ofbg-stone-500. Mis-pairings betweenhover:/placeholder:modifiers and theirdark:variants were swept across the codebase (hover:bg-stone-50 dark:bg-neutral-Xrewritten tohover:bg-stone-50 dark:hover:bg-neutral-X). Two corruption rounds from an earlier broken regex were also undone (\bbg-primary-50 was matchingbg-primary-500as a prefix and producingbg-primary-50 dark:bg-primary-500/150).The "select a runtime" picker (
BootCheckGate) and onboarding ChoiceCards both use!-important borders, so their dark variants also needed!(otherwise Tailwind's default border-color overrode the dark variant and the unselected card border read as white). The token usage pill, ToolTimelineBlock running/success/failure tones, WorkerThreadRefCard subagent chip, MeetingBotsCard meeting joiner, Composio toolkit tiles, ChannelTile, PillTabBar active state, and Memory Insights category cards (facts/preferences/relationships/skills/opinions/other) all got per-state dark variants instead of relying on Tailwind defaults.Two unrelated chat-surface QoL changes ride along (called out separately): the thread sidebar in chat defaults to closed on first render, and the Respond Queue side-panel on /chat is hidden behind a top-right toggle (it was always-on before and confusing to new users who don't know what it is).
Submission Checklist
<html>, which is already covered by the inherent React-Redux integration. No new code paths added.Impact
CLAUDE.md; no mobile/web variant.<html class>is one observer that fires only when the theme is toggled.themeslice persisted vialocalStorageunderpersist:theme; first launch reads'system'by default.chore: apply prettier/eslint auto-fixescommit is just whitespace/quoting normalization.Related
BootCheckGateduring cold start because the CEF child views are restored before React mounts the picker — needs awebview_account_hide_allinvocation in the gate's mount effect. Deferred to a follow-up.AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
Validation Run
Validation Blocked
Behavior Changes
Parity Contract
Duplicate / Superseded PR Handling
Summary by CodeRabbit
New Features
Improvements