254 Commits

Author SHA1 Message Date
Michael Ramos a6361356b9 chore: bump version to 0.11.2
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.11.2
2026-03-07 12:38:06 -08:00
Michael Ramos dcb979c08f test: add light mode code block fixture to test-hook.sh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 12:36:34 -08:00
Michael Ramos 819948c724 docs: add editor annotations and shared package to CLAUDE.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 12:26:59 -08:00
Michael Ramos cb4de46636 feat: VS Code editor annotations + theme integration (#239)
* 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>
2026-03-07 12:25:02 -08:00
Michael Ramos 411e1b932d feat: support git worktrees in code review (#241)
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
2026-03-07 10:06:55 -08:00
Michael Ramos c2037b78bf feat: --browser CLI flag & session discovery (#242)
* 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>
2026-03-07 09:45:33 -08:00
Michael Ramos 75968541c2 feat: Obsidian saving enhancements — separator, auto-save, sanitization (#240)
* 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>
2026-03-07 09:27:11 -08:00
Michael Ramos 6be5c013be fix(pinpoint): restore click handler after exiting plan diff (#238)
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>
2026-03-07 08:12:56 -08:00
dgrissen2 026194ad06 feat: smart file resolution for plannotator annotate (#237)
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>
2026-03-07 08:09:57 -08:00
theflysurfer c762bbe31e fix(annotate): resolve file paths correctly on Windows (#233)
* 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>
2026-03-07 07:58:17 -08:00
김영준E 2f5fd47f23 🐛 fix: render links inside bold/italic via recursive InlineMarkdown (#236)
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)
2026-03-07 07:07:37 -08:00
dgrissen2 4c87a8db83 fix: unreadable text in markdown code blocks (light mode) (#234)
* 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>
2026-03-07 07:06:46 -08:00
Michael Ramos 0ce299dd92 chore: bump version to 0.11.1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.11.1
2026-03-06 01:09:31 -08:00
Michael Ramos 0d68274975 feat: tabbed help dialog with selection modes video
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>
2026-03-06 00:52:53 -08:00
Michael Ramos 2739660fa6 feat: pinpoint mode for point-and-click annotations (#232)
* 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>
2026-03-06 00:27:59 -08:00
Michael Ramos 3f73ddaead Remove 1-second debounce guard from VS Code extension panel opening (#231)
The debounce was added under the mistaken belief that the extension was
launching multiple tabs. It's no longer needed.
2026-03-05 20:34:25 -08:00
Michael Ramos 64d2f29913 fix: validate plugin hooks.json on install (#230)
* 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>
2026-03-05 19:17:47 -08:00
Michael Ramos 1701424b4e feat: VS Code extension for in-editor plan review (#229)
* 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-vscode
Closes #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>
2026-03-05 16:56:57 -08:00
zerone0x b7bc82c607 fix: include untracked new files in uncommitted/unstaged diffs (#227) 2026-03-05 03:19:01 -08:00
Michael Ramos 7110f14f56 chore: bump version to 0.11.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.11.0
2026-03-04 16:55:38 -08:00
Michael Ramos 983246a18e fix: prevent CommentPopover from closing when interacting with ImageAnnotator (#226)
The click-outside handler matched only z-[90]/z-[100] classes, missing the
ImageAnnotator portal at z-[200]. Replace fragile z-index class matching with
a data-popover-layer attribute on all overlay portals.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 16:29:35 -08:00
Michael Ramos 2cb2152918 feat: What's New v0.11.0 first-open dialog (#225)
* feat: add What's New v0.11.0 first-open dialog

One-time announcement dialog highlighting auto-save drafts, short link
sharing with E2E encryption, and Obsidian vault browser. Supersedes the
PlanDiffMarketing dialog in the chain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: restore PlanDiffMarketing dialog in the chain

PlanDiffMarketing was incorrectly removed. It now stays in the sequence:
PermissionMode → UIFeatures → PlanDiffMarketing → WhatsNew v0.11.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: restore showPlanDiffMarketing to useEffect dependency arrays

The fix commit restored it to guard conditions but missed the dep
arrays, creating a stale closure bug for keyboard shortcuts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 15:59:22 -08:00
Michael Ramos 3dca97760a fix: add directive framing to deny message so Claude addresses feedback (#224)
Closes #215

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 15:20:38 -08:00
Michael Ramos a2735ea054 Make OpenCode submit_plan timeout configurable; align Claude timeout docs (#223) 2026-03-04 15:16:14 -08:00
Michael Ramos a7904739d6 feat(pi-extension): blocklist-only bash safety + interactive plan file path (#222)
* feat(pi-extension): blocklist-only bash safety + interactive plan file path

Switch from allowlist+blocklist to blocklist-only for bash command gating
during planning. Commands are now allowed unless they match a destructive
pattern, fixing false positives that blocked web fetching (curl, wget).

Preserve tools from other extensions during planning instead of clobbering
with a hardcoded tool list. Capture active tools on enter, restore on exit.

Add interactive plan file path selection: /plannotator accepts optional path
arg or shows input dialog. New /plannotator-set-file command for mid-session
path changes. Enables monorepo workflows with per-feature plan files.

Closes #221

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(marketing): add Pi to landing page badge and video links

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(pi-extension): remove bash regex gating, use prompt-based guidance

Delete DESTRUCTIVE_PATTERNS and isDestructiveCommand() from utils.ts.
Remove bash interception from tool_call handler. Bash is now unrestricted
during planning — the system prompt guides the agent not to run destructive
commands. Write/edit file restrictions to the plan file remain enforced.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 14:50:35 -08:00
Michael Ramos f20bf14cef refactor: extract applyCodeBlockAnnotation helper to deduplicate code block mark-wrapping
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:05:24 -08:00
Michael Ramos 092538ab8f feat: CommentPopover for better comment writing UX (#220)
* feat: replace comment textarea with CommentPopover (#219)

Replace the cramped toolbar textarea and awkward global comment inline
form with a new CommentPopover component that provides a proper writing
surface for long comments.

- New CommentPopover with popover mode (w-96) and expand-to-dialog mode
- Simplified AnnotationToolbar to menu-only (removed input step)
- Global comment button now opens CommentPopover instead of inline form
- Cmd/Ctrl+Enter to submit, Enter for newline (GitHub/Linear pattern)
- Popover tracks anchor element on scroll to stay pinned to content
- Image attachments supported in both modes

Closes #219

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: dismiss CommentPopover on new selection and memoize close handler

- Clear commentPopover state in highlighter CREATE handler when a new
  selection is made (prevents popover+toolbar coexisting)
- Wrap handleCommentClose in useCallback to prevent the click-outside
  effect from re-registering on every Viewer render

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:00:39 -08:00
maggiehe a6f08d2b9d fix: add missing /plannotator-annotate command to install scripts (#218) 2026-03-04 10:37:58 -08:00
Michael Ramos aa71ede0f3 Auto-save annotation drafts to survive server crashes (#217)
* feat: auto-save annotation drafts to survive server crashes

Adds server-side draft persistence so annotations are not lost when the
server process dies. Drafts are saved to ~/.plannotator/drafts/ keyed by
a SHA-256 content hash, debounced at 500ms. On reload, a dialog prompts
the user to restore or dismiss. Drafts are auto-deleted on approve/deny/feedback.

Closes #212

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove duplicate imports and update CLAUDE.md for draft feature

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: wrap handleRestoreDraft in useCallback and update shared-handlers comment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 10:12:58 -08:00
Michael Ramos d9c2d46113 refactor: extract shared route handlers and modularize plan server (#214)
* refactor: extract shared route handlers and modularize plan server

Pull duplicated /api/image, /api/upload, /api/agents handlers and
handleServerReady into shared-handlers.ts. Move /api/doc and Obsidian
reference endpoints into reference-handlers.ts. Eliminates ~120 lines
of verbatim duplication across index.ts, review.ts, and annotate.ts.

Closes #210

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: deduplicate OpencodeClient type and fix stale module comment

Export OpencodeClient interface from shared-handlers.ts and import it
in index.ts and review.ts instead of redefining inline. Also corrects
the module doc comment about /api/agents usage scope.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 23:17:16 -08:00
Michael Ramos e5f3cfffbb feat: Obsidian vault browser sidebar tab (#209)
* feat: add Obsidian vault browser sidebar tab (#207)

Add a third sidebar tab that lets users browse their Obsidian vault's
file tree, click any markdown file to open and annotate it as part of
plan feedback. Includes wikilink support for navigating between vault
files.

- New /api/reference/obsidian/files and /api/reference/obsidian/doc endpoints
- useVaultBrowser hook and VaultBrowser sidebar component
- Wikilink [[filename]] and [[filename|display]] detection in Viewer
- "Vault Browser" toggle in Settings > Obsidian
- Bare filename search for wikilink resolution within vault

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update docs and clear vault state on disable

- Add new reference endpoints to CLAUDE.md API table
- Update stale doc comments referencing "two tabs" in sidebar files
- Clear vaultPath and activeFile when vault browser is disabled

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: simplify vault browser prop passing and derived state

- Move VaultNode to packages/ui/types.ts (shared type location)
- Pass useVaultBrowser return as single object prop through SidebarContainer
- Replace showVaultTab/vaultPath useState with useMemo derivations
- Extract buildVaultDocUrl helper to deduplicate URL template
- Wrap onVaultFetchTree in useCallback
- Fix toggle switch size to match existing toggles (h-6 w-11)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 15:32:59 -08:00
Michael Ramos c7ae789a55 chore: bump version to 0.10.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.10.0
2026-03-02 17:36:08 -08:00
Michael Ramos 1ea075b241 fix: show error dialog on shared URL failure and fix import annotation merge (#205)
* chore: open v0.10.0 pre-release fix branch

Tracking two bugs found during pre-release testing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: show error dialog on shared URL failure and fix import annotation merge

Bug 1: When a shared URL fails to load (truncated hash, expired paste,
missing encryption key), the portal now shows a warning dialog explaining
the failure and an amber "Demo" badge so users know they're seeing sample
content — not their data.

Bug 2: Importing a teammate's share URL no longer replaces existing
annotations. Uses React functional state updaters to avoid stale closures
and passes the full merged annotation list to pendingSharedAnnotations so
DOM highlights are re-applied for all annotations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 16:42:56 -08:00
Michael Ramos ce8946b065 fix(paste-service): allow CORS from any localhost port
The hook server runs on random ports, so the paste service must accept
requests from any localhost origin, not just :3001.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 13:09:24 -08:00
Michael Ramos d2a159ca53 Add customizable filename format for Obsidian exports (#202)
* feat: add customizable filename format for Obsidian export (#167)

Add a filename format setting to the Obsidian integration that supports
template variables ({title}, {YYYY}, {MM}, {DD}, {Mon}, {D}, {HH}, {h},
{hh}, {mm}, {ss}, {ampm}). Default format preserves existing behavior.
Settings UI shows a live preview of the resulting filename.

https://claude.ai/code/session_01QycvXHHvAy38N36PVP3jFF

* fix: guard against double .md extension in custom filename format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-02 11:21:16 -08:00
Michael Ramos 7004321f81 test: add E2E encryption tests for paste service (#204)
Unit tests for encrypt/decrypt round-trip (key correctness, IV
uniqueness, tamper detection) and live integration tests that verify
the full compress → encrypt → POST → GET → decrypt → decompress
pipeline against the deployed Cloudflare Worker.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 10:45:41 -08:00
Michael Ramos b1f91936d6 Deploy paste service with E2E encryption (#203)
* feat: Deploy paste service with end-to-end encryption (zero-knowledge)

Add AES-256-GCM client-side encryption to the short URL sharing flow.
Plans are encrypted in the browser before upload — the paste service
stores only ciphertext it cannot read. The decryption key lives only
in the URL fragment (#key=...) and never leaves the browser.

- New packages/shared/crypto.ts with encrypt/decrypt via Web Crypto API
- Encrypt before POST in createShortShareUrl, decrypt on load
- Parse #key= fragment in useSharing.ts for both direct and import URLs
- Deploy Cloudflare Worker with KV namespaces to workers.dev
- Add deploy-paste CI/CD job to deploy.yml
- Fix Cache-Control from public to private, no-store
- Update ExportModal strings with encryption messaging
- Update README, docs, and blog with zero-knowledge encryption details

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address code review: fix short URL fallthrough and regex anchor

- Return false early when paste fetch fails on /p/<id> path instead of
  falling through to parseShareHash() (which can never succeed since the
  hash contains #key=, not plan data)
- Add end-anchor to importFromShareUrl regex to prevent false matches on
  URLs with extra path segments (regression from PR #188)
- Remove demo plan NOTE from README

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 10:21:53 -08:00
Michael Ramos bf4589e395 fix: guard Enter key handlers with isComposing for CJK IME support (#201)
CJK users press Enter to confirm IME character selection, which
was triggering form submission instead. Add !e.nativeEvent.isComposing
guard to all Enter-to-submit handlers in the plan editor UI.

Fixes #191

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:45:37 -08:00
김영준E 09384361e4 fix: Fix review showing empty files + propagate diff errors to UI (#200)
* 🐛 fix: force standard a/b diff prefix to handle mnemonic prefix config

When diff.mnemonicPrefix is enabled in git config, `git diff` uses
context-dependent prefixes (c/ for commit, w/ for worktree, i/ for
index) instead of the standard a/b. Both the internal parseDiffToFiles
and @pierre/diffs library expect a/b prefixes, causing silent parse
failures that result in an empty file list.

Add --src-prefix=a/ --dst-prefix=b/ to all git diff invocations to
ensure consistent output regardless of user git configuration.

* ✨ feat: propagate git diff errors to review UI

Previously, git diff errors were silently caught and returned as empty
patches. The UI showed "No changes" with no indication of failure.

- Add error field to DiffResult and propagate through ReviewServerOptions
- Include error in /api/diff and /api/diff/switch responses
- Show distinct error state in review UI (red icon + error message)
- Clear/set error state on diff type switch

* Fix mnemonic prefix handling in pi-extension runGitDiff

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>
2026-03-01 22:26:07 -08:00
김영준E 99c066644a fix: Fix diff view not clearing on sidebar tab switch (#199)
* 🐛 Clear diff view when switching from Versions to Contents tab

sidebar.activeTab and isPlanDiffActive were independent states with
no synchronization. Switching to TOC tab left the diff viewer active
in the main area. Add useEffect to reset isPlanDiffActive when tab
changes to "toc".

* 🐛 Dismiss diff view on Escape key and add dev mock API for version history

- Add keydown listener to clear isPlanDiffActive on Escape
- Add Vite dev plugin (dev-mock-api.ts) serving mock /api/plan endpoints
  with 3 plan versions so the Versions tab works during local development
- Guard setMarkdown against undefined plan from API response
2026-03-01 22:11:13 -08:00
김영준E cc44f6ad44 feat: Add code suggestions, inline editing, and refactor DiffViewer (#198)
* ✨ Add code suggestions, inline editing, and refactor DiffViewer

- GitHub-style suggestion blocks with syntax-highlighted diff view
- Inline annotation editing via toolbar (edit button on inline comments)
- Expandable two-pane code modal for writing suggestions
- Inline markdown rendering (bold, italic, code) in review comments
- Original code extraction from unified diff patches
- Decompose DiffViewer (615→200 lines) into focused components:
  FileHeader, InlineAnnotation, SuggestionBlock, SuggestionDiff,
  SuggestionModal, AnnotationToolbar, HighlightedCode
- Extract hooks: useAnnotationToolbar, useTabIndent
- Extract utils: detectLanguage, formatLineRange, patchParser,
  renderInlineMarkdown
- Fix suggestedCode trim stripping meaningful indentation
- Fix stale closure in useTabIndent consumers

* 🐛 Fix annotation edit field overwrite and add IME composition guard

- Conditionally spread only defined fields in handleEditAnnotation
  to prevent undefined values from wiping sibling fields
- Add !e.nativeEvent.isComposing guard to Cmd/Ctrl+Enter handlers
  in comment and suggested-code textareas for CJK input support
2026-03-01 22:00:10 -08:00
Code_G c4f3479e6d fix(opencode): add 10-min decision timeout and remote share URL notification
Add a 10-minute timeout on waitForDecision() with proper clearTimeout cleanup, and use writeRemoteShareLink for remote URL notification instead of os.hostname()/localhost.
2026-02-28 23:34:13 -08:00
Terry Kerr 1cc84e2668 fix: respect XDG Base Directory Specification of ~/.local/bin (#195)
As per https://specifications.freedesktop.org/basedir/latest/#variables

> User-specific executable files may be stored in $HOME/.local/bin.

There is no env var for users to set to change this behaviour, and
$XDG_DATA_HOME isn't a reasonable place to put them.
2026-02-28 22:59:32 -08:00
Gunit Bindal c470dcebf1 Add short URL sharing via paste service to fix URL truncation (#188)
* Add short URL sharing via paste service to fix URL truncation on Slack/messaging apps

Share URLs for large plans can be 10-40KB+ because the entire plan + annotations
are compressed into the URL hash. Services like Slack, WhatsApp, and Twitter
truncate these URLs, making shared plans unviewable (related: #142).

This adds an optional paste-service backend that stores compressed payloads and
returns short ~60-char URLs (e.g. share.plannotator.ai/p/aBcDeFgH).

Changes:
- Add Cloudflare Worker paste service (apps/paste-worker/) with KV storage
  and 90-day TTL for stored plans
- Add createShortShareUrl() and loadFromPasteId() to sharing utils
- Update useSharing hook to auto-generate short URLs with 1s debounce
- Update ExportModal to show short URL as primary copy target with
  full hash URL as backup
- Portal automatically supports /p/<id> paths via useSharing hook
- Fully backward compatible: hash-based URLs continue to work unchanged
- Graceful degradation: falls back to hash URLs if paste service is unavailable

* Fix critical bugs found during code review of short URL feature

- Worker: return only { id } so client constructs URL with its own
  shareBaseUrl (fixes self-hosted deployments)
- importFromShareUrl: handle /p/<id> short URLs in addition to hash
  URLs (fixes teammate import via short links)
- Anchor /p/<id> regex with ^ to prevent false matches on nested paths
- Pass shareBaseUrl to loadFromPasteId (was always defaulting)
- replaceState preserves base path instead of hardcoding /
- Clear stale shortShareUrl immediately on debounce to prevent showing
  outdated link during the 1s delay
- Add shareBaseUrl to dependency arrays for loadFromHash and
  importFromShareUrl callbacks
- Remove unused url field fallback from paste API response parsing

* Fix pasteApiUrl/shareBaseUrl confusion and add remote session share URLs

Bug fix (addresses @backnotprop's code review):
- useSharing hook now accepts separate `pasteApiUrl` parameter instead of
  incorrectly passing `shareBaseUrl` to `loadFromPasteId`. These are
  different domains (share portal vs paste backend). When omitted, the
  default `https://paste.plannotator.ai` is used correctly.
- Fixed in all three call sites: loadFromHash, importFromShareUrl, and
  generateShortUrl (via createShortShareUrl options).

Remote session share URLs (implements #192):
- When running on a remote instance (SSH, devcontainer), Plannotator now
  generates a share.plannotator.ai URL and prints it to stderr so the
  user can open the plan review in their local browser.
- Works for all three modes: plan review, code review, and annotate.
- Uses the same deflate-raw + base64url encoding as the client.
- Fails silently if URL generation fails — port forwarding still works.
- New server utility: packages/server/share-url.ts

* Address code review findings: fix spread limit, CORS, deps array

- share-url.ts: Replace btoa(String.fromCharCode(...compressed)) with
  a loop to avoid RangeError on plans >65K compressed bytes
- share-url.ts: Add cross-reference comment noting the server-side
  SharePayload is an intentional subset of the canonical UI type
- paste-worker: Only return CORS headers for allowed origins; disallowed
  origins get no CORS headers instead of misleading partial headers
- useSharing.ts: Remove unused shareBaseUrl from loadFromHash dependency
  array (loadFromHash only uses pasteApiUrl, not shareBaseUrl)

* Restructure paste service with pluggable storage, fix consent flow and security issues

Replaces monolithic paste-worker with multi-target paste-service architecture:
- Core logic (handler, storage interface, CORS) separated from deployment targets
- Filesystem store (self-hosted), KV store (Cloudflare), S3 stub (future)
- Bun binary target + Cloudflare Worker target
- Fix auto-upload removed: short URLs only created on explicit user click
- Clear stale short URL state when content changes
- Fix require('fs') in ESM module, add path-traversal guard in FsPasteStore
- Return 400 (not 500) for malformed JSON in both targets
- Fix compress() spread limit for large plans
- Updated docs: self-hosting guide, sharing guide, env vars, API endpoints

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Simplify paste service: extract shared router, remove TOCTOU patterns

- Extract duplicated HTTP routing from bun.ts and cloudflare.ts into
  handleRequest() in core/handler.ts — targets are now thin wrappers
- Move ID_PATTERN regex to core (was duplicated in both targets)
- Remove existsSync guard before mkdirSync({recursive:true}) in fs.ts
- Remove file.exists() check before file.json() in fs.ts get() — the
  try/catch already handles missing files
- Extract DEFAULT_SHARE_BASE constant in sharing.ts (was hardcoded twice)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Deduplicate compression, image parsing, and remote share URL logic

- Create packages/shared/ with compress/decompress (single source of truth)
  — both @plannotator/server and @plannotator/ui import from here
- Export parseShareableImages from sharing.ts, remove duplicate
  parseGlobalAttachments from useSharing.ts
- Extract writeRemoteShareLink() helper in share-url.ts, replace 3
  near-identical blocks in apps/hook/server/index.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix modulo bias in paste ID generation and remove nonexistent doc artifacts

- Use rejection sampling in generateId() to eliminate modulo bias
  (bytes >= 248 discarded, uniform distribution over 62 chars)
- Remove references to install-paste.sh and Docker image that don't
  exist yet; point self-hosting docs to GitHub Releases binaries

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>
2026-02-28 22:55:55 -08:00
Michael Ramos af18db7aab fix(ci): add NPM_TOKEN to publish steps
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.9.3
2026-02-26 21:31:07 -08:00
Michael Ramos 55e91fb7a1 chore: bump version to 0.9.3
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 21:22:22 -08:00
dgrissen2 93e9420cb4 feat: open linked .md files in read-only tabs with TOC navigation (#184)
* feat: open linked .md files in read-only tabs with TOC navigation

When a plan references local .md files via markdown links, they now open
in a new browser tab using the full Plannotator viewer in read-only mode.

Changes:
- server: add /api/doc?path= endpoint with 4-strategy path resolution
  (absolute, relative to project root, bare filename search). Returns 400
  with match list on ambiguous filenames, 404 when not found, 403 on
  path traversal. Only resolves files within process.cwd().
- Viewer: detect local .md links in InlineMarkdown and route them to
  /?doc=<path>&readonly=true with a new-tab icon. Add isReadOnly prop
  that guards the web-highlighter init (prevents phantom highlights) and
  suppresses annotation toolbars.
- App: parse ?doc= URL param on load and fetch from /api/doc instead of
  /api/plan. Set isReadOnly mode which shows a read-only banner, hides
  approve/deny/feedback buttons, ModeSwitcher, Settings, AnnotationPanel,
  and annotation panel toggle. Shows an error view (not a silent fallback)
  when the document cannot be found or is ambiguous. Opens the sidebar to
  the TOC tab automatically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: upgrade linked docs to same-view annotatable navigation with aggregated feedback

Replace the new-tab read-only approach with same-view navigation: clicking a
.md link swaps the plan content in-place, preserving all annotations via a
docCache. Users can annotate linked docs and all feedback (plan + linked docs)
is aggregated into the deny/approve payload sent to Claude.

Key changes:
- New useLinkedDoc hook for state swapping, caching, and highlight restoration
- React key on Viewer forces clean unmount/remount (fixes web-highlighter DOM crash)
- exportLinkedDocAnnotations() aggregates linked doc feedback with filepath context
- UI: 2px primary border, "Linked File" badge, "Copy file" button, TOC filepath
  indicator with back-to-plan button
- Fix Tailwind v4 cascade: move * { border-color } to @layer base so utility
  classes like border-primary actually override it
- Compact TOC spacing (padding, gaps, line-height) without affecting Version Browser
- Exclude node_modules/.git from /api/doc bare filename glob

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: harden /api/doc endpoint and align linked doc annotation output

Restrict /api/doc to .md/.mdx files only, fix heading hierarchy and
sorting in exportLinkedDocAnnotations, and update CLAUDE.md docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: complete linked doc annotations in getDocAnnotations and fix useMemo deps

- getDocAnnotations() now includes the active linked doc's live
  annotations alongside the cache, removing the implicit requirement
  to call back() before reading
- Remove unstable linkedDocHook object from annotationsOutput useMemo
  dependency array (was defeating memoization every render)
- Guard Cmd+Enter shortcut against linkedDocHook.isActive to prevent
  accidental approve/deny while viewing a linked doc

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: include linked doc annotations in approve feedback gate

The handleApprove gate only checked plan-level annotations, silently
dropping linked-doc-only feedback on "approve with notes" (OpenCode).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add getDocAnnotations to annotationsOutput useMemo deps

Ensures memo recomputes when navigating to/from linked docs, preventing
stale annotation output.

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>
2026-02-26 20:26:06 -08:00
Michael Ramos 7145f190ec Add "Open in VS Code Diff" button to plan diff viewer (#180)
* Add feature plan for VS Code diff viewer button

Scoping document for a new PlanDiffViewer button that opens
the plan diff in VS Code's native diff viewer via `code --diff`.

https://claude.ai/code/session_01FYcJkGsWfuiGy53x3pwCSW

* feat: add "Open in VS Code" button to plan diff viewer

Add a button in the PlanDiffViewer toolbar that opens the current
plan diff in VS Code's native side-by-side diff viewer. The server
writes both versions to temp files and spawns `code --diff`.

- New POST /api/plan/vscode-diff endpoint in packages/server/index.ts
- VS Code button with loading/error states in PlanDiffViewer.tsx
- Wire currentPlan/basePlan/baseVersion props from App.tsx

https://claude.ai/code/session_01FYcJkGsWfuiGy53x3pwCSW

* chore: update bun.lock after install

https://claude.ai/code/session_01FYcJkGsWfuiGy53x3pwCSW

* refactor: server reads plan versions directly instead of receiving from client

The server already has the current plan in its closure and can read
any version from disk via getPlanVersion(). No need to send plan
text from the UI — just send baseVersion.

https://claude.ai/code/session_01FYcJkGsWfuiGy53x3pwCSW

* chore: remove plan.md scoping document from repo

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: extract editor diff logic into shared packages/server/editor.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: use official VS Code SVG icon in plan diff viewer button

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: use history file paths directly for VS Code diff instead of writing temp files

Both plan versions already exist on disk in ~/.plannotator/history/.
Eliminates redundant file writes, fixes UPLOAD_DIR semantic misuse,
and corrects HTTP status codes for editor errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: rename editor.ts to ide.ts to avoid confusion with plan editor UI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add ide.ts and vscode-diff endpoint to CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-26 13:25:08 -08:00
김영준E 39b63c99c0 fix(ui): dismiss annotation toolbars on outside click and Escape (#182)
* fix: close annotation toolbars on outside click and escape

* refactor: share dismiss logic for annotation toolbars
2026-02-25 06:38:17 -08:00
Robert Dailey e8ad0d5c38 ci(release): add npm publishing with OIDC provenance and dry-run mode (#170)
Adds an npm-publish job to the release workflow that publishes
@plannotator/opencode and @plannotator/pi-extension to npm with
OIDC trusted publishing and provenance attestation.

- Add pull_request and workflow_dispatch triggers (with dry-run input)
- Derive DRY_RUN env var; publish only on tag pushes or explicit opt-in
- Pin all action refs to SHA for supply-chain security
- Gate the release job on tag refs to avoid spurious GH releases
- Add id-token: write permission at workflow and job level for OIDC
2026-02-24 12:30:47 -08:00