Skip to content

Preview Tab — Document Viewer/Editor in the Side Panel #162

Description

@jeonghun-jj-lee

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 .md files 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)
  • .md files render through the existing Markdown component (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 .md file has been AI-modified in the session (no empty state)
  • Non-.md files 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:

  1. Add "preview" constant to layout-tabs.ts
  2. Filter from sortable file-tabs in createSessionTabs (helpers.ts)
  3. Add to the active-tab priority chain
  4. 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 DialogSelectFile and shows a modal file picker
  • V2 layout: calls openFileBrowser() which opens SESSION_OPEN_FILE_TAB inline

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 Markdown component 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 .md files 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
  • Markdown component (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.

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions