Skip to content

RC-T40 Chat Fixes - #127

Merged
ucswift merged 2 commits into
masterfrom
develop
Aug 13, 2026
Merged

RC-T40 Chat Fixes#127
ucswift merged 2 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Aug 13, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added support for unit-wide dispatch chat channels.
    • Expanded incident channel grouping and updated incident icons.
    • Added shared light and dark theme support across web and native platforms.
  • Bug Fixes

    • Improved text alignment and vertical sizing in input and selection fields.
    • Restored incident command updates after hub reconnection.
    • Improved department rejoining and connection-state handling during reconnection.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ucswift, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 7 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d105053-f29a-49c0-b456-47a541a1586d

📥 Commits

Reviewing files that changed from the base of the PR and between 5e3b2ff and a09f7ec.

📒 Files selected for processing (7)
  • gluestack-ui.config.json
  • src/components/ui/gluestack-ui-provider/index.tsx
  • src/components/ui/input/index.tsx
  • src/components/ui/select/index.tsx
  • src/components/ui/text-field-metrics.ts
  • src/hooks/use-signalr-lifecycle.ts
  • tsconfig.json
📝 Walkthrough

Walkthrough

The changes add platform-specific theme loading, expand chat channel classification, restore incident state after SignalR update-hub reconnection, and apply platform-specific text metrics to native input controls.

Changes

Theme system

Layer / File(s) Summary
Shared theme tokens
theme-tokens.css
Adds shared color, typography, background, shadow, Tailwind, and focus tokens.
Platform theme entry points
global.css, global.web.css, src/lib/theme-styles.ts, src/lib/theme-styles.web.ts, src/app/_layout.tsx, src/components/ui/gluestack-ui-provider/index.tsx
Separates native system dark mode from web .dark-class handling and loads the matching stylesheet entry.

Chat channel classification

Layer / File(s) Summary
Chat channel contract
src/models/v4/chat/chatEnums.ts
Adds the UnitDispatch channel type with value 11.
Incident grouping and validation
src/components/chat/chat-utils.ts, src/app/(app)/chat.tsx, src/components/chat/__tests__/chat-utils.test.ts
Groups incident lead and incident dispatch channels as incidents. Tests cover incident grouping, unit dispatch grouping, and archived-channel exclusion.

SignalR update-hub recovery

Layer / File(s) Summary
Update-hub lifecycle handling
src/stores/signalr/signalr-store.ts
Adds dedicated lifecycle handlers for reconnect and disconnect events. Reconnection restores department and incident command state.
Lifecycle resume refresh
src/hooks/use-signalr-lifecycle.ts
Refreshes incident command state after successful update-hub reconnection when an active call exists.

Native input metrics

Layer / File(s) Summary
Input and select vertical metrics
src/components/ui/input/index.tsx, src/components/ui/select/index.tsx
Adds iOS and Android text metrics and preserves caller-provided styles after platform-specific fixes.

Estimated code review effort: 4 (Complex) | ~60 minutes

Mergeability Score: 🔵 Low · up to 5e3b2

The PR improves chat reconnect and theming behavior, but it can still reload the board after an unsuccessful update-hub reconnect and may trigger Stylelint failures from the new CSS syntax. It is mergeable with explicit owner awareness or follow-up on these bounded issues.

Sequence Diagram(s)

sequenceDiagram
  participant UpdateHub
  participant SignalRStore
  participant IncidentCommandStore
  UpdateHub->>SignalRStore: Emit reconnect event
  SignalRStore->>SignalRStore: Reconnect department
  SignalRStore->>IncidentCommandStore: Refresh open incident command
  UpdateHub->>SignalRStore: Emit disconnect event
  SignalRStore->>SignalRStore: Remove lifecycle handlers
Loading

Possibly related PRs

Suggested reviewers: github-actions

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title identifies real chat-related changes but does not describe the broader theme, lifecycle, and input fixes in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
theme-tokens.css (1)

675-676: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Stylelint does not recognize the Tailwind v4 at-rules used by the new theme files. The root cause is one configuration gap: scss/at-rule-no-unknown has no ignoreAtRules entries for Tailwind v4, and value-keyword-case treats the font family name as a keyword. Fix the stylelint configuration once; do not change the CSS.

  • theme-tokens.css#L675-L676: keep @theme and Roboto; add theme to ignoreAtRules and Roboto to ignoreKeywords.
  • theme-tokens.css#L543-L543: keep @theme inline; covered by the same ignoreAtRules entry.
  • global.css#L17-L17: keep @custom-variant; add custom-variant to ignoreAtRules.
  • global.web.css#L12-L12: keep @custom-variant; covered by the same ignoreAtRules entry.
🤖 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 `@theme-tokens.css` around lines 675 - 676, Update the Stylelint configuration,
not the CSS: add theme and custom-variant to scss/at-rule-no-unknown’s
ignoreAtRules, and add Roboto to value-keyword-case’s ignoreKeywords. This
covers theme-tokens.css lines 675-676 and 543-543, global.css line 17, and
global.web.css line 12; make no direct changes at those sites.

Source: Linters/SAST tools

🤖 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 `@src/hooks/use-signalr-lifecycle.ts`:
- Around line 140-149: Update connectUpdateHub and the reconnect handling in
use-signalr-lifecycle so connectUpdateHub returns or propagates an explicit
success/failure result instead of resolving after setup errors. Gate
handleIncidentCommandUpdated in the openCallId block on that successful
update-hub connection result, rather than only checking results[0].status ===
'fulfilled'.

In `@theme-tokens.css`:
- Around line 146-539: Remove the duplicated light and dark token declarations
between :root, :root.light, and the prefers-color-scheme dark block. Keep one
canonical definition for each theme and generate or reuse it for both the
media-query and explicit theme selectors, preserving all existing token values
and selector behavior.

---

Nitpick comments:
In `@theme-tokens.css`:
- Around line 675-676: Update the Stylelint configuration, not the CSS: add
theme and custom-variant to scss/at-rule-no-unknown’s ignoreAtRules, and add
Roboto to value-keyword-case’s ignoreKeywords. This covers theme-tokens.css
lines 675-676 and 543-543, global.css line 17, and global.web.css line 12; make
no direct changes at those sites.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e29b0187-62d1-4d82-bc04-a599a970dfa3

📥 Commits

Reviewing files that changed from the base of the PR and between 0d813e6 and 5e3b2ff.

📒 Files selected for processing (15)
  • global.css
  • global.web.css
  • src/app/(app)/chat.tsx
  • src/app/_layout.tsx
  • src/components/chat/__tests__/chat-utils.test.ts
  • src/components/chat/chat-utils.ts
  • src/components/ui/gluestack-ui-provider/index.tsx
  • src/components/ui/input/index.tsx
  • src/components/ui/select/index.tsx
  • src/hooks/use-signalr-lifecycle.ts
  • src/lib/theme-styles.ts
  • src/lib/theme-styles.web.ts
  • src/models/v4/chat/chatEnums.ts
  • src/stores/signalr/signalr-store.ts
  • theme-tokens.css

Comment thread src/hooks/use-signalr-lifecycle.ts
Comment thread theme-tokens.css
Comment on lines +146 to +539
@media (prefers-color-scheme: dark) {
:root {
--color-primary-0: 15 23 42;
--color-primary-50: 30 41 59;
--color-primary-100: 39 49 66;
--color-primary-200: 45 55 72;
--color-primary-300: 51 65 85;
--color-primary-400: 71 85 105;
--color-primary-500: 100 116 139;
--color-primary-600: 71 85 105;
--color-primary-700: 51 65 85;
--color-primary-800: 45 55 72;
--color-primary-900: 39 49 66;
--color-primary-950: 30 41 59;
--color-secondary-0: 11 12 12;
--color-secondary-50: 24 23 23;
--color-secondary-100: 39 38 38;
--color-secondary-200: 63 64 64;
--color-secondary-300: 81 82 82;
--color-secondary-400: 94 95 95;
--color-secondary-500: 114 115 115;
--color-secondary-600: 175 176 176;
--color-secondary-700: 219 219 219;
--color-secondary-800: 231 232 232;
--color-secondary-900: 241 242 242;
--color-secondary-950: 254 255 255;
--color-tertiary-0: 84 49 18;
--color-tertiary-50: 108 61 19;
--color-tertiary-100: 130 73 23;
--color-tertiary-200: 180 98 26;
--color-tertiary-300: 215 117 31;
--color-tertiary-400: 231 129 40;
--color-tertiary-500: 251 157 75;
--color-tertiary-600: 253 180 116;
--color-tertiary-700: 254 209 170;
--color-tertiary-800: 255 233 213;
--color-tertiary-900: 255 242 229;
--color-tertiary-950: 255 250 245;
--color-error-0: 83 19 19;
--color-error-50: 127 29 29;
--color-error-100: 153 27 27;
--color-error-200: 185 28 28;
--color-error-300: 220 38 38;
--color-error-400: 230 53 53;
--color-error-500: 239 68 68;
--color-error-600: 248 113 113;
--color-error-700: 252 165 165;
--color-error-800: 254 202 202;
--color-error-900: 254 226 226;
--color-error-950: 254 233 233;
--color-success-0: 27 50 36;
--color-success-50: 20 83 45;
--color-success-100: 22 101 52;
--color-success-200: 32 111 62;
--color-success-300: 42 121 72;
--color-success-400: 52 131 82;
--color-success-500: 72 151 102;
--color-success-600: 102 181 132;
--color-success-700: 132 211 162;
--color-success-800: 162 241 192;
--color-success-900: 202 255 232;
--color-success-950: 228 255 244;
--color-warning-0: 84 45 18;
--color-warning-50: 108 56 19;
--color-warning-100: 130 68 23;
--color-warning-200: 180 90 26;
--color-warning-300: 215 108 31;
--color-warning-400: 231 120 40;
--color-warning-500: 251 149 75;
--color-warning-600: 253 173 116;
--color-warning-700: 254 205 170;
--color-warning-800: 255 231 213;
--color-warning-900: 255 249 245;
--color-warning-950: 255 253 251;
--color-info-0: 3 38 56;
--color-info-50: 5 64 93;
--color-info-100: 7 90 131;
--color-info-200: 9 115 168;
--color-info-300: 11 141 205;
--color-info-400: 13 166 242;
--color-info-500: 50 180 244;
--color-info-600: 87 194 246;
--color-info-700: 124 207 248;
--color-info-800: 162 221 250;
--color-info-900: 199 235 252;
--color-info-950: 236 248 254;
--color-typography-0: 23 23 23;
--color-typography-50: 38 38 39;
--color-typography-100: 64 64 64;
--color-typography-200: 82 82 82;
--color-typography-300: 115 115 115;
--color-typography-400: 140 140 140;
--color-typography-500: 163 163 163;
--color-typography-600: 212 212 212;
--color-typography-700: 219 219 220;
--color-typography-800: 229 229 229;
--color-typography-900: 245 245 245;
--color-typography-950: 254 254 255;
--color-outline-0: 26 23 23;
--color-outline-50: 39 38 36;
--color-outline-100: 65 65 65;
--color-outline-200: 83 82 82;
--color-outline-300: 115 116 116;
--color-outline-400: 140 141 141;
--color-outline-500: 165 163 163;
--color-outline-600: 211 211 211;
--color-outline-700: 221 220 219;
--color-outline-800: 230 230 230;
--color-outline-900: 243 243 243;
--color-outline-950: 253 254 254;
--color-background-0: 18 18 18;
--color-background-50: 39 38 37;
--color-background-100: 65 64 64;
--color-background-200: 83 82 82;
--color-background-300: 116 116 116;
--color-background-400: 142 142 142;
--color-background-500: 162 163 163;
--color-background-600: 213 212 212;
--color-background-700: 220 219 219;
--color-background-800: 242 241 241;
--color-background-900: 246 246 246;
--color-background-950: 254 254 254;
--color-background-error: 66 43 43;
--color-background-warning: 65 47 35;
--color-background-success: 28 43 33;
--color-background-muted: 51 51 51;
--color-background-info: 26 40 46;
--color-indicator-primary: 247 247 247;
--color-indicator-info: 161 199 245;
--color-indicator-error: 232 70 69;
}
}

:root.dark {
--color-primary-0: 15 23 42;
--color-primary-50: 30 41 59;
--color-primary-100: 39 49 66;
--color-primary-200: 45 55 72;
--color-primary-300: 51 65 85;
--color-primary-400: 71 85 105;
--color-primary-500: 100 116 139;
--color-primary-600: 71 85 105;
--color-primary-700: 51 65 85;
--color-primary-800: 45 55 72;
--color-primary-900: 39 49 66;
--color-primary-950: 30 41 59;
--color-secondary-0: 11 12 12;
--color-secondary-50: 24 23 23;
--color-secondary-100: 39 38 38;
--color-secondary-200: 63 64 64;
--color-secondary-300: 81 82 82;
--color-secondary-400: 94 95 95;
--color-secondary-500: 114 115 115;
--color-secondary-600: 175 176 176;
--color-secondary-700: 219 219 219;
--color-secondary-800: 231 232 232;
--color-secondary-900: 241 242 242;
--color-secondary-950: 254 255 255;
--color-tertiary-0: 84 49 18;
--color-tertiary-50: 108 61 19;
--color-tertiary-100: 130 73 23;
--color-tertiary-200: 180 98 26;
--color-tertiary-300: 215 117 31;
--color-tertiary-400: 231 129 40;
--color-tertiary-500: 251 157 75;
--color-tertiary-600: 253 180 116;
--color-tertiary-700: 254 209 170;
--color-tertiary-800: 255 233 213;
--color-tertiary-900: 255 242 229;
--color-tertiary-950: 255 250 245;
--color-error-0: 83 19 19;
--color-error-50: 127 29 29;
--color-error-100: 153 27 27;
--color-error-200: 185 28 28;
--color-error-300: 220 38 38;
--color-error-400: 230 53 53;
--color-error-500: 239 68 68;
--color-error-600: 248 113 113;
--color-error-700: 252 165 165;
--color-error-800: 254 202 202;
--color-error-900: 254 226 226;
--color-error-950: 254 233 233;
--color-success-0: 27 50 36;
--color-success-50: 20 83 45;
--color-success-100: 22 101 52;
--color-success-200: 32 111 62;
--color-success-300: 42 121 72;
--color-success-400: 52 131 82;
--color-success-500: 72 151 102;
--color-success-600: 102 181 132;
--color-success-700: 132 211 162;
--color-success-800: 162 241 192;
--color-success-900: 202 255 232;
--color-success-950: 228 255 244;
--color-warning-0: 84 45 18;
--color-warning-50: 108 56 19;
--color-warning-100: 130 68 23;
--color-warning-200: 180 90 26;
--color-warning-300: 215 108 31;
--color-warning-400: 231 120 40;
--color-warning-500: 251 149 75;
--color-warning-600: 253 173 116;
--color-warning-700: 254 205 170;
--color-warning-800: 255 231 213;
--color-warning-900: 255 249 245;
--color-warning-950: 255 253 251;
--color-info-0: 3 38 56;
--color-info-50: 5 64 93;
--color-info-100: 7 90 131;
--color-info-200: 9 115 168;
--color-info-300: 11 141 205;
--color-info-400: 13 166 242;
--color-info-500: 50 180 244;
--color-info-600: 87 194 246;
--color-info-700: 124 207 248;
--color-info-800: 162 221 250;
--color-info-900: 199 235 252;
--color-info-950: 236 248 254;
--color-typography-0: 23 23 23;
--color-typography-50: 38 38 39;
--color-typography-100: 64 64 64;
--color-typography-200: 82 82 82;
--color-typography-300: 115 115 115;
--color-typography-400: 140 140 140;
--color-typography-500: 163 163 163;
--color-typography-600: 212 212 212;
--color-typography-700: 219 219 220;
--color-typography-800: 229 229 229;
--color-typography-900: 245 245 245;
--color-typography-950: 254 254 255;
--color-outline-0: 26 23 23;
--color-outline-50: 39 38 36;
--color-outline-100: 65 65 65;
--color-outline-200: 83 82 82;
--color-outline-300: 115 116 116;
--color-outline-400: 140 141 141;
--color-outline-500: 165 163 163;
--color-outline-600: 211 211 211;
--color-outline-700: 221 220 219;
--color-outline-800: 230 230 230;
--color-outline-900: 243 243 243;
--color-outline-950: 253 254 254;
--color-background-0: 18 18 18;
--color-background-50: 39 38 37;
--color-background-100: 65 64 64;
--color-background-200: 83 82 82;
--color-background-300: 116 116 116;
--color-background-400: 142 142 142;
--color-background-500: 162 163 163;
--color-background-600: 213 212 212;
--color-background-700: 220 219 219;
--color-background-800: 242 241 241;
--color-background-900: 246 246 246;
--color-background-950: 254 254 254;
--color-background-error: 66 43 43;
--color-background-warning: 65 47 35;
--color-background-success: 28 43 33;
--color-background-muted: 51 51 51;
--color-background-info: 26 40 46;
--color-indicator-primary: 247 247 247;
--color-indicator-info: 161 199 245;
--color-indicator-error: 232 70 69;
}

:root.light {
--color-primary-0: 248 250 252;
--color-primary-50: 241 245 249;
--color-primary-100: 226 232 240;
--color-primary-200: 203 213 225;
--color-primary-300: 148 163 184;
--color-primary-400: 100 116 139;
--color-primary-500: 71 85 105;
--color-primary-600: 51 65 85;
--color-primary-700: 45 55 72;
--color-primary-800: 39 49 66;
--color-primary-900: 30 41 59;
--color-primary-950: 15 23 42;
--color-secondary-0: 254 255 255;
--color-secondary-50: 241 242 242;
--color-secondary-100: 231 232 232;
--color-secondary-200: 219 219 219;
--color-secondary-300: 175 176 176;
--color-secondary-400: 114 115 115;
--color-secondary-500: 94 95 95;
--color-secondary-600: 81 82 82;
--color-secondary-700: 63 64 64;
--color-secondary-800: 39 38 38;
--color-secondary-900: 24 23 23;
--color-secondary-950: 11 12 12;
--color-tertiary-0: 255 250 245;
--color-tertiary-50: 255 242 229;
--color-tertiary-100: 255 233 213;
--color-tertiary-200: 254 209 170;
--color-tertiary-300: 253 180 116;
--color-tertiary-400: 251 157 75;
--color-tertiary-500: 231 129 40;
--color-tertiary-600: 215 117 31;
--color-tertiary-700: 180 98 26;
--color-tertiary-800: 130 73 23;
--color-tertiary-900: 108 61 19;
--color-tertiary-950: 84 49 18;
--color-error-0: 254 233 233;
--color-error-50: 254 226 226;
--color-error-100: 254 202 202;
--color-error-200: 252 165 165;
--color-error-300: 248 113 113;
--color-error-400: 239 68 68;
--color-error-500: 230 53 53;
--color-error-600: 220 38 38;
--color-error-700: 185 28 28;
--color-error-800: 153 27 27;
--color-error-900: 127 29 29;
--color-error-950: 83 19 19;
--color-success-0: 228 255 244;
--color-success-50: 202 255 232;
--color-success-100: 162 241 192;
--color-success-200: 132 211 162;
--color-success-300: 102 181 132;
--color-success-400: 72 151 102;
--color-success-500: 52 131 82;
--color-success-600: 42 121 72;
--color-success-700: 32 111 62;
--color-success-800: 22 101 52;
--color-success-900: 20 83 45;
--color-success-950: 27 50 36;
--color-warning-0: 255 253 251;
--color-warning-50: 255 249 245;
--color-warning-100: 255 231 213;
--color-warning-200: 254 205 170;
--color-warning-300: 253 173 116;
--color-warning-400: 251 149 75;
--color-warning-500: 231 120 40;
--color-warning-600: 215 108 31;
--color-warning-700: 180 90 26;
--color-warning-800: 130 68 23;
--color-warning-900: 108 56 19;
--color-warning-950: 84 45 18;
--color-info-0: 236 248 254;
--color-info-50: 199 235 252;
--color-info-100: 162 221 250;
--color-info-200: 124 207 248;
--color-info-300: 87 194 246;
--color-info-400: 50 180 244;
--color-info-500: 13 166 242;
--color-info-600: 11 141 205;
--color-info-700: 9 115 168;
--color-info-800: 7 90 131;
--color-info-900: 5 64 93;
--color-info-950: 3 38 56;
--color-typography-0: 254 254 255;
--color-typography-50: 245 245 245;
--color-typography-100: 229 229 229;
--color-typography-200: 219 219 220;
--color-typography-300: 212 212 212;
--color-typography-400: 163 163 163;
--color-typography-500: 140 140 140;
--color-typography-600: 115 115 115;
--color-typography-700: 82 82 82;
--color-typography-800: 64 64 64;
--color-typography-900: 38 38 39;
--color-typography-950: 23 23 23;
--color-outline-0: 253 254 254;
--color-outline-50: 243 243 243;
--color-outline-100: 230 230 230;
--color-outline-200: 221 220 219;
--color-outline-300: 211 211 211;
--color-outline-400: 165 163 163;
--color-outline-500: 140 141 141;
--color-outline-600: 115 116 116;
--color-outline-700: 83 82 82;
--color-outline-800: 65 65 65;
--color-outline-900: 39 38 36;
--color-outline-950: 26 23 23;
--color-background-0: 255 255 255;
--color-background-50: 246 246 246;
--color-background-100: 242 241 241;
--color-background-200: 220 219 219;
--color-background-300: 213 212 212;
--color-background-400: 162 163 163;
--color-background-500: 142 142 142;
--color-background-600: 116 116 116;
--color-background-700: 83 82 82;
--color-background-800: 65 64 64;
--color-background-900: 39 38 37;
--color-background-950: 24 23 24;
--color-background-error: 254 241 241;
--color-background-warning: 255 244 235;
--color-background-success: 237 252 242;
--color-background-muted: 247 248 247;
--color-background-info: 235 248 254;
--color-indicator-primary: 55 55 55;
--color-indicator-info: 83 153 236;
--color-indicator-error: 185 28 28;
}

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find where the web dark/light class is applied to <html> and whether it runs before paint.
rg -n --iglob '*.{ts,tsx,js,jsx,html}' -C4 'classList|documentElement|applyColorScheme'
fd -i -e html . | xargs -r rg -n -C3 'dark|colorScheme'

Repository: Resgrid/Dispatch

Length of output: 154


🏁 Script executed:

#!/bin/bash
set -u

printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(theme-tokens\.css|global(\.web)?\.css|use-selected-theme\.web\.tsx|.*html$|.*web.*\.(ts|tsx|js|jsx)$)$' || true

printf '%s\n' '--- theme and web entry references ---'
rg -n -C3 --glob '*.css' --glob '*.{ts,tsx,js,jsx,html}' \
  'theme-tokens|use-selected-theme|classList|documentElement|localStorage|prefers-color-scheme|dark:|colorScheme' . || true

Repository: Resgrid/Dispatch

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -u

log=/tmp/coderabbit-shell-logs/shell-output-K6f5ZU

printf '%s\n' '--- exact candidate paths from the captured search ---'
rg -n '(^|/)(theme-tokens\.css|global(\.web)?\.css|use-selected-theme\.web\.tsx|[^/]+\.html)(:|$)' "$log" | head -n 120 || true

printf '%s\n' '--- focused class and theme references ---'
rg -n -C5 --glob '!src/app/**' --glob '!src/components/**' \
  'classList|documentElement|applyColorScheme|use-selected-theme|prefers-color-scheme|localStorage' . \
  | head -n 300 || true

printf '%s\n' '--- HTML files and relevant contents ---'
git ls-files | rg '\.html$' | while IFS= read -r file; do
  printf '\n--- %s ---\n' "$file"
  rg -n -C5 'dark|light|theme|colorScheme|script' "$file" || true
done

Repository: Resgrid/Dispatch

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -u

printf '%s\n' '--- tracked files with relevant names ---'
git ls-files | grep -E '(^|/)(theme-tokens\.css|global(\.web)?\.css|use-selected-theme(\.web)?\.tsx|[^/]+\.html)$' || true

printf '%s\n' '--- tracked source references, excluding generated and embedded sources ---'
git ls-files '*.ts' '*.tsx' '*.js' '*.jsx' '*.css' '*.html' \
  | grep -Ev '(^|/)(node_modules|dist|build|webview-scripts\.ts)/|\.map$' \
  | xargs -r rg -n -C3 \
    'classList|documentElement|applyColorScheme|use-selected-theme|prefers-color-scheme|localStorage|theme-tokens|global\.web\.css|global\.css' \
  | head -n 500

Repository: Resgrid/Dispatch

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -u

printf '%s\n' '--- relevant tracked paths ---'
git ls-files | grep -E '(^|/)(theme-tokens\.css|global(\.web)?\.css|use-selected-theme(\.web)?\.tsx|_layout\.tsx|\+html\.tsx|index\.web\.tsx)$' || true

for file in global.css global.web.css src/app/_layout.tsx src/app/+html.tsx src/components/ui/gluestack-ui-provider/index.tsx src/components/ui/gluestack-ui-provider/index.web.tsx src/lib/hooks/use-selected-theme.web.tsx src/lib/hooks/use-selected-theme.ts; do
  if [ -f "$file" ]; then
    printf '\n--- %s ---\n' "$file"
    cat -n "$file" | sed -n '1,180p'
  fi
done

printf '\n--- theme token locations ---\n'
git ls-files | grep 'theme-tokens\.css$' | while IFS= read -r file; do
  printf '%s\n' "$file"
  wc -l "$file"
done

Repository: Resgrid/Dispatch

Length of output: 19102


🏁 Script executed:

#!/bin/bash
set -u

for file in src/components/ui/gluestack-ui-provider/script.ts src/components/ui/gluestack-ui-provider/script.tsx src/app/_layout.tsx src/app/'(app)'/_layout.tsx; do
  if [ -f "$file" ]; then
    printf '\n--- %s ---\n' "$file"
    cat -n "$file" | sed -n '1,260p'
  fi
done

printf '\n--- provider and selected-theme call sites ---\n'
git ls-files '*.ts' '*.tsx' \
  | grep -Ev '(^|/)(node_modules|dist|build)/|webview-scripts\.ts|form-render-source\.ts' \
  | xargs -r rg -n -C4 'GluestackUIProvider|useSelectedTheme|loadSelectedTheme|selectedTheme'

Repository: Resgrid/Dispatch

Length of output: 41153


🏁 Script executed:

#!/bin/bash
python3 - <<'PY'
from pathlib import Path
import re

text = Path("theme-tokens.css").read_text()

def block_after(marker):
    start = text.index(marker)
    brace = text.index("{", start)
    depth = 0
    for i in range(brace, len(text)):
        if text[i] == "{":
            depth += 1
        elif text[i] == "}":
            depth -= 1
            if depth == 0:
                return text[brace + 1:i]
    raise ValueError(marker)

def declarations(block):
    return dict(re.findall(r"(--color-[\w-]+)\s*:\s*([^;]+);", block))

light_root = declarations(block_after(":root {"))
dark_media = declarations(block_after("`@media` (prefers-color-scheme: dark)"))
dark_class = declarations(block_after(":root.dark"))
light_class = declarations(block_after(":root.light"))

for name, values in [
    ("light :root vs :root.light", (light_root, light_class)),
    ("dark media vs :root.dark", (dark_media, dark_class)),
]:
    left, right = values
    missing = sorted(set(left) ^ set(right))
    changed = sorted(k for k in set(left) & set(right) if left[k] != right[k])
    print(name)
    print("  token counts:", len(left), len(right))
    print("  missing:", missing)
    print("  changed:", changed)
PY

Repository: Resgrid/Dispatch

Length of output: 3311


🏁 Script executed:

#!/bin/bash
python3 - <<'PY'
from pathlib import Path
import re

text = Path("theme-tokens.css").read_text()

def css_block(selector, occurrence=0):
    positions = [m.start() for m in re.finditer(re.escape(selector), text)]
    start = positions[occurrence]
    brace = text.index("{", start)
    depth = 0
    for i in range(brace, len(text)):
        if text[i] == "{":
            depth += 1
        elif text[i] == "}":
            depth -= 1
            if depth == 0:
                return text[brace + 1:i]
    raise ValueError(selector)

def declarations(block):
    return dict(re.findall(r"(--color-[\w-]+)\s*:\s*([^;]+);", block))

root = declarations(css_block(":root {", 0))
media = declarations(css_block("`@media` (prefers-color-scheme: dark)", 0))
dark = declarations(css_block(":root.dark", 0))
light = declarations(css_block(":root.light", 0))

print("root sample:", root.get("--color-primary-0"), root.get("--color-background-0"))
print("media sample:", media.get("--color-primary-0"), media.get("--color-background-0"))
print("dark sample:", dark.get("--color-primary-0"), dark.get("--color-background-0"))
print("light sample:", light.get("--color-primary-0"), light.get("--color-background-0"))

for name, left, right in [
    ("light :root vs :root.light", root, light),
    ("dark media vs :root.dark", media, dark),
]:
    changed = [(k, left[k], right[k]) for k in sorted(set(left) & set(right)) if left[k] != right[k]]
    print(name, "counts:", len(left), len(right), "changed:", len(changed))
    for item in changed[:8]:
        print(" ", item)
PY

Repository: Resgrid/Dispatch

Length of output: 863


🏁 Script executed:

#!/bin/bash
set -u

printf '%s\n' '--- selector occurrences ---'
rg -n -F ':root.dark' theme-tokens.css
rg -n -F ':root.light' theme-tokens.css
rg -n -F '`@media` (prefers-color-scheme: dark)' theme-tokens.css

printf '%s\n' '--- relevant token blocks ---'
sed -n '1,35p' theme-tokens.css
sed -n '140,155p' theme-tokens.css
sed -n '270,290p' theme-tokens.css
sed -n '400,420p' theme-tokens.css

Repository: Resgrid/Dispatch

Length of output: 3927


Avoid duplicating theme tokens

The light tokens are duplicated in :root and :root.light. The dark tokens are duplicated in the media query and :root.dark. Maintain one canonical token source and generate both selector blocks to prevent drift.

🤖 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 `@theme-tokens.css` around lines 146 - 539, Remove the duplicated light and
dark token declarations between :root, :root.light, and the prefers-color-scheme
dark block. Keep one canonical definition for each theme and generate or reuse
it for both the media-query and explicit theme selectors, preserving all
existing token values and selector behavior.

Source: Linters/SAST tools

@ucswift

ucswift commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

Approve

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This PR is approved.

@ucswift
ucswift merged commit 05d04fa into master Aug 13, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant