Skip to content

Add PlanTree hybrid tree/node visualizer#22

Merged
JMRussas merged 3 commits intomainfrom
feature/plantree-hybrid-visualizer
Mar 7, 2026
Merged

Add PlanTree hybrid tree/node visualizer#22
JMRussas merged 3 commits intomainfrom
feature/plantree-hybrid-visualizer

Conversation

@JMRussas
Copy link
Owner

@JMRussas JMRussas commented Mar 7, 2026

Summary

  • Implements full PlanTree component suite with 19 files: recursive tree rendering, SVG dependency overlay (bezier curves between dependent tasks), CSS grid expand/collapse animations, WAI-ARIA treeview keyboard navigation (roving tabindex, arrow keys, Home/End/Escape), search/filter with <mark> text highlighting, and runtime HSL theme configurator with localStorage persistence
  • Adds centralized expand state (useExpandState), keyboard hook (useTreeKeyboard), search hook (useTreeSearch), and reactive theme hook (usePlanTreeTheme with MutationObserver for dark/light detection)
  • 52 new frontend tests (211 total), zero external dependencies added

Test plan

  • cd frontend && npx tsc --noEmit — clean
  • cd frontend && npm test — 211 tests passing (26 test suites)
  • Manual: open a project with an L2 plan, verify tree renders with colored nodes, dependency lines, keyboard navigation, search filtering, and theme configurator

Generated by Claude Code · Claude Opus 4.6

JMRussas and others added 2 commits March 7, 2026 02:01
Implements the full PlanTree component suite: SVG dependency overlay
with bezier curves, CSS grid expand/collapse animations, WAI-ARIA
treeview keyboard navigation, search/filter with text highlighting,
and runtime HSL theme configurator with localStorage persistence.

19 new files, 52 new tests (211 frontend total).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix Rules of Hooks: move all hooks above conditional return in
  PlanTreeNode (useRef, useRegisterNode, useDependencyContext,
  useCallback all called before visibility check)
- Fix CSS selector injection: use CSS.escape() in querySelector
  calls in useTreeKeyboard and index.tsx
- Remove duplicate Escape handler from NodeDetail that conflicted
  with useTreeKeyboard's Escape handling (double-fire)
- Fix stale closure in ThemeConfigurator togglePopover by using
  callback form of setOpen
- Pre-compute reverse dependency map (downstreamMap) in
  DependencyContext to replace O(n) forEach+includes scan per node
- Re-sync expand state when tree identity changes via useEffect on
  defaultMap, preserving user overrides for existing nodes
- Remove unnecessary recalculation on hover in DependencyOverlay
  (paths don't move, only styling changes)
- Reformat compressed single-line CSS rules in theme configurator
  and search sections for consistency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@JMRussas
Copy link
Owner Author

JMRussas commented Mar 7, 2026

Review Defect Fixes (commit 4472667)

8 defects found during self-review, all fixed:

High Severity

# File Defect Fix
3 PlanTreeNode.tsx:40 Rules of Hooks violationuseRef, useRegisterNode, useDependencyContext, useCallback called after conditional return null. React would throw when a node transitions visible→invisible. Moved all hook calls above the early return

Medium Severity

# File Defect Fix
1 useTreeKeyboard.ts:76, index.tsx:51 CSS selector injectionquerySelector(\[data-node-id="${id}"]`)vulnerable if ID contains"or]` Use CSS.escape(id)
6 NodeDetail.tsx:26 Double Escape handler — global keydown listener for Escape conflicts with useTreeKeyboard's Escape handler, calling onClose() twice Removed global listener; Escape handled by useTreeKeyboard
5 ThemeConfigurator.tsx:59 Stale closureif (open) setActive(null) captures previous render's open value Use callback form: setOpen(o => { if (o) setActive(null); return !o })

Low Severity

# File Defect Fix
2 DependencyContext.tsx:60 O(n) downstream scan per node per render (~12,800 iterations for 80 tasks) Pre-compute downstreamMap (reverse index) via useMemo in provider
7 useExpandState.ts:43 Expand state not re-synced when tree identity changes — new node IDs never added to map useEffect on defaultMap merges new defaults while preserving user overrides
8 DependencyOverlay.tsx:101 Unnecessary recalculation on hover — paths don't move, only styling changes Removed hover-triggered recalculate() call

Nit

# File Defect Fix
9 PlanTree.css:381-409 CSS formatting inconsistency — 17 rules compressed into single lines Expanded to multi-line format matching rest of file

The global Escape listener was removed in the prior commit to fix
double-firing with useTreeKeyboard. But this left the detail panel
with no Escape support when it has focus (e.g., user clicks inside
to select text). Added a scoped onKeyDown on the panel div instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@JMRussas JMRussas merged commit 0f36fa6 into main Mar 7, 2026
3 checks passed
@JMRussas JMRussas deleted the feature/plantree-hybrid-visualizer branch March 7, 2026 07:15
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