mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(studio): actually load the five book fonts the picker offers (#2183)
The @fontsource packages for Inter, Lora, EB Garamond, Playfair and Bebas were declared but never imported, so every pick fell through to its fallback stack, in the print path too. The faces load with the lazy Studio chunk now, in the exact weights BOOK_FONTS uses, including the MuseoModerno 500/600 the picker offered and nobody shipped. A guard test derives the required imports from the registry.
This commit is contained in:
@@ -18,6 +18,7 @@ import { PeerBadges } from './PeerBadges'
|
||||
import { TrimField } from './TrimField'
|
||||
import '../../styles/dashboard.css'
|
||||
import '../../styles/studio.css'
|
||||
import './bookFontFaces'
|
||||
|
||||
/**
|
||||
* The Studio shell: top bar, page rail, workbench, inspector.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* The @font-face rules behind the book's typefaces.
|
||||
*
|
||||
* bookFonts.ts names the seven families; this is where the faces they need
|
||||
* arrive. Poppins comes with the app chrome in main.tsx and is not repeated
|
||||
* here, and neither are MuseoModerno's 400 and 700: importing a face twice only
|
||||
* duplicates every rule. Its 500 and 600 are the book's alone, which is why
|
||||
* they are down here rather than up there.
|
||||
*
|
||||
* Pulled in by StudioShell rather than by main.tsx: nothing outside the editor
|
||||
* is set in Lora or Bebas Neue, so the faces travel with the Studio chunk and
|
||||
* cost the app's first paint nothing. Once that chunk is loaded they are
|
||||
* ordinary stylesheets on the document, which is what lets printSheets copy
|
||||
* them into the print frame with everything else.
|
||||
*
|
||||
* Upright only, as everywhere else in the app: the editor's italic toggle has
|
||||
* always been the browser's synthetic slant, and a second file per weight is a
|
||||
* lot of download to change that.
|
||||
*/
|
||||
import '@fontsource/inter/400.css'
|
||||
import '@fontsource/inter/500.css'
|
||||
import '@fontsource/inter/600.css'
|
||||
import '@fontsource/inter/700.css'
|
||||
|
||||
import '@fontsource/lora/400.css'
|
||||
import '@fontsource/lora/500.css'
|
||||
import '@fontsource/lora/600.css'
|
||||
import '@fontsource/lora/700.css'
|
||||
|
||||
import '@fontsource/eb-garamond/400.css'
|
||||
import '@fontsource/eb-garamond/500.css'
|
||||
import '@fontsource/eb-garamond/600.css'
|
||||
import '@fontsource/eb-garamond/700.css'
|
||||
|
||||
import '@fontsource/playfair-display/400.css'
|
||||
import '@fontsource/playfair-display/500.css'
|
||||
import '@fontsource/playfair-display/600.css'
|
||||
import '@fontsource/playfair-display/700.css'
|
||||
|
||||
import '@fontsource/museomoderno/500.css'
|
||||
import '@fontsource/museomoderno/600.css'
|
||||
|
||||
import '@fontsource/bebas-neue/400.css'
|
||||
@@ -13,6 +13,10 @@
|
||||
* every stack ends in a generic rather than in another named font nobody
|
||||
* shipped. What the editor draws is what the renderer has.
|
||||
*
|
||||
* The faces themselves are loaded next door in bookFontFaces.ts. A family named
|
||||
* here and not imported there falls back to exactly the system font this file
|
||||
* was written against, which is how five of the seven shipped (#2183).
|
||||
*
|
||||
* ── Why these seven ───────────────────────────────────────────────────────
|
||||
*
|
||||
* A photo book needs more than one voice and fewer than a menu. Two workhorse
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { BOOK_FONTS_IDS } from '@trek/shared'
|
||||
import { BOOK_FONTS, type BookFontId } from '../../../src/components/Studio/bookFonts'
|
||||
// Loaded for real as well as read: a weight a family does not ship fails to
|
||||
// resolve here rather than in the build.
|
||||
import '../../../src/components/Studio/bookFontFaces'
|
||||
|
||||
/**
|
||||
* The faces behind the stacks (#2183).
|
||||
*
|
||||
* The stacks were right while five of the seven families were never loaded at
|
||||
* all, so Lora, EB Garamond and Playfair Display each came out as Georgia and
|
||||
* picking between them changed nothing on screen or on paper. The checks read
|
||||
* the imports themselves rather than a list of names copied out of them, which
|
||||
* is the kind of second copy that let this go a release unnoticed.
|
||||
*/
|
||||
|
||||
const APP = 'src/main.tsx'
|
||||
const STUDIO = 'src/components/Studio/bookFontFaces.ts'
|
||||
|
||||
// Vitest runs with the client package as its root, so cwd is stable here.
|
||||
const read = (rel: string) => readFileSync(resolve(process.cwd(), rel), 'utf8')
|
||||
|
||||
/** Every `@fontsource/<package>/<weight>.css` a file imports. */
|
||||
function facesIn(rel: string): string[] {
|
||||
return [...read(rel).matchAll(/@fontsource\/([a-z0-9-]+)\/(\d+)\.css/g)]
|
||||
.map(m => `${m[1]}/${m[2]}`)
|
||||
}
|
||||
|
||||
/** @fontsource names its packages after the family, lowercased and hyphenated. */
|
||||
function faceOf(id: BookFontId, weight: number): string {
|
||||
return `${BOOK_FONTS[id].name.toLowerCase().replace(/ /g, '-')}/${weight}`
|
||||
}
|
||||
|
||||
describe('the bundled faces', () => {
|
||||
it('loads every family the picker offers, in the weights it offers', () => {
|
||||
const loaded = [...facesIn(APP), ...facesIn(STUDIO)]
|
||||
for (const id of BOOK_FONTS_IDS) {
|
||||
for (const weight of BOOK_FONTS[id].weights) {
|
||||
expect(loaded, `${BOOK_FONTS[id].name} ${weight}`).toContain(faceOf(id, weight))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/* A weight nothing can ask for is a download nobody wanted. */
|
||||
it('loads no weight the registry does not declare', () => {
|
||||
const declared = BOOK_FONTS_IDS.flatMap(id => BOOK_FONTS[id].weights.map(w => faceOf(id, w)))
|
||||
for (const face of facesIn(STUDIO)) {
|
||||
expect(declared, face).toContain(face)
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
* The book's faces are a few hundred kilobytes that only the editor uses, so
|
||||
* they belong to the lazy Studio chunk. Loading them from main.tsx would put
|
||||
* them in front of every first paint, including the ones that never open a
|
||||
* book.
|
||||
*/
|
||||
it('rides the Studio chunk rather than the app shell', () => {
|
||||
expect(read('src/components/Studio/StudioShell.tsx')).toContain("import './bookFontFaces'")
|
||||
for (const face of facesIn(STUDIO)) {
|
||||
expect(facesIn(APP), face).not.toContain(face)
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user