Allow zooming out to 10% (previously 50%) to better accommodate large constellation analyses. Added zoom constants (MIN_ZOOM=0.1, MAX_ZOOM=2.5) for consistency across fitView and ReactFlow component.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove three unnecessary eslint-disable comments for @typescript-eslint/no-unused-vars
that were no longer needed, as the destructured variables are actually used.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add double-click handler for minimized groups to quickly maximize them,
providing a faster alternative to using the context menu.
Implementation:
- Add handleNodeDoubleClick callback in GraphEditor
- Check if double-clicked node is a minimized group
- Call toggleGroupMinimized to maximize the group
- Wire handler to ReactFlow's onNodeDoubleClick prop
This improves UX by allowing users to quickly expand minimized groups
to see and edit individual relations within them.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements visual aggregation of edges when multiple relations exist
between two minimized groups, preventing overlapping edges and improving
graph readability.
Key features:
- Aggregate edges between minimized groups using normalized keys (sorted
node IDs) so bidirectional edges (A->B and B->A) merge together
- Display single neutral-styled edge (dark gray, solid, no arrows) with
relation counter badge showing "X relations"
- Disable context menu and use special info panel for aggregated edges
- Hide individual edge type labels when aggregated
- Store all original relation data in aggregatedRelations array
Implementation details:
- GraphEditor: Use Map to deduplicate edges with bidirectional key for
group-to-group connections, attach aggregatedCount metadata
- CustomEdge: Detect aggregated edges and apply neutral styling (#4b5563
color, undirected/no arrows, no type label)
- RightPanel: Show "Aggregated Relations" info panel instead of
EdgeEditorPanel for aggregated edges
- Context menu: Skip aggregated edges to prevent editing synthetic edges
Users can maximize groups to see and edit individual relations. This
keeps the graph clean when working with minimized groups while preserving
all underlying relationship data.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements proper edge rendering when both source and target actors are
in minimized groups. Edges are now correctly rerouted to connect the
minimized group nodes with dynamic floating edge calculations.
Key changes:
- Add edge rerouting logic to redirect edges from hidden actors to their
parent minimized groups
- Implement edge deduplication to prevent multiple edges between same
groups when multiple actors have connections
- Fix floating edge calculations with fallback dimensions to prevent NaN
- Use canonical parentId property instead of actorIds array for accuracy
- Create constants.ts for MINIMIZED_GROUP_WIDTH/HEIGHT (220x80)
- Replace magic numbers throughout codebase with named constants
- Remove sourceHandle/targetHandle properties when routing to groups to
avoid React Flow validation errors
Technical details:
- GraphEditor: Build actor-to-group mapping using parentId, deduplicate
edges with Map keyed by source_target, strip handle properties
- CustomEdge: Use floating edge params for minimized groups on both ends
- edgeUtils: Add fallback dimensions and division-by-zero guards in
getNodeIntersection and getEdgePosition
- graphStore: Use constants for minimized group dimensions
Edges between minimized groups now render with proper Bezier curves and
dynamic connection points that adapt to group positions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements comprehensive multi-selection support for bulk operations on
actors, relations, and groups.
Features:
- New MultiSelectProperties panel for 2+ selected elements
- Bulk operations: group, ungroup, delete, minimize/maximize
- Add actors to existing groups (expands group bounds)
- Reverse relation directions
- Change directionality for multiple relations
- Immediate UI feedback with local state for directionality
- Standardized panel layout with scrollable content and footer
Fixes:
- Group positioning: actors stay at absolute positions when added/removed
- Minimize/maximize: properly stores/restores dimensions and visibility
- Position conversion between absolute and relative coordinates
- Type-safe width/height handling for group dimensions
UI Consistency:
- All property panels use fragment wrapper pattern
- Scrollable content area with px-3 py-3 padding
- Fixed footer section with action buttons
- Consistent button styles across panels
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements comprehensive group minimize/maximize functionality and migrates
to React Flow v12 (@xyflow/react) with improved edge routing.
## Group Minimize/Maximize Features:
- Minimized groups render as compact 220×80px solid rectangles
- Original dimensions preserved in metadata and restored on maximize
- Child actors hidden (not filtered) to prevent React Flow state issues
- Solid color backgrounds (transparency removed for minimized state)
- Internal edges filtered out when group is minimized
- Dimension sync before minimize ensures correct size on maximize
## Floating Edges:
- Dynamic edge routing for connections to/from minimized groups
- Edges connect to closest point on minimized group border
- Regular actors maintain fixed handle connections
- Smooth transitions when toggling group state
## React Flow v12 Migration:
- Updated package from 'reactflow' to '@xyflow/react'
- Changed imports to named imports (ReactFlow is now named)
- Updated CSS imports to '@xyflow/react/dist/style.css'
- Fixed NodeProps/EdgeProps to use full Node/Edge types
- Added Record<string, unknown> to data interfaces for v12 compatibility
- Replaced useStore(state => state.connectionNodeId) with useConnection()
- Updated nodeInternals to nodeLookup (renamed in v12)
- Fixed event handler types for v12 API changes
## Edge Label Improvements:
- Added explicit z-index (1000) to edge labels via EdgeLabelRenderer
- Labels now properly render above edge paths
## Type Safety & Code Quality:
- Removed all 'any' type assertions in useDocumentHistory
- Fixed missing React Hook dependencies
- Fixed unused variable warnings
- All ESLint checks passing (0 errors, 0 warnings)
- TypeScript compilation clean
## Bug Fixes:
- Group drag positions now properly persisted to store
- Minimized group styling (removed dotted border, padding)
- Node visibility using 'hidden' property instead of array filtering
- Dimension sync prevents actors from disappearing on maximize
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements visual grouping of actors with context menu operations,
resizable containers, and complete history tracking integration.
Features:
- Create groups from multiple selected actors via context menu
- Groups visualized as resizable containers with child nodes
- Ungroup actors (non-destructive) or delete group with actors
- Right-click context menu with group-specific operations
- Dedicated GroupEditorPanel for group properties
- Smart minimum size constraint based on child node positions
- Full undo/redo support for group operations and resizes
Technical Implementation:
- GroupNode component with React Flow NodeResizer integration
- Atomic createGroupWithActors operation for consistent history
- Parent-child relationship using React Flow v11 parentId pattern
- Groups stored separately from actors in graphStore
- Fixed history tracking to sync graphStore before snapshots
- Resize tracking to prevent state sync loops during interaction
- Dynamic minimum dimensions to keep children inside bounds
- Sanitization of orphaned parentId references on state load
History Fixes:
- pushToHistory now syncs timeline with graphStore before snapshot
- Prevents missing groups/nodes in history states
- Ensures undo/redo correctly restores all graph elements
- Atomic state updates to avoid React Flow processing stale state
Storage & Persistence:
- Groups saved in timeline states and document structure
- Safe JSON serialization to prevent prototype pollution
- Cleanup utilities for removing __proto__ from localStorage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements a comprehensive label system and completely redesigns all
filtering (labels, actor types, relation types) to use intuitive positive
filtering where empty selection shows all items.
Label System Features:
- Create, edit, delete labels with names, colors, and scope (actors/relations/both)
- Inline editing with click-to-edit UI for quick modifications
- Quick-add label forms in config modals
- Autocomplete label selector with inline label creation
- Label badges rendered on nodes and edges (no overflow limits)
- Full undo/redo support for label operations
- Label validation and cleanup when labels are deleted
- Labels stored per-document in workspace system
Filter System Redesign:
- Changed from negative to positive filtering for all filter types
- Empty selection = show all items (intuitive default)
- Selected items = show only those items (positive filter)
- Consistent behavior across actor types, relation types, and labels
- Clear visual feedback with selection counts and helper text
- Auto-zoom viewport adjustment works for all filter types including labels
Label Cleanup & Validation:
- When label deleted, automatically removed from all nodes/edges across all timeline states
- Label references validated during node/edge updates
- Unknown label IDs filtered out to maintain data integrity
UI Improvements:
- All labels rendered without overflow limits (removed +N more indicators)
- Filter checkboxes start unchecked (select to filter, not hide)
- Helper text explains current filter state
- Selection counts displayed in filter section headers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Creates a new extendable settings store for application-wide preferences
that persist across browser sessions using Zustand's persist middleware.
Architecture:
- New settingsStore.ts using Zustand persist middleware
- Automatic localStorage synchronization
- Versioned schema (v1) for future migrations
- Clean, typed interface for easy extensibility
Changes:
- Created settingsStore for persistent global settings
- Migrated autoZoomEnabled from searchStore to settingsStore
- Updated GraphEditor to use settings store for auto-zoom
- Updated LeftPanel to use settings store for toggle control
- searchStore now focuses on ephemeral filter/search state
Settings Persistence:
- Storage key: 'constellation-settings'
- Automatically saves on any setting change
- Restores settings on page reload
- Same proven pattern as panelStore
Future Extensibility:
The settings store is designed to easily accommodate new settings:
- Theme preferences (light/dark mode)
- Default view options
- User interface preferences
- Any application-wide persistent settings
Benefits:
- User preferences persist across sessions
- Clean separation between ephemeral and persistent state
- Type-safe settings management
- Easy to extend for future features
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements automatic viewport adjustment to focus on filtered nodes when
search or filter criteria change, providing immediate visual feedback.
Features:
- Auto-zoom triggers when search text or type filters are applied
- 300ms debouncing prevents excessive zooming while typing
- Only zooms when some (but not all) nodes match filters
- Smooth 300ms animation with 20% padding and 2.5x max zoom
- Toggle control via compact icon button in search header
- Enabled by default, persists user preference in search store
UI Changes:
- Added CenterFocusStrong icon toggle in LeftPanel search header
- Icon is blue when enabled, gray when disabled
- Tooltip shows current state
- Positioned after "Reset filters" button
Implementation:
- Search store tracks autoZoomEnabled state
- GraphEditor monitors filter changes and calculates matching nodes
- Uses React Flow's fitView() with smart node filtering
- Skips zoom when disabled, no nodes, or no active filters
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removes the upper toolbar and consolidates undo/redo controls into the
Edit menu for a cleaner interface with more screen space.
Changes:
- Move undo/redo buttons from Toolbar to Edit menu with descriptions
- Remove Toolbar component from App layout
- Implement closeAllMenus event system for coordinated menu management
- Add event listeners to close menus when clicking on graph/timeline canvas
- Add cross-menu closing: context menus close menu bar and vice versa
- Fix React warning by deferring event dispatch with setTimeout
Benefits:
- Cleaner UI with more vertical space for graph editor
- Unified menu system prevents multiple menus open simultaneously
- Context menus and menu bar dropdowns coordinate properly
- Consistent UX: clicking anywhere closes open menus
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements a user-friendly dialog that prompts for document names before
creating new documents, replacing the default "Untitled Analysis" behavior.
Features:
- InputDialog component for text input with validation
- useCreateDocument hook to centralize naming logic
- Pre-filled default value "Untitled Analysis"
- Validation to prevent empty document names
- Helpful placeholder text with examples
- Keyboard shortcuts (Enter/Escape)
- Auto-focus and select input field
Updated all document creation entry points:
- Menu Bar: "New Document" and "New from Template"
- Document Tabs: "+" button
- Document Manager: "New Document" button
- Empty State: "New Document" button
- Keyboard shortcut: Ctrl+N
This provides a consistent UX across the application and reduces code
duplication by using a single reusable hook.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds full support for directed, bidirectional, and undirected relationships
between actors with visual arrow markers and intuitive controls.
**Type System:**
- Add EdgeDirectionality type (directed | bidirectional | undirected)
- Add directionality field to RelationData
- Add defaultDirectionality field to EdgeTypeConfig
**Visual Representation:**
- Directed edges: single arrow marker at target (→)
- Bidirectional edges: arrow markers at both ends (↔)
- Undirected edges: no arrow markers (—)
- Separate marker definitions for start/end to ensure correct orientation
**Property Panel Controls:**
- MUI ToggleButtonGroup for selecting directionality
- Visual connection indicator with directional symbols
- Reverse direction button (swaps source/target, only for directed edges)
- Live updates with 500ms debounce
**Edge Type Configuration:**
- Default directionality selector in edge type form
- Dropdown with helpful descriptions (→, ↔, —)
- Applied to both create and edit workflows
**Edge Creation:**
- New edges inherit defaultDirectionality from edge type config
- Falls back to 'directed' for backwards compatibility
**Reverse Direction:**
- Swaps source/target and sourceHandle/targetHandle
- Maintains edge ID and selection state
- Tracked in undo/redo history
Includes comprehensive UX specification document with wireframes,
interaction patterns, accessibility guidelines, and implementation phases.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements section 6.1 from UX_ANALYSIS.md - Graph Metrics and Analysis.
Transforms the empty "No Selection" state in the right panel into a
valuable analysis dashboard.
Features:
- Graph analysis utility with metric calculations:
- Actor/relation counts
- Graph density (connectivity ratio)
- Average connections per actor
- Most connected actors (top 5)
- Isolated actors count
- Connected components detection
- Breakdown by actor/relation type
- GraphMetrics component with sections:
- Overview: basic stats and density
- Most Connected Actors: ranked list
- Graph Structure: isolated nodes, components
- Type breakdowns: actors and relations by type
- Visual polish: icons, tooltips, hover states
- Warning highlights for isolated actors
- Info highlights for multiple components
- Integration:
- Replaces empty state in RightPanel
- Automatically updates when graph changes
- Memoized calculations for performance
- Consistent styling with existing panels
Now provides immediate analytical value when opening a document,
making the application live up to its "Analyzer" name.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements toast notifications from UX_ANALYSIS.md to provide clear,
non-intrusive feedback for user actions.
Features:
- Toast store with Zustand for global state management
- Four toast variants: success, error, info, warning
- Auto-dismiss after configurable duration (default 4s)
- Max 3 visible toasts with FIFO queue
- Smart positioning that avoids right panel overlap
- Smooth slide-in/fade-out animations
Notifications added for:
- File operations (import/export success/errors)
- Document operations (create/delete/rename/duplicate)
- Workspace operations (import/export)
Toast container dynamically repositions based on right panel state
to ensure toasts never overlap with critical UI elements.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented image export functionality using html-to-image that actually
slaps. No more bloated screenshots with miles of blank space - this baby
wraps your graph tighter than shrink wrap on a fresh deck.
Features:
- PNG export with proper 300 DPI quality (4x pixelRatio)
- SVG vector export for infinite scaling
- Smart bounds calculation that hugs your nodes
- Configurable padding (default: 10px of breathing room)
- Accessible via File menu
Technical highlights:
- Direct transform calculation instead of getViewportForBounds bloat
- Proper pixelRatio handling (not that 16x scaling nonsense)
- Based on React Flow's official pattern but actually optimized
- Uses html-to-image@1.11.11 (newer versions are broken)
Export quality goes hard. Print-ready PNGs. Crisp. Clean. Chef's kiss.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed TypeScript error where onAddNodeRequest callback parameter type
was incorrectly defined. The prop expects a function that receives a
callback, not the node type ID directly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace TypeScript 'any' types with explicit function signatures to fix ESLint errors. This improves type safety for the onAddNodeRequest callback.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed the "Edit Properties" option from both node and edge context menus,
leaving only the "Delete" option. Users can simply click on nodes/edges to
select them and edit properties in the right panel, making the context menu
option redundant.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed selection state inconsistency when adding nodes or edges. Previously,
both the new item and previously selected items would remain selected due to
a race condition between Zustand store updates and ReactFlow state syncing.
Changes:
- Added pendingSelectionRef to track items that should be selected after
Zustand sync completes
- Modified useEffect sync logic to preserve selection state normally, but
apply pending selection when new items are added
- Unified node creation logic between context menu and left panel to ensure
consistent behavior
- When adding nodes/edges, all other items are now properly deselected
The fix ensures selection state lives only in ReactFlow (not Zustand) and
is properly coordinated during store updates.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>