fix(studio): text elements use the real font stacks, not the stale three-font map (#2183)

This commit is contained in:
Maurice
2026-09-01 17:39:46 +02:00
committed by Maurice
parent 674dededd3
commit 1e6f1fe95c
4 changed files with 34 additions and 18 deletions
+4 -3
View File
@@ -2,7 +2,8 @@ import type { CSSProperties } from 'react'
import type {
BookElement, BookIconElement, BookPageSetup, BookPhotoElement, BookShapeElement, BookSpread,
} from '@trek/shared'
import { FONT_STACKS, photoSrc } from './bookRender'
import { photoSrc } from './bookRender'
import { fontStack } from './bookFonts'
import { iconComponent } from './iconLibrary'
import { HOLED_SHAPES, SHAPE_PATHS, scalePath, unitPath } from './shapes'
import { TravelElementView } from './TravelElements'
@@ -223,7 +224,7 @@ function PhotoView({ el, big, print, dropLabel }: {
{roomy && dropLabel && (
<span
style={{
fontFamily: FONT_STACKS.sans,
fontFamily: fontStack('sans'),
fontSize: `${labelSize}mm`,
fontWeight: 600,
letterSpacing: '0.16em',
@@ -403,7 +404,7 @@ export function ElementView({
// pt, not px: the document speaks the print's language, and CSS knows
// the conversion exactly.
fontSize: `${el.size}pt`,
fontFamily: FONT_STACKS[el.font],
fontFamily: fontStack(el.font),
fontWeight: el.weight,
fontStyle: el.italic ? 'italic' : undefined,
lineHeight: el.leading,
@@ -4,7 +4,7 @@ import type { BookElement, BookPageSetup, BookSpread } from '@trek/shared'
import { SpreadFold, SpreadView } from './SpreadView'
import { PeerCursors } from './PeerCursors'
import type { PeerCursor } from './useBookPresence'
import { FONT_STACKS } from './bookRender'
import { fontStack } from './bookFonts'
import { useSpreadInteraction, type HandleId } from './useSpreadInteraction'
import { useStudioStore } from '../../store/studioStore'
import { useTranslation } from '../../i18n'
@@ -271,7 +271,7 @@ export function StudioCanvas({
height: el.frame.h * scaled,
// The document is in pt; the overlay is on screen at the same zoom.
fontSize: `${el.size * (96 / 72) * zoom}px`,
fontFamily: FONT_STACKS[el.font],
fontFamily: fontStack(el.font),
fontWeight: el.weight,
fontStyle: el.italic ? 'italic' : undefined,
lineHeight: el.leading,
+3 -13
View File
@@ -6,19 +6,9 @@
* exactly the kind of file you edit constantly while designing.
*/
/**
* The book's typefaces.
*
* Poppins ships with the client already; Georgia is a system serif. Print will
* need these self-hosted and identical on the server, or the renderer silently
* substitutes — which is what the current Journey PDF export does today, asking
* for Inter while only Poppins is bundled.
*/
export const FONT_STACKS: Record<string, string> = {
sans: '"Poppins", system-ui, sans-serif',
serif: 'Georgia, "Times New Roman", serif',
display: '"MuseoModerno", "Poppins", system-ui, sans-serif',
}
// The typefaces live in bookFonts.ts (fontStack et al.) — a second stack map
// here is exactly how #2183 happened: the renderer kept a three-font copy
// while the picker grew to seven, and everything else fell back to Poppins.
export function photoSrc(photoId: number, big: boolean): string {
return `/api/photos/${photoId}/${big ? 'original' : 'thumbnail'}`
@@ -388,3 +388,28 @@ describe('travel elements', () => {
expect(el.style.width).toBe('80mm')
})
})
describe('text typefaces (#2183)', () => {
const text = (font: string): BookElement => ({
...common, id: 't1', kind: 'text', text: 'A heading', font,
size: 11, weight: 400, italic: false, align: 'left',
leading: 1.45, tracking: 0, color: '#1a1a1a',
} as unknown as BookElement)
it('sets every picker font on the element, not just the three the old map knew', () => {
// The regression: a second stack map in bookRender.ts covered sans/serif/
// display only, so playfair and friends silently inherited Poppins.
expect(firstElement(draw([text('playfair')]).container)!.style.fontFamily).toContain('Playfair Display')
expect(firstElement(draw([text('inter')]).container)!.style.fontFamily).toContain('Inter')
expect(firstElement(draw([text('garamond')]).container)!.style.fontFamily).toContain('EB Garamond')
expect(firstElement(draw([text('bebas')]).container)!.style.fontFamily).toContain('Bebas Neue')
})
it('serif means Lora, matching the picker, not the bare system Georgia', () => {
expect(firstElement(draw([text('serif')]).container)!.style.fontFamily).toContain('Lora')
})
it('an unknown family still falls back to Poppins instead of the machine default', () => {
expect(firstElement(draw([text('comic-sans')]).container)!.style.fontFamily).toContain('Poppins')
})
})