import { Link } from "../../lib/navigation"; import type { ArtifactCatalogItem } from "@opengeni/sdk"; import { ArtifactViewerHeader } from "../../../artifacts"; import { Loader2Icon, Maximize2Icon, PanelsTopLeftIcon, RefreshCwIcon, RotateCwIcon, SettingsIcon, } from "lucide-react"; import { lazy, useEffect, useRef, useState } from "react"; import { Button } from "../../../ui/button"; import { useAppContext } from "../../app-context"; import { EditableArtifactSurface, usePreloadAppSurface } from "../../lib/app-surfaces"; import { ArtifactLibrary, type ArtifactPinAction } from "../artifacts/artifact-library"; import { defaultArtifactFilters, filterArtifactCatalog } from "../../lib/artifact-catalog"; import { hasWorkspacePermission } from "../../lib/permissions"; import { cn } from "../../lib/utils"; const RetainedArtifactRoute = lazy(() => import("../../routes/retained-artifact").then((module) => ({ default: module.RetainedArtifactRoute, })), ); const ArtifactDetailRoute = lazy(() => import("../../routes/artifacts").then((module) => ({ default: module.ArtifactDetailRoute })), ); export type SessionEditableArtifactSummary = Readonly<{ id: string; modality: "document" | "spreadsheet" | "presentation" | "site" | "image" | "file"; title: string; versionId?: string | undefined; siteStatus?: "active" | "archived" | undefined; catalogItem?: ArtifactCatalogItem | undefined; }>; export type SessionEditableArtifactsStatus = "loading" | "ready" | "error"; export function SessionEditableArtifactsWorkspace({ workspaceId, sessionId, artifacts, status, onRetry, initialSelectedArtifactId, openArtifactRequest, onSelectedArtifactIdChange, onPin, }: Readonly<{ workspaceId: string; sessionId?: string | undefined; artifacts: readonly SessionEditableArtifactSummary[]; status: SessionEditableArtifactsStatus; onRetry: () => void; initialSelectedArtifactId?: string | null | undefined; openArtifactRequest?: | { artifactId: string; artifactKind?: SessionEditableArtifactSummary["modality"] | undefined; requestId: number; } | null | undefined; onSelectedArtifactIdChange?: ((artifactId: string | null) => void) | undefined; onPin?: ArtifactPinAction | undefined; }>) { // The editor is the host's separate chunk: start it with the workspace, as // when it shipped inside this module, so an artifact opens without a second wait. usePreloadAppSurface("editableArtifact", true); const [selectedArtifactId, setSelectedArtifactId] = useState( () => initialSelectedArtifactId ?? artifacts[0]?.id ?? null, ); const [browsing, setBrowsing] = useState(!initialSelectedArtifactId && !openArtifactRequest); const [filters, setFilters] = useState(defaultArtifactFilters); // A Site's Settings open in place of the Site, for the Site on screen only. const [settingsFor, setSettingsFor] = useState(null); // The Site's reload and full screen live in this header, not a second // toolbar under it. const [siteReload, setSiteReload] = useState(0); const [fullScreenSite, setFullScreenSite] = useState(null); const { accessContext } = useAppContext(); const canManageSites = hasWorkspacePermission(accessContext, workspaceId, "artifacts:publish"); const catalogItems = artifacts.flatMap((item) => (item.catalogItem ? [item.catalogItem] : [])); const handledRequestId = useRef(null); useEffect(() => { // Consume navigation before reconciling a selection whose synthetic summary // may have been replaced by this request. Later renders preserve manual choices. if (openArtifactRequest && handledRequestId.current !== openArtifactRequest.requestId) { handledRequestId.current = openArtifactRequest.requestId; const selected = openArtifactRequest.artifactKind ? `${openArtifactRequest.artifactKind}:${openArtifactRequest.artifactId}` : openArtifactRequest.artifactId; setSelectedArtifactId(selected); setBrowsing(false); onSelectedArtifactIdChange?.(selected); return; } if (status === "loading") return; // Discovery must not turn passive browsing into a remembered editor selection. if (browsing && catalogItems.length > 0) return; if ( selectedArtifactId && artifacts.some((artifact) => matchesArtifact(artifact, selectedArtifactId)) ) { return; } const next = artifacts[0]?.id ?? null; setSelectedArtifactId(next); onSelectedArtifactIdChange?.(next); }, [ artifacts, browsing, catalogItems.length, openArtifactRequest, onSelectedArtifactIdChange, selectedArtifactId, status, ]); const artifact = artifacts.find((candidate) => matchesArtifact(candidate, selectedArtifactId)) ?? artifacts[0]; if (browsing && catalogItems.length > 0) { return (

Session artifacts

{ const key = `${item.kind}:${item.id}`; setSelectedArtifactId(key); setBrowsing(false); onSelectedArtifactIdChange?.(key); }} />
); } if (!artifact) { const loading = status === "loading"; const failed = status === "error"; return (
{loading ? ( ) : failed ? ( ) : ( )}

{loading ? "Loading artifacts" : failed ? "Artifacts unavailable" : "No artifacts yet"}

{loading ? "Opening the shared workspace." : failed ? "The shared workspace could not be loaded." : "Ask the agent to create or import a Site, image, document, spreadsheet, or presentation."}

{failed ? ( ) : null}
); } const settingsOpen = artifact.modality === "site" && settingsFor === artifact.id; const settingsLabel = settingsOpen ? "Back to Site" : "Site settings"; return (
0 ? () => { setBrowsing(true); onSelectedArtifactIdChange?.(null); } : undefined } backLabel="Back to session artifacts" // One line: the icon already says what kind it is. subtitle="" actions={ <> {status === "error" ? ( ) : null} {artifact.modality === "site" && !settingsOpen ? ( <> ) : null} {artifact.modality === "site" && canManageSites ? ( ) : null} } />
{artifact.modality === "site" ? ( setFullScreenSite(next ? artifact.id : null), }} /> ) : artifact.modality === "image" || artifact.modality === "file" ? ( ) : ( )}
); } function matchesArtifact(artifact: SessionEditableArtifactSummary, selected: string | null) { // A file-route request can arrive before the catalog classifies its image MIME. return ( selected === `${artifact.modality}:${artifact.id}` || selected === artifact.id || (artifact.modality === "image" && selected === `file:${artifact.id}`) ); }