From 7e2c3e388e7c934636a9f205c6b24bac7f38e5b8 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 22 Jun 2026 22:37:02 +0200 Subject: [PATCH] feat: add desktop artifacts panel --- .../chat-artifacts/ui/artifacts-panel.tsx | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 surfsense_web/features/chat-artifacts/ui/artifacts-panel.tsx diff --git a/surfsense_web/features/chat-artifacts/ui/artifacts-panel.tsx b/surfsense_web/features/chat-artifacts/ui/artifacts-panel.tsx new file mode 100644 index 000000000..136faf8dd --- /dev/null +++ b/surfsense_web/features/chat-artifacts/ui/artifacts-panel.tsx @@ -0,0 +1,84 @@ +"use client"; + +import { useAtomValue } from "jotai"; +import { LayersIcon, XIcon } from "lucide-react"; +import { useMemo } from "react"; +import { Button } from "@/components/ui/button"; +import type { ArtifactKind, ChatArtifact } from "../model/artifact"; +import { chatArtifactsAtom } from "../state/artifacts-panel.atom"; +import { ArtifactRow } from "./artifact-row"; + +const GROUP_ORDER: { kind: ArtifactKind; label: string }[] = [ + { kind: "report", label: "Reports" }, + { kind: "resume", label: "Resumes" }, + { kind: "podcast", label: "Podcasts" }, + { kind: "video", label: "Presentations" }, + { kind: "image", label: "Images" }, +]; + +function groupByKind(artifacts: ChatArtifact[]): { label: string; items: ChatArtifact[] }[] { + return GROUP_ORDER.map(({ kind, label }) => ({ + label, + items: artifacts.filter((a) => a.kind === kind), + })).filter((group) => group.items.length > 0); +} + +function EmptyState() { + return ( +
+ +

No artifacts yet

+

+ Reports, podcasts, presentations, and images you generate will appear here. +

+
+ ); +} + +function ArtifactGroups({ artifacts }: { artifacts: ChatArtifact[] }) { + const groups = useMemo(() => groupByKind(artifacts), [artifacts]); + + if (groups.length === 0) return ; + + return ( +
+ {groups.map((group) => ( +
+

+ {group.label} +

+
+ {group.items.map((artifact) => ( + + ))} +
+
+ ))} +
+ ); +} + +/** Inner content shared by the desktop right-panel tab and the mobile drawer. */ +export function ArtifactsPanelContent({ onClose }: { onClose?: () => void }) { + const artifacts = useAtomValue(chatArtifactsAtom); + + return ( + <> +
+

Artifacts

+ {onClose && ( + + )} +
+ + + ); +}