mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 05:11:49 +01:00
fix(runner): self-host Phase 7 thread fonts
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
e995b59933
commit
011d5046b0
12 files changed
+301
-46
No files matched your search
@@ -51,6 +51,51 @@ async function seriousAxeViolations(page: Page): Promise<AxeViolation[]> {
|
||||
}
|
||||
|
||||
test.describe("Phase 7G issue thread", () => {
|
||||
test("loads the bundled sans and mono faces before the thread settles", async ({ page }) => {
|
||||
await open(page, "thread-baseline");
|
||||
|
||||
const probe = await page.evaluate(async () => {
|
||||
const normalizeFamily = (family: string) => family.trim().replace(/^['"]|['"]$/g, "");
|
||||
const styles = getComputedStyle(document.documentElement);
|
||||
const sansFamily = normalizeFamily(styles.getPropertyValue("--pit-font-sans").split(",")[0]);
|
||||
const monoFamily = normalizeFamily(styles.getPropertyValue("--pit-font-mono").split(",")[0]);
|
||||
const symbolFamily = normalizeFamily(styles.getPropertyValue("--pit-font-symbols"));
|
||||
await document.fonts.load(`400 16px "${symbolFamily}"`, "◐⏳\uFE0E");
|
||||
const statuses = (family: string) =>
|
||||
[...document.fonts]
|
||||
.filter((face) => normalizeFamily(face.family) === family)
|
||||
.map((face) => face.status);
|
||||
|
||||
return {
|
||||
sansFamily,
|
||||
monoFamily,
|
||||
symbolFamily,
|
||||
sansStatuses: statuses(sansFamily),
|
||||
monoStatuses: statuses(monoFamily),
|
||||
symbolStatuses: statuses(symbolFamily),
|
||||
sansWeightsLoaded: [400, 500, 600, 700].every((weight) =>
|
||||
document.fonts.check(`${weight} 16px "${sansFamily}"`, "Paperclip"),
|
||||
),
|
||||
monoWeightsLoaded: [400, 700].every((weight) =>
|
||||
document.fonts.check(`${weight} 16px "${monoFamily}"`, "PAP-17003"),
|
||||
),
|
||||
symbolsLoaded: document.fonts.check(`400 16px "${symbolFamily}"`, "◐⏳\uFE0E"),
|
||||
};
|
||||
});
|
||||
|
||||
expect(probe).toEqual({
|
||||
sansFamily: "Paperclip Issue Thread Inter",
|
||||
monoFamily: "Paperclip Issue Thread DejaVu Sans Mono",
|
||||
symbolFamily: "Paperclip Issue Thread Symbols",
|
||||
sansStatuses: ["loaded"],
|
||||
monoStatuses: ["loaded", "loaded"],
|
||||
symbolStatuses: ["loaded", "loaded"],
|
||||
sansWeightsLoaded: true,
|
||||
monoWeightsLoaded: true,
|
||||
symbolsLoaded: true,
|
||||
});
|
||||
});
|
||||
|
||||
test("baseline thread renders identity, turns, and the §3 item types", async ({ page }) => {
|
||||
await open(page, "thread-baseline");
|
||||
|
||||
|
||||
@@ -240,14 +240,27 @@ export function App() {
|
||||
useLayoutEffect(() => {
|
||||
// A deep link has to land on what it addressed. With `rec` that is the
|
||||
// record; without it, the opened section's header — otherwise the link
|
||||
// half-arrives with the section still below the fold. Layout effect so the
|
||||
// scroll is committed before paint and a capture can never race it.
|
||||
// half-arrives with the section still below the fold. Wait for the bundled
|
||||
// faces before measuring: a host fallback can otherwise move the retained
|
||||
// scroll position by a pixel even after the final webfonts replace it.
|
||||
if (route.panel === null || snapshot === null) return;
|
||||
const target =
|
||||
route.record === null
|
||||
? document.querySelector(`[data-evidence-section="${CSS.escape(route.panel)}"]`)
|
||||
: document.querySelector(`[data-record-id="${CSS.escape(route.record)}"]`);
|
||||
if (target !== null) scrollEvidenceIntoView(target, route.record === null ? "start" : "center");
|
||||
let cancelled = false;
|
||||
void (async () => {
|
||||
if (typeof document.fonts?.ready?.then === "function") {
|
||||
await document.fonts.ready;
|
||||
}
|
||||
if (cancelled) return;
|
||||
const target =
|
||||
route.record === null
|
||||
? document.querySelector(`[data-evidence-section="${CSS.escape(route.panel)}"]`)
|
||||
: document.querySelector(`[data-record-id="${CSS.escape(route.record)}"]`);
|
||||
if (target !== null) {
|
||||
scrollEvidenceIntoView(target, route.record === null ? "start" : "center");
|
||||
}
|
||||
})();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [route.panel, route.record, snapshot, panelOpen, segment, openSections]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# Phase 7 issue-thread fonts
|
||||
|
||||
The Phase 7 issue-thread bundle includes Latin-subset WOFF2 files so captures
|
||||
do not depend on fonts installed on the host.
|
||||
|
||||
## Inter
|
||||
|
||||
- Upstream: https://github.com/rsms/inter
|
||||
- Version: 4.1
|
||||
- Source: `ui/public/fonts/InterVariable.woff2`
|
||||
- License: SIL Open Font License 1.1
|
||||
- License text: https://github.com/rsms/inter/blob/v4.1/LICENSE.txt
|
||||
|
||||
The bundled file is subset from the repository's unmodified upstream Inter 4.1
|
||||
variable WOFF2. CSS exposes weights 400 through 700.
|
||||
|
||||
## DejaVu Sans Mono
|
||||
|
||||
- Upstream: https://dejavu-fonts.github.io/
|
||||
- Version: 2.37
|
||||
- Source faces: DejaVu Sans Mono Book and Bold; DejaVu Sans Book for status glyphs
|
||||
- License: Bitstream Vera Fonts license; DejaVu changes are public domain
|
||||
- License text: https://dejavu-fonts.github.io/License.html
|
||||
|
||||
The bundled files are Latin subsets of the Book and Bold faces. The source font
|
||||
copyright and permission notice follow.
|
||||
|
||||
> Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a
|
||||
> trademark of Bitstream, Inc.
|
||||
>
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
> of the fonts accompanying this license ("Fonts") and associated documentation
|
||||
> files (the "Font Software"), to reproduce and distribute the Font Software,
|
||||
> including without limitation the rights to use, copy, merge, publish,
|
||||
> distribute, and/or sell copies of the Font Software, and to permit persons to
|
||||
> whom the Font Software is furnished to do so, subject to the following
|
||||
> conditions:
|
||||
>
|
||||
> The above copyright and trademark notices and this permission notice shall be
|
||||
> included in all copies of one or more of the Font Software typefaces.
|
||||
>
|
||||
> The Font Software may be modified, altered, or added to, and in particular the
|
||||
> designs of glyphs or characters in the Fonts may be modified and additional
|
||||
> glyphs or characters may be added to the Fonts, only if the fonts are renamed
|
||||
> to names not containing either the words "Bitstream" or the word "Vera".
|
||||
>
|
||||
> This License becomes null and void to the extent applicable to Fonts or Font
|
||||
> Software that has been modified and is distributed under the "Bitstream Vera"
|
||||
> names.
|
||||
>
|
||||
> The Font Software may be sold as part of a larger software package but no copy
|
||||
> of one or more of the Font Software typefaces may be sold by itself.
|
||||
>
|
||||
> THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
> OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
|
||||
> TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION
|
||||
> BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL,
|
||||
> SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION
|
||||
> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO
|
||||
> USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
>
|
||||
> Except as contained in this notice, the names of Gnome, the Gnome Foundation,
|
||||
> and Bitstream Inc., shall not be used in advertising or otherwise to promote
|
||||
> the sale, use or other dealings in the Font Software without prior written
|
||||
> authorization from the Gnome Foundation or Bitstream Inc., respectively.
|
||||
|
||||
## Noto Sans Symbols 2
|
||||
|
||||
- Upstream: https://github.com/notofonts/symbols
|
||||
- Version: 2.003
|
||||
- Source face: Noto Sans Symbols 2 Regular
|
||||
- License: SIL Open Font License 1.1
|
||||
- License text: https://github.com/notofonts/symbols/blob/main/OFL.txt
|
||||
|
||||
The 1.2 kB bundled subset contains only the two hourglass glyphs that are not
|
||||
present in Inter or DejaVu Sans. It is exposed under the same package-specific
|
||||
symbol fallback family using a disjoint `unicode-range`.
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -6,6 +6,52 @@
|
||||
* raw color literals — `pnpm check:browser-tokens` enforces that.
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: "Paperclip Issue Thread Inter";
|
||||
src: url("./fonts/inter-latin.woff2") format("woff2");
|
||||
font-style: normal;
|
||||
font-weight: 400 700;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Paperclip Issue Thread DejaVu Sans Mono";
|
||||
src: url("./fonts/dejavu-sans-mono-latin-400.woff2") format("woff2");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Paperclip Issue Thread DejaVu Sans Mono";
|
||||
src: url("./fonts/dejavu-sans-mono-latin-700.woff2") format("woff2");
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
/* Inter intentionally omits several status glyphs. Keep those deterministic
|
||||
with two tiny, disjoint symbol subsets instead of consulting host fonts. */
|
||||
@font-face {
|
||||
font-family: "Paperclip Issue Thread Symbols";
|
||||
src: url("./fonts/dejavu-sans-symbols.woff2") format("woff2");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
unicode-range: U+00A7, U+00B7, U+2013-2014, U+2022, U+2026, U+203A, U+2192,
|
||||
U+22EF, U+2304, U+25AC, U+25B2, U+25B6, U+25BC, U+25C6, U+25CB, U+25D0-25D1,
|
||||
U+2699, U+26A0, U+2713, U+2715, U+275A;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Paperclip Issue Thread Symbols";
|
||||
src: url("./fonts/noto-sans-symbols2-hourglass.woff2") format("woff2");
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
unicode-range: U+231B, U+23F3, U+FE0E;
|
||||
}
|
||||
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
|
||||
@@ -41,8 +87,9 @@
|
||||
--pit-ring: oklch(0.78 0.11 275);
|
||||
|
||||
/* Type ramp */
|
||||
--pit-font-sans: "Inter", "DejaVu Sans", ui-sans-serif, system-ui, sans-serif;
|
||||
--pit-font-mono: "DejaVu Sans Mono", ui-monospace, "SFMono-Regular", monospace;
|
||||
--pit-font-sans: "Paperclip Issue Thread Inter", "Paperclip Issue Thread Symbols", "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||
--pit-font-mono: "Paperclip Issue Thread DejaVu Sans Mono", "Paperclip Issue Thread Symbols", "DejaVu Sans Mono", ui-monospace, monospace;
|
||||
--pit-font-symbols: "Paperclip Issue Thread Symbols";
|
||||
--pit-text-xs: 0.75rem;
|
||||
--pit-text-sm: 0.875rem;
|
||||
--pit-text-base: 1rem;
|
||||
@@ -94,6 +141,7 @@ body {
|
||||
*/
|
||||
body {
|
||||
font-family: var(--pit-font-sans);
|
||||
font-variant-emoji: text;
|
||||
font-size: var(--pit-text-sm);
|
||||
line-height: var(--pit-leading-normal);
|
||||
}
|
||||
|
||||
@@ -168,17 +168,17 @@ Hosts without the Playwright chromium system libraries can either run
|
||||
`pnpm --filter @paperclipai/paperclip-runner verify:rootless` or set
|
||||
`PAPERCLIP_RUNNER_CHROMIUM_PATH` to a preinstalled Chromium.
|
||||
|
||||
The committed PNGs are pinned to the Chromium build and font environment listed
|
||||
in `knowledge/evidence/phase-07/ui/index.md`, so `check:phase7:ui` needs that
|
||||
same browser and font set. Point `PAPERCLIP_RUNNER_CHROMIUM_PATH` at the
|
||||
recorded browser before comparing — and when that path is the agent-browser
|
||||
wrapper, also set `PAPERCLIP_CHROMIUM_BIN` to the exact binary, because the
|
||||
wrapper otherwise picks the newest installed Playwright Chromium. A different
|
||||
build or font set makes every shot mismatch at once, which reads like a
|
||||
regression but is not one. The recorder probes that `Inter` resolves distinctly
|
||||
from the DejaVu Sans fallback and refuses to record or compare when it does
|
||||
not; the drift report prints the Chromium version and font probe it recorded
|
||||
with.
|
||||
The committed PNGs are pinned to the Chromium build listed in
|
||||
`knowledge/evidence/phase-07/ui/index.md`, so `check:phase7:ui` needs that same
|
||||
browser. Point `PAPERCLIP_RUNNER_CHROMIUM_PATH` at the recorded browser before
|
||||
comparing — and when that path is the agent-browser wrapper, also set
|
||||
`PAPERCLIP_CHROMIUM_BIN` to the exact binary, because the wrapper otherwise
|
||||
picks the newest installed Playwright Chromium. The issue-thread bundle
|
||||
self-hosts its Latin Inter and DejaVu Sans Mono WOFF2 faces plus tiny status-glyph
|
||||
subsets, so host fontconfig directories do not participate in capture. The recorder probes the
|
||||
package-specific bundled families and refuses to record or compare when either
|
||||
face is absent or fails to load; the drift report prints the Chromium version
|
||||
and bundled-font probe it recorded with.
|
||||
|
||||
## Accessibility
|
||||
|
||||
|
||||
@@ -81,30 +81,95 @@ async function startPreview() {
|
||||
|
||||
// Set by `record` so a drift report can name the Chromium build that produced it.
|
||||
let browserVersion = "unknown";
|
||||
let fontProbe = { interWidth: 0, dejavuWidth: 0, interResolves: false };
|
||||
let fontProbe = {
|
||||
sansFamily: "Paperclip Issue Thread Inter",
|
||||
monoFamily: "Paperclip Issue Thread DejaVu Sans Mono",
|
||||
symbolFamily: "Paperclip Issue Thread Symbols",
|
||||
interWidth: 0,
|
||||
serifWidth: 0,
|
||||
monoWidth: 0,
|
||||
sansStatuses: [],
|
||||
monoStatuses: [],
|
||||
symbolStatuses: [],
|
||||
interResolves: false,
|
||||
monoResolves: false,
|
||||
symbolsResolve: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* The committed matrix is recorded with Inter active. When fontconfig cannot
|
||||
* resolve Inter, every text node falls back to DejaVu Sans and all 24 shots
|
||||
* drift at once — refuse to record or compare under that environment instead
|
||||
* of producing a whole-page mystery diff.
|
||||
* Prove that the Vite-managed font faces registered and loaded from the built
|
||||
* bundle. The family names are package-specific, so an ambient host font can
|
||||
* never make this probe pass when either asset is missing.
|
||||
*/
|
||||
async function probeFonts(browser) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const probe = await page.evaluate(() => {
|
||||
const canvas = document.createElement("canvas").getContext("2d");
|
||||
await page.goto(routeFor("thread-baseline", "desktop"));
|
||||
await page.waitForSelector('[data-thread-state="settled"]', { timeout: 30_000 });
|
||||
const probe = await page.evaluate(async () => {
|
||||
const normalizeFamily = (family) => family.trim().replace(/^['"]|['"]$/g, "");
|
||||
const styles = getComputedStyle(document.documentElement);
|
||||
const sansFamily = normalizeFamily(styles.getPropertyValue("--pit-font-sans").split(",")[0]);
|
||||
const monoFamily = normalizeFamily(styles.getPropertyValue("--pit-font-mono").split(",")[0]);
|
||||
const symbolFamily = normalizeFamily(styles.getPropertyValue("--pit-font-symbols"));
|
||||
const sample = "Wire the runner spike to the mock control plane";
|
||||
const requested = [
|
||||
...[400, 500, 600, 700].map((weight) => ({ family: sansFamily, weight })),
|
||||
...[400, 700].map((weight) => ({ family: monoFamily, weight })),
|
||||
{ family: symbolFamily, weight: 400, sample: "◐⏳\uFE0E" },
|
||||
];
|
||||
const loadResults = await Promise.all(
|
||||
requested.map(async ({ family, weight, sample: faceSample }) => {
|
||||
try {
|
||||
const loaded = await document.fonts.load(
|
||||
`${weight} 24px "${family}"`,
|
||||
faceSample ?? sample,
|
||||
);
|
||||
return loaded.length > 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
);
|
||||
await document.fonts.ready;
|
||||
|
||||
const faceStatuses = (family) =>
|
||||
[...document.fonts]
|
||||
.filter((face) => normalizeFamily(face.family) === family)
|
||||
.map((face) => face.status);
|
||||
const canvas = document.createElement("canvas").getContext("2d");
|
||||
const width = (font) => {
|
||||
canvas.font = font;
|
||||
return Math.round(canvas.measureText(sample).width);
|
||||
};
|
||||
const interWidth = width("700 24px Inter");
|
||||
const dejavuWidth = width('700 24px "DejaVu Sans"');
|
||||
const sansStatuses = faceStatuses(sansFamily);
|
||||
const monoStatuses = faceStatuses(monoFamily);
|
||||
const symbolStatuses = faceStatuses(symbolFamily);
|
||||
return {
|
||||
interWidth,
|
||||
dejavuWidth,
|
||||
interResolves: document.fonts.check("16px Inter") && interWidth !== dejavuWidth,
|
||||
sansFamily,
|
||||
monoFamily,
|
||||
symbolFamily,
|
||||
interWidth: width(`700 24px "${sansFamily}"`),
|
||||
serifWidth: width("700 24px serif"),
|
||||
monoWidth: width(`400 24px "${monoFamily}"`),
|
||||
sansStatuses,
|
||||
monoStatuses,
|
||||
symbolStatuses,
|
||||
interResolves:
|
||||
sansFamily === "Paperclip Issue Thread Inter" &&
|
||||
sansStatuses.length === 1 &&
|
||||
sansStatuses.every((status) => status === "loaded") &&
|
||||
loadResults.slice(0, 4).every(Boolean),
|
||||
monoResolves:
|
||||
monoFamily === "Paperclip Issue Thread DejaVu Sans Mono" &&
|
||||
monoStatuses.length === 2 &&
|
||||
monoStatuses.every((status) => status === "loaded") &&
|
||||
loadResults.slice(4, 6).every(Boolean),
|
||||
symbolsResolve:
|
||||
symbolFamily === "Paperclip Issue Thread Symbols" &&
|
||||
symbolStatuses.length === 2 &&
|
||||
symbolStatuses.every((status) => status === "loaded") &&
|
||||
loadResults[6] === true,
|
||||
};
|
||||
});
|
||||
await context.close();
|
||||
@@ -122,12 +187,13 @@ async function record(targetDir) {
|
||||
const recorded = [];
|
||||
try {
|
||||
fontProbe = await probeFonts(browser);
|
||||
if (!fontProbe.interResolves) {
|
||||
if (!fontProbe.interResolves || !fontProbe.monoResolves || !fontProbe.symbolsResolve) {
|
||||
throw new Error(
|
||||
`font environment mismatch: "Inter" does not resolve distinctly ` +
|
||||
`(Inter ${fontProbe.interWidth}px vs DejaVu Sans ${fontProbe.dejavuWidth}px). ` +
|
||||
"The committed matrix is recorded with Inter active; make Inter visible to " +
|
||||
"fontconfig before recording or checking.",
|
||||
"bundled font asset mismatch: the Phase 7 issue-thread faces did not load " +
|
||||
`from the built bundle (sans ${fontProbe.sansFamily}: ${fontProbe.sansStatuses.join(",") || "missing"}; ` +
|
||||
`mono ${fontProbe.monoFamily}: ${fontProbe.monoStatuses.join(",") || "missing"}; ` +
|
||||
`symbols ${fontProbe.symbolFamily}: ${fontProbe.symbolStatuses.join(",") || "missing"}). ` +
|
||||
"Run build:issue-thread and verify that Vite emitted all referenced WOFF2 assets.",
|
||||
);
|
||||
}
|
||||
for (const viewport of VIEWPORTS) {
|
||||
@@ -189,9 +255,12 @@ async function main() {
|
||||
`- Chromium ${browserVersion} — pin the exact binary with \`PAPERCLIP_CHROMIUM_BIN\``,
|
||||
" (honoured by the agent-browser wrapper) alongside",
|
||||
" `PAPERCLIP_RUNNER_CHROMIUM_PATH`.",
|
||||
`- Sans stack resolves to Inter (probe: Inter ${fontProbe.interWidth}px vs`,
|
||||
` DejaVu Sans ${fontProbe.dejavuWidth}px for the title string). The recorder`,
|
||||
" refuses to run when Inter does not resolve.",
|
||||
`- Bundled sans face: ${fontProbe.sansFamily} (weights 400/500/600/700,`,
|
||||
` probe ${fontProbe.interWidth}px vs generic serif ${fontProbe.serifWidth}px).`,
|
||||
`- Bundled mono face: ${fontProbe.monoFamily} (weights 400/700,`,
|
||||
` probe ${fontProbe.monoWidth}px). Status glyphs use the bundled`,
|
||||
` ${fontProbe.symbolFamily} subsets. The recorder refuses to run when any`,
|
||||
" Vite-managed face is missing or fails to load.",
|
||||
"",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
@@ -230,12 +299,11 @@ async function main() {
|
||||
"Phase 7 UI evidence is not byte-stable:",
|
||||
...drift,
|
||||
"",
|
||||
`Recorded with Chromium ${browserVersion}; sans probe Inter=${fontProbe.interWidth}px, DejaVu Sans=${fontProbe.dejavuWidth}px.`,
|
||||
"Committed PNGs are pinned to one Chromium build and one font environment.",
|
||||
"A wholesale mismatch usually means a different browser or font set, not a",
|
||||
"UI regression: set PAPERCLIP_RUNNER_CHROMIUM_PATH (and PAPERCLIP_CHROMIUM_BIN",
|
||||
"for the wrapper) to the Chromium that recorded them, and compare the font",
|
||||
"probe against knowledge/evidence/phase-07/ui/index.md.",
|
||||
`Recorded with Chromium ${browserVersion}; bundled Inter probe=${fontProbe.interWidth}px, mono=${fontProbe.monoWidth}px.`,
|
||||
"Committed PNGs are pinned to one Chromium build; their fonts are bundled.",
|
||||
"A wholesale mismatch usually means a different browser build, not ambient",
|
||||
"fontconfig state: set PAPERCLIP_RUNNER_CHROMIUM_PATH (and",
|
||||
"PAPERCLIP_CHROMIUM_BIN for the wrapper) to the Chromium that recorded them.",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
@@ -18,5 +18,8 @@ export default defineConfig({
|
||||
outDir: resolve(packageRoot, "dist-issue-thread"),
|
||||
emptyOutDir: true,
|
||||
target: "esnext",
|
||||
// Keep every WOFF2 inspectable in the built asset graph, including the
|
||||
// intentionally tiny supplemental-symbol subset.
|
||||
assetsInlineLimit: 0,
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user