FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

docs: scope specimen faces and skip highlighting huge code blocks · unjs/unifont@7ebd676 · GitHub

/ unifont Public

Commit 7ebd676

Browse files
committed
docs: scope specimen faces and skip highlighting huge code blocks
1 parent ea2d1cb commit 7ebd676

3 files changed

Lines changed: 82 additions & 16 deletions

File tree

‎docs/app/components/CodeBlock.vue‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ const props = defineProps<{
1111
1212
const { $highlightCode } = useNuxtApp()
1313
14+
// MicroLighter matches its grammar synchronously, and the cost is not linear in the length of the
15+
// block: 60 kB of resolved CSS locks the main thread for minutes. Past this it renders plain.
16+
const HIGHLIGHT_LIMIT = 16_000
17+
const highlighted = computed(() => props.language && props.code.length <= HIGHLIGHT_LIMIT ? props.language : undefined)
18+
1419
// The highlighter walks the DOM once per navigation, so a block whose contents change in place
1520
// has to ask for another pass.
1621
watch(() => [props.code, props.language], () => {
@@ -111,7 +116,7 @@ onBeforeUnmount(() => clearTimeout(timer))
111116
ref="pre"
112117
class="block__pre"
113118
:tabindex="overflowing ? 0 : undefined"
114-
><code :class="language ? `language-${language}` : undefined">{{ code }}</code></pre>
119+
><code :class="highlighted ? `language-${highlighted}` : undefined">{{ code }}</code></pre>
115120
</div>
116121
</template>
117122

‎docs/app/pages/fonts/[family].vue‎

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,35 @@ const { drop } = useFontWarmup()
3030
const requested = computed(() => data.value?.requested)
3131
const properties = computed(() => data.value?.properties)
3232
33-
// The `preview` preset rather than the current selection, so the URL is stable for a family: it
34-
// can be warmed on hover, and narrowing a facet does not drop and re-add every `@font-face`.
35-
const stylesheet = computed(() => {
36-
const scoped = provider.value ? `&provider=${encodeURIComponent(provider.value)}` : ''
37-
return `/api/v1/fonts/${encodeURIComponent(family.value)}/css?preset=preview${scoped}`
38-
})
33+
const scopedProvider = computed(() => (provider.value ? `&provider=${encodeURIComponent(provider.value)}` : ''))
34+
35+
// One weight, subset to the specimen's own glyphs, and the URL the grid warms on hover.
36+
const warmStylesheet = computed(() =>
37+
`/api/v1/fonts/${encodeURIComponent(family.value)}/css?preset=warm${scopedProvider.value}`,
38+
)
39+
40+
/*
41+
* The `preview` preset rather than the current selection, so the URL is stable for a family and
42+
* narrowing a facet does not drop and re-add every `@font-face`. Aliased: declaring it under the
43+
* family name would restyle the specimen to a different file the moment it lands.
44+
*/
45+
const previewFamily = computed(() => `${family.value} preview`)
46+
const stylesheet = computed(() =>
47+
`/api/v1/fonts/${encodeURIComponent(family.value)}/css?preset=preview&as=${encodeURIComponent(previewFamily.value)}${scopedProvider.value}`,
48+
)
3949
4050
useHead(() => ({
41-
link: [{ rel: 'stylesheet', href: stylesheet.value }],
51+
link: [
52+
{ rel: 'stylesheet', href: warmStylesheet.value },
53+
{ rel: 'stylesheet', href: stylesheet.value },
54+
],
4255
}))
4356
44-
// The warm stylesheet paints the specimen until the preview sheet is both parsed and loaded.
45-
// `document.fonts.ready` alone resolves immediately when no request is yet in flight.
57+
// A hover-warmed sheet declares the same faces, so it is released only once this page's own copy
58+
// is parsed and loaded. `document.fonts.ready` alone resolves immediately when no request is yet
59+
// in flight.
4660
if (import.meta.client) {
47-
watch(stylesheet, async (href) => {
61+
watch(warmStylesheet, async (href) => {
4862
await nextTick()
4963
const link = document.head.querySelector<HTMLLinkElement>(`link[rel="stylesheet"][href="${href}"]`)
5064
if (link && !link.sheet) {
@@ -108,8 +122,17 @@ watch([loadedRange, staticWeights], () => {
108122
}
109123
}, { immediate: true })
110124
125+
// The warm face holds one weight and only the glyphs of the untouched specimen, so an edit moves
126+
// to the preview alias. On events, not on the values, which normalise on load for a family
127+
// without a 400.
128+
const usingPreview = ref(false)
129+
function needsPreviewFace() {
130+
usingPreview.value = true
131+
}
132+
const specimenFamily = computed(() => (usingPreview.value ? previewFamily.value : family.value))
133+
111134
const previewStyle = computed(() => ({
112-
fontFamily: `'${family.value}', '${family.value} fallback', var(--font-display)`,
135+
fontFamily: `'${specimenFamily.value}', '${specimenFamily.value} fallback', var(--font-display)`,
113136
fontSize: `${size.value}px`,
114137
fontWeight: String(weight.value),
115138
fontStyle: italic.value ? 'italic' : 'normal',
@@ -156,6 +179,7 @@ function setProvider(name: string) {
156179
if (name === selectedProvider.value) {
157180
return
158181
}
182+
needsPreviewFace()
159183
router.replace({
160184
query: {
161185
...route.query,
@@ -168,6 +192,7 @@ function setProvider(name: string) {
168192
}
169193
170194
function apply(key: 'weights' | 'subsets' | 'styles', values: string[]) {
195+
needsPreviewFace()
171196
router.replace({
172197
query: { ...route.query, [key]: values.length ? values.join(',') : undefined },
173198
})
@@ -251,12 +276,35 @@ const kb = (bytes: number) => `${(bytes / 1024).toFixed(1)} kB`
251276
/* ── Coverage ───────────────────────────────────────────── */
252277
interface CoverageCheck { text: string, unrestricted: boolean, covered: string[], missing: string[], subsets: string[] }
253278
type CoverageRow = Pick<CoverageCheck, 'text' | 'unrestricted' | 'missing'>
279+
const coverageSubsets = computed(() => requested.value?.subsets.join(',') ?? '')
280+
281+
// Aliased away from the specimen family, so a subset the selection dropped cannot paint the row.
282+
const coverageFamily = computed(() => `${family.value} coverage`)
283+
const coverageStylesheet = computed(() => {
284+
if (!coverageSubsets.value) {
285+
return undefined
286+
}
287+
const params = new URLSearchParams({
288+
weights: String(sampleWeight.value),
289+
styles: 'normal',
290+
subsets: coverageSubsets.value,
291+
as: coverageFamily.value,
292+
})
293+
if (provider.value) {
294+
params.set('provider', provider.value)
295+
}
296+
return `/api/v1/fonts/${encodeURIComponent(family.value)}/css?${params}`
297+
})
298+
299+
useHead(() => ({
300+
link: coverageStylesheet.value ? [{ rel: 'stylesheet', href: coverageStylesheet.value }] : [],
301+
}))
254302
const { data: coverage } = await useFetch(
255303
() => `/api/v1/fonts/${encodeURIComponent(family.value)}/coverage`,
256304
// Server-rendered: on the client the coverage rows land after hydration and shift the page.
257305
{
258306
key: () => `coverage-${family.value}`,
259-
query: { provider },
307+
query: { provider, subsets: coverageSubsets },
260308
// Only the gaps are rendered.
261309
transform: ({ checks }: { checks: Record<string, CoverageCheck> }): { checks: Record<string, CoverageRow> } => ({
262310
checks: Object.fromEntries(
@@ -464,6 +512,7 @@ export default defineNuxtConfig({
464512
rows="2"
465513
spellcheck="false"
466514
:style="previewStyle"
515+
@input="needsPreviewFace()"
467516
/>
468517
</div>
469518

@@ -519,6 +568,7 @@ export default defineNuxtConfig({
519568
:min="loadedRange.min"
520569
:max="loadedRange.max"
521570
step="1"
571+
@input="needsPreviewFace()"
522572
>
523573
</p>
524574
<p
@@ -529,6 +579,7 @@ export default defineNuxtConfig({
529579
<select
530580
id="weight"
531581
v-model.number="weight"
582+
@change="needsPreviewFace()"
532583
>
533584
<option
534585
v-for="value in staticWeights"
@@ -546,6 +597,7 @@ export default defineNuxtConfig({
546597
v-model="italic"
547598
type="checkbox"
548599
:disabled="!italicLoaded"
600+
@change="needsPreviewFace()"
549601
>
550602
<span
551603
v-if="!providerHasItalic"
@@ -713,7 +765,7 @@ export default defineNuxtConfig({
713765
Coverage
714766
</h3>
715767
<p class="meta__lede">
716-
Checked against the <code>unicode-range</code> of every face the provider returned.
768+
Checked against the <code>unicode-range</code> of every face this selection resolved.
717769
</p>
718770
<ul
719771
v-if="coverage"
@@ -728,7 +780,7 @@ export default defineNuxtConfig({
728780
<span
729781
class="coverage__sample"
730782
:style="{
731-
fontFamily: `'${family}', '${family} fallback', var(--font-display)`,
783+
fontFamily: `'${coverageFamily}', '${coverageFamily} fallback', var(--font-display)`,
732784
fontWeight: String(sampleWeight),
733785
}"
734786
>{{ check.text }}</span>

‎docs/server/api/v1/fonts/[family]/coverage.get.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ export const SAMPLES: Record<string, string> = {
1414
'maths': '≈ ≠ ≤ ≥ ± × ÷ ∑ √ ∞',
1515
}
1616

17+
function list(value: unknown) {
18+
if (typeof value !== 'string' || !value.trim()) {
19+
return undefined
20+
}
21+
return value.split(',').map(part => part.trim()).filter(Boolean)
22+
}
23+
1724
export default defineEventHandler(async (event) => {
1825
const family = decodeURIComponent(getRouterParam(event, 'family') || '')
1926
if (!family) {
@@ -32,7 +39,9 @@ export default defineEventHandler(async (event) => {
3239
// `unicode-range` does not vary by weight within a family.
3340
weights: ['400'],
3441
styles: ['normal'],
35-
subsets: properties.subsets?.length ? properties.subsets : ['latin'],
42+
// Absent, everything the provider publishes: the answer then describes the family, not a
43+
// selection.
44+
subsets: list(query.subsets) ?? (properties.subsets?.length ? properties.subsets : ['latin']),
3645
formats: ['woff2'],
3746
}, allowed)
3847

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL