Files
wehub-resource-sync e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:40:13 +08:00

92 lines
3.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useEffect, useState } from "react";
import { Thread } from "../components/assistant-ui/thread";
import { ThreadList } from "@/components/assistant-ui/thread-list";
import { usePiThreadState } from "@assistant-ui/react-pi";
import type { PiRuntimeReadiness } from "@assistant-ui/react-pi";
// Type-only import — erased at build time, so no server code reaches the client.
import type { PiHandshake } from "@/lib/pi-server";
import { PiHandshakeProvider } from "../components/pi-handshake";
import { PiRuntimeProvider } from "./PiRuntimeProvider";
import { WorkspaceBrowser } from "../components/workspace-browser";
export default function Home() {
const [handshake, setHandshake] = useState<PiHandshake | null>(null);
const [workspacePath, setWorkspacePath] = useState("");
useEffect(() => {
let active = true;
fetch("/api/pi/handshake")
.then((response) => response.json())
.then((data: PiHandshake) => {
if (!active) return;
setHandshake(data);
setWorkspacePath(data.workspacePath ?? "");
})
.catch(() => {
/* handshake is best-effort; the runtime still works on env defaults. */
});
return () => {
active = false;
};
}, []);
return (
<PiRuntimeProvider workspacePath={workspacePath || undefined}>
<PiHandshakeProvider value={handshake}>
<div className="flex h-dvh flex-col overflow-hidden">
<Header
workspacePath={workspacePath}
onCommitWorkspace={setWorkspacePath}
/>
<ReadinessBanner fallback={handshake?.readiness} />
<div className="flex min-h-0 flex-grow">
<aside className="flex w-72 shrink-0 flex-col overflow-hidden border-r">
<div className="flex-1 overflow-y-auto p-3">
<ThreadList />
</div>
</aside>
<main className="flex min-h-0 min-w-0 flex-grow flex-col overflow-hidden">
<Thread />
</main>
</div>
</div>
</PiHandshakeProvider>
</PiRuntimeProvider>
);
}
function Header({
workspacePath,
onCommitWorkspace,
}: {
workspacePath: string;
onCommitWorkspace: (value: string) => void;
}) {
return (
<header className="flex items-center gap-3 border-b px-4 py-2">
<span className="font-semibold">assistant-ui × Pi</span>
<div className="ml-auto flex items-center gap-2">
<WorkspaceBrowser value={workspacePath} onCommit={onCommitWorkspace} />
</div>
</header>
);
}
function ReadinessBanner({
fallback,
}: {
fallback?: PiRuntimeReadiness | undefined;
}) {
const live = usePiThreadState((state) => state.readiness);
const readiness = live ?? fallback;
if (!readiness || readiness.state === "ready") return null;
return (
<div className="bg-destructive/10 text-destructive border-destructive/20 border-b px-4 py-2 text-sm">
{readiness.message ?? "The Pi runtime has no usable model selected."}
</div>
);
}