fix(fonts): load Google font previews

This commit is contained in:
Danila Poyarkov 2026-05-19 13:02:15 +03:00
parent 59550c1c07
commit 83af1453d8
3 changed files with 46 additions and 4 deletions

View file

@ -2,6 +2,10 @@
## Unreleased ## Unreleased
### Fixes
- Fix the web font picker so Google Fonts remain available in Safari, local font access is requested on first open when supported, font sources are labeled, and Google font previews load lazily for visible rows.
## 0.12.0 — 2026-05-18 ## 0.12.0 — 2026-05-18
### Added ### Added

View file

@ -4,7 +4,12 @@ import { FontPickerRoot } from '@open-pencil/vue'
import { useSelectUI } from '@/components/ui/select' import { useSelectUI } from '@/components/ui/select'
import { usePopoverUI } from '@/components/ui/popover' import { usePopoverUI } from '@/components/ui/popover'
import { listFamilies, localFontAccessState, requestLocalFontAccess } from '@/app/editor/fonts' import {
listFamilies,
loadFont,
localFontAccessState,
requestLocalFontAccess
} from '@/app/editor/fonts'
import type { FontPickerUi } from '@open-pencil/vue' import type { FontPickerUi } from '@open-pencil/vue'
@ -29,10 +34,19 @@ const ui = computed<FontPickerUi>(() => ({
emptyAction: 'mt-2 rounded bg-accent px-2 py-1 text-xs font-medium text-white disabled:opacity-50' emptyAction: 'mt-2 rounded bg-accent px-2 py-1 text-xs font-medium text-white disabled:opacity-50'
})) }))
const previewFontLoads = new Set<string>()
const localFontAccess = { const localFontAccess = {
state: localFontAccessState, state: localFontAccessState,
load: requestLocalFontAccess load: requestLocalFontAccess
} }
function loadPreviewFont(family: string, source: string) {
if (source !== 'google') return
if (previewFontLoads.has(family)) return
previewFontLoads.add(family)
void loadFont(family)
}
</script> </script>
<template> <template>
@ -53,7 +67,11 @@ const localFontAccess = {
</template> </template>
<template #item="{ family, selected, source }"> <template #item="{ family, selected, source }">
<div data-test-id="font-picker-item" class="flex min-w-0 flex-1 items-center gap-2"> <div
data-test-id="font-picker-item"
class="flex min-w-0 flex-1 items-center gap-2"
@vue:mounted="loadPreviewFont(family, source)"
>
<icon-lucide-check v-if="selected" class="size-3 shrink-0 text-accent" /> <icon-lucide-check v-if="selected" class="size-3 shrink-0 text-accent" />
<span v-else class="size-3 shrink-0" /> <span v-else class="size-3 shrink-0" />
<span class="truncate" :style="{ fontFamily: `'${family}', sans-serif` }">{{ <span class="truncate" :style="{ fontFamily: `'${family}', sans-serif` }">{{

View file

@ -23,20 +23,31 @@ async function openFontPicker(page: Page) {
async function installGoogleFontsMock(page: Page, families = ['Inter', 'OpenPencil Google Font']) { async function installGoogleFontsMock(page: Page, families = ['Inter', 'OpenPencil Google Font']) {
await page.addInitScript((googleFamilies) => { await page.addInitScript((googleFamilies) => {
const win = window as Window & { __googleFontsFetchCount?: number } const win = window as Window & {
__googleFontsFetchCount?: number
__googleFontPreviewFetchCount?: number
}
win.__googleFontsFetchCount = 0 win.__googleFontsFetchCount = 0
win.__googleFontPreviewFetchCount = 0
const originalFetch = window.fetch.bind(window) const originalFetch = window.fetch.bind(window)
window.fetch = async (input, init) => { window.fetch = async (input, init) => {
let url: string let url: string
if (typeof input === 'string') url = input if (typeof input === 'string') url = input
else if (input instanceof URL) url = input.href else if (input instanceof URL) url = input.href
else url = input.url else url = input.url
if (url.startsWith('https://fonts.openpencil.test/')) {
win.__googleFontPreviewFetchCount = (win.__googleFontPreviewFetchCount ?? 0) + 1
return new Response(new ArrayBuffer(8), { status: 200 })
}
if (url.startsWith('https://www.googleapis.com/webfonts/v1/webfonts')) { if (url.startsWith('https://www.googleapis.com/webfonts/v1/webfonts')) {
if (!url.includes('family=')) if (!url.includes('family='))
win.__googleFontsFetchCount = (win.__googleFontsFetchCount ?? 0) + 1 win.__googleFontsFetchCount = (win.__googleFontsFetchCount ?? 0) + 1
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
items: googleFamilies.map((family) => ({ family })) items: googleFamilies.map((family) => ({
family,
files: { regular: `https://fonts.openpencil.test/${encodeURIComponent(family)}.ttf` }
}))
}), }),
{ status: 200, headers: { 'content-type': 'application/json' } } { status: 200, headers: { 'content-type': 'application/json' } }
) )
@ -109,6 +120,15 @@ test('font picker lists Google fonts when local font API is unavailable', async
await expect( await expect(
page.getByTestId('font-picker-item').filter({ hasText: 'OpenPencil Google Font' }) page.getByTestId('font-picker-item').filter({ hasText: 'OpenPencil Google Font' })
).toBeVisible() ).toBeVisible()
await expect
.poll(() =>
page.evaluate(
() =>
(window as Window & { __googleFontPreviewFetchCount?: number })
.__googleFontPreviewFetchCount
)
)
.toBeGreaterThan(0)
await expect(page.getByText('Local fonts are not available in this browser.')).toHaveCount(0) await expect(page.getByText('Local fonts are not available in this browser.')).toHaveCount(0)
}) })