Skip to content

feat(mobile): themes with appearance override and custom import - #6617

Open
amanthanvi wants to merge 32 commits into
pingdotgg:mainfrom
amanthanvi:t3code/feat-6405
Open

feat(mobile): themes with appearance override and custom import#6617
amanthanvi wants to merge 32 commits into
pingdotgg:mainfrom
amanthanvi:t3code/feat-6405

Conversation

@amanthanvi

@amanthanvi amanthanvi commented Aug 14, 2026

Copy link
Copy Markdown

Mobile's Appearance screen was typography-only: no theme picker, no light/dark override (the app hard-followed the OS), no custom themes. This adds a Color scheme section — System/Light/Dark override plus the six built-in themes (T3 Code default, T3 Chat, Grove, Ocean, Ember, Iris) — applied across Uniwind tokens, navigation chrome, markdown, and the native terminal/review/source surfaces. The default theme is byte-for-byte identical to the shipped palette, pinned by a test that parses global.css and asserts all 62 tokens per mode. Custom themes import via pasted ThemeFile v1 JSON with the full web-accepted color syntax (hex, named, rgb, hsl, hwb, lab, lch, oklab, oklch, color()) normalized to RN-safe hex with size/count caps and clean removal fallback. Also fixes CSS hex alpha byte order in the Android review/terminal and iOS terminal decoders, ships a scheme-neutral splash, and syncs appearance mode + theme id across clients via the synced-preferences mechanism.

Depends on #6614 (stacked: this branch contains its commits — the synced-preferences mechanism lands there). Custom theme libraries stay device-local by design.

Closes #6405

Before After
Appearance screen with only font controls Color scheme section with mode control and six theme cards

Applied themes and import: Ocean dark thread · themed native terminal · paste-import sheet · imported theme applied · neutral splash. Verified end-to-end on the iOS simulator with a fresh native build (mode persistence across relaunch, native alpha decoding, import/removal fallback, OS-Light/app-Dark diff coherence, cold-launch splash).

Implemented by GPT-5.6 Sol (xhigh) via the Codex CLI, orchestrated and reviewed by Claude Fable 5 (Claude Code), with Claude Opus 5 run monitoring.


Note

Medium Risk
Broad UI/theming surface plus cross-device synced preferences (last-writer-wins) and native splash/build-plugin changes; default theme is regression-tested.

Overview
Adds a Color scheme experience on mobile: System/Light/Dark override, six built-in themes, and paste-import of ThemeFile v1 JSON (device-local). AppearancePreferencesProvider now drives Uniwind CSS variables, React Native Appearance, navigation chrome (including form-sheet backgrounds), markdown tokens, and native terminal/review/source theming via optional surface-color overrides. Appearance mode and theme id persist through synced client preferences (wired from Stack and settings).

Fixes 8-digit CSS hex alpha decoding on Android review/terminal and iOS terminal, and switches to a scheme-neutral splash (createSchemeNeutralSplashConfig plus an iOS config plugin that inlines the splash storyboard background).

Plan Mode (legacy) moves to synced prefs with stricter gating: /plan and /default slash commands and submit-time mode switches only when enabled and preferences are reconciled; new-task and thread composers share plan-mode helpers and safer trigger replacement.

Default T3 Code palette stays pinned to global.css via tests; imported themes normalize colors to RN-safe hex.

Reviewed by Cursor Bugbot for commit 3df7474. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add server-synced appearance themes and plan mode preferences to mobile

  • Introduces a syncedClientPreferences system that syncs planModeEnabled, appearanceMode, and themeId between mobile clients and the server via new WebSocket RPCs (syncedClientPreferencesGet/syncedClientPreferencesPatch) and a client-preferences.patch orchestration command.
  • Adds a full mobile theming API (mobileTheme.ts, mobileThemeFile.ts) supporting built-in themes, custom JSON theme imports, appearance mode overrides (light/dark/system), and CSS color normalization.
  • The navigation theme, status bar, headers, and sheet backgrounds in App.tsx and Stack.tsx now react to the user-selected theme and appearance mode rather than only the system color scheme.
  • Native surfaces (terminal, review diff, source file) accept nativeSurfaceColors overrides derived from the active theme, and markdown/composer styling uses CSS theme variables instead of hardcoded light/dark maps.
  • Plan mode preference loading is now a prerequisite for composer submission and outbox draining; the interaction mode is coerced to default when plan mode is disabled.
  • Adds DB migration 041 creating projection_synced_client_preferences with per-field last-writer-wins semantics and a 5-minute future-skew clamp on client timestamps.
  • Risk: existing clients that do not opt in via clientPreferencesStreamItem: true will not receive the new client-preferences-updated shell stream events; updated clients always opt in.

Macroscope summarized 3df7474.

Adds a Plan Mode (Legacy) toggle to mobile Settings (default off) that
gates the /plan and /default slash commands and forces outgoing turns to
default mode when off, mirroring web semantics across every send path
(composer, new task, queued outbox). Adds submit-time parsing and slash
support on the new-task composer. Introduces per-environment synced
client preferences (contracts schema, event-sourced server projection
with additive migration, incremental shell stream item) consumed by web
and mobile with LWW reconciliation, clock-skew clamping, and offline
device-local fallback.

Closes pingdotgg#6401
Copilot AI lite review requested due to automatic review settings August 14, 2026 15:00
@github-actions github-actions Bot added the vouch:unvouched PR author is not yet trusted in the VOUCHED list. label Aug 14, 2026
@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fbf320ea-937b-4483-82b6-acad7e6c221e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the size:XXL 1,000+ changed lines (additions + deletions). label Aug 14, 2026
Comment thread apps/mobile/src/state/synced-client-preferences-model.ts Outdated
Comment thread apps/mobile/src/state/use-thread-composer-state.ts Outdated
Comment thread apps/web/src/hooks/useSettings.ts Outdated
Comment thread apps/mobile/src/state/synced-client-preferences.ts Outdated
Comment thread apps/mobile/src/state/use-thread-outbox-drain.ts Outdated
Comment thread apps/web/src/hooks/useSettings.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a mobile theming system with an Appearance override (System/Light/Dark), built-in theme picker, and device-local custom theme import, while introducing a synced-client-preferences contract + server plumbing so appearance mode + selected theme id (and legacy plan mode) can converge across web/desktop/mobile per environment.

Changes:

  • Introduces SyncedClientPreferences (schema + RPCs) and threads it through orchestration read models, shell stream, receipts, event store, projector, and a new SQLite projection/migration.
  • Implements mobile Appearance “Color scheme” UI with built-in themes, ThemeFile v1 JSON paste-import, device-local theme library persistence, and theming applied across markdown/review/source/terminal surfaces.
  • Fixes native CSS hex alpha decoding (iOS/Android) and switches to a scheme-neutral splash configuration.

Reviewed changes

Copilot reviewed 86 out of 86 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/contracts/src/syncedClientPreferences.ts New schemas for synced client preferences + patch requests
packages/contracts/src/settings.test.ts Contract tests for synced preferences decoding/patch rules
packages/contracts/src/rpc.ts Adds syncedClientPreferences get/patch WS RPCs
packages/contracts/src/orchestration.ts Extends orchestration models/events/commands for preferences
packages/contracts/src/orchestration.test.ts Ensures legacy shell decoding + patch validation
packages/contracts/src/index.ts Re-exports syncedClientPreferences
packages/client-runtime/src/state/shellReducer.ts Applies client-preferences shell stream updates
packages/client-runtime/src/state/shellReducer.test.ts Tests shellReducer preference updates
packages/client-runtime/src/state/server.ts Adds environment RPC command for patching preferences
docs/user/plan-mode.md New user doc for Plan Mode (Legacy) sync behavior
docs/user/appearance.md New user doc for appearance + themes + import limits
docs/README.md Links new appearance doc from docs index
apps/web/src/components/settings/ThemeEditorHost.tsx Switches web theme editor to synced-theme hook
apps/web/src/components/settings/SettingsPanels.tsx Switches appearance panel/restore to synced-theme hook
apps/server/src/ws.ts Streams preference updates incrementally + adds RPC handlers
apps/server/src/serverRuntimeStartup.test.ts Updates projection snapshot query seam shape
apps/server/src/server.test.ts Tests incremental preference patch streaming behavior
apps/server/src/provider/Layers/ProviderSessionReaper.test.ts Updates projection snapshot query seam shape
apps/server/src/project/ProjectSetupScriptRunner.test.ts Updates projection snapshot query seam shape
apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts Expands receipt aggregateId union for preferences
apps/server/src/persistence/Migrations/041_ProjectionSyncedClientPreferences.ts New projection table + projector cursor seed
apps/server/src/persistence/Migrations/041_ProjectionSyncedClientPreferences.test.ts Tests migration creates projection + seeds cursor
apps/server/src/persistence/Migrations.ts Registers migration 041
apps/server/src/persistence/Layers/OrchestrationEventStore.ts Expands event store streamId/aggregateId unions
apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts Adds getSyncedClientPreferences to query interface
apps/server/src/orchestration/projector.ts Projects LWW preferences into read model
apps/server/src/orchestration/Normalizer.ts Clamps future-skewed preference stamps
apps/server/src/orchestration/Normalizer.test.ts Tests future-skew clamp logic
apps/server/src/orchestration/Layers/ProjectionPipeline.ts Adds SQL projector for preferences singleton
apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts Tests deterministic rebuild/out-of-order handling
apps/server/src/orchestration/Layers/OrchestrationEngine.ts Routes preference commands to singleton aggregate
apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts End-to-end receipts/projection behavior for preferences
apps/server/src/orchestration/decider.ts Emits client-preferences.patched events
apps/server/src/checkpointing/CheckpointDiffQuery.test.ts Updates projection snapshot query seam shape
apps/server/src/auth/RpcAuthorization.ts Adds auth scopes for new preference RPCs
apps/mobile/src/state/use-thread-outbox-drain.ts Reads live plan-mode preference during outbox drain
apps/mobile/src/state/use-thread-composer-state.ts Forces interactionMode default when plan mode disabled
apps/mobile/src/state/thread-outbox.test.ts Updates tests for plan-mode-aware queued sends
apps/mobile/src/state/thread-outbox-model.ts Adds plan-mode-aware queued settings + send decision helper
apps/mobile/src/state/synced-client-preferences.ts Mobile worker to reconcile/fan-out synced preferences
apps/mobile/src/state/synced-client-preferences.test.ts Tests reconciliation + fan-out + normalization behaviors
apps/mobile/src/state/synced-client-preferences-model.ts Pure reconciliation/write helpers for mobile preferences
apps/mobile/src/Stack.tsx Adds synced-preferences worker; simplifies header styling
apps/mobile/src/persistence/mobile-preferences.ts Persists appearance/theme/imported themes + LWW stamp
apps/mobile/src/lib/storage.test.ts Tests preference persistence + imported theme removal fallback
apps/mobile/src/lib/splashConfig.ts Scheme-neutral splash config helper
apps/mobile/src/lib/splashConfig.test.ts Tests scheme-neutral splash config
apps/mobile/src/lib/mobileThemeFile.test.ts Tests ThemeFile parsing/caps/normalization
apps/mobile/src/lib/mobileTheme.test.ts Tests token mapping + built-in parity with global.css
apps/mobile/src/features/threads/use-plan-mode-enabled.ts Hook for resolved plan-mode preference state
apps/mobile/src/features/threads/ThreadFeed.tsx Switches markdown/review theming to appearance preferences
apps/mobile/src/features/threads/ThreadComposer.tsx Plan-mode slash gating + submit-time parsing + safer replacement
apps/mobile/src/features/threads/plan-mode.ts Shared plan-mode helpers (slash/menu/submit/interactionMode)
apps/mobile/src/features/threads/plan-mode.test.ts Tests plan-mode helper behaviors
apps/mobile/src/features/threads/NewTaskDraftScreen.tsx Adds plan-mode slash popover + submit parsing + in-flight guard
apps/mobile/src/features/threads/new-task-submit.ts Extracts new-task submit eligibility/selection helpers
apps/mobile/src/features/threads/new-task-submit.test.ts Tests new-task submit helpers
apps/mobile/src/features/threads/new-task-flow-provider.tsx Uses new plan-mode preference hook + interactionMode resolver
apps/mobile/src/features/terminal/ThreadTerminalRouteScreen.tsx Themes terminal via nativeSurfaceColors + correct alpha
apps/mobile/src/features/terminal/terminalTheme.ts Adds optional native-surface overrides to terminal theme
apps/mobile/src/features/terminal/terminalTheme.test.ts Tests terminal overrides + alpha ordering stability
apps/mobile/src/features/terminal/NativeTerminalSurface.tsx Allows appearanceScheme override for fallback/native surface
apps/mobile/src/features/settings/SettingsRouteScreen.tsx Plan mode toggle now uses synced preference updates
apps/mobile/src/features/settings/SettingsAppearanceRouteScreen.tsx Adds ColorSchemeAppearanceSection to Appearance screen
apps/mobile/src/features/settings/appearance/sections/ColorSchemeAppearanceSection.tsx New UI: mode picker, theme cards, import/remove flows
apps/mobile/src/features/settings/appearance/AppearancePreferencesProvider.tsx Applies theme variables + supports import/remove + syncing
apps/mobile/src/features/review/useNativeReviewDiffBridge.ts Themes native review diff via nativeSurfaceColors
apps/mobile/src/features/review/ReviewSheet.tsx Uses effectiveColorScheme instead of system scheme
apps/mobile/src/features/review/nativeReviewDiffAdapter.ts Adds native theme overrides application helper
apps/mobile/src/features/review/nativeReviewDiffAdapter.test.ts Tests stable defaults + override behavior + alpha serialization
apps/mobile/src/features/files/SourceFileSurface.tsx Themes native source surface via shared theme helper
apps/mobile/src/features/files/nativeSourceFileAdapter.ts Reuses review theme creation for source surfaces
apps/mobile/src/features/files/nativeSourceFileAdapter.test.ts Tests stable defaults + override passthrough
apps/mobile/src/components/AppSymbol.tsx Adds Android mapping for “hammer” symbol
apps/mobile/src/App.tsx Navigation theme + status bar driven by appearance preferences
apps/mobile/plugins/withIosSplashScreenInlineBackground.cjs Inlines storyboard splash background color on iOS
apps/mobile/modules/t3-terminal/ios/T3TerminalView.swift Fixes CSS #RRGGBBAA parsing for iOS terminal colors
apps/mobile/modules/t3-terminal/android/.../T3TerminalView.kt Fixes CSS #RRGGBBAA parsing for Android terminal colors
apps/mobile/modules/t3-review-diff/android/.../T3ReviewDiffView.kt Fixes CSS #RRGGBBAA parsing for Android review colors
apps/mobile/global.css Adds missing markdown inline-code token variables
apps/mobile/app.config.ts Switches to scheme-neutral splash + iOS storyboard mod

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/README.md
Comment thread apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
@macroscopeapp

macroscopeapp Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found. Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

Aman Thanvi added 2 commits August 14, 2026 19:39
- Preserve offline web plan-mode writes
- Reject impossible preference timestamps
- Retry failed web preference patches
- Order rapid web toggles monotonically
- Re-read plan mode before queued mobile delivery
- Route environment-scoped preference writes correctly
- Ignore stale mobile preference acknowledgements
- Document the mobile Legacy settings location
- Use Hermes-compatible reconciliation sorting
- Bound mobile synced-preference stamps to the canonical future-skew window.
- Gate thread-composer enqueue mode selection on preference hydration.
- Preserve newer local Plan Mode intent in read-only mobile and web sessions.
- Add Plan Mode to the user-docs index.
Comment thread apps/mobile/src/state/synced-client-preferences.ts Outdated
Comment thread packages/contracts/src/orchestration.ts
Comment thread apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
Comment thread apps/mobile/src/lib/mobileThemeFile.ts Outdated
Comment thread apps/web/src/components/settings/SettingsPanels.tsx
Comment thread apps/mobile/src/features/threads/ThreadComposer.tsx
Comment thread apps/mobile/src/lib/mobileThemeFile.ts Outdated
Comment thread apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
Comment thread apps/mobile/src/state/synced-client-preferences.ts Outdated
Comment thread apps/web/src/hooks/useSettings.ts Outdated
Comment thread apps/mobile/src/Stack.tsx
Comment thread apps/web/src/hooks/useSettings.ts
Comment thread apps/mobile/src/state/preferences.ts
Comment thread apps/mobile/src/features/threads/use-plan-mode-enabled.ts Outdated
Comment thread apps/mobile/src/features/threads/new-task-submit.ts
Comment thread apps/mobile/src/features/threads/NewTaskDraftScreen.tsx

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0db7db6. Configure here.

Comment thread apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
Aman Thanvi added 15 commits August 15, 2026 03:11
Adds a Color scheme section to mobile Appearance: a System/Light/Dark
override and a six-theme picker (T3 Code default plus T3 Chat, Grove,
Ocean, Ember, Iris) applied across Uniwind tokens, navigation chrome,
markdown, and the native terminal/review/source surfaces. The default
theme is byte-for-byte identical to the shipped palette (pinned by a
global.css parity test). Supports pasting ThemeFile v1 JSON with the
full web-accepted color syntax (hex, named, rgb, hsl, hwb, lab, lch,
oklab, oklch, color()) normalized to RN-safe hex, with size/count caps
and clean removal fallback. Fixes CSS hex alpha byte order in the
Android review/terminal and iOS terminal decoders and ships a
scheme-neutral splash.

Closes pingdotgg#6405
Extends synced client preferences to reconcile and write through appearance mode and selected theme IDs on mobile and web. Keeps custom theme definitions and theme halves local, with a deterministic T3 Code fallback for unavailable IDs.
- Compose sequential synced-theme writes from the latest pending snapshot
- Document every supported mobile theme color syntax
- Remove a stale Hermes-test lint suppression
});
}
if (clientPatch.planModeEnabled !== undefined) {
syncedPlanModeHydrationController.write({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium hooks/useSettings.ts:639

A failed planModeEnabled sync from a secondary environment is never retried, so the UI remains optimistically updated while the server preference is lost on reload. useUpdateSettingsTarget records the write for every environmentId, but useSynchronizeSyncedClientPreferences deactivates the controller when that ID differs from the current primary ID; only the primary environment can provide the synchronization owner. Restrict synced-preference writes to the primary environment (while retaining the local persistence path) so secondary-environment failures cannot leave an unreachable pending write.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/hooks/useSettings.ts around line 639:

A failed `planModeEnabled` sync from a secondary environment is never retried, so the UI remains optimistically updated while the server preference is lost on reload. `useUpdateSettingsTarget` records the write for every `environmentId`, but `useSynchronizeSyncedClientPreferences` deactivates the controller when that ID differs from the current primary ID; only the primary environment can provide the synchronization owner. Restrict synced-preference writes to the primary environment (while retaining the local persistence path) so secondary-environment failures cannot leave an unreachable pending write.

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

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Mobile: themes and custom themes — parity with the desktop theme system

2 participants