T45 Website: native captures stand in where a browser cannot run, an OG image, no web notes

This commit is contained in:
ZacharyZhang-NY committed 2026-09-30 00:03:23 -04:00
1 parent 587e08b5cb
commit 327bdf4b7f
68 files changed
+315 -155

No files matched your search

+19 -1
View File
@@ -13,7 +13,8 @@ test("the home page shows the pitch, the scene and the credits", async ({ page }
const errors = watch(page);
await page.goto("/");
await expect(page.getByRole("heading", { level: 1 })).toContainText("component library for GPUI");
await expect(page.locator(".hero canvas")).toBeVisible();
await expect(page.locator("canvas.iso")).toBeAttached();
await expect(page.locator(".hero-iso")).toBeVisible();
await expect(page.locator("#foot")).toContainText("Solar Icons by 480 Design");
expect(errors).toEqual([]);
});
@@ -83,3 +84,20 @@ test("an unknown chapter says so", async ({ page }) => {
await expect(page.getByRole("heading", { level: 1 })).toHaveText("Not found");
expect(errors.join()).toContain("No chapter");
});
test("a story the web cannot run shows its native capture, in the site's mode", async ({ page }) => {
await page.emulateMedia({ colorScheme: "light" });
await page.goto("/components/forms/pathinput/");
const shot = page.locator("img.shot");
await expect(shot).toHaveAttribute("srcset", /pathinput-light\.jpg/);
await expect(page.locator(".stage iframe")).toHaveCount(0);
await page.locator("[data-mode]").click();
await expect(shot).toHaveAttribute("srcset", /pathinput-dark\.jpg/);
await expect.poll(() => shot.evaluate((img: HTMLImageElement) => img.naturalWidth)).toBeGreaterThan(0);
});
test("a live story that opens a window shows that window natively", async ({ page }) => {
await page.goto("/components/shell/aboutdialog/");
await expect(page.locator(".stage iframe")).toHaveCount(1);
await expect(page.locator(".native img.shot")).toHaveAttribute("srcset", /shell-about-window/);
});
+8
View File
@@ -5,6 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Ely · GPUI components</title>
<meta name="description" content="A component library for GPUI, in light and dark. Every component runs live in the browser." />
<meta property="og:type" content="website" />
<meta property="og:title" content="Ely · GPUI components" />
<meta property="og:description" content="A component library for GPUI. Every part runs live in the browser, compiled from Rust to WebAssembly." />
<meta property="og:image" content="https://ely-site.zhangyanghaha0407.workers.dev/og.png" />
<meta property="og:image:width" content="2400" />
<meta property="og:image:height" content="1260" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://ely-site.zhangyanghaha0407.workers.dev/og.png" />
<script>
// The saved mode, or the system's, before first paint.
try {
Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+17
View File
@@ -0,0 +1,17 @@
// Renders public/og.png from the hero; needs pnpm preview.
import { chromium } from "@playwright/test";
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1200, height: 630 }, deviceScaleFactor: 2, colorScheme: "light", reducedMotion: "reduce" });
await page.goto(process.env.SITE ?? "http://localhost:4173/");
await page.addStyleTag({
content: ".bar, .cta { display: none } .hero { min-height: 630px; padding: 56px } .words { zoom: 1.5 } .og { font-weight: 600; margin-bottom: 16px }",
});
await page.evaluate(() => {
document.querySelector(".glyphs").insertAdjacentHTML("afterend", '<div class="og">Ely</div>');
document.querySelector(".hero p").textContent = "Every component live in the browser, compiled from Rust.";
});
await page.waitForTimeout(1500);
await page.screenshot({ path: "public/og.png", clip: { x: 0, y: 0, width: 1200, height: 630 } });
await browser.close();
console.log("og: wrote public/og.png");
+7 -1
View File
@@ -1,5 +1,6 @@
import { all, chapter, chapters, esc, href, storyCount, type Chapter, type Story } from "./data";
import { icon } from "./icons";
import { isCaptured, native } from "./native";
import { preview } from "./preview";
/** Chapters on the left, a story or the index beside. */
@@ -22,7 +23,11 @@ export function components(main: HTMLElement, path: string[]): () => void {
}
document.title = `${story.title} · ${current.title} · Ely`;
body.innerHTML = detail(current, story);
return preview(body.querySelector<HTMLElement>(".live")!, current.slug, story.slug, story.title);
const live = body.querySelector<HTMLElement>(".live")!;
const shots = body.querySelector<HTMLElement>(".native")!;
if (isCaptured(current.slug, story.slug)) return native(live, current.slug, story.slug, story.title);
const [stopLive, stopShots] = [preview(live, current.slug, story.slug, story.title), native(shots, current.slug, story.slug, story.title)];
return () => (stopLive(), stopShots());
}
function sidebar(side: HTMLElement, current?: Chapter, story?: Story): void {
@@ -66,6 +71,7 @@ function detail(current: Chapter, story: Story): string {
<h1>${esc(story.title)}</h1>
<p class="names">${story.components.map(esc).join(" · ")}</p>
<div class="live"></div>
<div class="native"></div>
<nav class="pager" aria-label="More stories">${pager(prev, "prev", "Previous", icon("ArrowLeft"))}${pager(next, "next", "Next", icon("ArrowRight"))}</nav>
</article>`;
}
+33
View File
@@ -0,0 +1,33 @@
import shots from "./shots.json";
import { esc } from "./data";
import { mode, onMode, type Mode } from "./theme";
const captured = new Set<string>(shots.stories);
const windows: Record<string, string[]> = shots.windows;
/** Whether the site shows this story as a native capture. */
export function isCaptured(page: string, story: string): boolean {
return captured.has(`${page}/${story}`);
}
const src = (name: string, theme: Mode) => `/shots/${name}-${theme}.jpg`;
function figure(name: string, alt: string): string {
// Captures are at twice the pixels, drawn at their size.
return `<img class="shot" data-shot="${name}" srcset="${src(name, mode())} 2x" alt="${esc(alt)}" loading="lazy" />`;
}
/** Draws the story's capture, or the windows it opens, in the site's mode. */
export function native(host: HTMLElement, page: string, story: string, title: string): () => void {
const key = `${page}/${story}`;
if (captured.has(key)) {
host.innerHTML = `<p class="label">Native capture · macOS</p>${figure(`${page}--${story}`, `${title}, captured from the native gallery`)}`;
} else if (windows[key]) {
host.innerHTML = `<p class="label">The windows it opens, natively</p><div class="shots">${windows[key]
.map((name) => figure(name, `A window ${title} opens, captured natively`))
.join("")}</div>`;
} else {
return () => {};
}
return onMode((next) => host.querySelectorAll<HTMLImageElement>("img[data-shot]").forEach((img) => (img.srcset = `${src(img.dataset.shot!, next)} 2x`)));
}
+41
View File
@@ -0,0 +1,41 @@
{
"stories": [
"files/dragdropfiles",
"forms/fileinput-dropzone",
"forms/pathinput",
"media/imageupload",
"shell/dockbadge",
"shell/trayicon-traymenu",
"shell/windowdragregion",
"shell/windowresizeborder",
"terminal/terminal-terminaltabs-terminalsplit-terminaltoolbar-shellselector-terminalsearch-terminallink",
"theme/themeimporter-syntaxthemes"
],
"windows": {
"shell/splashscreen": [
"shell-splash-window"
],
"shell/aboutdialog": [
"shell-about-window"
],
"shell/multiwindow-manager": [
"shell-managed-window"
],
"shell/miniwindow-compactmode-pictureinpicture": [
"shell-mini-window",
"shell-pip-window"
],
"shell/updatedialog": [
"shell-update-window"
],
"shell/crashreporter": [
"shell-crash-window"
],
"navigation/searchpalette-spotlightsearch-quicklauncher": [
"navigation-launcher"
],
"theme/platform-style-iconthemeprovider-vibrancybackground": [
"theme-frosted-window"
]
}
}
+4
View File
@@ -126,6 +126,10 @@ canvas.iso { position: fixed; inset: 0; width: 100vw; height: 100vh; pointer-eve
.stage-skip { position: absolute; left: -9999px; }
.stage-skip:focus { position: static; display: inline-block; margin-bottom: 8px; font-size: 14px; color: var(--accent); }
img.shot { display: block; max-width: 100%; height: auto; margin-top: 12px; }
.native .label { margin-top: 48px; }
.shots { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 24px; }
.shots img.shot { max-width: min(100%, 560px); }
.pager { display: flex; justify-content: space-between; gap: 16px; margin-top: 32px; padding-top: 16px; border-top: 1px solid var(--line); }
.pager a { display: flex; flex-direction: column; gap: 4px; max-width: 48%; font-size: 14px; line-height: 20px; }
.pager a:hover { color: var(--accent); }