This commit is contained in:
hupenglong1
2026-05-20 15:04:19 +08:00
parent c4b935692c
commit 1a94cec822
27 changed files with 4831 additions and 1693 deletions
@@ -41,6 +41,7 @@ import {
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet";
import {
ACTIVE_SESSION_CHANGED_EVENT,
readActiveSessionId,
@@ -140,7 +141,7 @@ type ActivityItem = {
type LiveRunEvent = NonNullable<ClawRunStatus["events"]>[number];
export function ActivityPanel() {
export function ActivityPanel({ asDrawer = false }: { asDrawer?: boolean } = {}) {
const {
open,
mode,
@@ -221,20 +222,12 @@ export function ActivityPanel() {
prevLatestIdRef.current = latestId;
}, [items, mode, openActivity, selectedId]);
if (!open) return null;
if (!asDrawer && !open) return null;
if (mode === "files") {
return (
<SessionFilesPanel
key={`${sessionId ?? "active"}:${filesRefreshToken}`}
sessionId={sessionId}
onClose={close}
/>
);
}
const isFilesMode = mode === "files";
return (
<aside className="fixed inset-y-0 right-0 z-30 flex w-[min(23rem,calc(100vw-1rem))] flex-col border-l bg-background shadow-xl lg:static lg:z-auto lg:h-full lg:w-92 lg:shrink-0 lg:shadow-none">
const activityBody = (
<>
<header className="flex h-16 shrink-0 items-center justify-between border-b px-4">
<div className="min-w-0">
<h2 className="font-semibold text-base"></h2>
@@ -244,16 +237,18 @@ export function ActivityPanel() {
: "暂无链路"}
</p>
</div>
<Button
type="button"
variant="ghost"
size="icon"
className="size-8"
onClick={close}
aria-label="关闭活动面板"
>
<PanelRightCloseIcon className="size-4" />
</Button>
{!asDrawer ? (
<Button
type="button"
variant="ghost"
size="icon"
className="size-8"
onClick={close}
aria-label="关闭活动面板"
>
<PanelRightCloseIcon className="size-4" />
</Button>
) : null}
</header>
<div className="min-h-0 flex-1 overflow-y-auto px-4 py-4">
{visibleItems.length === 0 ? (
@@ -280,6 +275,52 @@ export function ActivityPanel() {
</div>
)}
</div>
</>
);
if (asDrawer) {
return (
<Sheet
open={open}
onOpenChange={(next) => {
if (!next) close();
}}
>
<SheetContent
side="right"
className="flex w-[min(25rem,calc(100vw-1rem))] flex-col gap-0 p-0 sm:max-w-md"
>
<SheetTitle className="sr-only">
{isFilesMode ? "聊天中的文件" : "活动"}
</SheetTitle>
{isFilesMode ? (
<SessionFilesPanel
key={`drawer:${sessionId ?? "active"}:${filesRefreshToken}`}
sessionId={sessionId}
onClose={close}
embedded
/>
) : (
activityBody
)}
</SheetContent>
</Sheet>
);
}
if (isFilesMode) {
return (
<SessionFilesPanel
key={`${sessionId ?? "active"}:${filesRefreshToken}`}
sessionId={sessionId}
onClose={close}
/>
);
}
return (
<aside className="fixed inset-y-0 right-0 z-30 flex w-[min(23rem,calc(100vw-1rem))] flex-col border-l bg-background shadow-xl lg:static lg:z-auto lg:h-full lg:w-92 lg:shrink-0 lg:shadow-none">
{activityBody}
</aside>
);
}
@@ -308,9 +349,11 @@ type SessionFilesPayload = {
function SessionFilesPanel({
sessionId,
onClose,
embedded = false,
}: {
sessionId: string | null;
onClose: () => void;
embedded?: boolean;
}) {
const [payload, setPayload] = useState<SessionFilesPayload | null>(null);
const [status, setStatus] = useState("正在读取聊天中的文件...");
@@ -377,8 +420,8 @@ function SessionFilesPanel({
const outputFiles = payload?.output ?? [];
const total = inputFiles.length + outputFiles.length;
return (
<aside className="fixed inset-y-0 right-0 z-30 flex w-[min(25rem,calc(100vw-1rem))] flex-col border-l bg-background shadow-xl lg:static lg:z-auto lg:h-full lg:w-96 lg:shrink-0 lg:shadow-none">
const body = (
<>
<header className="flex h-16 shrink-0 items-center justify-between border-b px-4">
<div className="min-w-0">
<h2 className="font-semibold text-base"></h2>
@@ -386,16 +429,18 @@ function SessionFilesPanel({
{status || `${total} 个文件`}
</p>
</div>
<Button
type="button"
variant="ghost"
size="icon"
className="size-8"
onClick={onClose}
aria-label="关闭文件面板"
>
<PanelRightCloseIcon className="size-4" />
</Button>
{!embedded ? (
<Button
type="button"
variant="ghost"
size="icon"
className="size-8"
onClick={onClose}
aria-label="关闭文件面板"
>
<PanelRightCloseIcon className="size-4" />
</Button>
) : null}
</header>
<div className="min-h-0 flex-1 overflow-y-auto px-4 py-4">
{!status && total === 0 ? (
@@ -406,6 +451,16 @@ function SessionFilesPanel({
<FileSection title="输入" files={inputFiles} />
<FileSection title="输出" files={outputFiles} />
</div>
</>
);
if (embedded) {
return <div className="flex min-h-0 flex-1 flex-col">{body}</div>;
}
return (
<aside className="fixed inset-y-0 right-0 z-30 flex w-[min(25rem,calc(100vw-1rem))] flex-col border-l bg-background shadow-xl lg:static lg:z-auto lg:h-full lg:w-96 lg:shrink-0 lg:shadow-none">
{body}
</aside>
);
}