FONTS: Maple Mono as the default monospace font #322

Closed
opened 2026-09-28 09:04:42 +00:00 by kayg · 10 comments
Owner

Owner (2026-09-28): 'default monospace font should be Maple Mono NF (the NF part works in the browser or not is for you to decide) https://github.com/subframe7536/maple-font'

Decision (orchestrator): ship Maple Mono (the plain web build, OFL-1.1, AGPL-compatible), not the NF (Nerd Font) build. The NF build only adds thousands of private-use icon glyphs (powerline, devicons) that calternal never renders; they would multiply the download for nothing. If a Nerd Font glyph ever appears in user text, the fallback stack shows it through the user's installed NF font: put 'Maple Mono NF' (a local() font, if installed) ahead of the web font in the stack, so users who have NF installed get it for free.
Build (reuse the #234 fonts system in Settings → Appearance → Text):

  • Self-host Maple Mono as variable woff2 (the wght axis; normal and italic), subset to Latin/Latin-ext like the other faces, with font-display: swap and fallback metrics (size-adjust/ascent-override) so switching causes no layout shift.
  • Make it the default Mono choice; keep the existing mono options in the dropdown. Existing users who never changed Mono get the new default; users who picked a font keep it.
  • Check the ligatures setting (Maple has coding ligatures): off by default in code blocks? Pick the calmer default and note it.
  • Licence: include OFL.txt with the font files and the credit in the NOTICE.
    Evidence: crops of a code block, the raw Markdown view, file paths/IDs and the Settings font picker, light and dark.
Owner (2026-09-28): 'default monospace font should be Maple Mono NF (the NF part works in the browser or not is for you to decide) https://github.com/subframe7536/maple-font' Decision (orchestrator): ship **Maple Mono** (the plain web build, OFL-1.1, AGPL-compatible), not the NF (Nerd Font) build. The NF build only adds thousands of private-use icon glyphs (powerline, devicons) that calternal never renders; they would multiply the download for nothing. If a Nerd Font glyph ever appears in user text, the fallback stack shows it through the user's installed NF font: put `'Maple Mono NF'` (a local() font, if installed) ahead of the web font in the stack, so users who have NF installed get it for free. Build (reuse the #234 fonts system in Settings → Appearance → Text): - Self-host Maple Mono as variable woff2 (the wght axis; normal and italic), subset to Latin/Latin-ext like the other faces, with `font-display: swap` and fallback metrics (size-adjust/ascent-override) so switching causes no layout shift. - Make it the **default Mono** choice; keep the existing mono options in the dropdown. Existing users who never changed Mono get the new default; users who picked a font keep it. - Check the ligatures setting (Maple has coding ligatures): off by default in code blocks? Pick the calmer default and note it. - Licence: include OFL.txt with the font files and the credit in the NOTICE. Evidence: crops of a code block, the raw Markdown view, file paths/IDs and the Settings font picker, light and dark.
Author
Owner

Starting #322 on branch job/maple-mono. Base SHA: 7bbaf5133c (dev at start).

Starting #322 on branch job/maple-mono. Base SHA: 7bbaf5133c00506deb0f421ef7a75f5f3c72b859 (dev at start).
Author
Owner

Research finding: upstream V7 provides separate plain (non-Nerd-Font) variable WOFF2 files for normal (130,908 bytes) and italic (152,816 bytes). Its OFL file credits the Maple Mono Project Authors and uses SIL OFL 1.1: upstream font files, OFL.txt. I will bundle the plain font and subset it to Latin and Latin Extended.

Compatibility decision: existing settings persist the old default, Spline Sans Mono, even when a User never changed the Mono choice. The old value cannot distinguish that default from a deliberate re-selection of Spline. I will map that legacy default to Maple, preserve existing JetBrains Mono and System choices, and mark new font writes so a new explicit Spline selection remains Spline.

Maple's code ligatures will be disabled in code blocks by default for calmer source reading. The User can still choose Maple Mono in Settings → Appearance → Text.

Research finding: upstream V7 provides separate plain (non-Nerd-Font) variable WOFF2 files for normal (130,908 bytes) and italic (152,816 bytes). Its OFL file credits the Maple Mono Project Authors and uses SIL OFL 1.1: [upstream font files](https://github.com/subframe7536/maple-font/tree/v7/woff2/var), [OFL.txt](https://github.com/subframe7536/maple-font/blob/v7/OFL.txt). I will bundle the plain font and subset it to Latin and Latin Extended. Compatibility decision: existing settings persist the old default, Spline Sans Mono, even when a User never changed the Mono choice. The old value cannot distinguish that default from a deliberate re-selection of Spline. I will map that legacy default to Maple, preserve existing JetBrains Mono and System choices, and mark new font writes so a new explicit Spline selection remains Spline. Maple's code ligatures will be disabled in code blocks by default for calmer source reading. The User can still choose Maple Mono in Settings → Appearance → Text.
Author
Owner

Font asset slice committed as 61a9a10c. Maple Mono V7 normal and italic variable WOFF2 faces are subset to Latin and Latin Extended (202,540 bytes total); FontTools confirms the wght 100–800 axis, coding ligatures on the Latin faces, and no private-use glyphs. The UI path in this worktree is Settings → Appearance → Fonts.

Font asset slice committed as 61a9a10c. Maple Mono V7 normal and italic variable WOFF2 faces are subset to Latin and Latin Extended (202,540 bytes total); FontTools confirms the wght 100–800 axis, coding ligatures on the Latin faces, and no private-use glyphs. The UI path in this worktree is Settings → Appearance → Fonts.
Author
Owner

Font asset slice committed as 61a9a10c. Maple Mono V7 normal and italic variable WOFF2 faces are subset to Latin and Latin Extended (202,540 bytes total). FontTools confirms the wght 100–800 axis, calt coding ligatures on the Latin faces, and no private-use glyphs. The UI path in this worktree is Settings → Appearance → Fonts.

Font asset slice committed as 61a9a10c. Maple Mono V7 normal and italic variable WOFF2 faces are subset to Latin and Latin Extended (202,540 bytes total). FontTools confirms the wght 100–800 axis, calt coding ligatures on the Latin faces, and no private-use glyphs. The UI path in this worktree is Settings → Appearance → Fonts.
Author
Owner

The appearance API migration is implemented. Existing settings stored the full font set when any font role changed, so legacy Spline cannot reveal whether Mono itself was selected. I treat an unversioned Spline value as the old default, preserve other stored mono choices, and mark new writes so a current explicit Spline choice stays intact. The focused server migration test passes: legacy Spline becomes Maple; JetBrains and System remain; versioned Spline remains. This ambiguity and migration rule are the design choice for owner review.

The appearance API migration is implemented. Existing settings stored the full font set when any font role changed, so legacy Spline cannot reveal whether Mono itself was selected. I treat an unversioned Spline value as the old default, preserve other stored mono choices, and mark new writes so a current explicit Spline choice stays intact. The focused server migration test passes: legacy Spline becomes Maple; JetBrains and System remain; versioned Spline remains. This ambiguity and migration rule are the design choice for owner review.
Author
Owner

The default and settings migration is committed as c97f6f61. The API schema and TypeScript client now include maple-mono. Focused checks passed: 5 web font tests and the Rust legacy migration test. I updated the adversarial appearance default assertion to the new Maple default; the valid Spline attack payloads remain in place.

The default and settings migration is committed as c97f6f61. The API schema and TypeScript client now include maple-mono. Focused checks passed: 5 web font tests and the Rust legacy migration test. I updated the adversarial appearance default assertion to the new Maple default; the valid Spline attack payloads remain in place.
Author
Owner

Production review evidence for Maple Mono, captured from the real production build at 2×. This comment has the light and dark sheets for desktop and phone:

  • maple-mono-light-desktop-2x.png
  • maple-mono-dark-desktop-2x.png
  • maple-mono-light-phone-2x.png
  • maple-mono-dark-phone-2x.png

The review run passed with 22 screenshots and 4 evidence sheets. No screenshots are committed.

Production review evidence for Maple Mono, captured from the real production build at 2×. This comment has the light and dark sheets for desktop and phone: - maple-mono-light-desktop-2x.png - maple-mono-dark-desktop-2x.png - maple-mono-light-phone-2x.png - maple-mono-dark-phone-2x.png The review run passed with 22 screenshots and 4 evidence sheets. No screenshots are committed.
Author
Owner

After merging dev at a8100be4, I rebuilt the production app and reran the review. The run passed with 22 screenshots and four refreshed 2× sheets. I replaced the earlier attachments on the evidence comment with the post-merge light/dark desktop and phone captures. The screenshots remain uncommitted under artifacts/.

After merging dev at a8100be4, I rebuilt the production app and reran the review. The run passed with 22 screenshots and four refreshed 2× sheets. I replaced the earlier attachments on the evidence comment with the post-merge light/dark desktop and phone captures. The screenshots remain uncommitted under artifacts/.
Author
Owner

The broad adversarial harness did not reach its appearance section. During the search-chaos setup, its log reported a 180-second restart-ready timeout and three owner passkey refresh failures. I had interrupted the slow harness setup while it was compiling extra CLI/sync targets; I cannot attribute these results to the Maple change from this run, and I am recording them for review rather than treating them as clean coverage.

A focused probe then ran once against the merged real local server. It passed: default Maple; hostile font IDs returned 422, 422, 413 and 422; malformed JSON returned 400; all 24 concurrent preference saves returned 200; explicit Spline persisted; readyz returned 200. No 5xx or server exit occurred.

The broad adversarial harness did not reach its appearance section. During the search-chaos setup, its log reported a 180-second restart-ready timeout and three owner passkey refresh failures. I had interrupted the slow harness setup while it was compiling extra CLI/sync targets; I cannot attribute these results to the Maple change from this run, and I am recording them for review rather than treating them as clean coverage. A focused probe then ran once against the merged real local server. It passed: default Maple; hostile font IDs returned 422, 422, 413 and 422; malformed JSON returned 400; all 24 concurrent preference saves returned 200; explicit Spline persisted; readyz returned 200. No 5xx or server exit occurred.
Author
Owner

Finished — Maple Mono default

Branch: job/maple-mono
Head: a8100be416d717a347f6995a03f44ac9bd825684 (also on origin/job/maple-mono)
Commits: 61a9a10c, c97f6f61, b5e97944; merged dev once before final gates.

Built

  • Bundled Maple Mono V7 variable WOFF2 faces for Latin and Latin Extended, normal and italic, with the OFL license and notices.
  • Set Maple Mono as the UI and server default. Added a versioned preference migration: old unversioned Spline defaults migrate to Maple; old JetBrains Mono and system choices persist; new explicit Spline choices persist.
  • Updated API schema and generated client types, font settings and tests, adversarial baseline, and production screenshot harness.
  • Captured four 2× light/dark desktop/phone evidence sheets. They are attached in the earlier evidence comment (comment 10491): maple-mono-light-desktop-2x.png, maple-mono-dark-desktop-2x.png, maple-mono-light-phone-2x.png, maple-mono-dark-phone-2x.png.

Files

NOTICE; apps/web/src/calternal-app.css; apps/web/static/fonts/{OFL-maple-mono.txt,README.md,maple-mono-latin-wght-normal.woff2,maple-mono-latin-wght-italic.woff2,maple-mono-latin-ext-wght-normal.woff2,maple-mono-latin-ext-wght-italic.woff2}; packages/ui/src/tokens.css; apps/web/src/app.html; apps/web/src/lib/appearance/background.svelte.ts; apps/web/src/lib/fonts.ts; apps/web/src/lib/fonts.test.ts; apps/web/src/lib/fontsGroup.svelte.test.ts; crates/calternal-server/src/appearance.rs; contracts/openapi.json; packages/api-client/src/generated.ts; tests/adversarial/attack.py; apps/web/e2e/fonts-review.mjs; docs/DESIGN.md.

Gates

  • cargo fmt --check — exit 0; no output.

  • cargo clippy --all-targets -- -D warnings — passed on retry after a shared sccache temporary-directory creation failure. Output: Finished \dev` profile [unoptimized + debuginfo] target(s) in 6m 22s`.

  • cargo test — exit 101. Final file-plugin library output:

    thread 'tokio-rt-worker' (2090527) panicked at crates/plugins/files/src/lib.rs:3803:45:
    atomic write 822 failed: entry not found
    
    failures:
        tests::internal_temp_paths_never_enter_index_during_atomic_write_reconcile_storm
    
    test result: FAILED. 119 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 319.41s
    
    error: test failed, to rerun pass `-p calternal-plugin-files --lib`
    

    The same test also timed out its five-minute completion assertion. I kept its expectation unchanged.

  • bun run check — passed:

    Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/maple-mono/apps/web
    Getting Svelte diagnostics...
    
    svelte-check found 0 errors and 0 warnings
    
  • bun run test — passed:

    Test Files  110 passed (110)
         Tests  711 passed (711)
       Duration  194.22s (transform 60%, environment 15%, import 14%, tests 8%, setup 3%)
    

    Vitest also printed jsdom scrollTo() and CSS parse warnings while passing.

  • One focused local appearance API probe passed: PASS local appearance probe: Maple default; hostile font IDs 422,422,413,422; malformed JSON 400; 24 concurrent saves returned 200; explicit Spline persisted; server ready.

  • The broader adversarial harness did not complete: it exited in search_chaos after SIGINT, with timed out waiting for restart-ready-4 after 1800 polls at 0.1s each and owner passkey refresh failures. It did not reach the appearance probe; the focused probe above covers that route.

Decisions and known gaps

The old settings format stored the full font preference object after changing any text role, so an unversioned Spline value cannot show whether Mono was explicitly selected or was the historical default. I treated that unversioned value as the old default and migrated it to Maple; new preferences carry a version marker so an explicit Spline choice is preserved. This compatibility choice is not specified in the design doc.

The full Rust suite has the file-service stress failure above, and the broad adversarial run is incomplete. The focused appearance API probe and both web gates passed. The repository is clean; cargo clean removed 15.7 GiB, and apps/web/build and apps/web/.svelte-kit were removed.

## Finished — Maple Mono default **Branch:** `job/maple-mono` **Head:** `a8100be416d717a347f6995a03f44ac9bd825684` (also on `origin/job/maple-mono`) **Commits:** `61a9a10c`, `c97f6f61`, `b5e97944`; merged `dev` once before final gates. ### Built - Bundled Maple Mono V7 variable WOFF2 faces for Latin and Latin Extended, normal and italic, with the OFL license and notices. - Set Maple Mono as the UI and server default. Added a versioned preference migration: old unversioned Spline defaults migrate to Maple; old JetBrains Mono and system choices persist; new explicit Spline choices persist. - Updated API schema and generated client types, font settings and tests, adversarial baseline, and production screenshot harness. - Captured four 2× light/dark desktop/phone evidence sheets. They are attached in the earlier evidence comment (comment 10491): `maple-mono-light-desktop-2x.png`, `maple-mono-dark-desktop-2x.png`, `maple-mono-light-phone-2x.png`, `maple-mono-dark-phone-2x.png`. ### Files `NOTICE`; `apps/web/src/calternal-app.css`; `apps/web/static/fonts/{OFL-maple-mono.txt,README.md,maple-mono-latin-wght-normal.woff2,maple-mono-latin-wght-italic.woff2,maple-mono-latin-ext-wght-normal.woff2,maple-mono-latin-ext-wght-italic.woff2}`; `packages/ui/src/tokens.css`; `apps/web/src/app.html`; `apps/web/src/lib/appearance/background.svelte.ts`; `apps/web/src/lib/fonts.ts`; `apps/web/src/lib/fonts.test.ts`; `apps/web/src/lib/fontsGroup.svelte.test.ts`; `crates/calternal-server/src/appearance.rs`; `contracts/openapi.json`; `packages/api-client/src/generated.ts`; `tests/adversarial/attack.py`; `apps/web/e2e/fonts-review.mjs`; `docs/DESIGN.md`. ### Gates - `cargo fmt --check` — exit 0; no output. - `cargo clippy --all-targets -- -D warnings` — passed on retry after a shared sccache temporary-directory creation failure. Output: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 6m 22s`. - `cargo test` — exit 101. Final file-plugin library output: ```text thread 'tokio-rt-worker' (2090527) panicked at crates/plugins/files/src/lib.rs:3803:45: atomic write 822 failed: entry not found failures: tests::internal_temp_paths_never_enter_index_during_atomic_write_reconcile_storm test result: FAILED. 119 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 319.41s error: test failed, to rerun pass `-p calternal-plugin-files --lib` ``` The same test also timed out its five-minute completion assertion. I kept its expectation unchanged. - `bun run check` — passed: ```text Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/maple-mono/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` - `bun run test` — passed: ```text Test Files 110 passed (110) Tests 711 passed (711) Duration 194.22s (transform 60%, environment 15%, import 14%, tests 8%, setup 3%) ``` Vitest also printed jsdom `scrollTo()` and CSS parse warnings while passing. - One focused local appearance API probe passed: `PASS local appearance probe: Maple default; hostile font IDs 422,422,413,422; malformed JSON 400; 24 concurrent saves returned 200; explicit Spline persisted; server ready`. - The broader adversarial harness did not complete: it exited in `search_chaos` after SIGINT, with `timed out waiting for restart-ready-4 after 1800 polls at 0.1s each` and owner passkey refresh failures. It did not reach the appearance probe; the focused probe above covers that route. ### Decisions and known gaps The old settings format stored the full font preference object after changing any text role, so an unversioned Spline value cannot show whether Mono was explicitly selected or was the historical default. I treated that unversioned value as the old default and migrated it to Maple; new preferences carry a version marker so an explicit Spline choice is preserved. This compatibility choice is not specified in the design doc. The full Rust suite has the file-service stress failure above, and the broad adversarial run is incomplete. The focused appearance API probe and both web gates passed. The repository is clean; `cargo clean` removed 15.7 GiB, and `apps/web/build` and `apps/web/.svelte-kit` were removed.
kayg closed this issue 2026-09-28 13:13:26 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#322
No description provided.