Repository navigation
feat(web): complete preview annotation artifacts - #64
Conversation
There was a problem hiding this comment.
Pull request overview
Completes preview annotation artifacts and expands browser/right-panel functionality across web, server, and mobile clients.
Changes:
- Adds annotation cards, prompt restoration, timeline rendering, and artifact actions.
- Adds right-panel tabs for previews, terminals, files, and diffs.
- Introduces authorized workspace reads, signed asset URLs, preview shortcuts, and Windows spawn compatibility.
Reviewed changes
Copilot reviewed 73 out of 74 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
pnpm-lock.yaml |
Updates workspace dependency links. |
packages/effect-codex-app-server/src/client.test.ts |
Uses Windows-safe process spawning. |
packages/effect-codex-app-server/package.json |
Adds shared shell dependency. |
packages/effect-acp/src/protocol.test.ts |
Uses Windows-safe process spawning. |
packages/effect-acp/src/client.test.ts |
Uses Windows-safe process spawning. |
packages/effect-acp/package.json |
Adds shared shell dependency. |
packages/contracts/src/project.ts |
Scopes workspace search/read contracts. |
packages/contracts/src/keybindings.ts |
Adds preview commands. |
packages/contracts/src/keybindings.test.ts |
Tests preview commands. |
packages/contracts/src/ipc.ts |
Exposes read-file and asset APIs. |
packages/client-runtime/src/state/composerPathSearch.ts |
Uses thread-scoped search targets. |
packages/client-runtime/src/composerPathSearchState.ts |
Implements thread-scoped searches. |
packages/client-runtime/src/composerPathSearchState.test.ts |
Updates search tests. |
oxlint-plugin-t3code/test/utils.ts |
Uses Windows-safe oxlint spawning. |
oxlint-plugin-t3code/package.json |
Adds shared shell dependency. |
KEYBINDINGS.md |
Documents preview shortcuts. |
apps/web/test/wsRpcHarness.ts |
Registers discovered-server streaming. |
apps/web/src/terminalLabels.ts |
Generates stable terminal labels. |
apps/web/src/terminalLabels.test.ts |
Tests terminal labels. |
apps/web/src/rpc/wsRpcClient.ts |
Adds file and asset RPC methods. |
apps/web/src/rightPanelStore.ts |
Stabilizes and expands panel state. |
apps/web/src/rightPanelStore.test.ts |
Tests surface operations. |
apps/web/src/localApi.test.ts |
Tests asset API forwarding. |
apps/web/src/lib/projectReactQuery.ts |
Uses authorized search scopes. |
apps/web/src/keybindings.ts |
Adds preview contexts. |
apps/web/src/keybindings.test.ts |
Tests preview shortcut resolution. |
apps/web/src/environmentApi.ts |
Exposes file and asset methods. |
apps/web/src/composerDraftStore.ts |
Atomically removes annotation artifacts. |
apps/web/src/composerDraftStore.test.ts |
Tests paired artifact removal. |
apps/web/src/components/ui/toast.tsx |
Adds icons and action slots. |
apps/web/src/components/ui/toast.logic.test.ts |
Tests additional toast actions. |
apps/web/src/components/ThreadTerminalDrawer.tsx |
Supports external terminal labels. |
apps/web/src/components/ThreadTerminalDrawer.browser.tsx |
Extends environment API mocking. |
apps/web/src/components/Sidebar.tsx |
Opens discovered localhost previews. |
apps/web/src/components/RightPanelTabs.tsx |
Adds tab menus and panel controls. |
apps/web/src/components/RightPanelTabs.browser.tsx |
Tests tab interactions. |
apps/web/src/components/preview/PreviewView.tsx |
Adds artifact and server actions. |
apps/web/src/components/preview/PreviewView.test.tsx |
Tests screenshot actions. |
apps/web/src/components/preview/PreviewPanelShell.tsx |
Supports maximized panels. |
apps/web/src/components/preview/PreviewPanelShell.test.ts |
Tests maximized layout. |
apps/web/src/components/preview/PreviewPanel.tsx |
Propagates maximized state. |
apps/web/src/components/preview/PreviewEmptyState.tsx |
Opens discovered server objects. |
apps/web/src/components/files/FilePreviewPanel.tsx |
Adds workspace file surfaces. |
apps/web/src/components/files/FilePreviewPanel.browser.tsx |
Tests file surfaces. |
apps/web/src/components/ChatView.tsx |
Integrates annotations and panel surfaces. |
apps/web/src/components/ChatView.browser.tsx |
Adds panel acceptance coverage. |
apps/web/src/components/ChatMarkdown.tsx |
Opens HTML/PDF links in preview. |
apps/web/src/components/ChatMarkdown.browser.tsx |
Tests previewable links. |
apps/web/src/components/chat/MessagesTimeline.tsx |
Renders annotation context cards. |
apps/web/src/components/chat/MessagesTimeline.test.tsx |
Tests annotation rendering. |
apps/web/src/components/chat/ComposerPreviewAnnotationCards.tsx |
Adds composer annotation cards. |
apps/web/src/components/chat/ComposerPreviewAnnotationCards.test.tsx |
Tests annotation cards. |
apps/web/src/components/chat/ChatComposer.tsx |
Integrates annotation drafts and search scopes. |
apps/web/src/browser/openFileInPreview.ts |
Opens signed workspace assets. |
apps/server/src/ws.ts |
Adds scoped reads and asset issuance. |
apps/server/src/workspace/Services/WorkspaceFileSystem.ts |
Defines workspace reads. |
apps/server/src/workspace/Services/WorkspaceEntries.ts |
Separates internal search input. |
apps/server/src/workspace/Layers/WorkspaceFileSystem.ts |
Implements protected file reads. |
apps/server/src/workspace/Layers/WorkspaceFileSystem.test.ts |
Tests reads and symlink rejection. |
apps/server/src/server.ts |
Registers the asset route. |
apps/server/src/server.test.ts |
Tests scoped workspace search. |
apps/server/src/keybindings.ts |
Adds default preview bindings. |
apps/server/src/keybindings.test.ts |
Tests preview defaults. |
apps/server/src/http.ts |
Serves signed asset bytes. |
apps/server/src/cli.ts |
Uses project-scoped search. |
apps/server/src/assets/AssetAccess.ts |
Implements signed asset capabilities. |
apps/server/src/assets/AssetAccess.test.ts |
Tests assets and symlink containment. |
apps/mobile-react/src/state/use-composer-path-search.ts |
Re-exports mobile search types. |
apps/mobile-react/src/state/queries.ts |
Uses thread-scoped path search. |
apps/mobile-react/src/features/threads/ThreadRouteScreen.tsx |
Passes thread identity to files. |
apps/mobile-react/src/features/threads/ThreadComposer.tsx |
Searches by thread. |
apps/mobile-react/src/features/files/ThreadFilesRouteScreen.tsx |
Reads files by thread. |
apps/mobile-react/src/features/files/thread-file-navigator-pane.tsx |
Threads authorization through navigation. |
apps/mobile-react/src/features/files/preload-workspace-file.ts |
Preloads thread-scoped files. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const contents = yield* fileSystem.readFileString(target.absolutePath).pipe( | ||
| Effect.mapError( | ||
| (cause) => | ||
| new WorkspaceFileSystemError({ | ||
| cwd: input.cwd, | ||
| relativePath: input.relativePath, | ||
| operation: "workspaceFileSystem.readFile", | ||
| detail: cause.message, | ||
| cause, | ||
| }), | ||
| ), | ||
| ); | ||
| return { relativePath: target.relativePath, contents }; |
There was a problem hiding this comment.
Fixed in 11ee5016d. Workspace reads now cap previews at 1 MB, report truncation, and avoid materializing the full file.
| if (input.resource._tag !== "workspace-file") { | ||
| return yield* issueAssetUrl({ resource: input.resource }); |
There was a problem hiding this comment.
Fixed in 11ee5016d. Project favicon asset requests now carry a project ID, and the server resolves the workspace root from the project snapshot before signing.
| <Menu key={surface.id}> | ||
| <MenuTrigger | ||
| render={ | ||
| <button |
There was a problem hiding this comment.
Fixed in 11ee5016d. Primary tab clicks only activate the tab; context actions moved to a dedicated overflow button.
| <PersistentThreadTerminalDrawer | ||
| threadRef={activeThreadRef} | ||
| threadId={activeThreadRef.threadId} | ||
| visible | ||
| terminalId={activeBrowserSurface.resourceId} |
There was a problem hiding this comment.
Fixed in 11ee5016d. Right-panel terminals no longer depend on the drawer-open flag, and the legacy drawer is not mounted while a terminal surface is active.
| const nextDraft: ComposerThreadDraftState = { | ||
| ...current, | ||
| images: current.images.filter((image) => image.id !== annotationId), | ||
| nonPersistedImageIds: current.nonPersistedImageIds.filter( | ||
| (imageId) => imageId !== annotationId, |
There was a problem hiding this comment.
Fixed in 11ee5016d. Removing an annotation now revokes its paired blob preview URL before removing the image.
Reconcile browser UI parity, preview annotations, file surfaces, and workspace-scoped access on current main. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
11ee501 to
c1a727d
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 80 out of 80 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (4)
apps/web/src/components/ChatView.tsx:4837
- The legacy drawer is suppressed only while a terminal surface is active. Starting a right-panel terminal sets the shared
terminalOpenflag via the terminalstartedevent; when the user switches that panel to Files/Diff/Preview, this condition becomes false and the same terminal reappears in the bottom drawer while its right-panel tab is still open. Suppress the legacy drawer whenever the active thread owns any terminal surface, not only when one is selected.
mountedThreadKey === activeThreadKey && activeBrowserSurface?.kind === "terminal" ? null : (
apps/web/src/components/RightPanelTabs.tsx:107
- Once a favicon request fails,
failedremains true for the lifetime of this tab component. Navigating the same preview tab to another origin with a valid favicon therefore keeps showing the generic globe forever. Key the icon by URL (or reset the failure state whenurlchanges) so each navigation can retry.
apps/server/src/http.ts:229 - This materializes the entire asset before responding. Workspace PDFs/images/HTML have no size limit, so each preview request can allocate the whole file in server memory and prevents efficient range/streamed delivery. Use
HttpServerResponse.file, as the attachment route does atapps/server/src/http.ts:170-187, while retaining these cache andnosniffheaders.
const fileSystem = yield* FileSystem.FileSystem;
const data = yield* fileSystem
.readFile(asset.path)
.pipe(Effect.catch(() => Effect.succeed(null)));
if (!data) {
return HttpServerResponse.text("Internal Server Error", { status: 500 });
}
return HttpServerResponse.uint8Array(data, {
status: 200,
contentType: Mime.getType(asset.path) ?? "application/octet-stream",
apps/web/src/components/files/FilePreviewPanel.tsx:65
- The server intentionally returns only the first 1 MB and sets
result.truncated, but this panel discards that flag and renders the prefix as though it were the complete file. Large source files are therefore silently misrepresented. Tracktruncatedwith the loaded contents and show a partial-file notice, matching the existing mobile file preview behavior inThreadFilesRouteScreen.tsx:140-147.
Clear preview annotations with sent composer content and preserve the new-browser surface until a preview session is created. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
main; this replaces the obsoletefollowup/browser-ui-paritybase.main; no older server/provider implementation was restored.Validation
pnpm fmt:checkpnpm lintpnpm typecheckcd apps/web && pnpm exec vp test run src/rightPanelStore.test.ts src/components/chat/ComposerPreviewAnnotationCards.test.tsx src/components/chat/MessagesTimeline.test.tsx src/components/preview/PreviewView.test.tsx src/components/preview/PreviewPanelShell.test.tscd packages/contracts && pnpm exec vp test run src/preview.test.tscd apps/server && pnpm exec vp test run src/mcp/McpHttpServer.test.ts src/mcp/McpPreviewAutomationEndToEnd.test.ts src/mcp/McpSessionRegistry.test.ts src/mcp/PreviewAutomationBroker.test.ts src/mcp/toolkits/preview/tools.test.ts