Skip to content

feat(pfm): code line range references, hover preview, sketch Graphviz#692

Merged
backnotprop merged 16 commits into
mainfrom
feat/pfm-code-snippets
May 11, 2026
Merged

feat(pfm): code line range references, hover preview, sketch Graphviz#692
backnotprop merged 16 commits into
mainfrom
feat/pfm-code-snippets

Conversation

@backnotprop
Copy link
Copy Markdown
Owner

Summary

Stacked on #690. Three features that enhance PFM rendering:

Code file line range references

  • file.ts:42 (single line) and file.ts:10-20 (range) are now fully supported end-to-end
  • Hover preview: hovering a code badge with line info shows a syntax-highlighted snippet popover (150ms delay, GitHub-style hover persistence)
  • Scroll-to-line: clicking opens the full CodeFilePopout scrolled to the referenced region
  • Server-side: /api/doc and /api/doc/exists strip line suffixes before file resolution
  • New parseCodePath() utility in packages/shared/code-file.ts
  • useCodeFilePopout moved to packages/ui/hooks/pfm/ (re-exported for compatibility)

Graphviz improvements

  • Responsive container height: computed from SVG aspect ratio instead of fixed 20rem minimum
  • White background removed: strips Graphviz's default <polygon fill="white"/>
  • Theme-aware defaults: replaces Graphviz default colors (black, #000000, lightgrey) with CSS custom properties via SVG post-processing — preserves user-specified colors
  • Google Fonts: custom fonts referenced in DOT source (e.g., Indie Flower) are loaded via <link> tags

Sketch mode (rough.js)

  • Graphviz diagrams render with a hand-drawn sketch style via rough.js (loaded from CDN)
  • Converts <rect>, <path>, <polygon>, <ellipse>, <circle>, <line> to rough equivalents
  • Arrow heads get reduced roughness for readability
  • New sketchDiagrams config setting (default: true), synced via ConfigStore

Test plan

  • Hover packages/ui/utils/parser.ts:261-286 — shows syntax-highlighted TypeScript snippet
  • Click same badge — CodeFilePopout opens scrolled to line 261
  • Hover a badge without line ref — no popover, just tooltip
  • Mouse from badge to popover — stays open for scrolling
  • Scroll within the hover popover
  • Graphviz diagrams render with sketch style and correct colors
  • Set "sketchDiagrams": false in config — diagrams render crisp
  • Plain digraph { A -> B } uses theme colors (not black on dark)
  • User-specified colors in DOT source are preserved

backnotprop added a commit that referenced this pull request May 11, 2026
1. Bundle rough.js as npm dep instead of CDN script tag — eliminates
   supply chain risk from same-origin third-party code execution
2. Fix Rules of Hooks violation in CodeSnippetPreview — move useMemo
   above conditional return
3. Add sketchDiagrams to getServerConfig return type (TypeScript fix)
4. Preserve :line suffix when using gate.resolved on click — resolved
   paths were dropping line info so popout opened at file top
5. Pi server parity — add parseCodePath to Pi's /api/doc and
   /api/doc/exists handlers for line-ref support
6. Pass baseDir to hover preview fetch for relative path resolution
   in linked doc / annotate flows
7. Remove dead SVG color replacement regex (line 294 was unreachable)
8. Pre-split highlighted lines to avoid O(n²) in preview render
@backnotprop backnotprop force-pushed the feat/hooks-settings-dialog branch from 3ebaf7e to cabe109 Compare May 11, 2026 12:15
backnotprop added a commit that referenced this pull request May 11, 2026
- Remove rough.js sketch mode (experimental, not worth the overhead)
- Remove sketchDiagrams config setting, Google Font loading
- Remove scroll-to-line from CodeFilePopout (shadow DOM issue, deferring)
- Fix ambiguous picker dropping line suffix on pick
- Fix hoverPreview in useCallback deps causing redundant fetches
- Validate lineEnd >= line in parseCodePath (swap if inverted)
Adds support for line ranges in code file references:
- `file.ts:42` — single line
- `file.ts:10-20` — line range

New parseCodePath() in packages/shared/code-file.ts extracts line info.
Server strips line suffix before file resolution, returns line info in
response. CodeFilePopout scrolls to the referenced line on open.
Hovering a code file badge with line info shows a small code snippet
popover after 300ms delay.

Moved useCodeFilePopout to packages/ui/hooks/pfm/ as the start of
organizing PFM-related hooks. Old path re-exports for compatibility.
handleDocExists was passing paths with :line suffixes to resolveCodeFile,
causing code file badges with line refs to show as "missing". Also add
unmount cleanup for the hover preview timer.
Use hljs.highlight() for syntax coloring in the code snippet popover,
with language auto-detected from file extension. Reduced hover delay
from 300ms to 150ms. Added smart positioning (show above when near
viewport bottom). Preview header shows filename and line range.
Use a 200ms dismiss delay with cancel-on-enter pattern — hovering
from the badge to the popover (or back) keeps it alive. The popover
only closes when the mouse leaves both the badge and popover for
200ms. Allows scrolling through longer code snippets.
The Graphviz container used a fixed min-height of 20rem regardless of
diagram dimensions, causing tiny horizontal diagrams to float in huge
empty space. Now computes height from the SVG's aspect ratio, capped
at 36rem. Also strips Graphviz's default white background polygon so
diagrams are transparent and work with dark mode.
Plain Graphviz diagrams (without user-specified colors) now inherit
Plannotator theme tokens: node fills use --card, strokes use --border,
text uses --foreground, edges use --muted-foreground. User-specified
fill/stroke/fontcolor in DOT source takes precedence via specificity.
CSS overrides clobber user-specified colors from DOT source because
CSS fill properties take precedence over SVG fill attributes. Switch
to JS-based replacement: only swap known Graphviz defaults (black,
#000000, lightgrey) with theme tokens, preserving any user-specified
colors like transparency hex values.
Replace hardcoded SKETCH_MODE flag with sketchDiagrams config option
in ~/.plannotator/config.json (default: true). Registered in the
ConfigStore settings system so it syncs via serverConfig and can be
toggled from the Settings UI. Also loads Google Fonts referenced in
Graphviz DOT source via <link> tags in the page head.
The outer container had overflow-hidden which clipped content without
allowing scroll. Switch to flex column layout with min-h-0 on the
code area so the scroll container respects the max-height constraint.
1. Bundle rough.js as npm dep instead of CDN script tag — eliminates
   supply chain risk from same-origin third-party code execution
2. Fix Rules of Hooks violation in CodeSnippetPreview — move useMemo
   above conditional return
3. Add sketchDiagrams to getServerConfig return type (TypeScript fix)
4. Preserve :line suffix when using gate.resolved on click — resolved
   paths were dropping line info so popout opened at file top
5. Pi server parity — add parseCodePath to Pi's /api/doc and
   /api/doc/exists handlers for line-ref support
6. Pass baseDir to hover preview fetch for relative path resolution
   in linked doc / annotate flows
7. Remove dead SVG color replacement regex (line 294 was unreachable)
8. Pre-split highlighted lines to avoid O(n²) in preview render
- Remove rough.js sketch mode (experimental, not worth the overhead)
- Remove sketchDiagrams config setting, Google Font loading
- Remove scroll-to-line from CodeFilePopout (shadow DOM issue, deferring)
- Fix ambiguous picker dropping line suffix on pick
- Fix hoverPreview in useCallback deps causing redundant fetches
- Validate lineEnd >= line in parseCodePath (swap if inverted)
@backnotprop backnotprop changed the base branch from feat/hooks-settings-dialog to main May 11, 2026 12:57
@backnotprop backnotprop force-pushed the feat/pfm-code-snippets branch from 1e1b58f to ab7f496 Compare May 11, 2026 12:57
…M text

Highlight each line separately instead of splitting a full-snippet
highlight — avoids broken spans on multi-line tokens. Added TODO for
createPortal on the hover popover. Removed stale "scrolled to that
region" claim from PFM reminder since scroll-to-line was deferred.
The popover rendered inline was trapped behind the annotation sidebar's
stacking context. Portal to document.body so it layers correctly.
@backnotprop backnotprop merged commit 347663a into main May 11, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant