mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix(desktop): keep the Buzz Term splash until the terminal is revealed
The welcome banner was dismissed by the first visible output from the active PTY, but TerminalBootstrap spawns the shell when AppShell mounts and a real shell prints its prompt immediately. The banner was therefore always gone before the user first opened the terminal. Dismiss on the first keystroke sent to the PTY, or on visible output that arrives after the terminal has been revealed at least once. Keystroke alone would leave the banner painted over live scrollback when a background job prints into a revealed-but-idle terminal. Also stop comparing a mounted DOM node against null in the welcome test helper: building that AssertionError message inspects the element, and the React fiber keys make the walk take ~2 minutes. A failure in these rows now reports in about a second. Co-authored-by: npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57cc <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz> Signed-off-by: npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57cc <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz>
This commit is contained in:
parent
6d159d1248
commit
e3deaffbe6
@@ -179,21 +179,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);
|
||||
});
|
||||
@@ -201,6 +235,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({
|
||||
@@ -221,6 +256,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(
|
||||
@@ -141,6 +142,7 @@ export function TerminalSubstrate({
|
||||
const appSurface = getAppSurface();
|
||||
if (!appSurface) return;
|
||||
if (next === "terminal") {
|
||||
revealedRef.current = true;
|
||||
previousFocusRef.current =
|
||||
document.activeElement instanceof HTMLElement
|
||||
? document.activeElement
|
||||
@@ -312,9 +314,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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user