diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx
index 83672d659da4..ca7ce3a6b3f8 100644
--- a/apps/web/src/components/ChatView.tsx
+++ b/apps/web/src/components/ChatView.tsx
@@ -6703,7 +6703,11 @@ function ChatViewContent(props: ChatViewProps) {
)}
{threadSyncPhase && !activeEnvironmentUnavailable ? (
-
+
) : null}
{
- it.each([
- ["loading", "Loading messages..."],
- ["syncing", "Syncing messages..."],
- ] as const)("renders the %s message sync phase", (phase, label) => {
- const markup = renderToStaticMarkup(
);
+ it("renders loading immediately without participating in composer layout", () => {
+ const markup = renderToStaticMarkup(
);
expect(markup).toContain('role="status"');
- expect(markup).toContain(label);
+ expect(markup).toContain("Loading messages...");
+ expect(markup).toContain("absolute");
+ expect(markup).toContain("calc(100% + 0.5rem)");
expect(markup).not.toContain("animate-");
});
+
+ it("withholds the cached-thread syncing phase initially", () => {
+ const markup = renderToStaticMarkup(
);
+
+ expect(markup).toBe("");
+ });
+
+ it("moves above the scroll-to-end control when it is visible", () => {
+ const markup = renderToStaticMarkup(
);
+
+ expect(markup).toContain("calc(100% + 2.75rem)");
+ });
});
diff --git a/apps/web/src/components/chat/ThreadSyncStatusPill.tsx b/apps/web/src/components/chat/ThreadSyncStatusPill.tsx
index d920a6d1953d..02bbee8d191f 100644
--- a/apps/web/src/components/chat/ThreadSyncStatusPill.tsx
+++ b/apps/web/src/components/chat/ThreadSyncStatusPill.tsx
@@ -1,15 +1,33 @@
import { LoaderCircleIcon } from "lucide-react";
+import { useEffect, useState } from "react";
import { threadSyncLabel, type ThreadSyncPhase } from "../../threadSync";
-export function ThreadSyncStatusPill({ phase }: { readonly phase: ThreadSyncPhase }) {
+export function ThreadSyncStatusPill({
+ phase,
+ raised,
+}: {
+ readonly phase: ThreadSyncPhase;
+ readonly raised: boolean;
+}) {
+ const [syncingVisible, setSyncingVisible] = useState(phase === "loading");
+
+ useEffect(() => {
+ if (phase !== "syncing") return;
+ const timeoutId = globalThis.setTimeout(() => setSyncingVisible(true), 300);
+ return () => globalThis.clearTimeout(timeoutId);
+ }, [phase]);
+
+ if (phase === "syncing" && !syncingVisible) return null;
+
const label = threadSyncLabel(phase);
return (
{label}