feat(dashboard): add resume loading overlay helpers

Extract overlay visibility helpers so the chat resume wait notice can be
tested without mounting ChatPage.
This commit is contained in:
xxxigm 2026-07-30 17:18:06 +07:00 committed by Austin Pickett
parent c31c27e03a
commit 490f7048dd
2 changed files with 116 additions and 0 deletions

View file

@ -0,0 +1,69 @@
import { describe, expect, it } from "vitest";
import {
PTY_RESUME_LOADING_MAX_MS,
shouldFinishResumeHydrationOnChunk,
shouldShowResumeLoadingOverlay,
} from "./pty-resume-loading";
describe("shouldFinishResumeHydrationOnChunk", () => {
it("finishes on the first non-empty chunk", () => {
expect(shouldFinishResumeHydrationOnChunk("")).toBe(false);
expect(shouldFinishResumeHydrationOnChunk("hello")).toBe(true);
});
it("keeps a positive hard-cap timeout for wedged resumes", () => {
expect(PTY_RESUME_LOADING_MAX_MS).toBeGreaterThan(0);
});
});
describe("shouldShowResumeLoadingOverlay", () => {
it("shows while a resume target is connecting or open and still hydrating", () => {
expect(
shouldShowResumeLoadingOverlay({
hasResumeTarget: true,
ptyState: "connecting",
hydrating: true,
}),
).toBe(true);
expect(
shouldShowResumeLoadingOverlay({
hasResumeTarget: true,
ptyState: "open",
hydrating: true,
}),
).toBe(true);
});
it("hides when there is no resume target", () => {
expect(
shouldShowResumeLoadingOverlay({
hasResumeTarget: false,
ptyState: "connecting",
hydrating: true,
}),
).toBe(false);
});
it("hides once hydration finishes", () => {
expect(
shouldShowResumeLoadingOverlay({
hasResumeTarget: true,
ptyState: "open",
hydrating: false,
}),
).toBe(false);
});
it("defers to reconnect / closed / ended overlays", () => {
for (const ptyState of ["reconnecting", "closed", "ended"] as const) {
expect(
shouldShowResumeLoadingOverlay({
hasResumeTarget: true,
ptyState,
hydrating: true,
}),
).toBe(false);
}
});
});

View file

@ -0,0 +1,47 @@
import type { PtyConnectionState } from "@/lib/pty-reconnect";
/**
* Hard cap so a wedged resume (never gets PTY payload) cannot leave the
* wait notice up forever.
*/
export const PTY_RESUME_LOADING_MAX_MS = 30000;
export const PTY_RESUME_LOADING_MESSAGE =
"Please wait while the conversation loads…";
export interface ResumeLoadingOverlayInput {
hasResumeTarget: boolean;
ptyState: PtyConnectionState;
hydrating: boolean;
}
/**
* Show a wait notice only while a resumed chat is still blank. Once the
* first real PTY payload arrives the terminal has something to show, so
* the notice hides and history can stream in underneath.
*
* Reconnect / ended / closed states keep their own overlays and must not
* stack this one on top.
*/
export function shouldShowResumeLoadingOverlay({
hasResumeTarget,
ptyState,
hydrating,
}: ResumeLoadingOverlayInput): boolean {
if (!hasResumeTarget || !hydrating) {
return false;
}
if (
ptyState === "reconnecting" ||
ptyState === "closed" ||
ptyState === "ended"
) {
return false;
}
return ptyState === "connecting" || ptyState === "open";
}
/** First non-empty PTY chunk means the blank window is over. */
export function shouldFinishResumeHydrationOnChunk(chunkText: string): boolean {
return chunkText.length > 0;
}