Swap the home screen model button for a dark/light mode switch - #131
Merged
Conversation
The Model button on the home screen header now toggles the app's appearance instead of opening the on-device AI model picker. The manual light/dark choice is persisted (falling back to the OS scheme until the user picks one) via a new theme preference in settings. Model switching moves to the caption editor (subtitles screen), next to Undo/Redo, so it lives alongside the rest of the captioning flow where the export screen already surfaces it for first-time selection. Fixes #130
There was a problem hiding this comment.
Pull request overview
This PR updates Pulse’s UI/UX around two global concerns: appearance and on-device caption model selection. It replaces the home screen’s model picker entry point with a persisted light/dark toggle, and relocates model switching into the caption editor where captioning actions live.
Changes:
- Replaced the home header “Model” affordance with a dark/light appearance switch (icon + label).
- Added persisted theme preference support and a new
useThemeToggle()hook while keepinguseTheme()’s return shape stable. - Moved the model-switcher entry point into the caption editor header and added Android glyph mappings for the new sun/moon icons.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/hooks/use-theme.ts | Adds persisted theme override resolution and exposes useThemeToggle() for the new header switch. |
| src/db/settings.ts | Persists a new appearance.theme setting and exposes query/set helpers. |
| src/components/icon.tsx | Maps sun.max.fill / moon.fill to Android Lucide icons. |
| src/app/subtitles.tsx | Adds a model-switcher header button and wires it to ModelSwitcherModal. |
| src/app/index.tsx | Removes model picker from home header and adds the appearance switch UI/accessibility wiring. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
useTheme() previously called useLiveQuery() directly, so every leaf component that renders themed content (ThemedText, ThemedView, cue rows, etc.) opened its own Drizzle live-query subscription. Move the live query into a ThemeProvider mounted once near the app root and have useTheme()/useThemeToggle() read the resolved scheme from context instead.
…nav theme ThemeProvider live-queries the settings table, which doesn't exist on a fresh install until MigrationGate applies drizzle/0003 — so it now mounts INSIDE the gate. The gate's pre-migration loading/error UI can still render ThemedView/ThemedText: outside the provider, useTheme() falls back to the OS scheme (the stored preference is unreadable before the DB exists anyway) instead of touching the DB. Also derive the expo-router navigation theme and the status bar style from the RESOLVED scheme (manual override, else OS) via the new useThemeMode(), so they can't drift from the app's own colors once the user flips the appearance switch.
…r button The model-switcher moved from the home header (where its button announced hint + selected state + the active model's label) into the caption editor, but HeaderBtn only exposed a static label — dropping that state from screen readers at the only remaining in-app switch point. HeaderBtn now takes optional hint/selected/valueText a11y props and the model button restores the old semantics: hint 'Choose the model used for captions', selected when a model is active, value = the model's label or 'Off'.
Passing selected: undefined for every header button (Undo/Redo) can be coerced falsy and read as an unnecessary 'not selected' by screen readers — only include the key when a caller actually provides it.
morepriyam
enabled auto-merge
July 26, 2026 18:55
github-merge-queue
Bot
removed this pull request from the merge queue due to Branch Protection failures
Jul 26, 2026
You're not authorized to push to this branch. Visit "About protected branches" for more information.
github-merge-queue
Bot
removed this pull request from the merge queue due to Branch Protection failures
Jul 27, 2026
You're not authorized to push to this branch. Visit "About protected branches" for more information.
github-merge-queue
Bot
removed this pull request from the merge queue due to Branch Protection failures
Jul 27, 2026
You're not authorized to push to this branch. Visit "About protected branches" for more information.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Replaces the "Model" button on the home screen header with a dark/light mode switch, and moves model switching into the caption editor where the rest of the captioning flow already lives.
Why
The home screen header used a "Model" button to open the on-device AI model picker, but that picker is really a captioning concern — it belongs with the caption editor, not the drafts list. This also frees up the header for a more universally useful control: a manual appearance toggle.
Changes
src/app/index.tsx): removed the "Model" button and its model-picker wiring. Added a dark/light mode switch (sun/moon icon + label) in its place.src/db/settings.ts,src/hooks/use-theme.ts): added a persisted theme preference.useTheme()now uses the manual override when set, otherwise falls back to the OS color scheme — same return shape, so no other screen needed changes. NewuseThemeToggle()hook drives the switch.src/components/icon.tsx): addedsun.max.fill/moon.fillAndroid glyph mappings to match the SF Symbols used on iOS.src/app/subtitles.tsx): added a model-switcher button next to Undo/Redo (tinted accent when a model is active), wired to the existing model picker modal. This is now the only in-app place to change the selected model; the export screen's "no model yet" prompt is unchanged.Testing
tsc --noEmit: no errorsexpo lint: no new warningsjest: full suite passes (119 passed, matching baseline)Fixes #130
Screenshots
Home header — appearance switch replaces the model button
main)Caption editor — the model switcher's new home
Live demo — theme flip + model switcher in the caption editor
ScreenRecording_07-26-2026.14-16-39_1.MP4