Repository navigation
Description
type: spec
date: 2026-08-11
status: draft
priority: p2
platform: opencode
tags: [spec, ui, markdown, side-panel, preview, authoring]
linked_plan: null
Preview Tab — Document Viewer/Editor in the Side Panel
Important
Problem: AI-modified .md files are only accessible through the Modified Files diff view, which shows raw unified diffs — unreadable for documentation-heavy workflows. There is no way to preview rendered documents or make quick edits without leaving the session.
Approach: Add a new "Preview" tab to the side panel (peer of Modified Files and Context) that lists AI-modified markdown files from the current session. Selecting a file renders it as a formatted document. A per-file toggle switches between rendered preview and a raw textarea for direct editing. Replace the existing "+" button with an extensible dropdown menu for opening panel types.
Approaches Considered:
- (A) New side panel tab + menu launcher (chosen) — clean separation of concerns; document authoring is a distinct activity from code review; follows the established tab-registration pattern (Context tab precedent); the menu makes all openable panels discoverable and extensible.
- (B) Extend the existing Review tab with a render mode — the original Preview Tab — Document Viewer/Editor in the Side Panel #162 design; smaller blast radius but conflates two purposes (code review vs document reading/editing) into one tab, making the UX ambiguous.
- (C) Dedicated editor panel (separate from side panel) — maximum screen real estate but breaks the side-panel mental model and requires new panel infrastructure.
Scope: .md files modified by the AI in the current session. Preview mode renders documents; raw mode enables direct editing with auto-save. The "+" button is replaced with a panel-type menu. LaTeX (.tex) is a follow-up — full-document LaTeX rendering in the browser is a hard problem.
Assumptions: The existing Markdown component handles GFM rendering (headings, code blocks, LaTeX math, tables, links, images) and is stable/performant for file-sized documents.
Acceptance Criteria
Preview tab
- A new "Preview" tab appears in the side panel alongside Modified Files and Context
- The tab lists all
.mdfiles the AI modified in the current session - Each file in the list shows an A (added) or M (modified) badge
- Clicking a file replaces the list with the rendered content (back button returns to list)
-
.mdfiles render through the existingMarkdowncomponent (full GFM: headings, bold/italic, code blocks with syntax highlighting, tables, links, images, LaTeX math, task lists) - A per-file toggle switches between Preview (rendered) and Raw (editable textarea) modes
- Raw mode shows a monospace textarea with the file source
- Edits in raw mode auto-save with ~1s debounce
- Cmd+S (Ctrl+S on Linux) triggers an immediate save in raw mode
- Saves write to disk via the existing file-write mechanism
- The tab only appears when at least one
.mdfile has been AI-modified in the session (no empty state) - Non-
.mdfiles never appear in this tab - The Modified Files tab and Context tab are completely unaffected
Panel menu (replaces the "+" button)
- The existing "+" button in the tab bar is replaced with a "+" button that opens a dropdown menu
- The menu lists available panel types: "Context", "Preview"
- Clicking a menu item opens that tab (same as if it were already open and you clicked its trigger)
- The
SESSION_OPEN_FILE_TAB("open-file") tab is removed — no more inline file browser - The old-layout dialog-based file open behavior is also removed
- The menu is the single extensible seam for adding future panel types
- Menu items that are already open could be visually indicated (dimmed or checkmarked)
Key Decisions
Tab registration pattern
Follow the Context tab precedent:
- Add
"preview"constant tolayout-tabs.ts - Filter from sortable file-tabs in
createSessionTabs(helpers.ts) - Add to the active-tab priority chain
- Add trigger + content in
session-side-panel.tsx(both v1 and v2 code paths)
Panel menu replaces "+" button
The current "+" button has two implementations:
- Old layout: lazy-imports
DialogSelectFileand shows a modal file picker - V2 layout: calls
openFileBrowser()which opensSESSION_OPEN_FILE_TABinline
Both are replaced with a single dropdown menu (a Popover or DropdownMenu from the UI kit). The menu is a static list of panel types — initially "Context" and "Preview". Each item calls the same tabs().open("context") / tabs().open("preview") + tabs().setActive(...) pattern. This becomes the canonical extensibility point for future panels (e.g., "Outline", "References", "Terminal").
The SESSION_OPEN_FILE_TAB constant and all code paths that reference it are removed.
File list — flat, with change badges
A flat file list (not a tree) showing basename + relative directory, with A/M badges sourced from the session file-change set. Same visual density as the Modified Files list. Click navigates to the content view; a back/breadcrumb button returns to the list.
Content view — two modes
| Mode | Behavior |
|---|---|
| Preview (default) | Rendered markdown via the existing Markdown component. Read-only. |
| Raw | Monospace textarea. Editable. Auto-save on debounce + Cmd+S for immediate flush. |
The toggle is per-file — each file remembers its mode independently within the session.
Save mechanism
Raw-mode edits auto-save via a debounced (~1s) write to disk using the same file-write channel the agent uses. Cmd+S bypasses the debounce for an immediate write. The file remains in the Preview tab list (it was AI-modified; user edits don't remove it).
Data contract
// File entry in the Preview tab list
interface PreviewFileEntry {
path: string // absolute file path
relativePath: string // relative to workspace root
basename: string // file name
extension: ".md"
changeType: "added" | "modified"
}
// Per-file view state
interface PreviewFileState {
mode: "preview" | "raw"
scrollPosition?: number // restore scroll on mode toggle
unsavedContent?: string // buffer for debounced save
}
// Panel menu item (extensible registry)
interface PanelMenuItem {
id: string // tab value, e.g. "context", "preview"
label: string // display name
icon: string // icon name from the icon set
available: () => boolean // whether to show (e.g. preview only when .md files exist)
}Constraints & Invariants
- The existing
Markdowncomponent must not be forked — extend via props/composition only - No changes to the Modified Files tab behavior or the Context tab
- No new panel layout types — the Preview tab lives inside the existing side panel tab system
- CSS custom properties from the existing theme — no hardcoded colors
- The tab is hidden when no
.mdfiles have been AI-modified in the session (no empty state) - File writes from raw mode use the same permissions/mechanism as agent file writes
- The panel menu is the ONLY way to open non-file tabs going forward (replaces all "+" button behaviors)
File Changes
New files
| File | Purpose |
|---|---|
packages/app/src/components/session/session-preview-tab.tsx |
Main tab component: file list + content view + mode toggle |
packages/app/src/components/session/preview-file-list.tsx |
Flat file list with A/M badges |
packages/app/src/components/session/preview-content.tsx |
Content renderer (wraps existing Markdown component) |
packages/app/src/components/session/raw-editor.tsx |
Monospace textarea with auto-save + Cmd+S |
packages/app/src/components/session/panel-menu.tsx |
Dropdown menu component (replaces "+" button) |
Modified files
| File | Change |
|---|---|
packages/app/src/context/layout-tabs.ts |
Add "preview" tab constant; remove SESSION_OPEN_FILE_TAB |
packages/app/src/pages/session/helpers.ts |
Add previewOpen signal, filter from sortable tabs, add to priority chain; remove open-file tab logic |
packages/app/src/pages/session/session-side-panel.tsx |
Replace "+" button with PanelMenu; add Preview tab trigger + content (both v1 and v2 paths); remove SESSION_OPEN_FILE_TAB content/references |
packages/app/src/components/session/index.ts |
Export new components |
Removed
| File / Export | Reason |
|---|---|
SESSION_OPEN_FILE_TAB constant |
No longer used — panel menu replaces the file browser tab |
DialogSelectFile lazy import in side panel |
Replaced by panel menu |
openFileBrowser() function |
Replaced by panel menu |
Prior Art
- Context tab (
session-context-tab.tsx) — the pattern for registering a non-file tab in the side panel - Modified Files / Review tab — the UX precedent for listing session-changed files with badges
Markdowncomponent (packages/session-ui/src/components/markdown.tsx) — full GFM + LaTeX math + streaming + syntax highlighting via shiki- Obsidian / Notion — the reading-mode vs editing-mode toggle UX precedent
- VS Code "..." menu in panel headers — precedent for a menu that opens different panel types
Follow-up: LaTeX (.tex) support
Full-document LaTeX rendering in the browser is hard. The options:
| Option | Trade-off |
|---|---|
| KaTeX "document mode" (hand-rolled parser) | KaTeX is a math renderer, not a document renderer. You would be writing a LaTeX parser. |
| LaTeX.js | Covers a subset. Breaks on tikz, pgfplots, biblatex, custom macros. |
| WASM TeX engine (SwiftLaTeX) | 30-100MB binary, multi-second compile, no arbitrary packages. |
| Server-side compilation | Requires TeX Live, compilation service, sandboxing. Way out of scope. |
The most pragmatic future path: detect .tex files, show syntax-highlighted source with inline math blocks rendered via KaTeX (i.e., "raw mode with math sprinkled in"). Not a full Overleaf-style preview, but useful. Separate issue when the need is clear.
Source
Revised from the original #162 spec (2026-08-10). Pivoted from "rendered markdown diffs in the Review tab" to a standalone Preview tab. Scoped to .md only after honest assessment of LaTeX rendering feasibility. Panel menu added to replace the single-purpose "+" button with an extensible launcher.
Activity
[-]Rendered Markdown Preview in the Review Side Panel[/-][+]Preview Tab — Document Viewer/Editor in the Side Panel[/+]feat(app): Preview Tab — Document Viewer/Editor in Side Panel (#162)