Skip to content

feat(theme): theme token contract and folder-based custom themes - #501

Merged
github-actions[bot] merged 1 commit into
router-for-me:miaomiaofrom
Roomen:feat/theme-contract
Oct 22, 2026
Merged

github-actions[bot] merged 1 commit into
router-for-me:miaomiaofrom
Roomen:feat/theme-contract

Conversation

@Roomen

@Roomen Roomen commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Rebased on miaomiao after #495 merged, so the branch is one commit on top of it.

  • Derive token kinds, the runtime whitelist, per-kind validation and theme.schema.json from src/themes/tokenContract.ts, and expose only appearance tokens: colors, opacity, corner radii, border widths and shadows. Size, spacing, layout and typography keys are ignored
  • Validate values by kind: corner radii up to 24px (radiusFull excepted), border widths from 0 to 4px, none or comma-separated shadows
  • Drop unknown or invalid tokens individually and keep the rest of the theme, with per-folder skip reasons and field-level diagnostics in the theme picker; broken JSON, invalid metadata, duplicate ids and unsupported schemaVersion still skip the whole file
  • Route buttons, switches, inputs, selects, tabs, dialogs and the shared surfaces through --border-width, --card-border-width and the radius and shadow tokens in the theme layer, so the default 1px/1px appearance is unchanged
  • Remove tokens without a consumer (--color-accent-contrast, --sidebar-thumb-bg, --theme-overlay-hover-background) and keep the theme overlay palette opaque
  • Validate icon and assets as relative theme-folder paths (.., absolute paths, URLs, backslashes and other extensions are dropped) without reading or serving the files
  • Load themes/<folder>/theme.json one level deep in Rust, skipping symlinked folders, symlinked theme.json and stray JSON, and rename fileName to folder across IPC, mocks and fixtures
  • Localize the folder hint, skip reasons and diagnostics in all four locales, and document the format, directory tree and themable controls in the three READMEs

Validation

  • bun run check
  • bun test: 1012 pass / 32 skip / 4 fail; the same 4 tests (uiLocalization 1, authFileQuotaPanel priority 2, stateControls 1) fail on miaomiao
  • bun run build
  • TMPDIR=/private/tmp/cpa-cargo-tmp cargo test --locked --manifest-path src-tauri/Cargo.toml: 931 pass / 1 fail / 9 ignored; the failure (agents::backups::tests::linked_configuration_and_backup_directories_are_rejected) also fails on miaomiao
  • node tests/theme-contract-ui.cjs (new): themed border widths, radii and shadows on real controls and surfaces, and full cleanup when switching to a theme that sets none of them
  • node tests/window-material-ui.cjs: 14 scenarios
  • node tests/theme-controls-ui.cjs
  • node tests/theme-stage2-regression-ui.cjs
  • node tests/theme-ui.cjs: the custom-theme assertions pass; the script later fails on a sidebar width assertion that also fails on feat/theme-tokens and miaomiao
  • Not run: native window material verification on a real desktop

Screenshots

Light
Dark
Sakura
Translucent

- Derive token kinds, the runtime whitelist, per-kind validation and `theme.schema.json` from `src/themes/tokenContract.ts`; expose only colors, opacity, radii, border widths and shadows, and ignore size, spacing, layout and typography keys
- Accept radius values up to 24px (`radiusFull` excepted) and border widths from 0 to 4px, and drop unknown or invalid tokens individually instead of rejecting the whole theme
- Route buttons, switches, inputs, selects, tabs, dialogs and the shared surfaces through `--border-width`, `--card-border-width` and the radius and shadow tokens in the theme layer
- Remove tokens without a consumer (`--color-accent-contrast`, `--sidebar-thumb-bg`, `--theme-overlay-hover-background`)
- Add `schemaVersion`, per-folder skip reasons and field-level diagnostics to the theme picker, and keep broken JSON, invalid metadata, duplicate ids and unsupported versions as whole-file skips
- Validate `icon` and `assets` as relative theme-folder paths without reading or serving the files
- Load `themes/<folder>/theme.json` one level deep in Rust, skip symlinked folders and `theme.json` files plus stray JSON, and rename `fileName` to `folder` across IPC, mocks and fixtures
- Localize the folder hint, skip reasons and diagnostics in all four locales
- Document the file format, directory tree and themable controls in the three READMEs

Validation: `bun run check`; `bun test` (991 pass, 32 skip, 4 baseline failures); `bun run build`; `cargo test --locked` (931 pass, 1 baseline failure); `node tests/theme-contract-ui.cjs`; `node tests/window-material-ui.cjs`; `node tests/theme-controls-ui.cjs`; `node tests/theme-stage2-regression-ui.cjs`.
@Roomen
Roomen force-pushed the feat/theme-contract branch from 6c1ef24 to 47a8341 Compare October 22, 2026 08:18
@github-actions
github-actions Bot merged commit 3fa9573 into router-for-me:miaomiao Oct 22, 2026
9 checks passed
@github-actions

Copy link
Copy Markdown

已自动合并并发布 Miaomiao Preview。

构建提交:3fa95738144b94b2a195abb3ac1d563753c9af76

下载页为滚动预览版,后续成功构建会替换安装包。

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