+
-
+
- {messages.map((message) => (
-
setOpenThread(target.messageId)}
- />
- ))}
+ {messages.map((message, index) => {
+ const at = new Date(message.at);
+ const previous = messages[index - 1];
+ const previousAt = previous === undefined ? undefined : new Date(previous.at);
+ const validAt = !Number.isNaN(at.getTime());
+ const newDay =
+ validAt &&
+ (previousAt === undefined ||
+ Number.isNaN(previousAt.getTime()) ||
+ !isSameDay(at, previousAt));
+ return (
+
+ {newDay ? : null}
+ {index === 0 && createdLine !== null ? createdLine : null}
+ setOpenThread(target.messageId)}
+ />
+
+ );
+ })}
{errorText(send.error)}
)}
-
+
{opened === undefined ? null : (
@@ -391,6 +453,7 @@ function Workbench({ workbenchTenantId }: { readonly workbenchTenantId: string }
participants={roster}
workbenchTenantId={workbenchTenantId}
rosterReady={participants.data !== undefined}
+ chatAgents={chatAgents}
/>
))}
diff --git a/apps/web/src/shell/app-shell.tsx b/apps/web/src/shell/app-shell.tsx
index 40362a19d..5fc21104c 100644
--- a/apps/web/src/shell/app-shell.tsx
+++ b/apps/web/src/shell/app-shell.tsx
@@ -14,7 +14,6 @@ import { useNavigate } from "../navigation";
import { saveArtifactContent } from "./library-artifacts";
import { APP_ROUTES, matchesRoute } from "../routes";
import type { SessionUser } from "../session";
-import { isWorkbenchPath } from "../workbench-path";
import { StageTopBar } from "./stage-top-bar";
import {
useCanvasColumnArtifact,
@@ -128,16 +127,26 @@ export function AppShell({
// Keyed by path so any navigation closes the phone-width sidebar.
const [sidebarOpenFor, setSidebarOpenFor] = useState
(null);
const sidebarOpen = sidebarOpenFor === path;
- // Desktop collapse. The chat and home screens carry no `StageTopBar`, so
- // they always show the sidebar rather than leave no way back.
+ // Desktop collapse; Cmd/Ctrl+B and each page's toggle button flip it.
const [collapsed, setCollapsed] = useState(false);
const narrow = useIsNarrow();
- const collapsible = path !== "/" && !isWorkbenchPath(path);
- const sidebarCollapsed = collapsed && collapsible;
+ const sidebarCollapsed = collapsed;
const toggleSidebar = () => {
if (isNarrow()) setSidebarOpenFor(sidebarOpen ? null : path);
else setCollapsed(!collapsed);
};
+ useEffect(() => {
+ const onKey = (event: KeyboardEvent) => {
+ if (!(event.metaKey || event.ctrlKey) || event.altKey || event.key.toLowerCase() !== "b") {
+ return;
+ }
+ event.preventDefault();
+ if (isNarrow()) setSidebarOpenFor((current) => (current === path ? null : path));
+ else setCollapsed((current) => !current);
+ };
+ window.addEventListener("keydown", onKey);
+ return () => window.removeEventListener("keydown", onKey);
+ }, [path]);
useEffect(() => {
if (!sidebarOpen) return;
const onKey = (event: KeyboardEvent) => {
diff --git a/e2e/browser/reply.test.ts b/e2e/browser/reply.test.ts
index 0633905df..0cdffb4b6 100644
--- a/e2e/browser/reply.test.ts
+++ b/e2e/browser/reply.test.ts
@@ -79,14 +79,14 @@ describeBrowser("worker reply", () => {
await page.type("input[aria-label='Worker name']", "Bea");
await clickText(page, "button", "Rename");
await page.waitForFunction(
- `document.querySelector(".bench-pill")?.innerText.includes("Bea ·")`,
+ `document.querySelector(".bench-pill")?.innerText.includes("Bea")`,
{
timeout: STEP_TIMEOUT,
},
);
await page.reload({ waitUntil: "networkidle0" });
await page.waitForFunction(
- `document.querySelector(".bench-pill")?.innerText.includes("Bea ·")`,
+ `document.querySelector(".bench-pill")?.innerText.includes("Bea")`,
{
timeout: STEP_TIMEOUT,
},