import { useEffect } from "react"; import { useQueryClient } from "@tanstack/react-query"; import type { Meta, StoryObj } from "@storybook/react-vite"; import type { ResolvedWorkspaceResource, WorkspaceFileContent, WorkspaceFileListFileItem, WorkspaceFileListResponse, } from "@paperclipai/shared"; import { FileViewerProvider, useRequiredFileViewer } from "@/context/FileViewerContext"; import { FileViewerSheet } from "@/components/FileViewerSheet"; import { IssueWorkspaceCard } from "@/components/IssueWorkspaceCard"; import { queryKeys } from "@/lib/queryKeys"; /** * Screenshot-review surface for PAP-10511, rendering the REAL FileViewerSheet / * IssueWorkspaceCard (not a static frame) so the captures are visual truth. * Stories seed the react-query cache so the components render without a backend. * Capture each at desktop + 360px and in both themes via the Storybook theme global. */ const ISSUE_ID = "issue-browse-demo"; function listKey(issueId: string) { return queryKeys.issues.fileResources(issueId, { workspace: "auto", mode: "changed", q: null, limit: 100, offset: 0 }); } function item(relativePath: string, minutesAgo: number, overrides: Partial = {}): WorkspaceFileListFileItem { return { kind: "file", provider: "git_worktree", title: relativePath.split("/").pop() ?? relativePath, relativePath, displayPath: relativePath, workspaceLabel: "issue execution workspace · PAP-1953", workspaceKind: "execution_workspace", workspaceId: "ws-1", contentType: "text/plain; charset=utf-8", byteSize: 2048, modifiedAt: new Date(Date.now() - minutesAgo * 60_000).toISOString(), previewKind: "text", capabilities: { preview: true, download: false, listChildren: false }, ...overrides, }; } const recentList: WorkspaceFileListResponse = { kind: "workspace_file_list", state: "available", workspace: { provider: "git_worktree", workspaceLabel: "issue execution workspace · PAP-1953", workspaceKind: "execution_workspace", workspaceId: "ws-1", }, query: { workspace: "auto", mode: "changed", q: null, limit: 100, offset: 0 }, items: [ item("ui/src/components/WorkspaceFileBrowser.tsx", 2), item("ui/src/components/FileViewerSheet.tsx", 2), item("server/src/routes/file-resources.ts", 14), item("doc/PRODUCT.md", 60), item("packages/shared/src/types/workspace-file-resource.ts", 64), item("server/src/services/very/deeply/nested/structure/workspace-file-resources.ts", 180), ], scannedCount: 412, truncated: true, }; function unavailable(reason: string): WorkspaceFileListResponse { return { kind: "workspace_file_list", state: "unavailable", unavailableReason: reason, workspace: null, query: { workspace: "auto", mode: "changed", q: null, limit: 100, offset: 0 }, items: [], scannedCount: 0, truncated: false, }; } const viewedResource: ResolvedWorkspaceResource = { kind: "file", provider: "git_worktree", title: "FileViewerSheet.tsx", displayPath: "ui/src/components/FileViewerSheet.tsx", workspaceLabel: "issue execution workspace · PAP-1953", workspaceKind: "execution_workspace", workspaceId: "ws-1", contentType: "text/plain; charset=utf-8", byteSize: 2048, previewKind: "text", capabilities: { preview: true, download: false, listChildren: false }, }; const viewedContent: WorkspaceFileContent = { resource: viewedResource, content: { encoding: "utf8", data: [ "export function FileViewerSheet({ issueId, state, open }: FileViewerSheetProps) {", " const viewer = useRequiredFileViewer();", " const browseMode = state === null && (showPromptWhenEmpty || viewer.browse);", " const cameFromBrowse = state !== null && viewer.browse;", " // … single sheet, two modes: browse ⇄ view", " return ;", "}", ].join("\n"), }, }; function BrowseSheet({ data }: { data: WorkspaceFileListResponse }) { const queryClient = useQueryClient(); queryClient.setQueryData(listKey(ISSUE_ID), data); return ( ); } function ViewFromBrowseInner() { const viewer = useRequiredFileViewer(); useEffect(() => { viewer.open( { path: "ui/src/components/FileViewerSheet.tsx", line: 3, column: null, workspace: "auto" }, { fromBrowse: true }, ); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ; } function ViewFromBrowse() { const queryClient = useQueryClient(); queryClient.setQueryData(listKey(ISSUE_ID), recentList); queryClient.setQueryData( queryKeys.issues.fileResource(ISSUE_ID, { path: "ui/src/components/FileViewerSheet.tsx", workspace: "auto", }), viewedResource, ); queryClient.setQueryData( queryKeys.issues.fileResourceContent(ISSUE_ID, { path: "ui/src/components/FileViewerSheet.tsx", workspace: "auto", }), viewedContent, ); return ( ); } function Placement() { const queryClient = useQueryClient(); queryClient.setQueryData(queryKeys.instance.experimentalSettings, { enableEnvironments: false, enableIsolatedWorkspaces: true, }); return (

Workspace

{}} onBrowseFiles={() => {}} onOpenFileByPath={() => {}} />
); } const meta: Meta = { title: "Issue/Workspace File Browser", parameters: { layout: "fullscreen" }, }; export default meta; type Story = StoryObj; export const BrowseRecent: Story = { render: () => }; export const BrowseNoWorkspace: Story = { render: () => }; export const BrowseRemoteWorkspace: Story = { render: () => }; export const BrowseCleanedUpWorkspace: Story = { render: () => }; export const ViewModeFromBrowse: Story = { render: () => }; export const CardPlacement: Story = { render: () => };