* feat: add Plan Diff marketing dialog, Pi origin support, and docs
- Add PlanDiffMarketing first-run dialog announcing the Plan Diff feature
with per-origin video demo URLs (Claude Code, OpenCode, Pi)
- Add 'pi' as a first-class origin with display name and violet badge
- Add Plan Diff blog post (plan-diff-see-what-changed.md)
- Add brief Plan Diff mentions across READMEs and marketing docs
* polish: tighten blog post copy and reduce em dash usage
* fix: move PlanDiffMarketing to plan-diff/ and fix stale useEffect deps
* add plan diff preview screenshot for marketing dialog
* feat: add plan diff UI with sidebar, badge, and dual view modes
Shows what changed between plan iterations when Claude revises after
feedback. Adds a +N/-M badge below repo info that toggles the diff view,
a shared left sidebar with TOC and Version Browser tabs, and two diff
modes: rendered (color-coded borders) and raw markdown (+/- lines).
Closes#138, closes#111
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: update CLAUDE.md project structure and align first-run dialog labels
- Add plan-diff/ and sidebar/ component subdirectories to CLAUDE.md
- Add new hooks and utils to CLAUDE.md project structure
- Rename "Table of Contents" to "Auto-open Sidebar" in UIFeaturesSetup
to match Settings.tsx label
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review findings for plan diff UX
- Fix badge stats mixing block counts with line counts (modifications
now fold into additions/deletions)
- Gate hasPreviousVersion on diffBasePlan being loaded to prevent
"Show Changes" no-op and ModeSwitcher disappearing
- Make sidebar reactive to Settings toggle (useEffect on tocEnabled)
- Match PlanDiffViewer badge layout to Viewer (flex-col) so badge
doesn't jump position on toggle
- Add "Exit Diff" label to the close button in diff view
- Remove dead CSS (plan-diff-removed-marker, plan-diff-modified)
- Clean up stale header comment and unused lines prop
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: second-round review cleanup for plan diff UX
- Fix stale "amber border" JSDoc in PlanCleanDiffView (actually green)
- Rename sidebar tab from "diff" to "versions" for clarity
- Gate VersionBrowser fetch on versionInfo being available
- Move .sidebar-tab-flag CSS into its own Sidebar section
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add loading state for version selection in sidebar
Add isSelectingVersion to selectBaseVersion, mirroring the existing
isLoadingVersions pattern. Shows "Loading..." on the selected version
button while the fetch is in progress.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address third-round code review findings
- Fix duplicate border/backdrop on TOC inside sidebar (className override)
- Fix loading indicator targeting wrong version button (fetchingVersion state)
- Fix "Show Changes" button silent no-op (gate on hasPreviousVersion)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move date to slug suffix, improve Other Plans UX
- Slug format changed from YYYY-MM-DD-{heading} to {heading}-YYYY-MM-DD
- Other Plans: single "coming soon" banner instead of per-item labels
- Strip date suffix from plan names in sidebar for readability
- Remove cursor-not-allowed from Other Plans items
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add Plan Diff section to CLAUDE.md, alert on version fetch failure
- Document plan diff feature: engine, view modes, state management, sidebar
- Update slug format documentation to {heading}-YYYY-MM-DD
- Show native alert when version fetch fails instead of silent swallow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add table rendering to clean diff view
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add plan version history (backend + API)
Save plan versions to ~/.plannotator/history/{project}/{slug}/ on every
plan arrival. Serves previousPlan and versionInfo via /api/plan, and adds
GET /api/plan/version?v=N for fetching any historical version. Enables
future diff UI. Deduplicates identical resubmissions.
Closes#138, closes#111
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add version listing and project history APIs
Add GET /api/plan/versions to list all versions of the current plan with
timestamps, and GET /api/plan/history to list all plans in the current
project. Both endpoints available in shared server and pi extension.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add version history section and API endpoints to CLAUDE.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: rename misleading "diff" terminology to "annotations" in plan review context
The plan review flow used "diff" naming (exportDiff, diffOutput, .diff.md,
"Raw Diff", "Download .diff") for what is actually user annotations/feedback
on a plan. This was confusing since the code review flow legitimately uses
"diff" for actual git diffs. Renames all plan-review-context "diff" references
to "annotations" across code, UI labels, file extensions, and docs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update stale exportDiff references in ANNOTATE.md
Missed during the diff→annotations rename. Updates two references
to exportDiff() → exportAnnotations() in the annotate flow docs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: rename remaining "Download Diff" label and tab type in App.tsx
The quick-save dropdown button still showed "Download Diff" and the
initialExportTab state type still used 'diff' instead of 'annotations'.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* 🐛 fix(opencode): add missing @plannotator/server dependency
The opencode build failed because @plannotator/server was not declared
in apps/opencode-plugin/package.json, causing bun bundler to fail
resolving workspace packages.
* ✨ feat(ui): add auto-close delay with countdown on completion overlay
- Extend auto-close setting from boolean to delay options (off/immediate/3s/5s)
- Extract shared CompletionOverlay component from both editor and review apps
- Show countdown timer on completion screen when delay is set
- Add checkbox on completion overlay to enable auto-close (defaults to 3s)
- Replace toggle with select dropdown in Settings
* ♻️ refactor(ui): extract useAutoClose hook and clean up completion overlay
- Extract countdown logic into useAutoClose hook with phase state machine
- Extract SVG icons into named components (CheckIcon, ChatBubbleIcon)
- Add AUTO_CLOSE_OPTIONS constant for dropdown single source of truth
- Remove dead getAutoClose/setAutoClose boolean wrappers
* 🐛 fix(ui): handle window.close() failure with fallback message
Browsers block window.close() on tabs not opened by script. Add tryClose()
helper that detects failure via window.closed check, and show a fallback
message guiding the user to close manually.
The /api/image endpoint serves files from the local filesystem based on
a user-provided path parameter with no validation. This allows any
process on localhost to read arbitrary files (e.g., /etc/passwd,
~/.ssh/id_rsa) by requesting /api/image?path=<any-path>.
This commit adds:
- Path validation requiring image file extensions (png, jpg, gif, etc.)
- Path resolution via path.resolve() to normalize traversal sequences
- Upload extension validation against the same image allowlist
- Shared image.ts module used by all three servers (plan, review, annotate)
[agent]
* fix(browser): use open -a only for PLANNOTATOR_BROWSER on macOS
BROWSER env var follows Unix convention (executable + URL as arg),
so BROWSER=open correctly resolves to `open <url>` instead of
the broken `open -a open <url>`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(browser): use cmd.exe only for PLANNOTATOR_BROWSER on WSL
Same fix as the macOS open -a change: when only BROWSER is set (e.g.
VS Code devcontainer helper script), execute it directly instead of
routing through cmd.exe /c start, which can't run Linux paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
* feat: use BROWSER env var as fallback for opening UI
Closes#119
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add missing reference docs (were gitignored by reference/ pattern)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: named image references and annotate command (#67, #109)
Add human-readable names to image attachments throughout the annotation
pipeline, and add a new `plannotator annotate <file.md>` command for
annotating arbitrary markdown files.
Image names: ImageAttachment type replaces plain string paths, upload
endpoints return originalName, editable name inputs under thumbnails,
[name] path format in exported feedback, backward-compatible sharing.
Annotate command: new server module reusing plan editor HTML with
mode:"annotate", CLI subcommand, slash commands for Claude Code and
OpenCode, annotate mode UI (hides Approve, shows Send Annotations).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move image name input to ImageAnnotator screen
The name input now appears on the full-screen annotator modal that opens
immediately when uploading/pasting an image, pre-populated from the
filename. Removes the disruptive inline name editing from thumbnails.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: stale closure in paste handler, update CLAUDE.md for new features
Fix race condition where globalAttachments was captured as empty array
in the paste event listener (missing dependency). Also update CLAUDE.md
to document ImageAttachment type, annotate server/flow, updated sharing
format with image support, and new slash commands.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add PLANNOTATOR_SHARE_URL env var so users can point share links at
their own self-hosted portal instance instead of share.plannotator.ai.
Threads the base URL through the same path as sharingEnabled: env var →
server options → API response → editor state → useSharing hook →
generateShareUrl(). Includes self-hosting guide and documentation.
Closes#12
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add drag-to-resize for the annotation panel, table of contents,
and file tree sidebar. Panel widths persist in cookie storage
across sessions and random-port hook invocations.
- Add useResizablePanel hook with left/right side support
- Extract ResizeHandle component with spread-friendly handleProps API
- Apply to AnnotationPanel, ReviewPanel, TableOfContents, FileTree
- Double-click handle to reset to default width
- Add min-w-0 on content areas for proper flex shrinking
* feat: add import teammate review from share link
Adds the ability to import annotations from a teammate's plannotator
share URL into the current plan review session.
- New ImportModal component with URL input, validation, and feedback
- importFromShareUrl() in useSharing hook: decodes share link,
deserializes annotations, deduplicates, and merges into session
- Import button in header toolbar (visible when sharing is enabled)
Workflow: teammate shares their annotated plan via share link,
user clicks Import, pastes the URL, annotations are merged in.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: cleanup auto-close timer and deduplicate global attachments on import
- Clear auto-close setTimeout when user manually closes ImportModal
(prevents orphaned callback from firing after unmount)
- Deduplicate global attachment paths on import to prevent duplicates
when importing the same share link twice
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move import button into export dropdown menu
Move "Import Review" from a standalone header button into the export
dropdown, separated by a divider. Reduces header clutter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: razb <razb@voyager-labs.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
One-time dialog introduces Table of Contents and Sticky Actions to
new and existing users. Sequences after Permission Mode Setup dialog
if both need to show, uses cookie-based detection to avoid repeat.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Decouple Obsidian/Bear saves from the approve/deny flow so users can
save plans to their notes app independently, then manually approve in
Claude Code (e.g. shift+tab for context clearing).
- Add POST /api/save-notes server endpoint
- Add "Notes" tab to Export dialog with per-integration save buttons
- Split Export button into button+dropdown with quick actions:
Copy Share Link, Download Diff, Save to Obsidian/Bear
- Add Cmd/Ctrl+S keyboard shortcut with configurable default action
- Add "Default Save Action" setting in Settings > Saving
- New utility: defaultNotesApp.ts for cookie-based preference
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Add Table of Contents sidebar with sticky action buttons
- Add hierarchical Table of Contents component with clickable navigation
- Implement useActiveSection hook for real-time section highlighting
- Add annotationHelpers utility for block identification
- Make Images, Global comment, and Copy plan buttons sticky during scroll
- Fix navigation scrolling to work within scrollable main container
* fix: Correct typo in opencode.json (CALUDE -> CLAUDE)
* docs: Update testing documentation - separate UI tests from integration/utility tests
* fix(ui): prevent code block annotation from breaking syntax highlighting
- Replace surroundContents() with plain text wrapper approach
- Add syntax highlighting restoration in removeHighlight()
- Fixes issue reported by kkharji in PR #122
The old approach used range.surroundContents() which wrapped syntax-highlighted
<span> elements, creating nested structure that broke the layout. The new approach
replaces code block innerHTML with plain text wrapped in <mark>, then restores
syntax highlighting when the annotation is removed.
Test coverage: 15 tests pass with edge cases for empty blocks, special chars,
large blocks (10k), unicode, and multiple annotation cycles.
* test(ui): add comprehensive code block annotation regression tests
- Add 15 test cases covering code block annotation behavior
- Test plain text wrapper approach vs nested span approach
- Verify syntax highlighting restoration on annotation removal
- Cover edge cases: empty blocks, special chars, large blocks, unicode
- Add happy-dom dev dependency for DOM testing
* feat: update opencode.json to include additional documentation instructions and configure Playwright MCP
* chore: stop tracking docs directory and opencode.json
* chore: move UI-TESTING.md to tests directory
* docs: extract UI testing checklist into separate file
- Move comprehensive feature checklists to UI-TESTING-CHECKLIST.md
- Keep main UI-TESTING.md focused on development workflow and setup
- Add reference link to checklist file for easy navigation
* docs: update UI testing documentation
* fix(ui): fix TOC active section tracking
IntersectionObserver was using root: null (viewport) instead of the
actual scroll container (<main>), and the effect only ran on mount
before headings were rendered. Pass the container as root and re-run
the observer when heading count changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* revert: remove Playwright MCP from marketplace.json and stale README links
Playwright MCP is dev tooling, not for end users. README referenced
docs/UI-TESTING.md and docs/CODE-STYLE.md which don't exist at those paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* style(ui): compact TOC styling and fix layout shifts
- Smaller text (text-xs) and tighter padding for compact feel
- Remove border-l-2 and font-medium from active state to prevent
layout shifts that pushed text into multiline
- Annotation count badges are now perfect circles (w-5 h-5)
- Add left padding to root-level heading
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(ui): add optional TOC and sticky actions settings
- New uiPreferences.ts utility for cookie-based persistence
- TOC toggle: conditionally renders sidebar in App.tsx
- Sticky Actions toggle: conditionally applies sticky positioning in Viewer.tsx
- Both default to enabled, persisted via cookies
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(ui): redesign Settings dialog with sidebar navigation
Replace flat settings list with sidebar + content panel layout:
- General tab: Identity, Permission Mode, Agent Switching
- Display tab: TOC, Sticky Actions, Tater Mode
- Saving tab: Plan Saving, Obsidian, Bear Notes
- Scrollable content area (max-h-[70vh])
- Sidebar hidden in review mode (single tab)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ui): fix sticky action bar positioning and add scroll-aware card background
Match main's button positioning across all breakpoints with responsive
margins that account for article padding. Use IntersectionObserver sentinel
to detect when the bar is stuck, revealing the card background only on scroll.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: display repo and branch info in plan document
Add subtle badge display in upper-left corner of plan document showing:
- Repository name (org/repo from git remote, with fallbacks)
- Current branch with git branch icon
New modular repo.ts handles detection with fallback chain:
1. Parse org/repo from git remote origin
2. Fall back to git repo root directory name
3. Fall back to current working directory
Closes#104
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add --no-git flag to sandbox script
Allows testing repo info fallback behavior when not in a git repository.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Detect mermaid code blocks and render as interactive diagrams
- Dark theme matching Plannotator UI
- Toggle between source code and rendered diagram view
- Use getBBox() to recalculate viewBox for proper centering
- Error state shows source code fallback
- Add mermaid diagram to demo plan
Closes#115
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add new "Comment" editor mode that shows input directly on selection
- Add type-to-comment in Selection mode: typing while toolbar is open
auto-transitions to comment input with the typed character
- Add initialStep/initialType props to AnnotationToolbar for direct input
- Fix autofocus timing for portal-rendered toolbar
Closes#93
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix: use @plannotator/web-highlighter for proper newline handling
- Switch from web-highlighter to @plannotator/web-highlighter@0.8.1
- Use source.text from highlighter (preserves newlines in cross-block selections)
- Update dependency to ^0.8.1 which fixes exports field for bundlers
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add Cmd+C keyboard shortcut for copying selected text
Closes#60
When text is selected and the annotation toolbar is visible, Cmd+C (Mac)
or Ctrl+C (Windows/Linux) will copy the selected text with proper newlines
preserved between block elements.
This works because @plannotator/web-highlighter@0.8.1 now captures text
using Selection.toString() which preserves newlines for cross-block
selections.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: don't intercept Cmd+C in input/textarea fields
Prevents the custom copy handler from intercepting Cmd+C when the user
is focused in an input or textarea (e.g., annotation comment input).
Previously, copying text from an input would copy the original plan
selection instead.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- No annotations → Approve (proceed with implementation)
- With annotations → Send Feedback (request revision)
Works in both plan review and code review UIs. Respects input focus
to avoid conflict with existing Cmd+Enter to save annotation edits.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The `files` array in package.json was missing plannotator.html and
review-editor.html, causing them to be omitted from npm publishes.
This explains why users were stuck on old versions.
Also fixes incorrect cache path in docs and install script.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>