feat: add semantic CSS stylesheets (#3274)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
Amruth Pillai
2026-07-30 12:39:15 +02:00
committed by GitHub
co-authored by Cursor Agent
parent 4ac19f81b3
commit d2ffbf9618
320 changed files with 78393 additions and 2915 deletions
@@ -0,0 +1,12 @@
import { createSemanticCssResume, readStylesheetSource } from "../../fixtures/semantic-css";
import { expect, test } from "../../fixtures/test";
test("@semantic-css starts new resumes in semantic mode when default-on is enabled", async ({
authPage: page,
}, testInfo) => {
await createSemanticCssResume(page, testInfo);
await expect(page.getByText("Converted stylesheet draft", { exact: true })).toHaveCount(0);
await expect.poll(() => readStylesheetSource(page)).toBe("@version 1;\n");
await expect(page.getByText("Applied", { exact: true }).filter({ visible: true })).toBeVisible();
});
@@ -0,0 +1,37 @@
import { readSemanticCssFixture, updateSemanticCssFixture } from "../../fixtures/db";
import { createSampleResumeFromDashboard, openSidebarSection } from "../../fixtures/resume";
import { resumeIdFromPage } from "../../fixtures/semantic-css";
import { expect, test } from "../../fixtures/test";
test("@semantic-css keeps the legacy editor available while both flags are off", async ({
authPage: page,
}, testInfo) => {
await createSampleResumeFromDashboard(page, testInfo);
await openSidebarSection(page, "Custom Styles");
await expect(page.getByLabel("Target Scope")).toBeVisible();
await expect(page.getByRole("textbox", { name: "Semantic CSS stylesheet" })).toHaveCount(0);
});
test("@semantic-css preserves a persisted stylesheet through an old-client resume update", async ({
authPage: page,
}, testInfo) => {
await createSampleResumeFromDashboard(page, testInfo);
const resumeId = resumeIdFromPage(page);
const source = { languageVersion: 1, text: "@version 1;\nname { color: #dc2626; }\n" };
await updateSemanticCssFixture(resumeId, { stylesheet: { mode: "semantic", source, applied: source } });
await page.reload();
await openSidebarSection(page, "Basics");
await page.getByLabel("Headline").fill("Preserves semantic stylesheet");
await expect
.poll(() => readSemanticCssFixture(resumeId))
.toMatchObject({
headline: "Preserves semantic stylesheet",
stylesheet: {
mode: "semantic",
source,
applied: source,
},
});
});
@@ -0,0 +1,29 @@
import { updateSemanticCssFixture } from "../../fixtures/db";
import { createSampleResumeFromDashboard, openSidebarSection } from "../../fixtures/resume";
import { resumeIdFromPage, waitForStablePreview } from "../../fixtures/semantic-css";
import { expect, test } from "../../fixtures/test";
test("@semantic-css keeps persisted semantic rendering active when authoring is off", async ({
authPage: page,
}, testInfo) => {
await createSampleResumeFromDashboard(page, testInfo);
const beforeCanvas = await waitForStablePreview(page);
const before = await beforeCanvas.evaluate((element) => (element as HTMLCanvasElement).toDataURL());
const source = { languageVersion: 1, text: "@version 1;\nname { color: #2563eb; font-size: 30pt; }\n" };
await updateSemanticCssFixture(resumeIdFromPage(page), {
stylesheet: { mode: "semantic", source, applied: source },
});
await page.reload();
const afterCanvas = await waitForStablePreview(page);
await expect
.poll(() => afterCanvas.evaluate((element) => (element as HTMLCanvasElement).toDataURL()))
.not.toBe(before);
await waitForStablePreview(page);
await openSidebarSection(page, "Custom Styles");
await expect(page.getByText("Semantic styles remain active", { exact: true })).toBeVisible();
await expect(
page.getByText("This instance does not currently allow Semantic CSS editing.", { exact: true }),
).toBeVisible();
await expect(page.getByLabel("Target Scope")).toHaveCount(0);
});
@@ -0,0 +1,66 @@
import { createHash } from "node:crypto";
import { readFile } from "node:fs/promises";
import { readSemanticCssFixture } from "../../fixtures/db";
import {
createSemanticCssResume,
downloadPdf,
replaceStylesheet,
seedSemanticCssResume,
waitForStablePreview,
waitForStylesheetStatus,
} from "../../fixtures/semantic-css";
import { expect, test } from "../../fixtures/test";
test.setTimeout(120_000);
const renderedPdfFingerprint = (pdf: Buffer) => {
const stablePdf = pdf
.toString("latin1")
.replace(/D:\d{14}Z/g, "D:00000000000000Z")
.replace(/[A-Z]{6}\+/g, "SUBSET+")
.replace(/\/ID \[<[\da-f]+> <[\da-f]+>\]/gi, "/ID [<ID> <ID>]");
return createHash("sha256").update(stablePdf, "latin1").digest("hex");
};
test("@semantic-css keeps preview and export on the last valid source", async ({ authPage: page }, testInfo) => {
const resumeId = await createSemanticCssResume(page, testInfo);
await seedSemanticCssResume(page, resumeId);
await replaceStylesheet(page, "@version 1;\nname { color: #dc2626; }\n");
await waitForStylesheetStatus(page, "Applied");
const canvas = await waitForStablePreview(page);
const validPreview = await canvas.evaluate((element) => (element as HTMLCanvasElement).toDataURL());
const validDownload = await downloadPdf(page);
const validPdf = await readFile(await validDownload.path());
expect(validPdf.subarray(0, 4).toString()).toBe("%PDF");
await replaceStylesheet(page, "@version 1;\nname { color: ; }\n");
await waitForStylesheetStatus(page, "Error");
await expect(page.getByText("Preview and export use the last valid version.", { exact: true })).toBeVisible();
await expect.poll(() => canvas.evaluate((element) => (element as HTMLCanvasElement).toDataURL())).toBe(validPreview);
const invalidDownload = await downloadPdf(page);
const invalidPdf = await readFile(await invalidDownload.path());
expect(invalidPdf.subarray(0, 4).toString()).toBe("%PDF");
expect(renderedPdfFingerprint(invalidPdf)).toBe(renderedPdfFingerprint(validPdf));
await expect
.poll(async () => (await readSemanticCssFixture(resumeId)).stylesheet)
.toMatchObject({
source: { text: "@version 1;\nname { color: ; }\n" },
applied: { text: "@version 1;\nname { color: #dc2626; }\n" },
});
await replaceStylesheet(page, "@version 1;\nname { color: #2563eb; }\n");
await waitForStylesheetStatus(page, "Applied");
await expect
.poll(() => canvas.evaluate((element) => (element as HTMLCanvasElement).toDataURL()))
.not.toBe(validPreview);
await waitForStablePreview(page);
const fixedDownload = await downloadPdf(page);
const fixedPdf = await readFile(await fixedDownload.path());
expect(fixedPdf.subarray(0, 4).toString()).toBe("%PDF");
expect(renderedPdfFingerprint(fixedPdf)).not.toBe(renderedPdfFingerprint(validPdf));
await expect
.poll(async () => (await readSemanticCssFixture(resumeId)).stylesheet?.applied.text)
.toBe("@version 1;\nname { color: #2563eb; }\n");
});
@@ -0,0 +1,35 @@
import { updateSemanticCssFixture } from "../../fixtures/db";
import { activateStylesheet, createSemanticCssResume, openSemanticCssEditor } from "../../fixtures/semantic-css";
import { expect, test } from "../../fixtures/test";
test.setTimeout(120_000);
test("@semantic-css converts legacy rules into an inactive draft before activation", async ({
authPage: page,
}, testInfo) => {
const preflightWorkerErrors: string[] = [];
page.on("console", (message) => {
if (message.location().url.includes("preflight.worker") && message.text().includes("Buffer is not defined")) {
preflightWorkerErrors.push(message.text());
}
});
const resumeId = await createSemanticCssResume(page, testInfo);
await updateSemanticCssFixture(resumeId, { legacyStyleRule: true });
await page.reload();
await openSemanticCssEditor(page);
await expect(page.getByText("Converted stylesheet draft", { exact: true })).toBeVisible();
await expect(page.getByRole("textbox", { name: "Semantic CSS stylesheet" })).toContainText("@version 1;");
await expect(page.getByRole("textbox", { name: "Semantic CSS stylesheet" })).toContainText(
'section[type="experience"]',
);
await expect(page.getByRole("textbox", { name: "Semantic CSS stylesheet" })).toContainText("padding-left: 7pt;");
await expect(page.getByText(/^Ready to activate(?: with warnings)?$/)).toBeVisible({ timeout: 30_000 });
await activateStylesheet(page);
expect(preflightWorkerErrors).toEqual([]);
await page.reload();
await expect(page.getByText("Converted stylesheet draft", { exact: true })).toHaveCount(0);
await expect(page.getByText("Applied", { exact: true }).filter({ visible: true })).toBeVisible();
});
@@ -0,0 +1,249 @@
import type { Page } from "@playwright/test";
import { readSemanticCssFixture } from "../../fixtures/db";
import {
createSemanticCssResume,
openSemanticCssEditor,
PORTABLE_STYLESHEET,
replaceStylesheet,
seedSemanticCssResume,
switchTemplate,
waitForStablePreview,
} from "../../fixtures/semantic-css";
import { expect, test } from "../../fixtures/test";
test.setTimeout(180_000);
const PORTABLE_MARKERS = {
exactItemField: [161, 193, 129, 255],
exactSection: [254, 215, 102, 255],
groupSelector: [42, 183, 202, 255],
header: [254, 74, 73, 255],
mediaQuery: [110, 231, 183, 255],
pagination: [212, 165, 165, 255],
placement: [247, 140, 107, 255],
richText: [179, 136, 235, 255],
systemVariable: [0, 132, 209, 255],
templatePart: [255, 0, 255, 255],
} as const;
const PORTABLE_ACCEPTANCE_STYLESHEET = `${PORTABLE_STYLESHEET}
page {
background-color: var(--accent);
}
header {
background-color: rgb(254, 74, 73);
}
section:is([type="experience"], [type="education"]) > section-heading {
background-color: rgb(42, 183, 202);
}
section[id="projects"] > section-items > item {
background-color: rgb(254, 215, 102);
}
section[id="experience"] item[id="experience-item-2"] field[name="period"] {
color: rgb(161, 193, 129);
}
rich-text list-item > list-item-content {
color: rgb(179, 136, 235);
}
region[placement="sidebar"] section {
background-color: rgb(247, 140, 107);
}
section[type="projects"] {
background-color: rgb(212, 165, 165);
padding: 12pt;
}
@media (max-width: 600pt) {
region[placement="sidebar"] section-heading {
background-color: rgb(110, 231, 183);
}
}
resume[template="azurill"] template-part[name="timeline-dot"] {
background-color: rgb(255, 0, 255);
}
`;
type PortableMarker = keyof typeof PORTABLE_MARKERS;
const BREAK_INSIDE_DIRECTIVE = "\tbreak-inside: avoid;\n";
const MIN_PRESENCE_AHEAD_DIRECTIVE = "\t-resume-min-presence-ahead: 24pt;\n";
const WITHOUT_PAGINATION_DIRECTIVES = PORTABLE_ACCEPTANCE_STYLESHEET.replace(BREAK_INSIDE_DIRECTIVE, "").replace(
MIN_PRESENCE_AHEAD_DIRECTIVE,
"",
);
const BREAK_INSIDE_ONLY_STYLESHEET = PORTABLE_ACCEPTANCE_STYLESHEET.replace(MIN_PRESENCE_AHEAD_DIRECTIVE, "");
const MIN_PRESENCE_AHEAD_ONLY_STYLESHEET = PORTABLE_ACCEPTANCE_STYLESHEET.replace(BREAK_INSIDE_DIRECTIVE, "");
const GENERIC_PORTABLE_MARKERS = [
"exactItemField",
"exactSection",
"groupSelector",
"header",
"mediaQuery",
"pagination",
"placement",
"richText",
] as const satisfies readonly PortableMarker[];
async function countPortableMarkersByPage(page: Parameters<typeof waitForStablePreview>[0]) {
await waitForStablePreview(page);
return page.locator('canvas[aria-label^="Resume page "]').evaluateAll((elements, markers) => {
const markerEntries = Object.entries(markers) as Array<[PortableMarker, readonly number[]]>;
const markerByColor = new Map(
markerEntries.map(([name, color]) => [
(((color[0] << 24) | (color[1] << 16) | (color[2] << 8) | color[3]) >>> 0).toString(),
name,
]),
);
return elements
.filter((element) => !element.closest('[aria-hidden="true"]'))
.map((element) => {
const canvas = element as HTMLCanvasElement;
const context = canvas.getContext("2d");
if (!context) throw new Error("Expected a 2D preview canvas.");
const pixels = context.getImageData(0, 0, canvas.width, canvas.height).data;
const counts = Object.fromEntries(markerEntries.map(([name]) => [name, 0])) as Record<PortableMarker, number>;
for (let index = 0; index < pixels.length; index += 4) {
const key =
((pixels[index] << 24) | (pixels[index + 1] << 16) | (pixels[index + 2] << 8) | pixels[index + 3]) >>> 0;
const marker = markerByColor.get(key.toString());
if (marker) counts[marker] += 1;
}
return counts;
});
}, PORTABLE_MARKERS);
}
async function waitForPortableApplied(page: Parameters<typeof waitForStablePreview>[0]) {
await expect(
page
.getByText(/^Applied(?: with warnings)?$/)
.filter({ visible: true })
.last(),
).toBeVisible({
timeout: 30_000,
});
}
async function applyPortableStylesheet(
page: Parameters<typeof waitForStablePreview>[0],
resumeId: string,
source: string,
) {
await openSemanticCssEditor(page);
await replaceStylesheet(page, source);
await expect
.poll(async () => (await readSemanticCssFixture(resumeId)).stylesheet?.applied.text, { timeout: 30_000 })
.toBe(source);
await waitForPortableApplied(page);
}
async function readProjectMinPresenceAhead(page: Page, resumeId: string) {
const fixture = await readSemanticCssFixture(resumeId);
const response = await page.request.get(
`/api/openapi/resumes/${encodeURIComponent(fixture.username)}/${encodeURIComponent(fixture.slug)}/style-projection`,
);
expect(response.ok(), `the public PDF projection request should succeed (${response.status()})`).toBe(true);
const projection = (await response.json()) as {
nodes: Record<string, { minPresenceAhead?: number }>;
};
const projectNodes = Object.entries(projection.nodes).filter(([key]) => key.endsWith("/section-projects"));
expect(projectNodes, "the public PDF projection should contain one projects section").toHaveLength(1);
return projectNodes[0]?.[1].minPresenceAhead;
}
test("@semantic-css applies every portable selector behavior across Onyx, Azurill, and Ditto", async ({
authPage: page,
}, testInfo) => {
const resumeId = await createSemanticCssResume(page, testInfo);
await seedSemanticCssResume(page, resumeId, {
portableLayout: "balanced",
experienceItemId: "experience-item-2",
hidePicture: true,
});
await applyPortableStylesheet(page, resumeId, PORTABLE_ACCEPTANCE_STYLESHEET);
await page.reload();
await openSemanticCssEditor(page);
await waitForPortableApplied(page);
for (const template of ["Onyx", "Azurill", "Ditto"]) {
await switchTemplate(page, template);
const pages = await countPortableMarkersByPage(page);
console.info(`PORTABLE_MARKER_EVIDENCE ${template} ${JSON.stringify(pages)}`);
for (const marker of GENERIC_PORTABLE_MARKERS) {
expect(
pages.reduce((total, page) => total + page[marker], 0),
`${template} should render the ${marker} sentinel`,
).toBeGreaterThan(20);
}
expect(
pages.reduce((total, page) => total + page.systemVariable, 0),
`${template} should resolve the system variable sentinel`,
).toBeGreaterThan(10_000);
if (template === "Azurill") {
expect(
pages.reduce((total, page) => total + page.templatePart, 0),
"Azurill should render the template-part sentinel",
).toBeGreaterThan(5);
}
}
});
test("@semantic-css pagination directives keep a portable project section together", async ({
authPage: page,
}, testInfo) => {
const resumeId = await createSemanticCssResume(page, testInfo);
await seedSemanticCssResume(page, resumeId, {
portableLayout: "pagination-stress",
experienceItemId: "experience-item-2",
hidePicture: true,
});
await switchTemplate(page, "Onyx");
await applyPortableStylesheet(page, resumeId, WITHOUT_PAGINATION_DIRECTIVES);
const withoutBreakInside = await countPortableMarkersByPage(page);
expect(withoutBreakInside.reduce((total, page) => total + page.pagination, 0)).toBeGreaterThan(20);
const withoutBreakInsideDistribution = withoutBreakInside.map((page) => page.pagination);
await applyPortableStylesheet(page, resumeId, BREAK_INSIDE_ONLY_STYLESHEET);
const breakInsideOnlyDistribution = (await countPortableMarkersByPage(page)).map((page) => page.pagination);
await applyPortableStylesheet(page, resumeId, WITHOUT_PAGINATION_DIRECTIVES);
const withoutMinPresenceAhead = await readProjectMinPresenceAhead(page, resumeId);
await applyPortableStylesheet(page, resumeId, MIN_PRESENCE_AHEAD_ONLY_STYLESHEET);
const minPresenceAheadOnly = await readProjectMinPresenceAhead(page, resumeId);
console.info(
`PORTABLE_PAGINATION_EVIDENCE ${JSON.stringify({
breakInside: {
withoutDirective: withoutBreakInsideDistribution,
withDirective: breakInsideOnlyDistribution,
},
minPresenceAhead: {
withoutDirective: withoutMinPresenceAhead ?? null,
withDirective: minPresenceAheadOnly,
},
})}`,
);
expect(breakInsideOnlyDistribution, "break-inside: avoid must independently change project pagination").not.toEqual(
withoutBreakInsideDistribution,
);
expect(withoutMinPresenceAhead, "the no-directive control must omit minPresenceAhead").toBeUndefined();
expect(
minPresenceAheadOnly,
"-resume-min-presence-ahead: 24pt must independently reach the project pagination props",
).toBe(24);
});
@@ -0,0 +1,26 @@
import { updateSemanticCssFixture } from "../../fixtures/db";
import {
createSemanticCssResume,
readStylesheetSource,
replaceStylesheet,
seedSemanticCssResume,
waitForStylesheetStatus,
} from "../../fixtures/semantic-css";
import { expect, test } from "../../fixtures/test";
test.setTimeout(120_000);
test("@semantic-css rebases a stale revision without dropping the focused draft", async ({
authPage: page,
}, testInfo) => {
const resumeId = await createSemanticCssResume(page, testInfo);
await seedSemanticCssResume(page, resumeId);
await replaceStylesheet(page, "@version 1;\nname { color: #dc2626; }\n");
await waitForStylesheetStatus(page, "Applied");
await updateSemanticCssFixture(resumeId, { bumpRevision: true });
const latest = "@version 1;\nname { color: #2563eb; }\n";
await replaceStylesheet(page, latest);
await waitForStylesheetStatus(page, "Applied");
await expect.poll(() => readStylesheetSource(page)).toBe(latest);
});
@@ -0,0 +1,110 @@
import { ACTIVE_PREVIEW_PAGE_SELECTOR, readPreviewPageDataUrl } from "../../fixtures/preview";
import {
createSemanticCssResume,
PORTABLE_STYLESHEET,
seedSemanticCssResume,
switchTemplate,
} from "../../fixtures/semantic-css";
import { expect, test } from "../../fixtures/test";
const templates = [
"Azurill",
"Bronzor",
"Chikorita",
"Ditgar",
"Ditto",
"Gengar",
"Glalie",
"Kakuna",
"Lapras",
"Leafish",
"Meowth",
"Onyx",
"Pikachu",
"Rhyhorn",
"Scizor",
] as const;
test.setTimeout(240_000);
async function previewClip(page: Parameters<typeof createSemanticCssResume>[0]) {
let previous: string | undefined;
await expect
.poll(
async () => {
const current = await page.evaluate(readPreviewPageDataUrl, ACTIVE_PREVIEW_PAGE_SELECTOR);
const stable = current === previous;
previous = current;
return stable;
},
{ intervals: [250], timeout: 15_000 },
)
.toBe(true);
const dimensions = await page.evaluate((selector) => {
const source = document.querySelector<HTMLCanvasElement>(selector);
if (!source) throw new Error("Expected an active first-page canvas.");
const box = source.getBoundingClientRect();
const width = Math.ceil(box.width);
const height = Math.ceil(box.height);
document.querySelector("[data-semantic-css-visual-page]")?.remove();
const surface = document.createElement("div");
surface.dataset.semanticCssVisualPage = "";
Object.assign(surface.style, {
background: "white",
height: "632px",
left: "0",
position: "fixed",
top: "0",
width: "447px",
zIndex: "2147483647",
});
const snapshot = document.createElement("canvas");
snapshot.width = source.width;
snapshot.height = source.height;
snapshot.style.width = "447px";
snapshot.style.height = "632px";
const context = snapshot.getContext("2d");
if (!context) throw new Error("Expected a 2D canvas context for the visual snapshot.");
context.drawImage(source, 0, 0);
surface.append(snapshot);
document.body.append(surface);
return { width, height };
}, ACTIVE_PREVIEW_PAGE_SELECTOR);
const { width, height } = dimensions;
expect({ width, height }).toEqual({ width: 447, height: 632 });
const surface = page.locator("[data-semantic-css-visual-page]");
const surfaceBox = await surface.boundingBox();
if (!surfaceBox) throw new Error("Expected a visible page-only visual snapshot surface.");
const clip = {
x: Math.floor(surfaceBox.x),
y: Math.floor(surfaceBox.y),
width: Math.ceil(surfaceBox.x + surfaceBox.width) - Math.floor(surfaceBox.x),
height: Math.ceil(surfaceBox.y + surfaceBox.height) - Math.floor(surfaceBox.y),
};
expect({ width: clip.width, height: clip.height }).toEqual({ width: 447, height: 632 });
return clip;
}
test("@semantic-css renders deterministic first-page previews for all templates", async ({
authPage: page,
}, testInfo) => {
const resumeId = await createSemanticCssResume(page, testInfo);
const portable = { languageVersion: 1, text: PORTABLE_STYLESHEET };
await seedSemanticCssResume(page, resumeId, {
basicsName: "Semantic CSS Acceptance",
experienceItemId: "experience-item-2",
hidePicture: true,
stylesheet: { mode: "semantic", source: portable, applied: portable },
});
for (const template of templates) {
await switchTemplate(page, template);
await expect(page).toHaveScreenshot(`${template.toLowerCase()}-first-page.png`, {
animations: "disabled",
caret: "hide",
clip: await previewClip(page),
maxDiffPixelRatio: 0,
});
await page.locator("[data-semantic-css-visual-page]").evaluate((element) => element.remove());
}
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB