feat: ship Buzz Term (#4347)

## Summary

- ship **Buzz Term** end to end: the terminal engine/runtime, mounted
desktop substrate, and user-visible naming
- add Quinn's tape-deck-inspired banner: a beveled chassis filled by the
`buzz term` wordmark, surrounded by a complete-hex field
- derive the wordmark's three-stop sweep from each theme's terminal
palette so primary, secondary, and accent roles remain visibly distinct
across all 62 shipped themes, including light themes
- paint the banner once on its own pointer-transparent canvas; PTY
rendering beneath it remains unchanged

## Banner behavior

- uses the renderer's shared `8.4 × 17` cell metrics and production
aspect ratio `2.0238`
- regenerates only for viewport/theme changes; palette switches repaint
correctly while the banner is visible
- dismisses on non-empty output from the active terminal session; empty
output and inactive sessions do not dismiss it
- fails closed below **70 columns** rather than squeezing or clipping
the wordmark
- adds **8 lines** to `terminalRenderer.ts` for shared cell metrics and
**zero lines inside `paint()`**

## Screenshots

| Buzz (light) | Buzz Dark |
|---|---|
| ![Buzz Term — Buzz
light](https://buzz.block.builderlab.xyz/media/fe4c50c1cd03645bff8f3cff588353618fd3004f320ab360165b6cb48f041eb6.png)
| ![Buzz Term — Buzz
Dark](https://buzz.block.builderlab.xyz/media/e5731f40fe020070c0b327287b4b0b6f0103adde852d0f6451ad66a0ff9d14bb.png)
|

| Kanagawa Lotus (light) | Red |
|---|---|
| ![Buzz Term — Kanagawa
Lotus](https://buzz.block.builderlab.xyz/media/260b211fe7bd232eb7faa4ec34c99baee5fed380556427c64495ceed3396a8c5.png)
| ![Buzz Term —
Red](https://buzz.block.builderlab.xyz/media/42a90830824683a0054e24649c58564134af831607736d823992181b792087d3.png)
|

Additional production-aspect finals:
[Vesper](https://buzz.block.builderlab.xyz/media/9ca6514b63f8cfb2107a85ca46f16a940c0883848e6fbc718e411af94aa13100.png),
[Min
Dark](https://buzz.block.builderlab.xyz/media/f67bd2970e5d64ffb07b1ae78ab58c847e6ebc23e7e7a48e067eb024dba64ec8.png),
and [Dark
Plus](https://buzz.block.builderlab.xyz/media/290fee08924f37d064abc687ecf3e9526ab05b87e8e56d610f23048949793dbe.png).

The screenshot harness was checked against the shipped painter at this
exact head: all **2,541 draw calls** matched on color, glyph, x, and y;
four deliberate divergence controls fired.

## Verification at `98ebc8f9048bd5f0ceb7e843b67874d642f0b7fd`

- desktop tests: **3,946 / 3,946**
- TypeScript: clean
- checks: pass (two pre-existing informational `useTemplate` notices
only)
- integration/e2e: PASS (independent exact-SHA lane; artifacts recorded
in the originating Buzz thread)
- artifact/dead-path sweep: clean
- redteam G1–G7: PASS
  - all six named banner emitter-deletion mutants die
- independent handwritten five-row full-wordmark fixture kills Quinn's
seven-mutant battery, including a one-pixel glyph change
- real `112 × 46` canvas-rect dismissal tests separately cover active
non-empty, active empty, and inactive non-empty output
- layer-drop and zero-draw painter mutants die; z-order and
pointer-events verified
  - CI's `tsc && vite build` includes all three banner modules
- performance at DPR 2 (worst-case measured envelope):
- one-time content paint: **~0.7–0.8 ms**, paid only when the banner is
built or its palette changes
- busy compositor, CSS `1277 × 697`, backing `2554 × 1394`: **470–497
µs/frame** for the full banner (**2.82–2.98%** of a 60 Hz frame)
- busy compositor, CSS `1920 × 1080`, backing `3840 × 2160`:
**1,139–1,212 µs/frame** (**6.83–7.27%**)
- empty, one-glyph, and full-banner controls converge: compositor cost
follows backing-layer area and DPR rather than painted-cell count
- in the actual idle welcome state, cost is below both vsync-clamped
rigs' resolution; it is not claimed as zero
- **Pane cross-rig spread: resolved at matched loop rate.** Two
independent rigs initially differed 2.3× (58–68 vs 136 µs/Mpx of backing
store; pane, CSS 1277×697 / backing 2554×1394, DPR 2). The cause of
*that* spread is rAF loop rate: the higher figure came from a
free-running loop at ~1600fps. Throttled to ~200–236fps, both rigs read
58–68 µs/Mpx (1.25–1.44% of a 60Hz frame). The busy-composite figures
quoted above remain the **unthrottled worst case** and are conservative
by ~2.3× at the pane. Not established: the mechanism and sign of
free-running distortion (one rig under-charges ~15%, the other
over-charges 2.3×), and the 1080p figure has not been re-measured
throttled.
- the layer paints only on generation/theme/resize and dismisses on
first non-empty active-session output, so the measurable busy cost is a
short-lived worst case rather than a persistent PTY paint-path tax

## Follow-ups in this PR

These are intentionally subsequent commits after the certified
static-banner head, not claims about `98ebc8f90`:

1. close the compositor metrology: remeasure the 1080p point throttled
and characterize the opposite-sign free-running rAF distortion, with
each measurement regime stated
2. add Tyler's animated honeycomb color waves, gated by
`prefers-reduced-motion`, a full 62-theme phase-sweep contrast check,
and DPR-2 per-tick performance certification
3. land the already-proven mounted theme-switch regression probe from
`RESEARCH/BUZZ_TERM_G3A_PROBE/`
4. bound the slow/hang-shaped G1-c mutant `waitFor`
5. optionally trim the generator to its ink bounding box, reducing the
minimum viewport from 70 to 62 columns

---------

Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz>
Signed-off-by: npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57cc <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz>
Signed-off-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Dawn (sprout agent) <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1t2tgm7d8f995uqvmnm8h88sg3wnpp9a5xysjf6dg3tjmgt3ltulqdp8ehr <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@buzz.block.builderlab.xyz>
Co-authored-by: npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6 <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz>
Co-authored-by: npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57cc <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz>
Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
This commit is contained in:
Tyler
2026-08-03 21:51:17 -04:00
committed by GitHub
parent b42b093613
commit 631b05c883
76 changed files with 16191 additions and 71 deletions

View File

@@ -457,14 +457,7 @@ test.describe("community rail", () => {
test("does not repair a remembered channel until live validation succeeds", async ({
page,
}) => {
await installMockBridge(
page,
{
channelsReadDelayMs: 300,
channelsReadErrors: [null, "temporary channel read failure"],
},
{ skipCommunitySeed: true },
);
await installMockBridge(page, undefined, { skipCommunitySeed: true });
await seedCommunities(page, [COMMUNITY_A, COMMUNITY_B], COMMUNITY_A.id);
await page.addInitScript((communityId) => {
window.localStorage.setItem(
@@ -483,6 +476,31 @@ test.describe("community rail", () => {
),
)
.not.toBeNull();
expect(
await page.evaluate(async () => {
const testWindow = window as Window & {
__BUZZ_E2E_DEFER_NEXT_CHANNELS_READ__?: () => void;
__BUZZ_E2E_RELEASE_CHANNELS_READ__?: () => number;
__BUZZ_E2E_CHANNELS_READ_PENDING__?: number;
__BUZZ_E2E_INVOKE_MOCK_COMMAND__?: (
command: string,
) => Promise<unknown>;
};
const deferNext = testWindow.__BUZZ_E2E_DEFER_NEXT_CHANNELS_READ__;
const release = testWindow.__BUZZ_E2E_RELEASE_CHANNELS_READ__;
const invoke = testWindow.__BUZZ_E2E_INVOKE_MOCK_COMMAND__;
if (!deferNext || !release || !invoke) {
throw new Error("missing channel-read latch seam");
}
deferNext();
const released = release();
await invoke("get_channels");
return {
released,
pending: testWindow.__BUZZ_E2E_CHANNELS_READ_PENDING__,
};
}),
).toEqual({ released: 0, pending: 0 });
await page.evaluate(() => {
const source = window.localStorage.getItem(
"buzz-channels.v1:ws://localhost:3000",
@@ -497,20 +515,88 @@ test.describe("community rail", () => {
JSON.stringify(snapshot),
);
});
await page.evaluate(() => {
const config = (
window as Window & {
__BUZZ_E2E__?: {
mock?: {
channelsReadError?: string;
channelsReadErrors?: (string | null)[];
};
};
}
).__BUZZ_E2E__;
if (!config) throw new Error("missing E2E config");
config.mock = {
...config.mock,
channelsReadError: "temporary channel read failure",
channelsReadErrors: ["temporary channel read failure"],
};
});
await expect
.poll(() =>
page.evaluate(
() => window.__BUZZ_E2E__?.mock?.channelsReadErrors?.length ?? 0,
),
)
.toBe(1);
await page.evaluate(() => {
const testWindow = window as Window & {
__BUZZ_E2E_DEFER_NEXT_CHANNELS_READ__?: () => void;
__BUZZ_E2E_INVALIDATE_CHANNELS__?: () => Promise<void>;
};
const deferNextChannelsRead =
testWindow.__BUZZ_E2E_DEFER_NEXT_CHANNELS_READ__;
const invalidateChannels = testWindow.__BUZZ_E2E_INVALIDATE_CHANNELS__;
if (!deferNextChannelsRead) {
throw new Error("missing channel-read defer seam");
}
if (!invalidateChannels) {
throw new Error("missing channel invalidation seam");
}
// Arm and trigger in one browser task so unrelated callbacks cannot
// claim the one-shot before the validation read starts.
deferNextChannelsRead();
void invalidateChannels();
});
await page.waitForFunction(
() =>
(
window as Window & {
__BUZZ_E2E_CHANNELS_READ_PENDING__?: number;
}
).__BUZZ_E2E_CHANNELS_READ_PENDING__ === 1,
);
expect(
await page.evaluate(async () => {
const invoke = (
window as Window & {
__BUZZ_E2E_INVOKE_MOCK_COMMAND__?: (
command: string,
) => Promise<unknown>;
}
).__BUZZ_E2E_INVOKE_MOCK_COMMAND__;
if (!invoke) throw new Error("missing mock command seam");
try {
await invoke("get_channels");
return null;
} catch (error) {
return error instanceof Error ? error.message : String(error);
}
}),
).toBe("temporary channel read failure");
expect(
await page.evaluate(
() =>
(
window as Window & {
__BUZZ_E2E_CHANNELS_READ_PENDING__?: number;
}
).__BUZZ_E2E_CHANNELS_READ_PENDING__,
),
).toBe(1);
await page.getByTestId(`community-rail-button-${COMMUNITY_B.id}`).click();
await expect(page).toHaveURL(/#\/channels\/general$/);
await expect
.poll(() =>
page.evaluate((communityId) => {
const raw = window.localStorage.getItem(
"buzz-community-destinations",
);
return raw ? JSON.parse(raw)[communityId] : null;
}, COMMUNITY_B.id),
)
.toEqual({ kind: "channel", channelId: "general" });
await page.waitForTimeout(400);
await expect(page).not.toHaveURL(/#\/channels\/general$/);
await expect
.poll(() =>
page.evaluate((communityId) => {
@@ -522,7 +608,45 @@ test.describe("community rail", () => {
)
.toEqual({ kind: "channel", channelId: "general" });
await expect(page.getByTestId("channel-general")).toBeVisible();
const released = await page.evaluate(
() =>
(
window as Window & {
__BUZZ_E2E_RELEASE_CHANNELS_READ__?: () => number;
}
).__BUZZ_E2E_RELEASE_CHANNELS_READ__?.() ?? 0,
);
expect(released).toBe(1);
expect(
await page.evaluate(async () => {
const testWindow = window as Window & {
__BUZZ_E2E_INVOKE_MOCK_COMMAND__?: (
command: string,
) => Promise<unknown>;
__BUZZ_E2E_CHANNELS_READ_PENDING__?: number;
};
const invoke = testWindow.__BUZZ_E2E_INVOKE_MOCK_COMMAND__;
if (!invoke) throw new Error("missing mock command seam");
let message: string | null = null;
try {
await invoke("get_channels");
} catch (error) {
message = error instanceof Error ? error.message : String(error);
}
return {
message,
pending: testWindow.__BUZZ_E2E_CHANNELS_READ_PENDING__,
};
}),
).toEqual({ message: "temporary channel read failure", pending: 0 });
await expect
.poll(() =>
page.evaluate(
() => window.__BUZZ_E2E__?.mock?.channelsReadErrors?.length ?? 0,
),
)
.toBe(0);
await expect
.poll(() =>
page.evaluate((communityId) => {
@@ -533,6 +657,32 @@ test.describe("community rail", () => {
}, COMMUNITY_B.id),
)
.toEqual({ kind: "channel", channelId: "general" });
await page.evaluate(async () => {
const testWindow = window as Window & {
__BUZZ_E2E__?: {
mock?: { channelsReadError?: string };
};
__BUZZ_E2E_INVALIDATE_CHANNELS__?: () => Promise<void>;
};
const config = testWindow.__BUZZ_E2E__;
const invalidateChannels = testWindow.__BUZZ_E2E_INVALIDATE_CHANNELS__;
if (!config?.mock) throw new Error("missing E2E mock config");
if (!invalidateChannels) {
throw new Error("missing channel invalidation seam");
}
config.mock.channelsReadError = undefined;
await invalidateChannels();
});
await expect
.poll(() =>
page.evaluate((communityId) => {
const raw = window.localStorage.getItem(
"buzz-community-destinations",
);
return raw ? JSON.parse(raw)[communityId] : null;
}, COMMUNITY_B.id),
)
.toEqual({ kind: "home" });
});
test("does not restore a remembered destination on cold boot", async ({