Clear /tmp/jiti on install to avoid stale cached Pi extension. Add a
PI USERS section to the post-install output with the install command.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pulsing dot on the Settings gear button and "new" pill badges on the
Display and Labels tabs signal newly added settings. Dismissed on first
open via cookie-based version gating.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: remove non-critical welcome dialogs
Remove UI Features Setup, Plan Diff Marketing, and What's New v0.11.0
dialogs from the first-run cascade. Only the Permission Mode Setup
dialog remains as it's the only one that affects agent behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: extract demo plan content to separate file
Moves the 334-line PLAN_CONTENT string from App.tsx to demoPlan.ts to reduce clutter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add quick annotation labels for one-click preset feedback
Add preset label chips (Needs tests, Security concern, Break this up, etc.)
that allow instant annotation without typing. Includes ⚡ toolbar button,
Alt+1..8 keyboard shortcuts, label customization in Settings, and label
summary in export output.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add quick label selection mode for one-click annotations
* feat: redesign quick label picker UX and add label tips
- Redesign FloatingQuickLabelPicker as a vertical context-menu style list
with cursor-anchored positioning (appears at mouseup point, not selection center)
- Unify label dropdown: toolbar and quick-label mode now share the same
FloatingQuickLabelPicker component (removed duplicate InlineQuickLabelDropdown)
- Fix above/below flip positioning (follow CommentPopover pattern with
conditional translateY)
- Add label tips: optional instruction text on QuickLabel that gets injected
into agent feedback as a blockquote below the label
- Add tip editor in Settings with three visual states (empty/editing/filled)
- Add "Missing overview" default label with a tip for requesting narrative context
- Extend keyboard shortcuts from Alt+1-8 to Alt+1-9
- Suppress input method toggle (Alt) when label picker is open
- Reorder default labels: Clarify this, Needs tests, Consider edge cases,
Missing overview, Security concern, Break this up, Wrong order, Discuss first,
Nice approach
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: curate default labels, add cyan/amber colors, bare digit shortcuts
Finalize the 10 default quick labels based on user feedback data:
clarify, overview, verify, example, patterns, alternatives, regression,
out-of-scope, tests, nice-approach. Each label gets a unique color
(added cyan and amber to the palette). Bare digit keys (1-0) now apply
labels when the picker is open, Alt+N still works everywhere. Tip editor
cursor starts at beginning for readability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: prevent duplicate annotation when digit key fires both toolbar and picker handlers
When the quick label picker is open from the toolbar's zap button,
let FloatingQuickLabelPicker own all keyboard input instead of both
components handling the same keypress.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
The annotate server was missing the /api/doc endpoint, so clicking
.md links (e.g., [see other](sibling.md)) in annotated files showed
"Failed to connect to server" — the request fell through to the SPA
catch-all and returned HTML instead of JSON.
Three changes:
1. annotate.ts — Add /api/doc route, auto-injecting the source
file's parent directory as ?base= so relative links resolve
from the correct location.
2. reference-handlers.ts — handleDoc() now accepts an optional
?base= query param and tries resolving relative paths against
it before falling back to the project-root glob search.
3. App.tsx — handleOpenLinkedDoc passes the current file's
directory as ?base= when navigating linked-doc → linked-doc,
so chained relative links resolve correctly.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Adds full inline image rendering to the Viewer for both annotate and
plan review modes. Previously,  syntax was treated as plain
text.
## What changed
### Viewer.tsx
- Added `` regex to InlineMarkdown before the link pattern.
Images starting with `!` were silently falling through to plain text
because `!` was not in the special-character scan set — added it.
- Added `imageBaseDir` prop (flows Viewer → BlockRenderer → InlineMarkdown)
so relative image paths in markdown resolve against the annotated file's
directory rather than the server's cwd.
- Added `onImageClick` callback (same prop chain) to open lightbox on click.
- Added `ImageLightbox` component: full-screen dark backdrop, image scaled
to 90vw/85vh with object-contain, alt text caption, closes on Escape or
backdrop click. Portaled to document.body.
- Images render with `cursor-zoom-in` to hint they're clickable.
### App.tsx
- Captures `filePath` from `/api/plan` response (already sent by annotate
server) and derives `imageBaseDir` (directory of the markdown file).
- Passes `imageBaseDir` to `<Viewer>`.
### ImageThumbnail.tsx
- `getImageSrc()` now accepts an optional `base` parameter. For relative
local paths, appends `&base=<dir>` to the `/api/image` query string.
### shared-handlers.ts
- `handleImage` falls back to resolving the path relative to a `base`
query param when the primary path doesn't exist and the path is relative.
Covers the common case of `` in a markdown
file opened from an arbitrary directory.
## Behavior
- Remote URLs (https://...) render directly, no proxy.
- Absolute local paths route through /api/image as before.
- Relative paths resolve against the markdown file's directory via ?base=.
- Click any image → lightbox. Escape or click outside → dismiss.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
Add preset label chips (Needs tests, Security concern, Break this up, etc.)
that allow instant annotation without typing. Includes ⚡ toolbar button,
Alt+1..8 keyboard shortcuts, label customization in Settings, and label
summary in export output.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
* feat: add mobile compatibility
- Add responsive hamburger menu with all header actions (MobileMenu)
- Annotation panel renders as full-screen overlay on mobile with backdrop and close button
- Panel starts closed on mobile (<768px)
- Touch support for resize handles, pinpoint annotations, and toolstrip buttons
- Mobile text selection creates annotations via highlighter.fromRange() bridge
- Card action buttons always visible on touch devices (hover:none media query)
- Settings modal uses horizontal tab bar on mobile
- CommentPopover width capped to viewport on small screens
- Replace mousedown with pointerdown for touch-compatible click-outside handling
- Add useIsMobile reactive hook for breakpoint detection
- Desktop layout (>=768px) unchanged
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: mobile layout polish — panel gap, button overlap, responsive labels
- AnnotationPanel: fix bottom gap on mobile overlay (inset-y-12 → top-12 bottom-0)
- Viewer: push in-plan action buttons below badges on mobile (mt-6), add
clear-right before frontmatter, show short labels (Comment/Copy) on mobile
- App: reduce mobile horizontal padding to 8px (px-2)
- test-hook.sh: build review before hook to fix missing dist error
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add plan width display setting with compact/default/wide options
Adds a configurable plan width preference (compact 832px, default 1040px,
wide 1280px) with an abstract layout preview in Settings. Dynamic max-width
flows through to Viewer, PlanDiffViewer, and the toolstrip. Default is
compact to preserve existing behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
* feat: add Graphviz rendering for plan mode
Support graphviz/dot/gv fenced blocks in the shared viewer and add a Graphviz sample diagram to PLAN_CONTENT for quick validation.
* fix: use imperative wheel listener with passive:false in GraphvizBlock
* fix(annotate): support Windows markdown paths in CLI annotate flow
* fix tmp path
* fix resolve-file.ts
* Update command to use EXE_PATH variable
* Update command path for plugin hooks in install.ps1
* test: add core test suite for path resolution, storage, remote detection, and install scripts
- resolve-file: absolute paths, relative paths, case-insensitive search, ignored dirs, extension filtering, ambiguity, Windows separators
- storage: slug generation, tilde expansion, version history, deduplication
- remote: env var detection (PLANNOTATOR_REMOTE, SSH_TTY), port config and validation
- image: tmpdir usage, extension validation
- install scripts: JSON structure validation, checksum verification, arch detection, full exe path in hooks
79 tests, 35ms
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: add test workflow and gate release builds on tests
- New test.yml: runs `bun test` on PRs and pushes to main
- release.yml: tests must pass before build job runs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve pre-existing test failures for CI compatibility
- project.test.ts: make repo name assertion portable (works in CI where
checkout dir differs from local dev)
- vscode mock: add missing APIs needed by editor-annotations.ts
(comments, languages, Range, CodeActionKind, decorations)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: remove Viewer.test.tsx and happy-dom dependency
Tests didn't exercise any application code — they manually constructed
DOM elements inline and verified DOM API behavior, not Viewer.tsx logic.
The mock highlighter didn't simulate real hljs, and one test could never
fail due to its try/catch structure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use targeted markdown glob and restore clean lockfile
- Replace **/* glob with **/*.[mM][dD]{,[xX]} to only scan markdown
files during case-insensitive search (avoids iterating every file)
- Restore package.json key ordering from main, only removing happy-dom
- Regenerate bun.lock from main's base to eliminate version drift
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: drop redundant test and fix weak assertion
- Remove redundant PLANNOTATOR_REMOTE=TRUE test (already covered by true)
- Fix UPLOAD_DIR assertion that would pass even with hardcoded /tmp
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>
Windows users were always shown the bash install command. Now uses
navigator.platform to show the PowerShell command on Windows.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add Pi origin support to code review UI and update banner
Pi users were seeing "OpenCode" badge and getting the wrong update
command. Adds 'pi' to origin types in review/annotate servers, shows
violet "Pi" badge in code review header, and copies the correct
`pi install` command in the update banner.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: update Pi extension from install scripts when pi is available
The install scripts now check if `pi` is on PATH and run
`pi install npm:@plannotator/pi-extension` to update the extension.
Silent for users without Pi installed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add Codex support documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(codex): note plan mode not yet supported
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(codex): move plan mode notice to top
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: note plan mode not yet supported in Codex sections
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: remove redundant Codex description line
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(codex): lead with supported features
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs(codex): use ! prefix for shell commands
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add per-file git add from code review UI (#254)
Adds the ability to `git add` individual files directly from the code
review interface. Users can stage approved files then switch to the new
"Unstaged changes" diff view to see only remaining work.
- POST /api/git-add endpoint with worktree support
- gitAddFile/gitResetFile utilities in packages/server/git.ts
- "Staged changes" and "Unstaged changes" added to diff type dropdown
- useGitAdd hook encapsulating all staging state and API logic
- "Git Add" / "Added" toggle button in FileHeader (next to Viewed)
- Visual indicator (left border) for staged files in sidebar FileTree
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* style: use green background tint for staged files instead of left border
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: support staged/unstaged diffs in worktrees and display stage errors
Adds "staged" and "unstaged" to worktree sub-type allowlists (server parser,
client parser, and runGitDiff switch), displays stageError in FileHeader,
and documents /api/git-add in CLAUDE.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: clean up git add feature — fix stale closure, timeout leak, dedup validation
- useGitAdd: use ref for stagedFiles to stabilize stageFile callback,
clear error timeout on reset, drop unnecessary useMemo
- App.tsx: wrap onFileViewed in useCallback
- FileTree: merge duplicate staged count conditionals
- review.ts: reuse exported validateFilePath for /api/file-content
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Persist the IPC port in workspaceState and try to rebind to it on
activation so restored terminals still have a valid PLANNOTATOR_VSCODE_PORT.
Falls back to a random port if the preferred one is taken.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add capture-phase paste listener to AttachmentsButton so users can
Cmd+V / Ctrl+V images directly into per-annotation attachments when
the popover is open. Uses stopPropagation to prevent the global paste
handler in App.tsx from also processing the event. Adds a platform-aware
keyboard shortcut hint in the drop zone.
Also fixes a regression from #237 where resolveMarkdownFile rejected
absolute paths outside the project root. The original behavior allowed
any absolute path the user explicitly provided; the security boundary
should only apply to relative path / bare filename search.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: bidirectional scroll navigation between annotations and highlights
When reviewing long plans with many annotations, it's hard to visually
connect which annotation card corresponds to which highlighted line.
- Click annotation card → scrolls content to the highlight + applies
a bright cyan "focused" color for visibility
- Click highlighted text → scrolls the right panel to bring the
corresponding annotation card into view
- Works with both web-highlighter and manually created (shared/imported)
annotations
- Handles edge cases: global comments (no highlight), multi-node
selections, already-visible elements
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: scroll to center and skip focus on annotation creation
- Change scrollIntoView block from 'nearest' to 'center' so targets
appear prominently in the middle of the viewport
- Track just-created annotation IDs to skip scroll+focus effect when
a new annotation is added (user is already looking at it)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: split worktree/diff-type into separate controls
The single dropdown that mixed diff type selection (uncommitted, last-commit,
branch) with worktree switching was confusing — entering a worktree silently
replaced all dropdown options with no persistent indicator of which worktree
was active.
Split into two controls:
- Context dropdown: switches between main repo and worktrees (only shown
when worktrees exist, highlighted when a worktree is active)
- View dropdown: always shows the same diff type options regardless of context
Server changes:
- GitContext now exposes worktrees as a separate field
- Removed getWorktreeDiffOptions() and back-to-main sentinel handling
- Simplified /api/diff/switch (no more diffOptions replacement)
Client changes:
- New activeWorktreePath state + handleWorktreeSwitch callback
- handleDiffSwitch composes worktree prefix from context automatically
- Derived activeDiffBase strips worktree prefix for dropdown display
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: derive activeWorktreePath from diffType instead of separate state
Eliminates redundant state — activeWorktreePath and activeDiffBase are
now both derived in a single useMemo from the composite diffType string.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: move DiffOption, WorktreeInfo, GitContext to @plannotator/shared
Eliminates duplicate interface definitions across server and client.
Types now live in packages/shared/types.ts and are imported by both
packages/server/git.ts and packages/review-editor/.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: expandable diff context in code review (closes#243)
Switch from PatchDiff to FileDiff component from @pierre/diffs to enable
GitHub-style "show more lines" buttons between hunks. The library handles
all expansion UI when provided full file contents via oldLines/newLines.
- Add /api/file-content endpoint to serve old/new file content per diff type
- Add getFileContentsForDiff() helper with ref mapping for all diff types
- Parse patch client-side via getSingularPatch(), augment with file contents
- Update @pierre/diffs from 1.0.4 to 1.0.11 (scroll sync fix, no breaking changes)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: upgrade @pierre/diffs to beta, re-parse patch with full file contents
The previous approach spread file content arrays onto a partial-mode
FileDiffMetadata, causing hunk index mismatches and Shiki decoration
errors. Now uses processFile() to re-parse the patch with oldFile/newFile
so hunk indices are computed against the full file (isPartial: false),
which is required for expansion to work correctly.
Also fixes a flash error on file switch by tagging fileContents with the
filePath they were fetched for, preventing stale contents from being
paired with the wrong patch during the render before useEffect fires.
- Upgrade @pierre/diffs from ^1.0.x to ^1.1.0-beta.19 in all workspaces
- Add test fixtures for disjoint hunks, deleted/renamed/new files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: abort stale file-content fetches, validate path params
- Add AbortController to file-content fetch effect to cancel in-flight
requests when switching files
- Reject path traversal (.. or absolute paths) on /api/file-content
- Document /api/file-content endpoint in CLAUDE.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Group changed files by directory with collapsible folders, aggregate
+/- stats per folder, and auto-expand on keyboard navigation.
Single-child directories collapse (e.g., src/utils/ as one node).
Falls back to flat list for single-directory diffs.
Closes#161
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* chore: pin GitHub Actions to SHAs and add Renovate config
Pin all actions in deploy.yml to full commit SHAs to prevent tag
force-push supply chain attacks. Upgrade actions/checkout from v4
to v6.0.2 to match release.yml.
Add renovate.json scoped to GitHub Actions only — weekly grouped
PRs with automerge, zero noise from npm dependencies.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: move renovate.json to .github/renovate.json
Reduces root directory clutter. Renovate supports this path natively.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add shared EditorAnnotation type
Single source of truth for the editor annotation interface,
imported by both @plannotator/server and @plannotator/ui.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: editor annotation server endpoints
In-memory store with POST/GET/DELETE endpoints for editor annotations.
The array lives in the handler closure and dies with the server session.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: editor annotation UI — polling hook, card, panel, and export
- useEditorAnnotations hook with auto-disable polling (500ms interval)
- EditorAnnotationCard with file path, code preview, and comment
- AnnotationPanel conditionally renders editor annotation section
- exportEditorAnnotations formats annotations for Claude feedback
- App.tsx wires hook, includes in output memo and send feedback gate
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: VS Code extension editor annotation command
Cmd+Shift+. or right-click to capture selected text as an annotation.
POSTs through the cookie proxy to the plannotator server. Adds amber
left-border decorations on annotated lines, cleared on panel close.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: inline comment threads, lightbulb menu, and stronger decorations
Replace showInputBox with VS Code CommentController for inline annotation
threads anchored to selected code. Add CodeActionProvider for lightbulb
discoverability. Stronger decoration styling with gutter icon.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: VS Code theme integration for webview
Bridge VS Code CSS variables to Plannotator's CSS variable system via
postMessage between wrapper page and proxied iframe. Automatically adopts
the active VS Code color theme (dark/light/custom) without touching any
UI components or CSS files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: gate editor annotation polling behind VS Code detection
Only poll /api/editor-annotations when running inside a VS Code webview
(window.__PLANNOTATOR_VSCODE). Browser and shared URL users now have
zero network cost from the editor annotations feature. Also fixes poll
interval from 500ms to 2000ms.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: faster polling, prevent unnecessary re-renders, unify HTTP helper
- Poll interval 2s → 500ms for snappier annotation pickup
- Shallow equality check on annotation IDs prevents React re-renders
when poll data is unchanged
- Merge postToProxy/deleteFromProxy into single requestProxy function
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: editor annotations support in code review
Wire existing editor annotation infrastructure into the review flow
so VS Code users can annotate files outside the diff during code review.
- Add editor annotation endpoints to review server (same 3-line pattern)
- Call useEditorAnnotations hook in review App.tsx
- Display editor annotations in ReviewPanel with "Editor" divider
- Include editor annotations in feedback export and gating logic
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
feat: support git worktrees in code review (#196)
Adds full worktree support to the code review flow:
- Detects worktrees via `git worktree list --porcelain` and surfaces them in the diff dropdown
- Selecting a worktree enters "worktree mode" with scoped diff options (uncommitted, last commit, vs main)
- "Back to main repo" restores the original dropdown
- Handles initial commits gracefully (no HEAD~1 crash)
Closes#196
* feat: add --browser CLI flag and session discovery (#135)
- Add `--browser <name>` global flag to override which browser opens
(sets PLANNOTATOR_BROWSER, works with existing openBrowser() logic)
- Add `plannotator sessions` subcommand to list active server sessions
with `--open [N]` to reopen in browser and `--clean` to prune stale entries
- Track active sessions in ~/.plannotator/sessions/<pid>.json with
automatic stale cleanup via PID liveness checks
- Register/unregister sessions in all three server modes (plan, review, annotate)
- Export ./sessions and ./project from @plannotator/server package
Closes#135
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add integration test for session discovery
Non-interactive test script that validates:
- Session file creation/cleanup lifecycle
- Session file content (all fields)
- `plannotator sessions` listing, --open, --clean
- Stale PID auto-cleanup
Also syncs bun.lock versions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add --browser flag and session discovery to configuration page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add troubleshooting page
Covers lost tabs (sessions --open), data storage locations,
browser issues, and hook not firing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: harden title sanitization for Obsidian filenames
Strip parentheses, brackets, curly braces, hash, tilde, and backtick
from plan titles when generating Obsidian filenames. Re-trim after
stripping to handle edge cases where stripped chars leave leading/trailing
whitespace. Closes part of #235.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: filename separator + auto-save on arrival for Obsidian
Filename separator: adds a dropdown in Obsidian settings to replace
spaces with dashes or underscores in generated filenames. Fixes issues
with CLI tools fumbling space-escaped paths inside Obsidian vaults.
The setting flows through the save request to generateFilename() as a
post-processing step. Live preview reflects the choice.
Auto-save on arrival: adds an opt-in toggle that automatically saves
plans to Obsidian the moment they load in the browser, before the user
approves or denies. This fixes the workflow where approving from the
Claude Code CLI kills the hook server before the user can Cmd+S.
Guarded by a ref to fire only once per session.
Closes#235, closes#228.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: pass filenameSeparator in ExportModal Obsidian save path
The ExportModal's save-to-Obsidian callsite was missing the
filenameSeparator config, causing the separator preference to be
silently ignored when saving via the Export Modal.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The pinpoint click effect read `highlighterRef.current` at effect setup
time and bailed early if it was null. After exiting plan diff, the
Viewer fully remounts — usePinpoint's click effect ran before the
highlighter init effect, so `highlighterRef.current` was still null.
The effect returned early without installing the click handler, and
nothing triggered it to re-run since the dependencies (stable refs,
unchanged `isActive`) didn't change.
Move the highlighter read into the click handler body so it's resolved
lazily at click time, when the highlighter is guaranteed to be ready.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add case-insensitive file resolution so users can open markdown files
without typing exact paths. Applies to both the CLI (`plannotator
annotate`) and the `/api/doc` endpoint for linked documents.
Resolution strategies (tried in order):
1. Exact path — absolute or relative to cwd (existing behavior)
2. Case-insensitive relative path — `docs/setup.md` matches `docs/SETUP.md`
3. Bare filename search — `setup.md` searches the entire project tree
Ambiguity handling:
- 1 match → opens the file, prints "Resolved: /full/path"
- 0 matches → "File not found: <input>"
- 2+ matches → "Ambiguous filename: found N matches" with full paths
Skips node_modules, .git, dist, build, .next, __pycache__, .obsidian,
.trash during search. Restricts results to .md/.mdx/.markdown files
and enforces project root boundary (no path traversal).
Extracted shared resolveMarkdownFile() into packages/server/resolve-file.ts
and refactored /api/doc handler to use it, removing ~40 lines of inline
resolution logic.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
* fix(annotate): resolve file paths correctly on Windows
- Use PLANNOTATOR_CWD env var to get original working directory
- Strip @ prefix from file paths (Claude Code reference syntax)
- Add debug logging (PLANNOTATOR_DEBUG=1)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: add missing path import for annotate subcommand
The dynamic `import("path")` was removed but `path.isAbsolute()` and
`path.join()` are still used, causing a ReferenceError at runtime.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Julien Fernandez <theflysurfer@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Michael Ramos <mdramos8@gmail.com>
Bold-wrapped links like **[text](url)** were rendered as plain bold text
because the bold regex matched first and treated inner content as a string.
Fix: recurse into InlineMarkdown for bold/italic inner content so nested
markdown (links, code, etc.) is properly parsed.
Affected:
- Viewer.tsx (main plan view)
- PlanCleanDiffView.tsx (diff view duplicate)
* fix: unreadable text in markdown code blocks (light mode)
highlight.js's markdown grammar tokenizes underscores, asterisks, and
backticks as emphasis/strong/code spans. The github-dark theme assigns
these tokens colors (#c9d1d9, #8b949e) that are nearly invisible against
light backgrounds, making large sections of markdown code blocks appear
"washed out."
The existing light-mode overrides in index.css cover keywords, strings,
comments, and numbers — but not .hljs-emphasis, .hljs-strong, or
.hljs-code. This patch forces those three token types to inherit the
base code color, which is already correctly overridden for light mode.
Tokens fixed:
- .hljs-emphasis: underscores in variable names (e.g. data_quality_good)
were parsed as italic emphasis — now inherits color, removes italic
- .hljs-strong: **bold** markers used #c9d1d9 — now inherits color
- .hljs-code: `backtick` inline code markers used #8b949e — now inherits
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add markdown file that reproduces light-mode highlighting bug
Contains a ```markdown code block with underscores in variable names,
**bold** markers, and `backtick` code — all patterns that trigger the
washed-out text rendering this PR fixes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Adds tabs to the help dialog: "Selection Modes" (new video) and
"How Plannotator Works" (existing video). Defaults to selection modes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add pinpoint mode for point-and-click annotations
Adds a new input method toggle (Drag / Pinpoint) orthogonal to the
existing annotation action modes (Selection / Comment / Redline).
In pinpoint mode, hovering over blocks and inline elements shows a
dashed outline with a label tooltip. Clicking auto-selects the
element's text via highlighter.fromRange(), firing the same CREATE
event as drag-selection — all downstream annotation flow is unchanged.
New files:
- blockTargeting.ts: resolves hover targets (inlines, cells, list items, blocks)
- usePinpoint.ts: hover/click state machine with text-range bridge
- PinpointOverlay.tsx: dashed outline + label badge
- InputMethodToggle.tsx: Drag/Pinpoint segmented toggle
- inputMethod.ts: cookie-based persistence
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: unified AnnotationToolstrip with smooth animations
Merge InputMethodToggle + ModeSwitcher into a single compact toolbar
with two pill groups: input method (Select/Pinpoint) and action mode
(Markup/Comment/Redline). Buttons expand to show labels on hover/active
with explicit pixel-width transitions. Suppress transitions on mount
to prevent initial flash.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: keyboard shortcuts tab and settings reorganization
Add Shortcuts tab to settings with OS-aware key cap styling and
organized shortcut groups. Move Obsidian and Bear integrations
to dedicated tabs under an Integrations sidebar group, leaving
compact navigation links on the Saving tab.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Alt/Option key to switch between Select and Pinpoint
Hold Alt to temporarily switch input method (reverts on release).
Double-tap Alt to permanently toggle. Added to keyboard shortcuts
panel in settings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove md:py-8 to use consistent py-3 padding
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: toolbar polish — icon swap, full opacity active states, styling
Move text-bracket icon to Select, pencil to Markup. Active buttons
use full opacity backgrounds with contrasting foreground. Help link
lowercase with updated styling. Revert md:py-8 padding. Add gap
between buttons in pill groups.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: match toolbar button sizing to main branch
Use text-xs (12px) and px-2.5 padding to match main branch button
rendering. Active state uses bg-background with no color tint.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: smarter pinpoint hover — edge-zone tables, row targeting, list groups
Table edge-zone detection: top/bottom edges target whole table, left/right
edges target the specific row, interior targets individual cells. List group
wrappers create hover zones in gaps between consecutive list items. Demo
plan now includes sample tables for testing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: remove unused InputMethodToggle component
Superseded by AnnotationToolstrip which unified input method + mode switching.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: remove unimplemented S3 paste store stub
Never used — paste service runs on Cloudflare KV and local FS.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: remove dead ModeSwitcher component
Replaced by AnnotationToolstrip which unified mode switching + input method toggle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: validate plugin hooks.json on install to ensure correct timeout
The Claude Code plugin marketplace can leave stale hooks.json with
incorrect timeout values (e.g. 1800 instead of 345600). Install scripts
now check for and overwrite the plugin's hooks.json if it exists.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: warn about duplicate hook when plugin and settings.json both configure plannotator
Detects "command".*plannotator in settings.json (only matches hook configs, not
enabledPlugins) and only warns when the plugin is also installed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: define CLAUDE_SETTINGS variable in install.cmd
The duplicate hook warning referenced !CLAUDE_SETTINGS! but the variable
was never set, causing the warning to silently never fire for CMD users.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: integrate VS Code extension from community (7tg/plannotator-vscode)
Opens Plannotator plan reviews, code reviews, and annotations inside VS Code
tabs instead of an external browser. Intercepts PLANNOTATOR_BROWSER via env
var injection in integrated terminals, with cookie persistence and auto-close.
Original repository: https://github.com/7tg/plannotator-vscodeCloses#91
Co-Authored-By: Barbaros Gören <tayyipgoren@gmail.com>
* chore: adapt VS Code extension for monorepo
- Update package.json: publisher → backnotprop, repo → plannotator, add private
- Replace node -e URL encoding with curl --data-urlencode in router script
- Simplify panel-manager tests to keep only behavioral tests
- Add dev:vscode, build:vscode, package:vscode scripts to root
- Add *.vsix to .gitignore
- Update bun.lock with new workspace member
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address code review and duplicate panel issue
- Fix macOS browser: detect script paths in PLANNOTATOR_BROWSER and
invoke directly instead of via 'open -a' (which only accepts app
names/.app bundles)
- Update CLAUDE.md: add vscode-extension to project structure,
development, and build sections
- Fix command title: 'Simple Browser' -> 'Open URL in Editor'
- Add 1s debounce to panel opens: Claude Code fires ExitPlanMode hook
twice, spawning two plannotator processes on different ports that
both hit the IPC server simultaneously
- Add .vscode/launch.json + tasks.json for F5 debugging
---------
Co-authored-by: Barbaros Gören <tayyipgoren@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>