feat(app): Preview Tab — Document Viewer/Editor in Side Panel - #182
Merged
Conversation
- Add SESSION_PREVIEW_TAB constant and tab registration in layout-tabs.ts - Add previewOpen signal and preview tab filtering in helpers.ts - Create SessionPreviewTab component: file list with A/M badges, rendered markdown preview via existing Markdown component, raw editor with debounced auto-save and Cmd+S support - Create PanelMenu dropdown component replacing the + button in both v1 and v2 side panel layouts - Wire Preview tab trigger and content in both layout paths - Tab only shows when .md files have been AI-modified in the session - Export new components from session/index.ts Remaining work: - Add /file/write server endpoint for raw editor saves - Remove SESSION_OPEN_FILE_TAB and related dead code paths - Test end-to-end in the running app
…rowser Server: - Add POST /file/write endpoint to the file API group with path containment check and writeWithDirs for safe file writes from the Preview tab's raw editor Client (session-side-panel): - Remove the SESSION_OPEN_FILE_TAB inline file browser trigger from both v1 and v2 tab lists (the spec removes the inline file opener) - Remove openFileBrowser() function (replaced by PanelMenu) - Remove the lazy DialogSelectFile import from the old + button - Remove unused useDialog import and openFileKeybind memo - Simplify the For-each-panelTabs render (no more Show branch for open-file tab) - SessionFileBrowserTab still renders file content for open file tabs (only the 'open-file' tab trigger is removed, not file viewing)
jeonghun-jj-lee
marked this pull request as ready for review
August 11, 2026 01:33
- Make Preview menu item always visible (available: () => true) so users can discover it even when no .md files are modified yet - Remove nested button in PanelMenu trigger — the DropdownMenu.Trigger already renders as a button, so just put the Icon directly inside it - Show the Preview tab trigger whenever previewOpen() is true (not gated on hasMarkdownFiles) — the tab's internal file list handles the empty state message - Simplify PanelMenu: remove unused IconButton/IconButtonV2 imports, use Kobalte's gutter/placement props for positioning
- Add preprocessMarkdown() that converts ```math fenced blocks to $$...$$ display math blocks (the Markdown component's KaTeX extension only matches $$ delimiters, not GitHub-flavored ```math) - Replace the subtle single-button toggle with a visible segmented control: both Preview and Raw buttons shown side-by-side, the active mode gets a highlighted background so the current state is obvious - Remove unused toggleMode function
Right-clicking a file in the Preview tab's file list now shows a context menu with 'Copy full path' and 'Copy filename' options, matching the Files Changed panel's behavior. Uses the same MenuV2 context menu pattern from session-review-file-preview-v2.
- Math wrapping: display math (.katex-display) gets overflow-x:auto so wide equations scroll horizontally instead of overflowing the panel - Save status: shows 'Saving...' during the write, then 'Saved' for 2s after success (both debounced auto-save and Cmd+S) - Mode toggle: replaced text buttons with an icon-only SegmentedControlV2 (same component as the unified/split toggle in Files Changed), with TooltipV2 on hover showing 'Preview' or 'Raw' respectively - Slightly smaller KaTeX font size (0.9em) to fit better in narrow panels
- SegmentedControlV2: override default 232px width with !w-auto and !flex-none !px-2 on items so it's compact icon-only size - PanelMenu trigger: smaller (w-6 h-6), matching the icon button sizing that was there before; add px-2 to the v2 sticky wrapper for proper spacing within the tab bar - Save status: inline in the header row next to the filename (shows 'Saving...' / 'Saved' text when active)
…cator - Panel menu wrapper: pl-3 pr-3 on both v1/v2 layouts to give clear separation between the last tab and the + button - Zoom control: +/- buttons with a percentage textbox, rendered to the left of the preview/raw toggle (range 50%–200%, step 10%) - Save indicator: green text for 'Saved', muted for 'Saving...' - Reverted PanelMenu to simpler trigger (plain button with Icon)
- Use writeClipboardViaBridge (works in Amicode webview) with navigator.clipboard.writeText as fallback — fixes copy not working - Zoom percentage input: w-9 → w-11 for more room - Plus button: -ml-0.5 to sit closer to the minus button
The textarea was using value={props.content} which made it a controlled
component — every keystroke re-set the value from the signal, destroying
the browser's native undo stack. Now the initial content is set via the
ref callback and the textarea manages its own state natively. Cmd+A,
Cmd+Z, Cmd+Shift+Z, Cmd+C/V all work as expected.
…atch zoom height - Panel menu: moved OUTSIDE Tabs.List as a sibling in the header flex row (both v1 and v2 layouts). This prevents any Kobalte tab-list CSS interaction with the dropdown trigger — the tabs render normally and the + button sits cleanly after them. - Raw editor: stopPropagation on all Cmd/Ctrl keydown events so the app's global command handler doesn't steal Cmd+A, Cmd+Z, Cmd+Shift+Z - Zoom control: h-6 → h-7 (28px) to match SegmentedControlV2 height
- Raw editor: use el.addEventListener('keydown', ...) directly on the
DOM element instead of SolidJS's onKeyDown. SolidJS uses event
delegation (handlers on document root), so stopPropagation from a
delegated handler doesn't prevent other delegated handlers. A native
listener fires in the capture/bubble phase on the element itself,
before delegation, so stopPropagation actually works.
- Panel menu wrapper: pl-3 pr-4 on both layouts for more breathing room
between the last tab and the + button
- Panel menu wrapper: pl-2 pr-1 (was pl-3 pr-4) — the pr-4 was pushing the button AWAY from the right edge; reducing pr moves it closer to the panel's right edge - Raw editor textarea: add selection:bg-blue-500/30 so Cmd+A selection is visually distinguishable (the transparent bg + outline-none may have made native selection invisible)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements #162 — a Preview tab in the side panel for viewing/editing AI-modified
.mdfiles, plus a Panel Menu replacing the old "+" button.Preview Tab
.mdfiles the AI modified in the current session, with A (added) / M (modified) badgesMarkdowncomponent (full GFM: headings, code blocks, tables, LaTeX math, etc.).mdfile has been AI-modified (no empty state)Panel Menu
SESSION_OPEN_FILE_TABinline file browser is removed (the command palette Cmd+O dialog still works)Server
POST /file/writeendpoint for raw editor auto-save (path containment check +writeWithDirs)Files Changed
New:
packages/app/src/components/session/session-preview-tab.tsx— main tab: file list + content view + mode toggle + raw editorpackages/app/src/components/session/panel-menu.tsx— dropdown menu componentModified:
packages/app/src/context/layout-tabs.ts— addSESSION_PREVIEW_TABconstantpackages/app/src/pages/session/helpers.ts— addpreviewOpensignal, filter preview from sortable tabspackages/app/src/pages/session/session-side-panel.tsx— wire Preview tab + PanelMenu in both v1/v2 paths; removeSESSION_OPEN_FILE_TABinline triggerpackages/app/src/components/session/index.ts— export new componentspackages/opencode/src/server/routes/instance/httpapi/groups/file.ts— add write endpoint definitionpackages/opencode/src/server/routes/instance/httpapi/handlers/file.ts— add write handlerTesting
Closes #162