Merge PR #4410: restore terminal splash

Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
This commit is contained in:
npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta
2026-08-02 17:24:15 -04:00
2 changed files with 50 additions and 8 deletions
@@ -183,21 +183,55 @@ const VISIBLE_FRAME = {
};
async function expectWelcome(view, present) {
// Compare a boolean, never the node: an AssertionError carrying a mounted
// element inspects it to build its message, and `__reactFiber$` makes that
// walk the whole fiber graph — a failure takes ~2min to report instead of ms.
await waitFor(() =>
present
? assert.ok(view.container.querySelector(".buzz-terminal-welcome"))
: assert.equal(
view.container.querySelector(".buzz-terminal-welcome"),
null,
),
assert.equal(
view.container.querySelector(".buzz-terminal-welcome") !== null,
present,
),
);
}
test("non-empty output from the active PTY dismisses the welcome overlay", async () => {
async function reveal(view) {
const substrate = view.container.querySelector(".buzz-terminal-substrate");
toggleChord();
await waitFor(() =>
assert.equal(substrate.dataset.terminalOwner, "terminal"),
);
}
test("spawn-time output before the first reveal keeps the welcome overlay", async () => {
const subject = fixture({ frame: EMPTY_FRAME });
await ready(subject.view);
await expectWelcome(subject.view, true);
subject.rerender({ frame: VISIBLE_FRAME });
await expectWelcome(subject.view, true);
await reveal(subject.view);
await expectWelcome(subject.view, true);
});
test("the first keystroke dismisses the welcome overlay", async () => {
const subject = fixture({ frame: VISIBLE_FRAME });
await ready(subject.view);
await reveal(subject.view);
await expectWelcome(subject.view, true);
fireEvent.input(subject.view.getByLabelText("Terminal input"), {
target: { value: "l" },
});
await expectWelcome(subject.view, false);
});
test("non-empty output after the reveal dismisses the welcome overlay", async () => {
const subject = fixture({ frame: EMPTY_FRAME });
await ready(subject.view);
await reveal(subject.view);
await expectWelcome(subject.view, true);
subject.rerender({ frame: VISIBLE_FRAME });
await expectWelcome(subject.view, false);
});
@@ -205,6 +239,7 @@ test("non-empty output from the active PTY dismisses the welcome overlay", async
test("empty active output keeps the welcome overlay", async () => {
const subject = fixture({ frame: EMPTY_FRAME });
await ready(subject.view);
await reveal(subject.view);
await expectWelcome(subject.view, true);
subject.rerender({
@@ -225,6 +260,7 @@ test("non-empty output from an inactive PTY keeps the welcome overlay", async ()
],
});
await ready(subject.view);
await reveal(subject.view);
await expectWelcome(subject.view, true);
subject.rerender({
@@ -101,6 +101,7 @@ export function TerminalSubstrate({
const previousFocusRef = React.useRef<HTMLElement | null>(null);
const reportedFocusRef = React.useRef<boolean | null>(null);
const scrollBySessionRef = React.useRef(new Map<string, number>());
const revealedRef = React.useRef(false);
const activeSession = sessions.find((session) => session.active);
const activeSessionId = activeSession?.id ?? null;
const frames = React.useMemo(
@@ -146,6 +147,7 @@ export function TerminalSubstrate({
const appSurface = getAppSurface();
if (!appSurface) return;
if (next === "terminal") {
revealedRef.current = true;
previousFocusRef.current =
document.activeElement instanceof HTMLElement
? document.activeElement
@@ -339,9 +341,13 @@ export function TerminalSubstrate({
consumeFrame(delivered.frame);
if (
delivered.sessionId === activeSessionId &&
revealedRef.current &&
hasVisibleOutput(delivered.frame)
) {
// Policy: first visible output from the active PTY removes the overlay.
// Policy: the banner is a splash for the reveal, so spawn-time shell
// output must not dismiss it. Only visible output from the active PTY
// that arrives after the terminal has been revealed (or the first
// keystroke, see sendInput) removes the overlay.
setWelcomeVisible(false);
}
}