Skip to content

fix(web): apply theme before first paint to avoid light flash and Dark Reader misdetection - #7571

Merged
Calcium-Ion merged 1 commit into
QuantumNous:mainfrom
RedwindA:fix/web-darkreader-theme-detection
Oct 17, 2026
Merged

Calcium-Ion merged 1 commit into
QuantumNous:mainfrom
RedwindA:fix/web-darkreader-theme-detection

Conversation

@RedwindA

@RedwindA RedwindA commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ 提交说明 / PR Notice

English template: .github/PULL_REQUEST_TEMPLATE/en.md

Important

  • 描述可用 AI 辅助。提交前请审阅并精炼全文,仅保留维护者需要复核的要点,并声明对其负责。PR 正文及后续评论均不得直接粘贴未经人工过滤的大段 AI 文本;多次提交此类内容可能会被 block。
  • 开启透传请勿反馈问题;透传会直接发送内容,不会经过 new-api 的逻辑处理。
  • 请按本模板填写后再提交。

🔗 关联任务 / Related Issue

  • None

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix)
  • ✨ 新功能 (New feature)
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

📝 变更描述 / Description

改善了暗色模式的用户体验

问题: 主题 class(html.dark / html.light)要等 React 挂载后才由 ThemeProvider 加上。

  1. 在深色模式下刷新页面,JS bundle 加载完成前页面会先白屏闪一下。
  2. Dark Reader 等浏览器扩展只在页面加载时检测一次 html.dark / color-scheme。它错过了主题 class,就会把 new-api 自带的深色主题再反色一遍。

改动:

  • web/index.html:在 <head> 中加入内联脚本,首次绘制前读取 localStorage['newapi:theme:v1:mode'],值为 system 或无效时跟随 prefers-color-scheme,再把 dark / light class 加到 <html> 上。解析逻辑与 ThemeProvider 保持一致。
  • web/src/styles/theme.css:.dark 增加 color-scheme: dark,Dark Reader 会读取这个属性。
  • 新增 web/src/context/__tests__/theme-boot-script.test.ts:直接从 index.html 提取脚本执行,覆盖 保存为 dark / 保存为 light 但系统为深色 / 未保存且系统为深色 / 保存值无效 四种情况。

📸 运行证明 / Proof of Work

1. 单元测试

$ cd web && bunx vitest run src/context/__tests__/theme-boot-script.test.ts
 Test Files  1 passed (1)
      Tests  4 passed (4)

2. 刷新白闪对比(录屏)

refresh-compare.mp4

慢速模式
image

3. Dark Reader 检测

image

✅ 提交前检查项 / Checklist

  • 人工确认: 无论描述是否由 AI 生成,我已审阅并精炼全文,仅保留可复核的要点,并声明对其准确性与完整性负责。PR 正文及后续评论均未直接粘贴未经人工过滤的大段 AI 文本。
  • 非重复提交: 我已搜索现有的 Issues 与 PRs,确认不是重复提交。
  • 新功能关联 Issue: 若此 PR 标记为 New feature,我已关联对应 Issue;若尚无 Issue,我已先自行创建。
  • 事前沟通: 若改动较大或涉及方向性变更,已在关联 Issue 中与维护者沟通并达成一致。
  • 功能范围: 本 PR 不是 Coding Plan、逆向渠道、第三方封装接口,也不是对 Codex 渠道类型的改动。
  • 非透传: 本 PR 不是针对开启透传后的转发行为;透传会直接发送内容,不会经过 new-api 的逻辑处理。
  • 范围聚焦: 本 PR 为一项聚焦改动,未包含无关代码。
  • 本地验证: 已按变更路径实际验证,并写明命令与观察结果。仅声明 go build 通过或测试通过,不视为有效证明。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

Summary by CodeRabbit

  • New Features

    • The selected theme now appears before the page’s first render, reducing flashes of the wrong theme.
    • Native controls now follow the dark theme.
  • Tests

    • Added coverage for saved theme preferences and system-mode fallback.

… dark mode

Dark Reader checks html.dark / color-scheme only once during page load,
but the theme class was added by ThemeProvider after React mounted.
Resolve the saved theme in an inline head script and declare
color-scheme: dark on .dark.
@coderabbitai

coderabbitai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: ea1c6b00-e9ff-491b-aab8-ad422e96ec82

📥 Commits

Reviewing files that changed from the base of the PR and between c2b7a9a and 86d5a85.

📒 Files selected for processing (3)
  • web/index.html
  • web/src/context/__tests__/theme-boot-script.test.ts
  • web/src/styles/theme.css

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


Walkthrough

The page now resolves and applies a theme before first paint. The dark theme declares color-scheme: dark, and tests cover saved, missing, and invalid theme preferences.

Changes

Theme initialization

Layer / File(s) Summary
Theme boot and dark styling
web/index.html, web/src/styles/theme.css, web/src/context/__tests__/theme-boot-script.test.ts
The inline script applies a saved dark or light preference, or uses the system preference when the saved value is missing or invalid. Storage errors fall back to system mode. The dark theme declares color-scheme: dark. Tests cover the preference cases.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: calcium-ion

Merge Risk: ⚪ Minimal · up to 86d5a

The saved or system theme is applied consistently before and after the app mounts. No identified issue prevents merging after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 86d5a

The new step reads a saved display preference and applies a theme class; it does not appear to expose data or grant authority. Whether every deployment permits the new inline script remains unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The new code’s independently influenceable input is the browser-local preference; its observed output is the document’s theme class, not a privileged operation or data-store write.

Trust Boundaries and Controls

  • observed — The boot script accepts only dark and light as stored outcomes and falls back to the system preference for other values; the runtime storage reader independently validates values against its allowed set.

Hardening Proposals

  • proposed — Where a deployment enforces a restrictive script policy, authorize this specific boot script without broadly permitting inline scripts.
🚥 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%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files. (2 skipped: 2 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary change: applying the theme before first paint to prevent a light flash and Dark Reader misdetection.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

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

A rabbit checks the morning light
And reads the saved theme just right
If none is found, the system guides
A dark mode paints the native sides
Before first paint, the colors gleam

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

@RedwindA
RedwindA marked this pull request as ready for review September 25, 2026 08:10
@RedwindA RedwindA changed the title fix(web): apply theme class before first paint so Dark Reader detects dark mode fix(web): apply theme before first paint to avoid light flash and Dark Reader misdetection Sep 25, 2026
@RedwindA
RedwindA marked this pull request as draft September 25, 2026 08:26
@RedwindA
RedwindA marked this pull request as ready for review September 25, 2026 08:29
@Calcium-Ion
Calcium-Ion merged commit b0a98ac into QuantumNous:main Oct 17, 2026
3 checks passed
shirosoralumie648 added a commit to shirosoralumie648/newapi-rev that referenced this pull request Oct 17, 2026
Sync 54 upstream commit(s) via merge (fork history preserved).
Behavior changes from upstream:
- 4013729 fix: disable sensitive word filtering by default and clear the default word list (QuantumNous#7814)

Upstream commits:
- 5e95951 feat(channels): add drag mode for channel priority ordering
- 5a59d69 fix(pricing): hide task prices left at zero when another price is set
- 3e2ce2c fix: align model card summary with status bar hours (QuantumNous#7847)
- b0a98ac fix(web): apply theme class before first paint so Dark Reader detects dark mode (QuantumNous#7571)
- f92c576 fix(web): cover Cloudflare clef and NVIDIA model families in provider rules (QuantumNous#7840)
- ff36846 fix(relayconvert): 保留 Chat/Claude → Responses 转换中 input_image 的 detail 字段 (QuantumNous#7855)
- 09109ec fix(middleware): 过载哨兵改用容器口径并加入连续性与新鲜度判定 (QuantumNous#7317)
- d032204 feat(aws): add Mantle Messages API support
- ca2cae6 fix(relaykit): emit summary on reasoning items announced without one (QuantumNous#7833)
- 45fbb14 fix(rate-limit): count each group with its own limit separately
- 6549a17 fix(dashboard): draw flow chart labels in the theme text color (QuantumNous#7821)
- c9179d3 fix(playground): support the reasoning field in chat responses (QuantumNous#7831)
- 908f94d fix(web): hide pages the user cannot open from console search
- 19d435e fix(relay): mask only the custom upstream address in error messages (QuantumNous#7822)
- 718a4e7 fix(vertex): use the multi-region hostnames for us and eu
- f196934 feat(plugins): bind Hailuo, Vidu and Veo plugins to New API channels
- bfdad29 fix(vidu): bill the model, duration and resolution Vidu generates
- 150ab3c chore(licenses): refresh the third-party dependency inventory
- 76cd511 build: upgrade moejs to v0.1.0-alpha.7
- 1b63c28 fix(claude): preserve explicit zero usage and cache-only billing (QuantumNous#7816)
- c9f7b62 fix(web): sort pricing models by input, output and per-request price (QuantumNous#7813)
- a0bf6a8 fix(vertex): pass project_id in API Key mode to avoid 404 on official endpoints (QuantumNous#6330)
- 4013729 fix: disable sensitive word filtering by default and clear the default word list (QuantumNous#7814)
- 7de10f2 feat(token): let an API key read its owner's quota
- ef60ef2 fix(relay): keep the cause of a failed body read for every later reader
- d10eafb fix(relay): report the real cause when a request body cannot be read
- 63cecab feat(channel): support Sub2API balance queries (QuantumNous#7749)
- b4be2a7 feat(ui): show newly added pricing groups at the top (QuantumNous#7782)
- 633b4c5 feat(logs): export usage logs from the common log page (QuantumNous#7774)
- dd59854 chore(deps-dev): bump undici from 6.28.0 to 6.29.0 in /electron (QuantumNous#7624)
- 93cf4ba chore(deps-dev): bump js-yaml from 4.3.1 to 4.3.2 in /electron (QuantumNous#7327)
- 3c9fb2a chore(deps-dev): bump brace-expansion from 1.1.16 to 1.1.21 in /electron (QuantumNous#7623)
- 9f04810 chore(deps): bump ai from 7.0.31 to 7.0.136 in /web (QuantumNous#7767)
- 3fe394c chore(deps): bump github.com/gorilla/websocket from 1.5.0 to 1.5.3 (QuantumNous#7001)
- 1c58f3d chore(deps-dev): bump fast-uri from 3.1.5 to 3.1.7 in /electron (QuantumNous#7169)
- 7f51a97 chore(deps-dev): bump js-yaml from 4.3.1 to 4.3.2 in /electron (QuantumNous#7327)
- 7e25797 chore(deps-dev): bump electron from 39.8.10 to 41.10.6 in /electron (QuantumNous#7625)
- bf4e1eb build(deps-dev): bump oxfmt from 0.57.0 to 0.72.0 in /web (QuantumNous#7766)
- 82306b3 build(deps-dev): bump @tanstack/router-plugin in /web (QuantumNous#7768)
- c730add build(deps): bump stream-markdown-parser from 1.1.3 to 1.2.18 in /web (QuantumNous#7769)
- b8c2dc1 build(deps): bump yace from 1.1.0 to 1.1.1 in /web (QuantumNous#7770)
- ba29f06 chore(deps-dev): bump @xmldom/xmldom from 0.8.13 to 0.8.15 in /electron (QuantumNous#7187)
- 767a82b fix(claude): forward thinking.block_binding and every anthropic-beta value (QuantumNous#7732)
- 2b85a8f fix(auth): show legal consent warning on password login (QuantumNous#7771)
- 2ed0587 fix(ci): stabilize checks and configure Bun dependency updates (QuantumNous#7765)
- 9948b1e fix(pricing): accept declared endpoint arrays in pricing conversion (QuantumNous#7724)
- 4bfcf28 chore(web): bump @lobehub/icons to 5.22.0 (QuantumNous#7764)
- fc08b76 fix(web): preserve recorded billing tiers in usage log previews (QuantumNous#7763)
- ba0e478 fix(relaykit): preserve assistant text with tool calls (QuantumNous#6871)
- 34b1942 fix(pricing): allow root access when the model square is disabled (QuantumNous#7761)
- b22a43b fix: mark task settlement logs with is_task for tiered billing display (QuantumNous#7446)
- bbf60b4 fix(logs): vertically align billing source icons on mobile (QuantumNous#7760)
- 3f926c3 fix(metrics): count completed streams after context cancellation (QuantumNous#7638)
- 3827fd8 fix(web): show cache read price on expression model cards (QuantumNous#7757)

Local tests not run (CI-only).
lizhongyi1209 pushed a commit to lizhongyi1209/new-api that referenced this pull request Oct 28, 2026
… dark mode (QuantumNous#7571)

Dark Reader checks html.dark / color-scheme only once during page load,
but the theme class was added by ThemeProvider after React mounted.
Resolve the saved theme in an inline head script and declare
color-scheme: dark on .dark.
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.

2 participants