fix: keep embedded session controls visible and handle empty memory
Publish documentation / publish (push) Successful in 34s

Cap the embedded shell at its portal container height so steering and stop controls remain accessible. Skip vector retrieval for an empty authoritative Memory archive and compute SQL-rule embeddings lazily.

Validated with 54 Memory tests, 90 frontend tests, five browser scenarios, frontend and Docker builds, and a read-only comparison against the real empty Memory archive.
This commit is contained in:
User
2026-09-14 17:15:00 +02:00
parent 49333a2d35
commit d6cdffea62
8 changed files with 241 additions and 4 deletions
+40
View File
@@ -579,3 +579,43 @@ test("host header and left rail leave the application a bounded working area", a
expect(search!.width).toBeGreaterThan(180);
expect(await inspect(page)).toEqual({ overflow: 0, serif: [], tiny: [] });
});
for (const width of [390, 1280]) {
test(`embedded active session keeps steering and stop inside the portal at ${width}px`, async ({ page }, testInfo) => {
await page.setViewportSize({ width, height: 822 });
await fixtures(page, "embedded");
await page.route("**/api/sessions", route => route.request().method() === "POST"
? route.fulfill({ json: { id: "layout-session" } }) : route.fallback());
await page.route("**/api/sessions/layout-session", route => route.fulfill({ json: { id: "layout-session", active: true, status: "open", workspace } }));
await page.goto("/");
// The Omics template bounds #root below its topbar and clips overflow.
await page.addStyleTag({ content: `
body { margin: 0; padding-top: 70px; }
#root { height: calc(100dvh - 110px); overflow: hidden; }
` });
const prompt = page.getByRole("textbox", { name: "New question", exact: true });
await prompt.fill("Review the cohort");
await page.getByRole("button", { name: "Send", exact: true }).click();
const steering = page.getByRole("textbox", { name: "Steering", exact: true });
await expect(steering).toBeVisible();
// Drive the same store projection as streaming, without a real model.
await page.evaluate(async () => {
const path = "/src/store/sessionStore.ts";
const { useSessionStore } = await import(/* @vite-ignore */ path);
useSessionStore.getState().applyEvent({ type: "text_delta", text: "Reviewing the cohort\n".repeat(80) });
});
await steering.fill("Please pause the analysis\n".repeat(15));
const stop = page.getByRole("button", { name: "Stop and save session", exact: true });
await page.locator("#root").evaluate(el => { el.scrollTop = 0; });
const root = await page.locator("#root").boundingBox();
for (const control of [steering, stop]) {
const box = await control.boundingBox();
expect(box!.y).toBeGreaterThanOrEqual(root!.y);
expect(box!.y + box!.height).toBeLessThanOrEqual(root!.y + root!.height);
await expect(control).toBeInViewport();
}
await page.screenshot({ path: testInfo.outputPath("embedded-session.png") });
await stop.click();
await expect(page.getByRole("dialog", { name: "Stop the session?" })).toBeVisible();
});
}
+2
View File
@@ -222,6 +222,8 @@ body.thot-full-document { margin: 0; }
background: oklch(var(--background));
color-scheme: light;
}
/* Embedded hosts can reserve space for their own header/footer around #root. */
.thot-host--embedded { max-height: 100%; }
.thot-host[data-theme="dark"] { color-scheme: dark; }
.thot-host--full { --thot-shell-gutter: max(20px, 1.5rem); }
.thot-host--full > .thot-context-layout {