From ebe2d4b49399cc06a46451463d0295265c27c6fa Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Tue, 31 Mar 2026 11:42:32 +0200 Subject: [PATCH] Remove stale planning docs from docs/ folder All files were AI-generated planning artifacts (plans, specs, summaries, proposals) from past development sessions. None are referenced in the codebase or actively maintained. Source code and CLAUDE.md are the authoritative reference. Co-Authored-By: Claude Sonnet 4.6 --- docs/ARCHITECTURE_REVIEW.md | 1477 --------- docs/DIRECTIONAL_RELATIONSHIPS_UX_SPEC.md | 791 ----- docs/EDGE_OVERLAP_UX_PROPOSAL.md | 610 ---- docs/EDGE_OVERLAP_VISUAL_GUIDE.md | 602 ---- docs/IMPLEMENTATION_CHECKLIST.md | 755 ----- docs/KEYBOARD_SHORTCUTS.md | 279 -- docs/MULTI_FILE_PLAN.md | 564 ---- docs/PERSISTENCE_PLAN.md | 339 -- docs/PHASE_1_COMPLETION_SUMMARY.md | 409 --- docs/PHASE_4_1_COMPLETION_SUMMARY.md | 293 -- docs/PHASE_4_1_TEST_PLAN.md | 344 -- docs/PHASE_6_2_STRICT_NULL_CHECKS.md | 127 - docs/PROJECT_SUMMARY.md | 382 --- docs/QUICK_REFERENCE.md | 443 --- docs/SIDE_PANELS_UX_DESIGN.md | 2826 ----------------- docs/STATE_MANAGEMENT_REFACTORING_PLAN.md | 1029 ------ docs/TECHNICAL_SPEC_BIBLIOGRAPHY.md | 1635 ---------- docs/TEMPORAL_ANALYSIS_SUMMARY.md | 602 ---- docs/TEMPORAL_QUICK_START.md | 462 --- docs/TEMPORAL_SCENARIO_IMPLEMENTATION_PLAN.md | 1311 -------- docs/TEMPORAL_SCENARIO_UX_CONCEPT.md | 1149 ------- docs/UNDO_REDO_IMPLEMENTATION.md | 368 --- docs/UX_ANALYSIS.md | 1469 --------- docs/UX_ANALYSIS_ACTOR_TYPE_SETTINGS.md | 1838 ----------- docs/UX_BIBLIOGRAPHY_DESIGN.md | 638 ---- docs/UX_CONCEPT_ACTOR_GROUPING.md | 912 ------ docs/UX_CONCEPT_MULTI_VERSION_GRAPH.md | 1318 -------- docs/VISUAL_EXAMPLES.md | 916 ------ docs/WORKSPACE_PERSISTENCE.md | 152 - 29 files changed, 24040 deletions(-) delete mode 100644 docs/ARCHITECTURE_REVIEW.md delete mode 100644 docs/DIRECTIONAL_RELATIONSHIPS_UX_SPEC.md delete mode 100644 docs/EDGE_OVERLAP_UX_PROPOSAL.md delete mode 100644 docs/EDGE_OVERLAP_VISUAL_GUIDE.md delete mode 100644 docs/IMPLEMENTATION_CHECKLIST.md delete mode 100644 docs/KEYBOARD_SHORTCUTS.md delete mode 100644 docs/MULTI_FILE_PLAN.md delete mode 100644 docs/PERSISTENCE_PLAN.md delete mode 100644 docs/PHASE_1_COMPLETION_SUMMARY.md delete mode 100644 docs/PHASE_4_1_COMPLETION_SUMMARY.md delete mode 100644 docs/PHASE_4_1_TEST_PLAN.md delete mode 100644 docs/PHASE_6_2_STRICT_NULL_CHECKS.md delete mode 100644 docs/PROJECT_SUMMARY.md delete mode 100644 docs/QUICK_REFERENCE.md delete mode 100644 docs/SIDE_PANELS_UX_DESIGN.md delete mode 100644 docs/STATE_MANAGEMENT_REFACTORING_PLAN.md delete mode 100644 docs/TECHNICAL_SPEC_BIBLIOGRAPHY.md delete mode 100644 docs/TEMPORAL_ANALYSIS_SUMMARY.md delete mode 100644 docs/TEMPORAL_QUICK_START.md delete mode 100644 docs/TEMPORAL_SCENARIO_IMPLEMENTATION_PLAN.md delete mode 100644 docs/TEMPORAL_SCENARIO_UX_CONCEPT.md delete mode 100644 docs/UNDO_REDO_IMPLEMENTATION.md delete mode 100644 docs/UX_ANALYSIS.md delete mode 100644 docs/UX_ANALYSIS_ACTOR_TYPE_SETTINGS.md delete mode 100644 docs/UX_BIBLIOGRAPHY_DESIGN.md delete mode 100644 docs/UX_CONCEPT_ACTOR_GROUPING.md delete mode 100644 docs/UX_CONCEPT_MULTI_VERSION_GRAPH.md delete mode 100644 docs/VISUAL_EXAMPLES.md delete mode 100644 docs/WORKSPACE_PERSISTENCE.md diff --git a/docs/ARCHITECTURE_REVIEW.md b/docs/ARCHITECTURE_REVIEW.md deleted file mode 100644 index 5d14f29..0000000 --- a/docs/ARCHITECTURE_REVIEW.md +++ /dev/null @@ -1,1477 +0,0 @@ -# Constellation Analyzer - Architectural Code Review Report - -## Executive Summary - -**Project**: Constellation Analyzer (React-based visual graph editor) -**Codebase Size**: ~21,616 lines of TypeScript/TSX across 114 files -**Architecture Rating**: **B+ (Very Good)** -**Test Coverage**: **Not implemented** (0 test files) -**Review Date**: 2025-10-22 -**Git Commit**: `60d13ed` - -This is a sophisticated React-based graph visualization application with a mature, well-architected codebase. The project demonstrates excellent architectural patterns including clean separation of concerns, comprehensive state management, and thoughtful handling of complex document/history management. While generated by AI (as noted in README), the code quality is surprisingly high and follows industry best practices. - -### Key Metrics -- **Stores**: 10 Zustand stores (graphStore, workspaceStore, historyStore, timelineStore, etc.) -- **Components**: 66 component files organized in 12 subdirectories -- **Hooks**: 8 custom hooks for business logic abstraction -- **Major Store Files**: 2,467 lines (workspaceStore: 1,506 lines, graphStore: 564 lines, historyStore: 399 lines) -- **Technology Stack**: React 18.2, TypeScript 5.2, Vite 5.1, React Flow 12.3, Zustand 4.5, Material-UI 5.15 - ---- - -## 1. Overall Architecture Assessment - -### Architecture Pattern: **Feature-Based Layered Architecture** - -The application follows a well-structured layered architecture with clear separation: - -``` -┌─────────────────────────────────────────────────────────┐ -│ Presentation Layer │ -│ (Components: Editor, Nodes, Edges, Panels, Config) │ -└─────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────┐ -│ Business Logic Layer │ -│ (Hooks: useGraphWithHistory, useDocumentHistory) │ -└─────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────┐ -│ State Management Layer │ -│ (Zustand Stores: graphStore, workspaceStore, etc.) │ -└─────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────┐ -│ Persistence Layer │ -│ (localStorage, file I/O, serialization) │ -└─────────────────────────────────────────────────────────┘ -``` - -**Strengths:** -- Clear separation of concerns across layers -- Well-defined data flow from UI → Hooks → Stores → Persistence -- Proper abstraction of React Flow complexity behind custom components -- Clean type definitions in dedicated `/types` directory - ---- - -## 2. Key Architectural Strengths - -### 2.1 Sophisticated Multi-Document Workspace System ⭐⭐⭐⭐⭐ - -**Location**: `/src/stores/workspaceStore.ts` (1,506 lines) - -The workspace management is exceptionally well-designed: - -```typescript -interface Workspace { - workspaceId: string; - workspaceName: string; - documents: Map; - documentMetadata: Map; - documentOrder: string[]; - activeDocumentId: string | null; - settings: WorkspaceSettings; -} -``` - -**Architectural Highlights:** -- **Lazy document loading**: Documents loaded on-demand, unloaded when inactive -- **Tab-based interface**: Full multi-document editing with tab management -- **Atomic transactions**: Type operations use transaction pattern with automatic rollback -- **Export/Import**: Comprehensive ZIP-based workspace import/export -- **Per-document dirty tracking**: Unsaved changes tracked independently per document - -**Code Quality Example** - Transaction Pattern for Safety: -```typescript -executeTypeTransaction: ( - operation: () => T, - rollback: () => void, - operationName: string -): T | null => { - try { - const result = operation(); - return result; - } catch (error) { - console.error(`${operationName} failed:`, error); - try { - rollback(); - console.log(`Rolled back ${operationName}`); - } catch (rollbackError) { - console.error(`Rollback failed for ${operationName}:`, rollbackError); - } - // Show user-friendly error - useToastStore.getState().showToast( - `Failed to ${operationName}: ${errorMessage}`, - 'error' - ); - return null; - } -} -``` - -This transaction pattern protects against localStorage quota errors and ensures data consistency. - -### 2.2 Advanced Undo/Redo with Timeline Support ⭐⭐⭐⭐⭐ - -**Location**: `/src/stores/historyStore.ts` (399 lines) - -The history system is remarkably sophisticated: - -- **Per-document history**: Each document maintains independent undo/redo stacks (max 50 actions) -- **Complete document snapshots**: Captures entire document state (timeline + types + labels + graph) -- **Timeline integration**: Undo/redo works across both graph edits AND timeline state switches -- **Deep serialization**: Properly handles Map serialization/deserialization - -```typescript -export interface DocumentSnapshot { - timeline: { - states: Map; - currentStateId: StateId; - rootStateId: StateId; - }; - nodeTypes: NodeTypeConfig[]; - edgeTypes: EdgeTypeConfig[]; - labels: LabelConfig[]; -} -``` - -**Critical Implementation Detail** - Atomic State Restoration: -```typescript -flushSync(() => { - loadGraphState({ - nodes: currentState.graph.nodes, - edges: currentState.graph.edges, - groups: currentState.graph.groups || [], - nodeTypes: restoredState.nodeTypes, - edgeTypes: restoredState.edgeTypes, - labels: restoredState.labels || [], - }); -}); -``` - -Using React's `flushSync` ensures atomic state updates, preventing React Flow from processing intermediate states with dangling references. - -### 2.3 History-Tracked Operations Pattern ⭐⭐⭐⭐⭐ - -**Location**: `/src/hooks/useGraphWithHistory.ts` - -Excellent architectural pattern to prevent history bypass: - -```typescript -/** - * ✅ USE THIS HOOK FOR ALL GRAPH MUTATIONS IN COMPONENTS ✅ - * - * ⚠️ IMPORTANT: Always use this hook instead of `useGraphStore()` directly - */ -export function useGraphWithHistory() { - const graphStore = useGraphStore(); - const { pushToHistory } = useDocumentHistory(); - - const addNode = useCallback((node: Actor) => { - graphStore.addNode(node); - scheduleHistoryPush('Add Node', 0); // Immediate history tracking - }, [graphStore, scheduleHistoryPush]); - - // ... all mutations wrapped with history tracking -} -``` - -**Benefits:** -- Enforces history tracking through API design -- Debounced history for drag operations (300ms delay) -- Immediate history for discrete actions (add/delete) -- Prevents recursive history pushes during undo/redo - -### 2.4 Timeline/Branching State System ⭐⭐⭐⭐ - -**Location**: `/src/stores/timelineStore.ts`, `/src/types/timeline.ts` - -Sophisticated branching timeline system: - -```typescript -export interface ConstellationState { - id: StateId; - label: string; - description?: string; - parentStateId?: string; // Enables branching - graph: { - nodes: SerializedActor[]; - edges: SerializedRelation[]; - groups?: SerializedGroup[]; - }; - metadata?: { - date?: string; - tags?: string[]; - color?: string; - notes?: string; - }; - createdAt: string; - updatedAt: string; -} -``` - -**Architectural Excellence:** -- **Branching support**: States can have multiple children (alternative scenarios) -- **Graph snapshots**: Each state stores complete graph snapshot -- **Global types**: Node/edge types shared across all timeline states -- **Metadata flexibility**: Optional metadata for dates, tags, notes - -### 2.5 Security-Conscious Design ⭐⭐⭐⭐ - -**Location**: `/src/utils/safeStringify.ts`, `/src/utils/cleanupStorage.ts` - -The codebase shows security awareness: - -```typescript -// safeStringify.ts - Prototype pollution prevention -export function safeStringify(obj: unknown): string { - return JSON.stringify(obj, (key, value) => { - // SECURITY: Filter out __proto__ and other dangerous properties - if (key === '__proto__' || key === 'constructor' || key === 'prototype') { - return undefined; - } - return value; - }); -} - -// cleanupStorage.ts - Automatic cleanup on startup -if (needsStorageCleanup()) { - console.log('[Security] Cleaning up localStorage to remove __proto__ attributes...'); - const { cleaned, errors } = cleanupAllStorage(); -} -``` - -**Security Measures:** -- **Prototype pollution prevention**: Filters `__proto__`, `constructor`, `prototype` during serialization -- **Automatic cleanup**: Scans and repairs existing localStorage on startup -- **Safe parsing**: Custom JSON parser that strips dangerous properties -- **No dangerous patterns**: No use of `dangerouslySetInnerHTML`, `eval()`, or `innerHTML` - -### 2.6 Type Safety and TypeScript Usage ⭐⭐⭐⭐⭐ - -**Location**: `/src/types/index.ts`, `/src/types/timeline.ts`, `/src/types/bibliography.ts` - -Excellent TypeScript usage: - -```typescript -// Proper use of discriminated unions -export type EdgeDirectionality = 'directed' | 'bidirectional' | 'undirected'; -export type NodeShape = 'rectangle' | 'circle' | 'roundedRectangle' | 'ellipse' | 'pill'; -export type LabelScope = 'actors' | 'relations' | 'both'; - -// Strong typing with branded types (using Node, Edge) -export interface ActorData extends Record { - label: string; - type: string; - description?: string; - labels?: string[]; - citations?: string[]; - metadata?: Record; -} -export type Actor = Node; // React Flow branded type - -// Comprehensive action interfaces -export interface GraphActions { - addNode: (node: Actor) => void; - updateNode: (id: string, updates: Partial) => void; - // ... 20+ well-typed operations -} -``` - -**TypeScript Configuration** (`tsconfig.json`): -- `strict: true` - Full strict mode enabled -- `noUnusedLocals: true` - Catches unused variables -- `noUnusedParameters: true` - Catches unused parameters -- `noFallthroughCasesInSwitch: true` - Prevents switch fallthrough bugs - -### 2.7 Clean Component Organization ⭐⭐⭐⭐ - -**Component Directory Structure:** -``` -src/components/ -├── Common/ # Reusable UI primitives (Toast, Dialog, EmptyState) -├── Config/ # Configuration panels (NodeType, EdgeType, Labels) -├── Editor/ # Graph editor core (GraphEditor, ContextMenu) -├── Edges/ # Custom edge rendering -├── Menu/ # Application menu bar -├── Nodes/ # Custom node rendering -│ └── Shapes/ # Node shape variants (Circle, Rectangle, etc.) -├── Panels/ # Side/bottom panels (LeftPanel, RightPanel, GraphAnalysisPanel) -├── Timeline/ # Timeline UI (BottomPanel, CreateStateDialog) -├── Toolbar/ # Editor toolbar -└── Workspace/ # Document management (DocumentTabs, DocumentManager) -``` - -**Component Quality:** -- **Single Responsibility**: Each component has clear, focused purpose -- **Prop interfaces**: All components have well-defined TypeScript interfaces -- **Presentation/Container separation**: Logic in hooks, UI in components -- **Consistent naming**: Clear, descriptive component names - ---- - -## 3. Component Architecture Analysis - -### 3.1 Main Application Structure - -**File**: `/src/App.tsx` (305 lines) - -Well-structured root component: - -```typescript -function App() { - return ( - - - - - - ); -} - -function AppContent() { - // Layout: Header → MenuBar → DocumentTabs → Main (Left + Editor + Right) + Bottom - // Clean separation of concerns - // Proper context usage (ReactFlow, KeyboardShortcuts) - // Ref forwarding for imperative APIs (leftPanelRef.focusSearch()) -} -``` - -**Strengths:** -- Proper context provider nesting -- Separation of provider wrapper from content component (enables useReactFlow) -- Ref forwarding for cross-component communication -- Clear visual layout hierarchy - -### 3.2 Graph Editor Component - -**File**: `/src/components/Editor/GraphEditor.tsx` - -Core visualization component integrating React Flow: - -**Architecture Highlights:** -- **Custom Node Types**: `CustomNode`, `GroupNode` with custom rendering -- **Custom Edge Type**: `CustomEdge` with directional styling -- **Viewport Persistence**: Saves/restores viewport per document -- **Auto-zoom on Search**: Intelligent focus on filtered nodes -- **Context Menu**: Right-click operations on canvas/nodes/edges -- **Selection Management**: Single and multi-selection support - -**Good Pattern** - Memoized Node Array: -```typescript -const allNodes = useMemo(() => { - const minimizedGroupIds = new Set( - storeGroups.filter((group) => group.data.minimized).map((group) => group.id) - ); - - const visibleNodes = storeNodes.map((node) => { - const nodeWithParent = node as Actor & { parentId?: string }; - if (nodeWithParent.parentId && minimizedGroupIds.has(nodeWithParent.parentId)) { - return { ...node, hidden: true }; // Don't filter, just hide - } - return node; - }); - - // IMPORTANT: Groups MUST appear before child nodes - return [...storeGroups, ...visibleNodes]; -}, [storeNodes, storeGroups]); -``` - -Proper handling of React Flow's parent-child requirements. - ---- - -## 4. State Management Architecture - -### 4.1 Store Organization ⭐⭐⭐⭐⭐ - -**10 Well-Designed Zustand Stores:** - -1. **graphStore** (564 lines) - Core graph state (nodes, edges, groups, types) -2. **workspaceStore** (1,506 lines) - Multi-document workspace management -3. **historyStore** (399 lines) - Per-document undo/redo -4. **timelineStore** - Timeline/branching states -5. **bibliographyStore** - Citation management (citation.js integration) -6. **editorStore** - Editor settings (grid, snap, zoom) -7. **panelStore** - UI panel visibility -8. **searchStore** - Search/filter state -9. **settingsStore** - Application settings -10. **toastStore** - Toast notifications - -**Architecture Pattern:** -```typescript -// ⚠️ IMPORTANT: DO NOT USE THIS STORE DIRECTLY IN COMPONENTS ⚠️ -// Use useGraphWithHistory() to ensure undo/redo history is tracked - -interface GraphStore { - nodes: Actor[]; - edges: Relation[]; - groups: Group[]; - nodeTypes: NodeTypeConfig[]; - edgeTypes: EdgeTypeConfig[]; - labels: LabelConfig[]; -} -``` - -**Warning Comments**: Excellent use of warning comments to prevent misuse. - -### 4.2 State Synchronization Points - -The codebase has **4 critical state synchronization points** (well-documented): - -**SYNC POINT 1**: `document → graphStore` (Document load) -- When switching documents, loads document's graph into graphStore - -**SYNC POINT 2**: `graphStore → timeline.currentState` (Graph edits) -- After every graph mutation, updates current timeline state - -**SYNC POINT 3**: `timeline → document` (Document save) -- When saving, serializes complete timeline to document structure - -**SYNC POINT 4**: `document types → graphStore` (Type management) -- When modifying types/labels, syncs from document to graphStore if active - -**Excellent Documentation**: -```typescript -/** - * ═══════════════════════════════════════════════════════════════════════════ - * SYNC POINT 3: timeline → document - * ═══════════════════════════════════════════════════════════════════════════ - * - * When: Document save (auto-save or manual) - * What: Serializes entire timeline (all states) to document.timeline - * Source of Truth: timelineStore (transient working copy) - * Direction: timelineStore → document.timeline → localStorage - */ -``` - -This level of architectural documentation is exceptional. - -### 4.3 Data Flow Architecture - -**Unidirectional Data Flow:** - -``` -User Action - ↓ -Component Event Handler - ↓ -useGraphWithHistory() Hook - ↓ -graphStore Mutation - ↓ -Timeline State Update - ↓ -History Snapshot (if tracked) - ↓ -Auto-Save (1 second delay) - ↓ -localStorage Persistence -``` - -Clean, predictable data flow with proper separation at each layer. - ---- - -## 5. Hook Architecture - -### 5.1 Custom Hooks ⭐⭐⭐⭐ - -**8 Well-Designed Hooks:** - -1. **useGraphWithHistory** - Wraps graph operations with history tracking -2. **useDocumentHistory** - Provides undo/redo for active document -3. **useGlobalShortcuts** - Global keyboard shortcut manager -4. **useKeyboardShortcuts** - Component-level keyboard shortcuts -5. **useBibliographyWithHistory** - Bibliography operations with undo/redo -6. **useGraphExport** - PNG/SVG export functionality -7. **useConfirm** - Confirmation dialog hook -8. **useCreateDocument** - Document creation workflow - -**Hook Pattern Example** - Proper Encapsulation: -```typescript -export function useDocumentHistory() { - const activeDocumentId = useWorkspaceStore((state) => state.activeDocumentId); - const markDocumentDirty = useWorkspaceStore((state) => state.markDocumentDirty); - const loadGraphState = useGraphStore((state) => state.loadGraphState); - const historyStore = useHistoryStore(); - - // Initialize history for active document - useEffect(() => { - if (!activeDocumentId) return; - const history = historyStore.histories.get(activeDocumentId); - if (!history) { - historyStore.initializeHistory(activeDocumentId); - } - }, [activeDocumentId, historyStore]); - - return { undo, redo, canUndo, canRedo, pushToHistory }; -} -``` - -Proper use of selectors, effect cleanup, and memoization. - ---- - -## 6. Critical Issues and Concerns - -### 6.1 ⚠️ HIGH PRIORITY: Zero Test Coverage - -**Severity**: **CRITICAL** -**Impact**: **HIGH** - -**Issue**: No unit tests, integration tests, or E2E tests found (0 test files). - -**Risks**: -- Complex history/undo logic untested -- State synchronization bugs may go undetected -- Refactoring is risky without test safety net -- Timeline branching logic has no automated validation - -**Recommended Test Coverage**: - -```typescript -// Priority 1: Core State Management -describe('graphStore', () => { - it('should add node with proper validation'); - it('should cascade delete edges when node deleted'); - it('should handle orphaned parentId references'); -}); - -describe('historyStore', () => { - it('should maintain independent history per document'); - it('should properly serialize/deserialize Maps'); - it('should enforce max history size of 50'); - it('should handle undo/redo with timeline state switches'); -}); - -describe('workspaceStore', () => { - it('should rollback on localStorage quota error'); - it('should handle document deletion with unsaved changes'); - it('should lazy load documents on demand'); -}); - -// Priority 2: Critical Hooks -describe('useGraphWithHistory', () => { - it('should prevent recursive history pushes during undo'); - it('should debounce rapid drag operations'); - it('should track immediate history for discrete actions'); -}); - -// Priority 3: Complex Business Logic -describe('Timeline branching', () => { - it('should create branching states correctly'); - it('should handle state deletion with children'); - it('should preserve parent-child relationships'); -}); - -// Priority 4: Edge Cases -describe('Security', () => { - it('should strip __proto__ during serialization'); - it('should clean up existing localStorage on init'); -}); -``` - -**Test Framework Recommendation**: Vitest (already using Vite) + React Testing Library - ---- - -### 6.2 ⚠️ MEDIUM PRIORITY: Store Complexity and Size - -**Severity**: **MEDIUM** -**Impact**: **MAINTAINABILITY** - -**Issue**: `workspaceStore.ts` is 1,506 lines - too large for single responsibility. - -**Code Smell**: -```typescript -// workspaceStore.ts handles: -// 1. Workspace lifecycle (create, load, clear) -// 2. Document CRUD (create, delete, duplicate, rename) -// 3. Document I/O (import/export, ZIP operations) -// 4. Type management (add/update/delete node/edge types, labels) -// 5. Transaction management (executeTypeTransaction) -// 6. Viewport persistence -// 7. Auto-save coordination -``` - -**Refactoring Recommendation**: - -```typescript -// Current: workspaceStore.ts (1,506 lines) -// Proposed: Split into focused modules - -src/stores/workspace/ -├── workspaceStore.ts // Core workspace state (200 lines) -├── documentOperations.ts // CRUD operations (300 lines) -├── documentIO.ts // Import/export (200 lines) -├── typeManagement.ts // Type configuration (400 lines) -├── transactionManager.ts // Transaction pattern (100 lines) -└── persistence.ts // Already separate ✓ -``` - -**Benefits**: -- Easier to test individual modules -- Clearer separation of concerns -- Reduced cognitive load when making changes -- Better code organization - ---- - -### 6.3 ⚠️ MEDIUM PRIORITY: Performance - Unnecessary Re-renders - -**Severity**: **MEDIUM** -**Impact**: **PERFORMANCE** - -**Issue**: Some components may re-render unnecessarily due to Zustand selector patterns. - -**Anti-Pattern Found**: -```typescript -// ❌ Causes re-render on ANY workspace state change -const workspaceStore = useWorkspaceStore(); -const activeDocId = workspaceStore.activeDocumentId; - -// ✅ Better: Selective subscription -const activeDocId = useWorkspaceStore((state) => state.activeDocumentId); -``` - -**Recommendation**: Audit components for proper Zustand selector usage. - -**Tools**: Use React DevTools Profiler to identify hot components. - ---- - -### 6.4 ⚠️ LOW PRIORITY: Error Handling Consistency - -**Severity**: **LOW** -**Impact**: **USER EXPERIENCE** - -**Issue**: Error handling is inconsistent across the codebase. - -**Patterns Found**: - -```typescript -// Pattern 1: Try-catch with toast (✓ Good) -try { - await exportWorkspaceToZip(...); - useToastStore.getState().showToast('Workspace exported successfully', 'success'); -} catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error'; - useToastStore.getState().showToast(`Failed to export: ${message}`, 'error', 5000); -} - -// Pattern 2: Console error only (✗ User not informed) -} catch (error) { - console.error('Failed to load document:', error); - // No user notification -} - -// Pattern 3: Window.confirm for errors (✗ Poor UX) -if (!confirmed) return false; -``` - -**Recommendation**: Implement consistent error boundary and error handling strategy. - -```typescript -// Centralized error handler -class AppError extends Error { - constructor( - message: string, - public readonly userMessage: string, - public readonly severity: 'error' | 'warning' | 'info' - ) { - super(message); - } -} - -// Usage -throw new AppError( - 'localStorage.setItem failed: QuotaExceededError', - 'Storage is full. Please delete some documents to free up space.', - 'error' -); -``` - ---- - -### 6.5 ⚠️ LOW PRIORITY: localStorage Quota Management - -**Severity**: **LOW** -**Impact**: **EDGE CASES** - -**Issue**: While transaction rollback handles quota errors, there's no proactive quota monitoring. - -**Current Handling**: -```typescript -executeTypeTransaction: (operation: () => T, rollback: () => void) => { - try { - const result = operation(); - return result; - } catch (error) { - // Catches QuotaExceededError, rolls back - rollback(); - useToastStore.getState().showToast('Operation failed', 'error'); - return null; - } -} -``` - -**Enhancement Recommendation**: -```typescript -// Add quota monitoring -export function getLocalStorageUsage(): { used: number; available: number; percentage: number } { - let used = 0; - for (let key in localStorage) { - if (localStorage.hasOwnProperty(key)) { - used += localStorage[key].length + key.length; - } - } - - // Typical browser limit: 5-10MB - const available = 10 * 1024 * 1024; // 10MB - const percentage = (used / available) * 100; - - return { used, available, percentage }; -} - -// Warn user when approaching limit -if (usage.percentage > 80) { - showToast('Storage almost full. Consider exporting and deleting old documents.', 'warning'); -} -``` - ---- - -### 6.6 ⚠️ LOW PRIORITY: Missing Input Validation - -**Severity**: **LOW** -**Impact**: **DATA INTEGRITY** - -**Issue**: Limited validation on user inputs (node labels, document titles, etc.) - -**Example Vulnerability**: -```typescript -// No validation on document title -createDocument: (title = 'Untitled Analysis') => { - // What if title is extremely long? Contains special characters? - newDoc.metadata.title = title; - saveDocumentToStorage(documentId, newDoc); -} -``` - -**Recommendation**: Add Zod or Yup schema validation. - -```typescript -import { z } from 'zod'; - -const DocumentTitleSchema = z - .string() - .min(1, 'Title cannot be empty') - .max(100, 'Title too long') - .regex(/^[a-zA-Z0-9\s\-_()]+$/, 'Invalid characters in title'); - -const NodeLabelSchema = z - .string() - .min(1) - .max(50); -``` - ---- - -## 7. Performance Considerations - -### 7.1 ⚠️ Large Document Performance ⭐⭐⭐ - -**Current State**: React Flow handles large graphs well, but some concerns: - -1. **Timeline State Storage**: Each timeline state stores complete graph snapshot - - For document with 100 nodes × 10 timeline states = 1000 serialized nodes - - Memory footprint could be large - -2. **History Snapshots**: Each undo action stores complete document - - 50 undo actions × large document = significant memory usage - -**Recommendation**: Implement lazy loading or differential snapshots for large documents. - -```typescript -// Instead of storing complete graphs: -interface ConstellationState { - graph: { - nodes: SerializedActor[]; // Full snapshot (current) - edges: SerializedRelation[]; - }; -} - -// Consider delta-based approach: -interface ConstellationState { - basedOn?: StateId; // Parent state ID - diff: { // Only store changes from parent - addedNodes: SerializedActor[]; - removedNodeIds: string[]; - modifiedNodes: Partial[]; - // ... - }; -} -``` - -### 7.2 ✅ Good: Memoization Usage - -**Well-implemented performance optimizations**: - -```typescript -// Proper useMemo for expensive computations -const allNodes = useMemo(() => { - return [...storeGroups, ...storeNodes]; -}, [storeNodes, storeGroups]); - -// useCallback for event handlers -const handleAddNode = useCallback((nodeTypeId: string) => { - // ... -}, [dependencies]); - -// Zustand selector optimization -const activeDocId = useWorkspaceStore((state) => state.activeDocumentId); // Only re-renders when this value changes -``` - ---- - -## 8. Security Assessment ⭐⭐⭐⭐ - -### 8.1 ✅ Excellent: Prototype Pollution Prevention - -The codebase demonstrates security awareness: - -**File**: `/src/utils/safeStringify.ts` -```typescript -export function safeStringify(obj: unknown): string { - return JSON.stringify(obj, (key, value) => { - if (key === '__proto__' || key === 'constructor' || key === 'prototype') { - return undefined; // Strip dangerous properties - } - return value; - }); -} -``` - -**File**: `/src/utils/cleanupStorage.ts` -```typescript -// Automatic cleanup on app startup -if (needsStorageCleanup()) { - console.log('[Security] Cleaning up localStorage...'); - cleanupAllStorage(); -} -``` - -**Security Measures**: -- ✅ No `dangerouslySetInnerHTML` usage found -- ✅ No `eval()` or `Function()` constructor usage -- ✅ No `innerHTML` manipulation -- ✅ Prototype pollution prevention in JSON serialization -- ✅ Automatic cleanup of existing vulnerable data - -### 8.2 ⚠️ Minor: XSS Risk in User-Generated Content - -**Potential Risk**: Node labels and descriptions are user-entered and rendered. - -**Current Rendering** (likely safe with React's auto-escaping): -```typescript -
{data.label}
-
{data.description}
-``` - -**Recommendation**: Explicitly sanitize if HTML rendering is ever added. - -```typescript -import DOMPurify from 'dompurify'; - -// If ever rendering HTML (currently not needed): -
-``` - ---- - -## 9. TypeScript Quality Assessment ⭐⭐⭐⭐⭐ - -### 9.1 Excellent Type Safety - -**tsconfig.json Configuration**: -```json -{ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true -} -``` - -**Type Definitions Quality**: - -```typescript -// ✅ Discriminated unions -export type EdgeDirectionality = 'directed' | 'bidirectional' | 'undirected'; - -// ✅ Generic constraints -export interface GraphActions { - updateNodeType: (id: string, updates: Partial>) => void; -} - -// ✅ Branded types (React Flow integration) -export type Actor = Node; -export type Relation = Edge; - -// ✅ Utility type usage -export interface ConstellationState { - graph: { - nodes: SerializedActor[]; - edges: SerializedRelation[]; - groups?: SerializedGroup[]; // Optional for backward compatibility - }; -} -``` - -### 9.2 ⚠️ Minor: Some Type Assertions - -**Found Some Type Assertions**: -```typescript -// Could be stronger typed -const nodeWithParent = node as Actor & { parentId?: string; extent?: 'parent' }; - -// @ts-expect-error usage (acceptable for citation.js without types) -// @ts-expect-error - citation.js doesn't have TypeScript definitions -import { Cite } from "@citation-js/core"; -``` - -**Recommendation**: Consider creating proper type definitions for React Flow parent extensions. - -```typescript -// Define extended types -export interface ActorWithParent extends Actor { - parentId?: string; - extent?: 'parent'; -} - -// Use in code -const nodeWithParent = node as ActorWithParent; -``` - ---- - -## 10. Best Practices Adherence - -### 10.1 ✅ Excellent Adherence - -1. **✅ Single Responsibility Principle**: Most components/stores have clear, focused purpose -2. **✅ DRY (Don't Repeat Yourself)**: Common logic abstracted into hooks -3. **✅ Separation of Concerns**: Clear layer separation (UI, hooks, stores, persistence) -4. **✅ Composition Over Inheritance**: React composition pattern used throughout -5. **✅ Explicit Dependencies**: Proper dependency arrays in useEffect/useCallback -6. **✅ Immutable Updates**: Zustand state updates follow immutability -7. **✅ Type Safety**: Strong TypeScript usage with strict mode -8. **✅ Error Boundaries**: Transaction pattern with rollback -9. **✅ Progressive Enhancement**: Features degrade gracefully (optional metadata) - -### 10.2 ⚠️ Could Improve - -1. **⚠️ Code Comments**: While some files have excellent docs, others lack comments -2. **⚠️ Function Length**: Some functions exceed 50-100 lines (e.g., workspaceStore operations) -3. **⚠️ Magic Numbers**: Some hardcoded values (e.g., `300` for debounce delay) - -**Recommendation**: Extract magic numbers to constants. - -```typescript -// Current -debounceTimerRef.current = window.setTimeout(() => { /*...*/ }, 300); - -// Better -const HISTORY_DEBOUNCE_MS = 300; -debounceTimerRef.current = window.setTimeout(() => { /*...*/ }, HISTORY_DEBOUNCE_MS); -``` - ---- - -## 11. Recommended Improvements (Prioritized) - -### Priority 1: Critical (Must Do) - -#### 1.1 Implement Test Coverage ⚠️⚠️⚠️ -**Effort**: HIGH | **Impact**: CRITICAL - -```bash -# Install testing dependencies -npm install -D vitest @testing-library/react @testing-library/jest-dom happy-dom - -# Add to package.json -"scripts": { - "test": "vitest", - "test:ui": "vitest --ui", - "test:coverage": "vitest --coverage" -} -``` - -**Test Suite Roadmap**: -1. Unit tests for stores (graphStore, historyStore, workspaceStore) -2. Integration tests for history/undo system -3. Hook tests (useGraphWithHistory, useDocumentHistory) -4. Component tests for critical UI (GraphEditor, DocumentManager) -5. E2E tests for key workflows (document create → edit → save → undo) - -**Target Coverage**: Aim for 70% code coverage minimum, 90% for critical paths. - ---- - -### Priority 2: High (Should Do) - -#### 2.1 Refactor workspaceStore ⚠️⚠️ -**Effort**: MEDIUM | **Impact**: HIGH (Maintainability) - -Split the 1,506-line file into logical modules: - -```typescript -// src/stores/workspace/ -├── workspaceStore.ts // Core state (200 lines) -├── documentOperations.ts // CRUD (300 lines) -├── documentIO.ts // Import/export (200 lines) -├── typeManagement.ts // Types/labels (400 lines) -└── transactionManager.ts // Transaction pattern (100 lines) -``` - -#### 2.2 Implement Error Boundaries ⚠️⚠️ -**Effort**: LOW | **Impact**: HIGH (UX) - -```typescript -// src/components/Common/ErrorBoundary.tsx -class ErrorBoundary extends React.Component { - componentDidCatch(error: Error, errorInfo: ErrorInfo) { - // Log to monitoring service - console.error('ErrorBoundary caught:', error, errorInfo); - - // Show user-friendly error - useToastStore.getState().showToast( - 'Something went wrong. Please refresh the page.', - 'error' - ); - } - - render() { - if (this.state.hasError) { - return this.setState({ hasError: false })} />; - } - return this.props.children; - } -} - -// Wrap app in boundary - - - -``` - -#### 2.3 Add Input Validation ⚠️⚠️ -**Effort**: MEDIUM | **Impact**: HIGH (Data Integrity) - -```bash -npm install zod -``` - -```typescript -// src/validation/schemas.ts -import { z } from 'zod'; - -export const DocumentTitleSchema = z - .string() - .min(1, 'Title required') - .max(100, 'Title too long') - .regex(/^[a-zA-Z0-9\s\-_()]+$/, 'Invalid characters'); - -export const NodeLabelSchema = z - .string() - .min(1, 'Label required') - .max(50, 'Label too long'); - -// Use in store -createDocument: (title: string) => { - const validated = DocumentTitleSchema.parse(title); - // ... -} -``` - ---- - -### Priority 3: Medium (Nice to Have) - -#### 3.1 Performance Monitoring ⚠️ -**Effort**: LOW | **Impact**: MEDIUM - -```typescript -// Add performance markers -performance.mark('document-save-start'); -saveDocumentToStorage(documentId, doc); -performance.mark('document-save-end'); -performance.measure('document-save', 'document-save-start', 'document-save-end'); - -// Log slow operations -const measure = performance.getEntriesByName('document-save')[0]; -if (measure.duration > 1000) { - console.warn(`Slow save: ${measure.duration}ms for document ${documentId}`); -} -``` - -#### 3.2 localStorage Quota Monitoring ⚠️ -**Effort**: LOW | **Impact**: MEDIUM - -Implement proactive storage usage monitoring (see section 6.5). - -#### 3.3 Bundle Size Optimization ⚠️ -**Effort**: MEDIUM | **Impact**: MEDIUM - -```bash -# Analyze bundle -npm install -D rollup-plugin-visualizer - -# vite.config.ts -import { visualizer } from 'rollup-plugin-visualizer'; - -export default defineConfig({ - plugins: [ - react(), - visualizer({ open: true, gzipSize: true }) - ] -}); -``` - -**Optimization Targets**: -- Tree-shake Material-UI (only import needed icons) -- Code-split bibliography feature (lazy load citation.js) -- Lazy load heavy components (DocumentManager modal) - ---- - -### Priority 4: Low (Future Enhancements) - -#### 4.1 Accessibility (a11y) Audit -**Effort**: MEDIUM | **Impact**: LOW (but important) - -- Add ARIA labels to graph nodes/edges -- Keyboard navigation for graph -- Screen reader support for node properties -- Color contrast validation - -#### 4.2 Performance - Differential Snapshots -**Effort**: HIGH | **Impact**: LOW (only for very large documents) - -See section 7.1 for differential snapshot approach. - -#### 4.3 Documentation -**Effort**: MEDIUM | **Impact**: LOW - -- API documentation (TypeDoc) -- Architecture decision records (ADR) -- User guide / onboarding tutorial - ---- - -## 12. Scalability Assessment - -### Current Scalability: ⭐⭐⭐⭐ - -**Handles Well**: -- ✅ Multiple documents (tested with lazy loading) -- ✅ Medium-sized graphs (100-200 nodes) via React Flow -- ✅ Timeline branching (10-20 states per document) -- ✅ History tracking (50 actions per document) - -**Potential Bottlenecks**: -- ⚠️ Very large graphs (500+ nodes) - React Flow may struggle -- ⚠️ Many timeline states (50+) - memory usage grows -- ⚠️ localStorage limits (5-10MB per domain) - -**Scaling Recommendations**: - -1. **For Large Graphs**: - - Implement virtualization for node list panels - - Consider pagination or clustering for large graphs - - Add "performance mode" that disables animations - -2. **For Many Documents**: - - Implement document archiving (move to IndexedDB) - - Add search/filter in DocumentManager - - Limit open tabs (currently MAX_OPEN_DOCUMENTS = 10) - -3. **For localStorage Limits**: - - Migrate to IndexedDB for larger storage (50MB+) - - Implement compression for stored documents (pako/zlib) - - Add document size indicators in UI - ---- - -## 13. Code Quality Metrics - -### Overall Code Quality: **A-** - -| Metric | Rating | Notes | -|--------|--------|-------| -| **Architecture** | A | Clean layered architecture, well-organized | -| **Type Safety** | A+ | Excellent TypeScript usage, strict mode | -| **State Management** | A | Sophisticated Zustand patterns, clear data flow | -| **Component Design** | A- | Well-structured, some large files | -| **Error Handling** | B | Good transaction pattern, inconsistent elsewhere | -| **Performance** | B+ | Good memoization, some optimization opportunities | -| **Security** | A | Prototype pollution prevention, safe practices | -| **Testing** | F | **Zero tests - critical gap** | -| **Documentation** | B+ | Excellent in-code docs, missing API docs | -| **Maintainability** | B+ | Well-organized, but some files too large | - ---- - -## 14. Technology Stack Assessment - -### Current Stack: ⭐⭐⭐⭐⭐ - -**Excellent Technology Choices**: - -1. **React 18.2** ✅ - - Modern hooks, concurrent features - - Excellent ecosystem - -2. **TypeScript 5.2** ✅ - - Strong type safety - - Latest features (satisfies operator, etc.) - -3. **Vite 5.1** ✅ - - Fast HMR (Hot Module Replacement) - - Optimized production builds - - Better DX than CRA - -4. **React Flow 12.3** ✅ - - Perfect for graph visualization - - Performant with large graphs - - Rich customization API - -5. **Zustand 4.5** ✅ - - Lightweight (< 1KB) - - Simple API, no boilerplate - - Perfect for this use case - -6. **Material-UI 5.15** ✅ - - Rich component library - - Good accessibility - - Consistent design - -7. **Tailwind CSS 3.4** ✅ - - Rapid UI development - - Small production bundle - - Modern utility-first approach - -**No Technology Debt Detected** - All dependencies are modern and well-maintained. - ---- - -## 15. Deployment and DevOps - -### Current State - -**Build Configuration**: ✅ Well-configured -```json -{ - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" - } -} -``` - -**Missing**: -- ⚠️ CI/CD pipeline (GitHub Actions, GitLab CI) -- ⚠️ Automated testing in pipeline -- ⚠️ Deployment automation -- ⚠️ Environment configuration (.env support) - -**Recommendation**: - -```yaml -# .github/workflows/ci.yml -name: CI -on: [push, pull_request] -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 20 - cache: 'npm' - - run: npm ci - - run: npm run lint - - run: npm run test - - run: npm run build -``` - ---- - -## 16. Summary and Final Recommendations - -### Architecture Grade: **B+ (Very Good)** - -This is a **well-architected, production-ready codebase** with sophisticated patterns and excellent TypeScript usage. The multi-document workspace system, undo/redo implementation, and timeline branching demonstrate advanced architectural thinking. - -### Top 5 Action Items - -1. **⚠️⚠️⚠️ CRITICAL: Implement Test Suite** - - Start with unit tests for stores - - Add integration tests for history system - - Target 70% code coverage - -2. **⚠️⚠️ HIGH: Refactor workspaceStore** - - Split into focused modules - - Improve maintainability - -3. **⚠️⚠️ HIGH: Add Error Boundaries** - - Graceful error recovery - - Better user experience - -4. **⚠️ MEDIUM: Input Validation** - - Add Zod schemas - - Prevent data integrity issues - -5. **⚠️ MEDIUM: Performance Monitoring** - - Track slow operations - - Monitor localStorage usage - -### Architectural Strengths to Maintain - -1. **✅ Transaction Pattern**: Keep using atomic operations with rollback -2. **✅ History-Tracked Operations**: Maintain the `useGraphWithHistory` pattern -3. **✅ State Synchronization Documentation**: The "SYNC POINT" comments are excellent -4. **✅ Security Practices**: Continue prototype pollution prevention -5. **✅ Type Safety**: Maintain strict TypeScript configuration - -### Long-Term Vision - -This codebase is well-positioned for growth: -- **Extensible**: Easy to add new node/edge types -- **Maintainable**: Clear architecture, good separation -- **Scalable**: Handles multiple documents, lazy loading -- **Secure**: Security-conscious design - -**With test coverage added**, this would easily be an **A-grade architecture**. - ---- - -## Appendix A: File Structure Overview - -``` -constellation-analyzer/ -├── src/ (21,616 lines TypeScript/TSX) -│ ├── components/ (66 component files) -│ │ ├── Common/ (reusable UI primitives) -│ │ ├── Config/ (configuration panels) -│ │ ├── Editor/ (graph editor core) -│ │ ├── Edges/ (custom edge rendering) -│ │ ├── Menu/ (application menu) -│ │ ├── Nodes/ (custom node rendering) -│ │ ├── Panels/ (side/bottom panels) -│ │ ├── Timeline/ (timeline UI) -│ │ ├── Toolbar/ (editor toolbar) -│ │ └── Workspace/ (document management) -│ ├── stores/ (10 Zustand stores) -│ │ ├── graphStore.ts (564 lines) -│ │ ├── workspaceStore.ts (1,506 lines) -│ │ ├── historyStore.ts (399 lines) -│ │ ├── timelineStore.ts -│ │ ├── bibliographyStore.ts -│ │ ├── editorStore.ts -│ │ ├── panelStore.ts -│ │ ├── searchStore.ts -│ │ ├── settingsStore.ts -│ │ └── toastStore.ts -│ ├── hooks/ (8 custom hooks) -│ │ ├── useGraphWithHistory.ts -│ │ ├── useDocumentHistory.ts -│ │ ├── useGlobalShortcuts.ts -│ │ └── ... -│ ├── types/ (TypeScript definitions) -│ │ ├── index.ts -│ │ ├── timeline.ts -│ │ └── bibliography.ts -│ ├── utils/ (utility functions) -│ │ ├── safeStringify.ts (security) -│ │ ├── cleanupStorage.ts (security) -│ │ ├── nodeUtils.ts -│ │ ├── graphAnalysis.ts -│ │ └── ... -│ ├── contexts/ (React contexts) -│ ├── styles/ (global CSS) -│ ├── App.tsx (root component) -│ └── main.tsx (entry point) -├── public/ (static assets) -├── package.json (dependencies) -├── tsconfig.json (TypeScript config) -├── vite.config.ts (Vite config) -├── tailwind.config.js (Tailwind config) -├── eslint.config.cjs (ESLint config) -└── README.md (documentation) -``` - ---- - -## Appendix B: Dependencies Analysis - -**Production Dependencies** (14 packages): -- @citation-js/core + plugins (5 packages) - Bibliography management -- @emotion/react + styled - Material-UI peer deps -- @mui/icons-material + material - UI components -- @xyflow/react 12.3.5 - Graph visualization ⭐ -- react 18.2 + react-dom - UI framework ⭐ -- zustand 4.5 - State management ⭐ -- html-to-image - Export functionality -- jszip - Workspace export - -**Dev Dependencies** (13 packages): -- @types/react + react-dom - TypeScript definitions -- @typescript-eslint/* - TypeScript linting -- @vitejs/plugin-react - Vite React plugin -- eslint + plugins - Code linting -- typescript 5.2 - Type checking ⭐ -- vite 5.1 - Build tool ⭐ -- tailwindcss + postcss + autoprefixer - Styling - -**Dependency Health**: ✅ All modern, actively maintained packages. No known security vulnerabilities. - ---- - -## Appendix C: Key Files to Review - -**Critical Files** (understand these first): - -1. `/src/stores/workspaceStore.ts` (1,506 lines) - Multi-document workspace -2. `/src/stores/graphStore.ts` (564 lines) - Core graph state -3. `/src/stores/historyStore.ts` (399 lines) - Undo/redo system -4. `/src/hooks/useGraphWithHistory.ts` - History-tracked operations pattern -5. `/src/hooks/useDocumentHistory.ts` - Document-level history management -6. `/src/App.tsx` - Application structure -7. `/src/components/Editor/GraphEditor.tsx` - Core graph editor -8. `/src/types/index.ts` - Type definitions - -**Security-Critical Files**: -1. `/src/utils/safeStringify.ts` - Prototype pollution prevention -2. `/src/utils/cleanupStorage.ts` - Storage security - -**Architecture Documentation**: -1. `/CLAUDE.md` - Project overview -2. `/README.md` - User documentation -3. In-code "SYNC POINT" comments in workspaceStore.ts - ---- - -**Report Generated**: 2025-10-22 -**Reviewer**: Claude Code with code-review-ai:architect-review agent -**Codebase Version**: Git commit `60d13ed` - ---- - -*This architectural review is based on static code analysis and best practices. Runtime behavior testing recommended to validate findings.* diff --git a/docs/DIRECTIONAL_RELATIONSHIPS_UX_SPEC.md b/docs/DIRECTIONAL_RELATIONSHIPS_UX_SPEC.md deleted file mode 100644 index bca0ba9..0000000 --- a/docs/DIRECTIONAL_RELATIONSHIPS_UX_SPEC.md +++ /dev/null @@ -1,791 +0,0 @@ -# Directional Relationships - UX Design Specification - -## Executive Summary - -This document outlines the UX design for implementing directional relationships in Constellation Analyzer. The design focuses on providing clear visual feedback, intuitive controls, and minimal friction for users working with directed, bidirectional, and undirected relationships. - ---- - -## 1. Visual Representation on Graph Canvas - -### 1.1 Arrow Marker Styles - -**Directed Relationships (A → B)** -- Single arrowhead at target end -- Standard SVG marker with solid fill matching edge color -- Marker size: 8x8px (scales with zoom but maintains readability) -- Positioned 6px from target node edge to prevent overlap - -**Bidirectional Relationships (A ↔ B)** -- Arrowheads at both source and target ends -- Same styling as directed arrows -- Both markers use same color as edge - -**Undirected Relationships (A — B)** -- No arrow markers -- Edge styling unchanged from current implementation -- Visual distinction through absence of markers - -### 1.2 Edge Rendering Implementation - -**Current State:** React Flow's BaseEdge with Bezier curves -**Enhancement:** Add markerStart and markerEnd attributes to SVG path - -```typescript -// Marker definitions in SVG defs - - - - - - -// Applied to edge path -markerEnd: directionality === 'directed' || directionality === 'bidirectional' - ? `url(#arrow-${edgeColor})` : undefined -markerStart: directionality === 'bidirectional' - ? `url(#arrow-${edgeColor})` : undefined -``` - -### 1.3 Visual Feedback During Edge Creation - -When user drags to create a new edge: -- Show preview with arrow based on default directionality for selected relation type -- Connection line includes appropriate markers in real-time -- Provides immediate feedback about relationship direction - ---- - -## 2. Property Panel Controls - -### 2.1 Layout Structure (Edge Properties View) - -**Current Layout:** -``` -[Relation Type] ← dropdown -[Custom Label] ← text input -[Connection Info] ← From/To display -[Delete Button] ← action button -``` - -**Enhanced Layout:** -``` -[Relation Type] ← dropdown -[Custom Label] ← text input (optional) - ---- NEW SECTION --- -[Directionality] ← segmented button group (3 options) -[Direction Controls] ← conditional: show reverse button for directed ---- END NEW --- - -[Connection Info] ← From/To display with enhanced directionality info -[Delete Button] ← action button -``` - -### 2.2 Directionality Control Component - -**Component Type:** MUI ToggleButtonGroup (segmented control) - -**Visual Design:** -``` -┌──────────────────────────────────────────────┐ -│ Directionality │ -│ ┌────────┬─────────────┬──────────────┐ │ -│ │ → │ ↔ │ — │ │ -│ │Directed│Bidirectional│ Undirected │ │ -│ └────────┴─────────────┴──────────────┘ │ -└──────────────────────────────────────────────┘ -``` - -**States:** -- Active: Blue background (#2196F3), white icon/text -- Inactive: White background, gray text (#666) -- Hover: Light gray background (#F5F5F5) - -**Accessibility:** -- ARIA labels: "Directed relationship", "Bidirectional relationship", "Undirected relationship" -- Keyboard navigation: Tab to focus, arrow keys to change selection -- Screen reader announces: "Directionality, {current selection}" - -### 2.3 Reverse Direction Control - -**Display Condition:** Only shown when directionality = "directed" - -**Component Type:** IconButton with swap arrow icon - -**Visual Design:** -``` -┌──────────────────────────────────────────────┐ -│ Direction │ -│ ┌──────────────────────────────────────┐ │ -│ │ [Actor A] → [Actor B] [⇄] │ │ -│ └──────────────────────────────────────┘ │ -│ "Swap source and target" │ -└──────────────────────────────────────────────┘ -``` - -**Behavior:** -- Click to swap source and target -- Triggers smooth animation on canvas (optional enhancement) -- Updates connection info display immediately -- Adds to undo/redo history as "Reverse Relation Direction" - -**Accessibility:** -- ARIA label: "Reverse direction, swap source and target" -- Tooltip: "Reverse Direction (R)" - includes keyboard shortcut -- Keyboard shortcut: R (when edge selected) - -### 2.4 Enhanced Connection Info Display - -**Current Display:** -``` -Connection Info -From: actor-1 -To: actor-2 -``` - -**Enhanced Display:** -``` -┌──────────────────────────────────────────────┐ -│ Connection │ -│ │ -│ [Actor A] → [Actor B] [Reverse ⇄] │ -│ │ -│ Source: Actor A │ -│ Target: Actor B │ -└──────────────────────────────────────────────┘ -``` - -For bidirectional: -``` -│ [Actor A] ↔ [Actor B] │ -``` - -For undirected: -``` -│ [Actor A] — [Actor B] │ -``` - -**Visual Enhancements:** -- Actor names are actual labels, not IDs -- Visual arrow indicator matches canvas representation -- Color-coded background for actor badges matching node type colors - ---- - -## 3. Interaction Patterns - -### 3.1 Setting Directionality - -**Primary Flow:** -1. User selects an edge (click on edge) -2. Right panel opens showing edge properties -3. User clicks appropriate directionality button -4. Change applies immediately (debounced 500ms for history) -5. Canvas updates arrow markers in real-time -6. No "Save" button needed (live updates) - -**Default Behavior:** -- New edges inherit default directionality from edge type config -- If not specified, default to "directed" - -### 3.2 Reversing Direction - -**Flow:** -1. User has directed edge selected -2. User clicks reverse button (or presses R key) -3. Source and target swap in state -4. Canvas updates immediately -5. Connection info display updates -6. Action added to undo/redo history - -**Visual Feedback:** -- Brief highlight animation on edge (optional) -- Reverse button shows brief "pressed" state -- Toast notification: "Direction reversed" (optional, may be too noisy) - -### 3.3 Keyboard Shortcuts - -**New Shortcuts:** -- `D` - Set selected edge to Directed -- `B` - Set selected edge to Bidirectional -- `U` - Set selected edge to Undirected -- `R` - Reverse direction (directed edges only) - -**Shortcut Display:** -- Add to keyboard shortcuts help dialog -- Show in tooltips for directionality buttons -- Disabled state when no edge selected - -### 3.4 Bulk Operations (Future Enhancement) - -When multiple edges selected: -- Directionality controls affect all selected edges -- Reverse button reverses all directed edges -- Confirmation dialog for bulk changes (>3 edges) - ---- - -## 4. Edge Type Defaults - -### 4.1 Data Model Extension - -**Current EdgeTypeConfig:** -```typescript -interface EdgeTypeConfig { - id: string; - label: string; - color: string; - style?: 'solid' | 'dashed' | 'dotted'; - description?: string; -} -``` - -**Enhanced EdgeTypeConfig:** -```typescript -interface EdgeTypeConfig { - id: string; - label: string; - color: string; - style?: 'solid' | 'dashed' | 'dotted'; - description?: string; - defaultDirectionality?: 'directed' | 'bidirectional' | 'undirected'; -} -``` - -### 4.2 Edge Type Manager UI Enhancement - -**Location:** Left Panel > Manage Edge Types dialog - -**Addition to Edge Type Form:** -``` -┌──────────────────────────────────────────────┐ -│ Default Directionality │ -│ ┌────────┬─────────────┬──────────────┐ │ -│ │ → │ ↔ │ — │ │ -│ │Directed│Bidirectional│ Undirected │ │ -│ └────────┴─────────────┴──────────────┘ │ -│ │ -│ New relations of this type will use this │ -│ directionality by default │ -└──────────────────────────────────────────────┘ -``` - -**Recommended Defaults:** -- "influences" → Directed -- "collaborates" → Bidirectional -- "related to" → Undirected -- "reports to" → Directed -- "partners with" → Bidirectional - -### 4.3 Smart Defaults Based on Type Name - -System can suggest defaults based on relation type label: -- Words like "leads", "manages", "influences" → Directed -- Words like "collaborates", "partners", "exchanges" → Bidirectional -- Words like "related", "associated", "connected" → Undirected - -Display suggestion in UI: "Suggested: Directed (based on type name)" - ---- - -## 5. Information Display - -### 5.1 Edge Label Display on Canvas - -**Current:** Shows custom label or type label in white box at edge midpoint - -**Enhancement:** Add directional indicator in label (optional) - -``` -┌─────────────────┐ -│ influences → │ ← directed -└─────────────────┘ - -┌─────────────────┐ -│ collaborates ↔ │ ← bidirectional -└─────────────────┘ - -┌─────────────────┐ -│ related to │ ← undirected (no indicator) -└─────────────────┘ -``` - -**Implementation Note:** This is subtle and optional - arrows on edge path may be sufficient. - -### 5.2 Connection List (Actor Properties) - -**Location:** Right Panel > Actor Properties > Connections section - -**Current Display:** -``` -Connections (3) -• influences → actor-2 -• collaborates ← actor-3 -• related to → actor-4 -``` - -**Enhanced Display:** -``` -┌──────────────────────────────────────────┐ -│ Connections (3) │ -│ │ -│ → influences Developer B │ -│ Directed outgoing │ -│ │ -│ ↔ collaborates with Manager A │ -│ Bidirectional │ -│ │ -│ — related to Stakeholder C │ -│ Undirected │ -└──────────────────────────────────────────┘ -``` - -**Visual Elements:** -- Leading icon shows directionality type -- Text indicates direction relative to selected node -- Subtle background color per relation type -- Click to select that edge - -### 5.3 Graph Metrics Panel - -**Location:** Right Panel (when nothing selected) - -**Addition to Metrics:** -``` -┌──────────────────────────────────────────┐ -│ Graph Analysis │ -│ │ -│ Relations │ -│ • Total: 15 │ -│ • Directed: 8 (53%) │ -│ • Bidirectional: 5 (33%) │ -│ • Undirected: 2 (14%) │ -│ │ -│ [existing metrics...] │ -└──────────────────────────────────────────┘ -``` - ---- - -## 6. User Feedback Mechanisms - -### 6.1 Visual Feedback States - -**Edge Selection:** -- Selected edge: Thicker stroke (3px → 4px) -- Selected edge with directionality: Arrows slightly larger -- Hover: Subtle glow effect (box-shadow) - -**Property Panel:** -- Active directionality button: Blue background with animation -- Saving indicator: "Saving changes..." (existing pattern) -- Success: No notification (silent success for live updates) - -### 6.2 Status Indicators - -**Edge Creation:** -- As user drags from node handle, preview edge shows default directionality -- Tooltip near cursor: "Creating [EdgeType] relation →" - -**Edge Modification:** -- Undo toast: "Changed to bidirectional" with undo link -- Error states: "Cannot reverse undirected relation" (if user attempts) - -### 6.3 Help Text - -**Property Panel Help:** -- Directionality section includes info icon (ℹ️) -- Click shows tooltip: "Set how this relationship flows between actors. Directed flows one way, bidirectional flows both ways, and undirected has no specific direction." - -**First-Time User Experience:** -- When user creates first edge, show brief highlight on directionality control -- Optional: One-time tooltip "Choose relationship direction" - ---- - -## 7. Accessibility Considerations - -### 7.1 Screen Reader Support - -**Edge Selection:** -``` -"Relation selected: influences, directed from Actor A to Actor B" -``` - -**Directionality Change:** -``` -"Directionality changed to bidirectional. Relation now flows both ways between Actor A and Actor B" -``` - -**Reverse Action:** -``` -"Direction reversed. Relation now flows from Actor B to Actor A" -``` - -### 7.2 Keyboard Navigation - -**Tab Order in Properties Panel:** -1. Relation Type dropdown -2. Custom Label input -3. Directionality toggle group (arrow keys to change) -4. Reverse button (if directed) -5. Delete button - -**Focus Indicators:** -- Clear blue outline (2px) on focused elements -- Toggle buttons show focus state distinct from selection - -### 7.3 Color Contrast - -**Arrow Markers:** -- Maintain sufficient contrast against canvas background -- Minimum 3:1 contrast ratio for graphical objects (WCAG 2.1) -- Consider edge color when rendering markers - -**Controls:** -- Active state: Blue (#2196F3) on white - 4.5:1 ratio ✓ -- Inactive state: Gray (#666) on white - 5.7:1 ratio ✓ - -### 7.4 Visual Indicators Beyond Color - -**Directionality not solely dependent on color:** -- Arrow shape conveys meaning -- Text labels supplement visual indicators -- Icon + text in toggle buttons - ---- - -## 8. Implementation Priority - -### Phase 1: Core Functionality (MVP) -1. Add directionality field to RelationData type -2. Implement arrow markers on canvas (CustomEdge component) -3. Add directionality toggle in property panel -4. Update edge creation to use default directionality - -**User Value:** Basic directional relationships working end-to-end - -### Phase 2: Enhanced Controls -1. Add reverse direction button -2. Implement keyboard shortcuts (D, B, U, R) -3. Add defaultDirectionality to EdgeTypeConfig -4. Update Edge Type Manager with default setting - -**User Value:** Efficient editing and sensible defaults - -### Phase 3: Visual Polish -1. Enhanced connection info display with visual indicators -2. Update connections list in actor properties -3. Add directionality breakdown to graph metrics -4. Improve arrow marker styling (size, positioning) - -**User Value:** Better information display and understanding - -### Phase 4: Advanced Features -1. Bulk directionality operations -2. Smart default suggestions based on type name -3. Visual animations for direction changes -4. Export/import with directionality data - -**User Value:** Advanced workflows and refinements - ---- - -## 9. Edge Cases & Error Handling - -### 9.1 Self-Referencing Edges - -**Issue:** Edge where source === target - -**Solution:** -- Allow all directionality types -- Render with loop path (React Flow built-in) -- Arrow direction still meaningful (self-influence) - -### 9.2 Duplicate Edges - -**Issue:** Multiple edges between same two nodes - -**Current:** Already supported (different edge IDs) - -**Enhancement:** -- Visual offset for parallel edges (React Flow handles) -- Directionality independent per edge -- Example: A → B (influences) and A ← B (reports to) both valid - -### 9.3 Migration of Existing Data - -**Issue:** Existing edges have no directionality field - -**Solution:** -```typescript -// Default to 'directed' for backwards compatibility -const directionality = edge.data?.directionality || 'directed'; -``` - -**Migration Script (optional):** -- Add directionality field to all existing edges -- Set based on edge type default if available -- Otherwise, default to 'directed' - -### 9.4 Invalid States - -**Prevention:** -- TypeScript types enforce valid directionality values -- Toggle group only allows 3 options -- Reverse button disabled for non-directed edges - -### 9.5 Performance Considerations - -**Arrow Markers:** -- Create marker definitions once in SVG defs -- Reference by ID (efficient SVG pattern) -- One marker definition per color (shared across edges) - -**Rendering:** -- React Flow handles efficient re-rendering -- Memo CustomEdge component (already implemented) -- No performance impact expected - ---- - -## 10. Testing Checklist - -### 10.1 Functional Tests - -- [ ] Create directed edge, verify arrow at target -- [ ] Create bidirectional edge, verify arrows at both ends -- [ ] Create undirected edge, verify no arrows -- [ ] Change directionality via toggle buttons -- [ ] Reverse direction, verify source/target swap -- [ ] Edge type default directionality applies to new edges -- [ ] Keyboard shortcuts work (D, B, U, R) -- [ ] Undo/redo preserves directionality changes -- [ ] Export/import preserves directionality data - -### 10.2 Visual Tests - -- [ ] Arrows render at correct size and position -- [ ] Arrow color matches edge color -- [ ] Selected edge shows enhanced visual feedback -- [ ] Connection info display shows correct arrows -- [ ] Graph metrics show directionality breakdown -- [ ] Edge labels (optional arrows) display correctly - -### 10.3 Accessibility Tests - -- [ ] Screen reader announces directionality -- [ ] Keyboard navigation through controls works -- [ ] Focus indicators visible on all interactive elements -- [ ] Color contrast meets WCAG 2.1 AA -- [ ] Arrow markers distinguishable without color - -### 10.4 Edge Cases - -- [ ] Self-referencing edges render correctly -- [ ] Multiple edges between same nodes work -- [ ] Existing data without directionality field loads -- [ ] Very long edge labels don't break layout -- [ ] Rapid directionality changes don't cause issues - ---- - -## 11. User Documentation - -### 11.1 Help Dialog Addition - -**Section: Editing Relations** - -``` -Relationship Direction - -Constellation Analyzer supports three types of relationship directionality: - -• Directed (→): One-way relationships that flow from source to target - Example: "Actor A influences Actor B" - -• Bidirectional (↔): Two-way relationships that flow in both directions - Example: "Actor A collaborates with Actor B" - -• Undirected (—): Relationships without a specific direction - Example: "Actor A is related to Actor B" - -To set directionality: -1. Select a relation on the canvas -2. In the properties panel, choose the directionality type -3. For directed relations, use the Reverse button to swap direction - -Keyboard Shortcuts: -D - Set to Directed -B - Set to Bidirectional -U - Set to Undirected -R - Reverse direction -``` - -### 11.2 Tooltips - -**Property Panel:** -- Directionality info icon: "Control how this relationship flows between actors" -- Reverse button: "Reverse Direction (R) - Swap source and target actors" -- Directed button: "Directed (D) - One-way relationship" -- Bidirectional button: "Bidirectional (B) - Two-way relationship" -- Undirected button: "Undirected (U) - No specific direction" - ---- - -## 12. Design Rationale - -### 12.1 Why Segmented Control for Directionality? - -**Decision:** Use MUI ToggleButtonGroup instead of dropdown or radio buttons - -**Rationale:** -- Visual representation of options (icons + labels) -- Common pattern for mutually exclusive choices -- Faster interaction than dropdown (no need to open/close) -- Icons provide immediate recognition (→, ↔, —) -- Fits well within panel width constraints (~320px) - -### 12.2 Why Inline Reverse Button? - -**Decision:** Place reverse button next to connection info, not in separate section - -**Rationale:** -- Contextual to the information being reversed -- Common pattern (Gmail's "Swap" button in email composition) -- Doesn't clutter the main directionality control -- Only appears when relevant (directed edges) -- Visual proximity to source/target display reinforces what it does - -### 12.3 Why Live Updates Instead of Save Button? - -**Decision:** Maintain existing pattern of live property updates - -**Rationale:** -- Consistency with existing edge/node property editing -- Reduces friction (fewer clicks) -- Undo/redo provides safety net for mistakes -- Modern pattern (Google Docs, Figma, etc.) -- Debounced updates prevent history spam - -### 12.4 Why Default to Directed? - -**Decision:** Default new edges to "directed" when no type default specified - -**Rationale:** -- Most common use case in constellation analyses -- Directionality is often meaningful (influence, reports to, etc.) -- Easy to change to bidirectional or undirected if needed -- Backwards compatible with existing mental models -- Forces users to think about relationship direction - -### 12.5 Why Show Arrows on Canvas? - -**Decision:** Use arrow markers instead of subtle visual indicators - -**Rationale:** -- Immediately clear and unambiguous -- Standard graph visualization convention -- Works at all zoom levels -- No learning curve (universal symbol) -- Screen reader compatible (can be described) - ---- - -## 13. Future Enhancements - -### 13.1 Conditional Directionality - -**Concept:** Directionality that changes based on context or time - -**Example:** "influences" might be bidirectional in some scenarios, directed in others - -**UI Addition:** "Conditional" option with rule builder - -**Priority:** Low (advanced use case) - -### 13.2 Weighted Directionality - -**Concept:** Different strengths for each direction in bidirectional relationships - -**Example:** A → B (strong), B → A (weak) shown as thicker/thinner arrows - -**UI Addition:** Slider controls for each direction strength - -**Priority:** Medium (useful for advanced analysis) - -### 13.3 Visual Edge Routing - -**Concept:** Curved paths that better indicate directionality - -**Example:** Arcing paths that visually "flow" from source to target - -**Implementation:** Custom edge routing algorithm - -**Priority:** Low (aesthetic improvement) - -### 13.4 Directional Filtering - -**Concept:** Filter graph to show only certain directionality types - -**Example:** "Show only bidirectional relationships" - -**UI Location:** Toolbar filter dropdown - -**Priority:** Medium (useful for large graphs) - -### 13.5 Directional Analytics - -**Concept:** Advanced metrics based on relationship direction - -**Example:** -- Actors with most incoming directed edges (influenced by many) -- Actors with most outgoing directed edges (influence many) -- Bidirectional relationship clusters - -**UI Location:** Graph Metrics panel, new "Directional Analysis" section - -**Priority:** Medium (valuable for analysis) - ---- - -## Files to Modify - -### TypeScript Types -- `/home/jbruhn/dev/constellation-analyzer/src/types/index.ts` - - Add `directionality` field to `RelationData` - - Add `defaultDirectionality` field to `EdgeTypeConfig` - -### Components -- `/home/jbruhn/dev/constellation-analyzer/src/components/Edges/CustomEdge.tsx` - - Add arrow marker rendering logic - - Handle directionality prop - -- `/home/jbruhn/dev/constellation-analyzer/src/components/Panels/RightPanel.tsx` - - Add directionality toggle group - - Add reverse direction button - - Update connection info display - -### Stores -- Store files (need to verify exact location) - - Add directionality handling in edge operations - -### Utilities -- Edge creation utilities - - Apply default directionality from edge type config - ---- - -## Conclusion - -This UX specification provides a comprehensive, user-centered approach to implementing directional relationships in Constellation Analyzer. The design prioritizes: - -1. **Clarity**: Visual arrows and clear controls make directionality obvious -2. **Efficiency**: Keyboard shortcuts and live updates reduce friction -3. **Consistency**: Follows existing MUI-based design patterns -4. **Accessibility**: Screen reader support and keyboard navigation throughout -5. **Flexibility**: Supports all three directionality modes with sensible defaults - -The phased implementation approach allows for incremental delivery of value while maintaining code quality and user experience standards. diff --git a/docs/EDGE_OVERLAP_UX_PROPOSAL.md b/docs/EDGE_OVERLAP_UX_PROPOSAL.md deleted file mode 100644 index e2fa1c6..0000000 --- a/docs/EDGE_OVERLAP_UX_PROPOSAL.md +++ /dev/null @@ -1,610 +0,0 @@ -# Edge Overlap UX Design Proposal -## Constellation Analyzer - Handling Overlapping Edges - -**Date:** 2026-02-05 -**Status:** Proposal -**Problem:** When multiple relations exist between the same two nodes, or when edges cross each other, they overlap and become difficult to distinguish, select, or understand. - ---- - -## Current Implementation Analysis - -### What We Have -The codebase uses **@xyflow/react** (React Flow v12) with custom edge rendering: - -1. **CustomEdge component** (`src/components/Edges/CustomEdge.tsx`) - - Renders edges as cubic Bezier curves - - Uses `getFloatingEdgeParams()` for calculating intersection points with various node shapes - - Supports directional arrows (directed, bidirectional, undirected) - - Shows edge labels with type indicators and custom text - - Implements visual filtering with opacity changes - -2. **Edge Routing** (`src/utils/edgeUtils.ts`) - - Smart shape-aware intersection calculation (circle, ellipse, pill, rounded rectangle) - - Bezier curves with control points at 40% of inter-node distance - - Single path between any two nodes - no multi-edge handling - -3. **Edge Aggregation for Groups** - - When groups are minimized, multiple internal edges are aggregated into a single edge - - Shows count badge (e.g., "5 relations") - - Uses neutral gray color for aggregated edges - -### Current Gaps -- **No offset for parallel edges**: Multiple edges between same nodes overlap completely -- **No edge crossing detection**: Edges can cross over each other with no visual differentiation -- **Selection challenges**: Overlapping edges are hard to click/select -- **Visual clutter**: Dense graphs become illegible with many crossing edges - ---- - -## Research: Best Practices for Edge Overlap - -### Industry Standards - -1. **Dagre/Graphviz Approach** - - Hierarchical edge routing with splines - - Edge bundling for crossing edges - - Used by: Mermaid, PlantUML - -2. **Force-Directed Graphs (D3, Cytoscape)** - - Physics-based layout to minimize crossings - - Edge bundling for hierarchical structures - - Used by: Neo4j Browser, Gephi - -3. **Network Diagram Tools (Draw.io, Lucidchart)** - - Manual routing with waypoints - - Orthogonal connectors (90-degree angles) - - Automatic routing around obstacles - -4. **React Flow Patterns** - - Edge offset for parallel edges - - Custom edge components with hover states - - Edge label positioning to avoid overlap - -### User Expectations from Similar Tools - -**Graph Databases (Neo4j, ArangoDB)** -- Clear visual separation between parallel edges -- Interactive hover to highlight connection paths -- Edge bundling for many-to-many relationships - -**Diagramming Tools (Miro, FigJam)** -- Smooth bezier curves with collision avoidance -- Hover states that bring edges to front -- Smart label positioning - -**Network Analysis (Gephi, Cytoscape)** -- Edge bundling for dense areas -- Opacity/width to show edge importance -- Interactive filtering to reduce visual noise - ---- - -## Design Solution 1: Parallel Edge Offset (Recommended) - -### Visual Approach -**Offset parallel edges** with curved paths that arc away from the center line between nodes. - -#### Implementation Details -- When multiple edges exist between same source/target, calculate offset curves -- Use consistent offset distance (e.g., 30px) that scales with zoom -- Maximum of 3 parallel edges visible; beyond that, show aggregation badge -- Bidirectional edges use center position (no offset) - -#### Visual Example (ASCII) -``` - A ─────────────→ B (Single edge: straight bezier) - - A ═══════════⟩ B (Multiple edges: curved offsets) - ⟨───────────── -``` - -#### Interaction Patterns - -**Selection** -- Click tolerance increases for offset edges (larger hit area) -- Selected edge highlighted with thicker stroke (3px -> 4px) -- Non-selected parallel edges dim to 50% opacity -- Hover shows all parallel edges with tooltip - -**Hover States** -- Individual edge highlight on hover -- Show edge type and label in tooltip -- Dim other edges to 30% opacity -- Bring hovered edge to top layer (z-index manipulation) - -**Multi-Edge Badge** -- Show count when 4+ edges between same nodes: "4 relations" -- Click badge to expand into offset view -- Badge position: midpoint of straight line between nodes -- Color: use neutral gray (matches aggregation pattern) - -#### Accessibility Considerations - -**Keyboard Navigation** -- Tab through edges in document order (source node ID order) -- Arrow keys to navigate between parallel edges -- Space/Enter to select edge -- Screen reader announces: "Relation 2 of 4 from Person A to Organization B, Collaborates type" - -**Screen Readers** -- Edge count announced: "4 relations between nodes" -- Each edge individually focusable and describable -- Alternative text includes source, target, type, and label - -**Color Contrast** -- Maintain WCAG AA standards (4.5:1 for text, 3:1 for UI components) -- Don't rely solely on color - use patterns (dashed/dotted) and labels -- High contrast mode: increase stroke width and use distinct patterns - -**Motor Impairments** -- Larger click targets (minimum 44x44px according to WCAG 2.1 AAA) -- Increase hover tolerance for parallel edges -- Longer hover delay before tooltip appears (300ms) - -#### Implementation Complexity - -**Effort: Medium (3-5 days)** - -**Changes Required:** -1. Modify `getFloatingEdgeParams()` to accept edge index parameter -2. Add offset calculation to bezier control points -3. Update `GraphEditor` to detect parallel edges and pass index -4. Create edge grouping utility function -5. Update CustomEdge to handle offset rendering -6. Add hover state management for edge groups - -**Benefits:** -- Clear visual distinction between parallel edges -- Minimal performance impact (mathematical calculation only) -- Consistent with user expectations from diagramming tools -- Works at all zoom levels -- Preserves existing edge aggregation for groups - -**Trade-offs:** -- Visual complexity increases with many parallel edges -- Requires recalculation when edges added/removed -- May need edge limit policy (e.g., max 5 visible, then aggregate) - ---- - -## Design Solution 2: Edge Bundling with Interaction - -### Visual Approach -**Bundle overlapping edges** into a single visual path that expands on interaction. - -#### Implementation Details -- Detect edge clusters (edges that cross within 20px threshold) -- Render as single thick edge with width indicating count -- On hover, "explode" bundle into individual edges with labels -- Use color gradient to show multiple edge types in bundle - -#### Visual Example (ASCII) -``` -Default State: - A ══════════════⟩ B (Thick bundle: 5 edges) - -Hover/Expanded State: - A ═══════════⟩ B - ───────────→ - ─ ─ ─ ─ ─→ - · · · · · → - ⟨───────── -``` - -#### Interaction Patterns - -**Default State** -- Show edge count badge on bundle -- Use widest stroke width to indicate bundle (4-8px) -- Color: blend of constituent edge types (or neutral gray) - -**Hover State** -- Animate expansion into individual offset edges (300ms transition) -- Show all edge labels -- Individual edges selectable in expanded state -- Background blur/dim for focus - -**Selection** -- Click bundle to expand permanently until deselected -- Click individual edge when expanded to select it -- Selected edge shows properties panel -- Double-click bundle to "pin" expansion - -**Touch Devices** -- Tap bundle to expand -- Tap again to collapse -- Long-press for context menu -- Pinch to zoom focuses on bundle - -#### Accessibility Considerations - -**Keyboard Navigation** -- Tab to bundle -- Enter/Space to expand bundle -- Arrow keys to navigate within expanded bundle -- Escape to collapse bundle -- Screen reader: "Edge bundle containing 5 relations. Press Enter to expand." - -**Visual Indicators** -- Animated expansion provides visual feedback -- Count badge always visible -- Edge labels appear only when expanded -- High contrast mode: use distinct patterns for bundle indicator - -**Cognitive Load** -- Progressive disclosure reduces initial complexity -- Expansion animation helps user track state change -- Consistent interaction pattern across all bundles -- Visual hierarchy: bundles stand out in dense graphs - -#### Implementation Complexity - -**Effort: High (7-10 days)** - -**Changes Required:** -1. Create edge clustering algorithm (detect overlapping edges) -2. Build BundledEdge component with expansion animation -3. Add state management for expanded/collapsed bundles -4. Implement hover detection and animation system -5. Create bundle label component with count badge -6. Update selection logic to handle bundle vs individual edge -7. Performance optimization for large graphs (edge clustering can be expensive) - -**Benefits:** -- Dramatically reduces visual clutter in dense graphs -- Progressive disclosure matches user intent -- Scalable to very large graphs -- Innovative UX that stands out -- Reduces cognitive load in default state - -**Trade-offs:** -- Higher implementation complexity -- Requires state management for bundle expansion -- Animation performance concerns on large graphs -- May confuse users unfamiliar with the pattern -- Edge clustering algorithm is computationally expensive - ---- - -## Design Solution 3: Smart Edge Routing with Collision Avoidance - -### Visual Approach -**Automatically route edges** around nodes and other edges to minimize crossings. - -#### Implementation Details -- Use A* pathfinding or similar algorithm to route edges -- Create orthogonal or curved paths that avoid node boundaries -- Calculate collision-free paths on layout change -- Option to manually add waypoints for fine-tuning - -#### Visual Example (ASCII) -``` -Default: - A → B → C - ↓ ↓ - D → E → F - -With Smart Routing: - A → B → C - ↓ ↓ ↓ - D ←─╯ ↓ - ↓ ↓ - └─→ E → F -``` - -#### Interaction Patterns - -**Automatic Routing** -- Edges automatically reroute when nodes move -- Smooth animation (300ms) when path changes -- Maintain edge label positions at path midpoints -- Option to disable auto-routing (manual mode) - -**Manual Waypoints** -- Double-click edge to add waypoint -- Drag waypoint to adjust path -- Right-click waypoint to remove -- Waypoints persist across sessions - -**Path Highlighting** -- Hover edge to highlight entire path -- Show direction arrows along path -- Dim other edges when hovering -- Show path length in tooltip (optional) - -**Configuration** -- Toggle between "curved" and "orthogonal" routing -- Adjust routing algorithm sensitivity -- Set collision avoidance distance -- Enable/disable automatic rerouting - -#### Accessibility Considerations - -**Keyboard Navigation** -- Tab to select edge -- Arrow keys to navigate waypoints -- Delete key to remove waypoint -- Screen reader: "Edge with 3 waypoints. From Person A to Organization B." - -**Visual Clarity** -- Orthogonal paths easier to follow than curves -- Clear directional indicators -- Path highlighting on focus -- High contrast mode: thicker paths with distinct patterns - -**Cognitive Load** -- Auto-routing reduces manual work -- Can be confusing when paths change automatically -- Manual waypoints give user control -- Learning curve for waypoint editing - -#### Implementation Complexity - -**Effort: Very High (15-20 days)** - -**Changes Required:** -1. Implement edge routing algorithm (A*, Dijkstra, or orthogonal routing) -2. Create waypoint system for manual editing -3. Add collision detection for nodes and edges -4. Implement path smoothing and bezier curve generation -5. Add animation system for path updates -6. Create configuration panel for routing options -7. Performance optimization (routing is CPU-intensive) -8. Handle edge cases (loops, self-edges, complex layouts) - -**Benefits:** -- Eliminates edge crossings in most cases -- Professional appearance (like diagramming tools) -- User control through manual waypoints -- Scales to complex graphs -- Industry-standard approach - -**Trade-offs:** -- Very high implementation complexity -- Significant performance impact on large graphs -- May produce unexpected routing in complex scenarios -- Requires substantial testing and edge case handling -- Can feel "overengineered" for simple graphs -- Breaking change from existing bezier curve UX - ---- - -## Recommendation: Solution 1 (Parallel Edge Offset) - -### Rationale - -After analyzing the three solutions against project requirements, I recommend **Solution 1: Parallel Edge Offset** for the following reasons: - -#### 1. **Best Effort/Value Ratio** -- **Medium complexity** (3-5 days) vs High (7-10 days) or Very High (15-20 days) -- Solves the primary problem: parallel edges between same nodes -- Incremental improvement that doesn't require architectural changes - -#### 2. **Preserves Existing UX Patterns** -- Maintains current bezier curve style -- Consistent with existing group aggregation behavior -- No breaking changes to user workflows -- Works within React Flow's existing architecture - -#### 3. **User-Centered Design** -- Immediate visual clarity for parallel edges -- Familiar pattern from other diagramming tools (Draw.io, Miro) -- Low learning curve -- Accessible with keyboard and screen readers - -#### 4. **Performance** -- Pure mathematical calculation (no expensive algorithms) -- Scales well to large graphs (O(n) complexity) -- No animation/state management overhead -- Works at all zoom levels without recalculation - -#### 5. **Accessibility** -- WCAG AA compliant -- Screen reader support straightforward -- Keyboard navigation well-defined -- High contrast mode compatible - -#### 6. **Extensibility** -- Foundation for future enhancements -- Can add edge bundling later if needed -- Compatible with future smart routing -- Doesn't preclude other solutions - -### Implementation Plan - -#### Phase 1: Core Offset Logic (Day 1-2) -1. Create `calculateEdgeOffset()` utility function -2. Detect parallel edges in `GraphEditor` -3. Pass offset index to `CustomEdge` component -4. Modify `getFloatingEdgeParams()` to accept offset parameter -5. Calculate perpendicular offset for bezier control points - -#### Phase 2: Visual Refinement (Day 2-3) -1. Implement hover states for parallel edge groups -2. Add selection highlighting -3. Create edge count badge component (for 4+ edges) -4. Test at different zoom levels -5. Ensure labels don't overlap - -#### Phase 3: Interaction & Accessibility (Day 3-4) -1. Keyboard navigation for parallel edges -2. Screen reader announcements -3. Click tolerance adjustment -4. Tooltip improvements -5. High contrast mode testing - -#### Phase 4: Testing & Documentation (Day 4-5) -1. Unit tests for offset calculation -2. Integration tests for edge groups -3. Visual regression tests -4. User documentation -5. Code review and refinement - -### Success Metrics - -**Visual Clarity** -- Parallel edges clearly distinguishable at all zoom levels -- No overlap between offset curves -- Labels remain readable - -**Interaction Quality** -- Click target accuracy > 95% for offset edges -- Hover states respond within 100ms -- Keyboard navigation covers all edges - -**Performance** -- No frame rate impact on graphs up to 500 edges -- Edge offset calculation < 5ms per edge -- Zoom/pan remains smooth - -**Accessibility** -- WCAG AA compliance verified -- Screen reader testing with NVDA/JAWS -- Keyboard-only navigation successful -- High contrast mode functional - ---- - -## Future Enhancements - -After implementing Solution 1, consider these incremental improvements: - -### Short-term (1-3 months) -1. **Edge hover tooltips**: Show full edge information on hover -2. **Edge filtering**: Hide edges by type/label to reduce clutter -3. **Edge path highlighting**: Show full path on selection -4. **Curved edge labels**: Orient labels along curve path - -### Medium-term (3-6 months) -1. **Edge bundling** (Solution 2): For dense graphs with many crossings -2. **Edge strength visualization**: Vary width/opacity by strength property -3. **Edge animation**: Flowing particles to show direction/activity -4. **Edge grouping controls**: Manual grouping/ungrouping - -### Long-term (6-12 months) -1. **Smart routing** (Solution 3): Optional for complex layouts -2. **Layout algorithms**: Auto-arrange to minimize crossings -3. **Edge styles library**: More edge type options (elbow, stepped, etc.) -4. **3D graph view**: For very complex networks - ---- - -## Appendix: Technical Specifications - -### Edge Offset Calculation Algorithm - -```typescript -/** - * Calculate perpendicular offset for parallel edges - * @param sourcePos Source node position - * @param targetPos Target node position - * @param edgeIndex Index in parallel edge group (0 = center, 1 = first offset, 2 = second offset) - * @param offsetDistance Base offset distance in pixels (default 30) - * @returns Offset vector { x, y } - */ -function calculateEdgeOffset( - sourcePos: { x: number; y: number }, - targetPos: { x: number; y: number }, - edgeIndex: number, - offsetDistance: number = 30 -): { x: number; y: number } { - // Calculate edge direction vector - const dx = targetPos.x - sourcePos.x; - const dy = targetPos.y - sourcePos.y; - const length = Math.sqrt(dx * dx + dy * dy); - - if (length === 0) return { x: 0, y: 0 }; - - // Normalize - const nx = dx / length; - const ny = dy / length; - - // Perpendicular vector (rotate 90 degrees) - const perpX = -ny; - const perpY = nx; - - // Alternate sides for even/odd indices - // 0: center (no offset) - // 1: +offset (top/right side) - // 2: -offset (bottom/left side) - // 3: +offset * 2 (further top/right) - // 4: -offset * 2 (further bottom/left) - const side = edgeIndex % 2 === 0 ? -1 : 1; - const magnitude = Math.ceil(edgeIndex / 2); - const offset = side * magnitude * offsetDistance; - - return { - x: perpX * offset, - y: perpY * offset - }; -} -``` - -### Edge Grouping Detection - -```typescript -/** - * Group edges by source-target pair - * @param edges Array of all edges - * @returns Map of edge groups, keyed by "sourceId_targetId" - */ -function groupParallelEdges(edges: Relation[]): Map { - const groups = new Map(); - - edges.forEach(edge => { - // Normalize key: always alphabetically sorted for bidirectional grouping - const key = [edge.source, edge.target].sort().join('_'); - - if (!groups.has(key)) { - groups.set(key, []); - } - groups.get(key)!.push(edge); - }); - - return groups; -} -``` - -### Performance Considerations - -**Memory:** -- Edge offset calculation: O(1) per edge -- Edge grouping: O(n) space for n edges -- Total memory impact: ~50 bytes per edge - -**CPU:** -- Offset calculation: ~0.1ms per edge (pure math) -- Grouping detection: O(n) time for n edges -- Total impact: < 100ms for 1000 edges - -**Rendering:** -- No additional DOM nodes -- Same SVG path rendering as current implementation -- Z-index manipulation on hover (no re-render) - ---- - -## Conclusion - -Implementing **Parallel Edge Offset** (Solution 1) provides the best balance of: -- User experience improvement -- Implementation complexity -- Performance impact -- Accessibility compliance -- Future extensibility - -This solution directly addresses the stated problem of overlapping edges between the same two nodes, while maintaining compatibility with the existing codebase architecture and user expectations. - -The implementation can be completed in 3-5 days and provides a solid foundation for future enhancements like edge bundling or smart routing if needed. - ---- - -**Next Steps:** -1. Review this proposal with the team -2. Approve implementation plan and timeline -3. Create implementation tickets -4. Begin Phase 1 development -5. Iterate based on user feedback - -**Questions or feedback?** Open an issue or discussion on the repository. diff --git a/docs/EDGE_OVERLAP_VISUAL_GUIDE.md b/docs/EDGE_OVERLAP_VISUAL_GUIDE.md deleted file mode 100644 index 175d109..0000000 --- a/docs/EDGE_OVERLAP_VISUAL_GUIDE.md +++ /dev/null @@ -1,602 +0,0 @@ -# Edge Overlap Visual Design Guide -## Constellation Analyzer - Visual Specifications for Parallel Edge Offset - -**Companion Document to:** EDGE_OVERLAP_UX_PROPOSAL.md -**Date:** 2026-02-05 - ---- - -## Visual Design Patterns - -### 1. Single Edge (Current State) - -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ │ │ - │ A │ ─────────────→ │ B │ - │ │ │ │ - └──────┘ └──────┘ - - Stroke: 2px - Color: Based on edge type - Curve: Cubic Bezier with 40% control point distance -``` - -**Current Behavior:** -- Clean, simple bezier curve -- Works well for single connections -- Professional appearance - ---- - -### 2. Parallel Edges (Proposed Design) - -#### Two Edges Between Same Nodes - -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ ╭───────────→ │ │ - │ A │ │ B │ - │ │ ╰───────────→ │ │ - └──────┘ └──────┘ - - Upper edge: +30px offset - Lower edge: -30px offset - Both: 2px stroke - Curves: Smooth, symmetrical arcs -``` - -**Visual Properties:** -- Offset: 30px perpendicular to center line -- Arc depth: Proportional to edge length -- Spacing: Consistent at all zoom levels -- Labels: Positioned at curve midpoint - -#### Three Edges Between Same Nodes - -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ ╭──────────────→│ │ - │ A │ ────────────────│ B │ (center, no offset) - │ │ ╰──────────────→│ │ - └──────┘ └──────┘ - - Top edge: +30px offset - Center edge: 0px offset (straight bezier) - Bottom edge: -30px offset -``` - -**Visual Hierarchy:** -- Center edge is most prominent (straight) -- Offset edges curve away from center -- Equal visual weight for all edges - -#### Four+ Edges (Aggregation Badge) - -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ ╭──────────────→│ │ - │ A │ ─────┌───┐─────│ B │ - │ │ │ 4 │ │ │ │ - │ │ ╰────└───┘─────→│ │ - └──────┘ └──────┘ - - Top 3 edges: Visible with offsets - Badge: "4 relations" at center - Badge style: Gray pill, white text - Hover: Expand to show all 4 edges -``` - -**Badge Design:** -- Background: #6b7280 (gray-500) -- Text: White, 12px, medium weight -- Border radius: 12px (pill shape) -- Padding: 4px 8px -- Shadow: 0 2px 4px rgba(0,0,0,0.1) - ---- - -### 3. Hover States - -#### Default State -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ ╭───────────→ │ │ - │ A │ ────────────→ │ B │ - │ │ ╰───────────→ │ │ - └──────┘ └──────┘ - - All edges: 100% opacity - No highlights -``` - -#### Hover Single Edge -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ ╭───────────→ │ │ (30% opacity, dimmed) - │ A │ ━━━━━━━━━━━━→ │ B │ (100% opacity, 3px stroke, highlighted) - │ │ ╰───────────→ │ │ (30% opacity, dimmed) - └──────┘ └──────┘ - ┌─────────────┐ - │ Collaborates│ (Tooltip) - │ Type: Work │ - └─────────────┘ -``` - -**Hover Behavior:** -- Hovered edge: 3px stroke (from 2px) -- Hovered edge: 100% opacity -- Other parallel edges: 30% opacity -- Tooltip appears after 200ms -- Z-index: Bring hovered edge to top layer - -#### Selection State -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ ╭───────────→ │ │ (50% opacity, dimmed) - │ A │ ━━━━━━━━━━━━→ │ B │ (4px stroke, blue outline, selected) - │ │ ╰───────────→ │ │ (50% opacity, dimmed) - └──────┘ └──────┘ - - Selected edge: 4px stroke - Selected edge: Blue glow (#3b82f6) - Other parallel edges: 50% opacity - Selection persists until deselected -``` - ---- - -### 4. Bidirectional Edges - -#### Bidirectional vs Two Directed Edges - -**Bidirectional (Single Edge):** -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ │ │ - │ A │ ⟨────────────⟩ │ B │ - │ │ │ │ - └──────┘ └──────┘ - - Single edge with arrows at both ends - No offset (uses center line) - Marker-start and marker-end -``` - -**Two Directed Edges:** -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ ╭───────────→ │ │ - │ A │ │ B │ - │ │ ╰←──────────╯ │ │ - └──────┘ └──────┘ - - Two separate edges with offsets - Offset: ±30px - Each has single arrow -``` - -**Design Decision:** -- Bidirectional edges: No offset, use center line -- Two separate directed edges: Apply offset -- Visual distinction clear to users -- Preserves semantic meaning - ---- - -### 5. Edge Label Positioning - -#### Label on Curved Edge - -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ ╭──┌─────────┐→ │ │ - │ A │ │Collabora.│ │ B │ - │ │ └─────────┘ │ │ - └──────┘ └──────┘ - - Label positioned at bezier t=0.5 (midpoint) - Background: White with border - Padding: 8px 12px - Font: 12px, medium weight - Max-width: 200px (wrap text) -``` - -**Label Collision Avoidance:** -- Labels offset 5px above curve for top edge -- Labels offset 5px below curve for bottom edge -- Center edge: label on curve (existing behavior) -- Labels never overlap edge paths - -#### Multiple Labels on Parallel Edges - -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ ╭─┌────────┐──→ │ │ - │ A │ │Reports To│ │ B │ - │ │ ─┌──────────┐──→│ │ - │ │ │Collabora.│ │ │ - │ │ ╰┌─────────┐───→│ │ - │ │ │Depends On│ │ │ - └──────┘ └─────────┘ └──────┘ - - Labels staggered to prevent overlap - Each label aligned with its edge curve - Smart positioning algorithm -``` - ---- - -### 6. Zoom Level Behavior - -#### Zoom Out (0.5x) -``` - Node A Node B - ┌─┐ ┌─┐ - │A│ ╭─────→ │B│ - │ │ ───────→ │ │ - │ │ ╰─────→ │ │ - └─┘ └─┘ - - Offset: 30px (constant, not scaled) - Stroke: 1px (minimum) - Labels: Hidden or summarized - Badge: Visible -``` - -**Design Note:** Offset distance remains constant in screen pixels, creating proportionally larger curves when zoomed out. This maintains visual separation. - -#### Zoom In (2.0x) -``` - Node A Node B - ┌──────────┐ ┌──────────┐ - │ │ ╭────────────────────→ │ │ - │ A │ │ B │ - │ │ ──────────────────────→ │ │ - │ │ │ │ - │ │ ╰────────────────────→ │ │ - └──────────┘ └──────────┘ - - Offset: 30px (constant) - Stroke: 3px (scaled up) - Labels: Fully visible with more detail - Curves: More pronounced -``` - -**Design Note:** At higher zoom, offset appears smaller relative to nodes, but remains visually distinct. - ---- - -### 7. Color and Styling - -#### Edge Type Colors (Existing) -``` -Collaborates: #3b82f6 (blue) -Reports To: #10b981 (green) -Depends On: #f59e0b (orange) -Influences: #8b5cf6 (purple) -``` - -#### Edge Styles (Existing) -``` -Solid: ──────────── -Dashed: ─ ─ ─ ─ ─ ─ -Dotted: · · · · · · -``` - -#### New States -``` -Default: stroke-width: 2px, opacity: 1.0 -Hover: stroke-width: 3px, opacity: 1.0 -Dimmed: stroke-width: 2px, opacity: 0.3 -Selected: stroke-width: 4px, opacity: 1.0, glow: #3b82f6 -``` - -#### Aggregation Badge -``` -Background: #6b7280 -Text: #ffffff -Border: None -Shadow: 0 2px 4px rgba(0,0,0,0.1) -``` - ---- - -### 8. Accessibility Visual Indicators - -#### High Contrast Mode -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ ╔═══════════⟩ │ │ (4px stroke, solid) - │ A │ ═════════════⟩ │ B │ (4px stroke, dashed) - │ │ ╚═══════════⟩ │ │ (4px stroke, dotted) - └──────┘ └──────┘ - - All strokes: 4px (increased from 2px) - Distinct patterns for each edge type - Colors: High contrast (black/white basis) -``` - -#### Focus Indicator (Keyboard Navigation) -``` - Node A Node B - ┌──────┐ ┌──────┐ - │ │ ╭───────────→ │ │ - │ A │ ┏━━━━━━━━━━━┓→ │ B │ (Focus ring: 2px offset) - │ │ ╰───────────→ │ │ - └──────┘ └──────┘ - - Focus ring: 2px blue outline (#3b82f6) - Offset: 4px from edge path - Visible only when focused via keyboard -``` - ---- - -### 9. Animation Specifications - -#### Edge Creation Animation -``` -Frame 0: Node A · Node B - · - · -Frame 1: Node A ·········· Node B - -Frame 2: Node A ─────────────→ Node B - -Duration: 300ms -Easing: ease-out -Effect: Draw from source to target -``` - -#### Hover Transition -``` -Frame 0: Normal state (2px, 100% opacity) -Frame 1: Transitioning (2.5px, 100% opacity) -Frame 2: Hover state (3px, 100% opacity) - -Duration: 150ms -Easing: ease-in-out -Effect: Smooth width increase -``` - -#### Selection Transition -``` -Frame 0: Normal state -Frame 1: Glow appears (opacity: 0 → 0.5) -Frame 2: Width increases (2px → 4px) -Frame 3: Full selection state - -Duration: 200ms -Easing: ease-out -Effect: Blue glow + width increase -``` - ---- - -### 10. Responsive Behavior - -#### Mobile View (< 768px) -``` -- Offset distance: 40px (increased for touch targets) -- Stroke width: 3px (increased for visibility) -- Minimum click target: 44x44px -- Labels: Hidden by default, show on tap -- Badge: Always visible -``` - -#### Tablet View (768px - 1024px) -``` -- Offset distance: 35px -- Stroke width: 2px -- Click target: 44x44px -- Labels: Show on hover -- Badge: Visible when 4+ edges -``` - -#### Desktop View (> 1024px) -``` -- Offset distance: 30px (default) -- Stroke width: 2px -- Click target: natural edge width -- Labels: Always visible -- Badge: Visible when 4+ edges -``` - ---- - -### 11. Edge Case Visual Handling - -#### Self-Loop Edge -``` - Node A - ┌──────┐ - │ │ ╭─╮ - │ A │ │ │ (Loop extends 80px from node) - │ │ ╰─╯ - └──────┘ - - Rendered as circular arc - Extends 80px from node edge - Arrow points back to source - Label positioned outside loop -``` - -#### Very Short Distance Between Nodes -``` - Node A Node B - ┌───┐ ┌───┐ - │ A │╭→ │ B │ - │ │╰→ │ │ - └───┘ └───┘ - - Offset: Reduced to 15px (50% of default) - Curves: Sharper to fit space - Labels: Hidden to prevent overlap - Badge: Positioned above nodes -``` - -#### Long Distance Between Nodes -``` - Node A Node B - ┌───┐ ┌───┐ - │ A │ ╭───────────────────────────────────────→ │ B │ - │ │ ─────────────────────────────────────────→ │ │ - │ │ ╰───────────────────────────────────────→ │ │ - └───┘ └───┘ - - Offset: 30px (constant) - Curves: Gentle (control point distance capped at 150px) - Labels: Positioned at midpoint - Visual: Offset less noticeable but still distinct -``` - ---- - -## Design Tokens - -### Spacing -```typescript -const EDGE_OFFSET_BASE = 30; // Base offset in pixels -const EDGE_OFFSET_MOBILE = 40; // Increased for touch -const EDGE_OFFSET_MIN = 15; // Minimum for close nodes -const LABEL_OFFSET = 5; // Label offset from curve -const BADGE_PADDING = '4px 8px'; // Badge internal padding -``` - -### Strokes -```typescript -const STROKE_DEFAULT = 2; // Default edge width -const STROKE_HOVER = 3; // Hovered edge width -const STROKE_SELECTED = 4; // Selected edge width -const STROKE_DIMMED = 2; // Width when dimmed (opacity changes) -const STROKE_HIGH_CONTRAST = 4; // Width in high contrast mode -``` - -### Opacity -```typescript -const OPACITY_DEFAULT = 1.0; // Normal edge visibility -const OPACITY_DIMMED = 0.3; // Non-hovered parallel edges -const OPACITY_SEMI_DIMMED = 0.5; // Non-selected parallel edges -const OPACITY_FILTERED = 0.2; // Edges filtered out by search -``` - -### Colors -```typescript -const COLOR_SELECTION_GLOW = '#3b82f6'; // Blue focus/selection -const COLOR_BADGE_BG = '#6b7280'; // Gray badge background -const COLOR_BADGE_TEXT = '#ffffff'; // White badge text -const COLOR_LABEL_BG = '#ffffff'; // White label background -const COLOR_LABEL_BORDER = '#d1d5db'; // Gray label border -``` - -### Timing -```typescript -const DURATION_HOVER = 150; // Hover transition duration (ms) -const DURATION_SELECTION = 200; // Selection animation duration (ms) -const DURATION_CREATION = 300; // Edge creation animation (ms) -const DURATION_TOOLTIP_DELAY = 200; // Delay before tooltip appears (ms) -``` - -### Bezier Curves -```typescript -const CONTROL_POINT_RATIO = 0.4; // 40% of distance between nodes -const CONTROL_POINT_MIN = 40; // Minimum control point distance (px) -const CONTROL_POINT_MAX = 150; // Maximum control point distance (px) -``` - ---- - -## Implementation Reference - -### CSS Classes (for styled edges) -```css -.edge-default { - stroke-width: 2px; - opacity: 1; - transition: stroke-width 150ms ease-in-out, opacity 150ms ease-in-out; -} - -.edge-hover { - stroke-width: 3px; - opacity: 1; - z-index: 100; -} - -.edge-selected { - stroke-width: 4px; - opacity: 1; - filter: drop-shadow(0 0 4px #3b82f6); -} - -.edge-dimmed { - opacity: 0.3; -} - -.edge-badge { - background: #6b7280; - color: #ffffff; - border-radius: 12px; - padding: 4px 8px; - font-size: 12px; - font-weight: 500; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.edge-label { - background: #ffffff; - border: 1px solid #d1d5db; - border-radius: 4px; - padding: 8px 12px; - font-size: 12px; - font-weight: 500; - max-width: 200px; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); -} - -/* High Contrast Mode */ -@media (prefers-contrast: high) { - .edge-default { - stroke-width: 4px; - } -} - -/* Focus indicator for keyboard navigation */ -.edge-focused { - outline: 2px solid #3b82f6; - outline-offset: 4px; -} -``` - ---- - -## Conclusion - -This visual guide provides detailed specifications for implementing parallel edge offset in the Constellation Analyzer. All measurements, colors, and animations are designed to: - -1. **Maintain visual consistency** with existing design patterns -2. **Ensure accessibility** across different modes and devices -3. **Scale gracefully** from mobile to desktop -4. **Provide clear interaction feedback** through hover, selection, and focus states -5. **Handle edge cases** without breaking the visual hierarchy - -Use this guide alongside the main UX proposal document for implementation. - ---- - -**Related Files:** -- Main proposal: `/home/jbruhn/dev/constellation-analyzer/EDGE_OVERLAP_UX_PROPOSAL.md` -- Current edge implementation: `/home/jbruhn/dev/constellation-analyzer/src/components/Edges/CustomEdge.tsx` -- Edge utilities: `/home/jbruhn/dev/constellation-analyzer/src/utils/edgeUtils.ts` diff --git a/docs/IMPLEMENTATION_CHECKLIST.md b/docs/IMPLEMENTATION_CHECKLIST.md deleted file mode 100644 index 611a1a8..0000000 --- a/docs/IMPLEMENTATION_CHECKLIST.md +++ /dev/null @@ -1,755 +0,0 @@ -# Temporal & Scenario Analysis - Implementation Checklist - -## Phase 1: Core State Management (Week 1-2) - -### Data Model Setup -- [ ] Create `/src/types/temporal.ts` - - [ ] Define `StateType` enum - - [ ] Define `TemporalMetadata` interface - - [ ] Define `ScenarioMetadata` interface - - [ ] Define `StateRelationship` interface - - [ ] Define `AnalysisState` interface - - [ ] Define `StateDiff` interface - - [ ] Define `ActorJourney` interface - - [ ] Define `Timeline` interface - - [ ] Define `ScenarioTree` interface - -- [ ] Update `/src/stores/persistence/types.ts` - - [ ] Add `states` property to `ConstellationDocument` - - [ ] Add `supportsStates` to document metadata - - [ ] Ensure backward compatibility - -- [ ] Create migration helper - - [ ] Function to enable states for existing documents - - [ ] Function to create initial state from current graph - - [ ] Test migration with sample documents - -### State Store Implementation -- [ ] Create `/src/stores/stateStore.ts` - - [ ] Basic store structure with Zustand - - [ ] State CRUD operations - - [ ] `createState()` - - [ ] `loadState()` - - [ ] `deleteState()` - - [ ] `updateStateMetadata()` - - [ ] State retrieval - - [ ] `getState()` - - [ ] `getAllStates()` - - [ ] `getStatesByType()` - - [ ] Navigation helpers - - [ ] `getNextState()` - - [ ] `getPreviousState()` - -### Snapshot Functionality -- [ ] Create `/src/utils/stateSnapshot.ts` - - [ ] `captureCurrentGraph()` - serialize current graph state - - [ ] `restoreGraphFromState()` - load state into graph - - [ ] `validateStateData()` - ensure data integrity - - [ ] Handle edge cases (empty graph, large graphs) - -### Basic UI Integration -- [ ] Update `/src/components/Toolbar/Toolbar.tsx` - - [ ] Add "Capture State" button - - [ ] Add current state indicator - - [ ] Add loading/saving state indicators - -- [ ] Create `/src/components/TemporalAnalysis/StateSelector.tsx` - - [ ] Basic dropdown UI - - [ ] List all states - - [ ] Click to load state - - [ ] Show current state indicator - - [ ] Search/filter functionality - -- [ ] Create hooks - - [ ] `/src/hooks/useStateManagement.ts` - - [ ] `useCurrentState()` - - [ ] `useCaptureState()` - - [ ] `useLoadState()` - -### Integration with Workspace -- [ ] Update `/src/stores/workspaceStore.ts` - - [ ] Add `captureCurrentState()` action - - [ ] Add `restoreState()` action - - [ ] Ensure states saved with document - - [ ] Handle state data in export/import - -### Testing -- [ ] Test state creation -- [ ] Test state loading -- [ ] Test state persistence -- [ ] Test with empty graphs -- [ ] Test with large graphs (100+ nodes) -- [ ] Test edge cases (missing data, corrupted states) - ---- - -## Phase 2: Temporal Analysis (Week 3-4) - -### Timeline Data Management -- [ ] Extend `/src/stores/stateStore.ts` - - [ ] Timeline CRUD operations - - [ ] `createTimeline()` - - [ ] `deleteTimeline()` - - [ ] `updateTimeline()` - - [ ] Timeline state management - - [ ] `addStateToTimeline()` - - [ ] `removeStateFromTimeline()` - - [ ] `reorderTimeline()` - - [ ] Timeline queries - - [ ] `getStatesByTimeline()` - - [ ] `getTimelineStates()` (ordered) - -### Temporal Metadata -- [ ] Create `/src/components/TemporalAnalysis/StateMetadataEditor.tsx` - - [ ] Temporal metadata form - - [ ] Date/time picker - - [ ] Sequence number input - - [ ] Label input - - [ ] Period range inputs - - [ ] Display format selector - - [ ] Notes/description textarea - - [ ] Tags input - -### Timeline Panel UI -- [ ] Create `/src/components/TemporalAnalysis/TimelinePanel.tsx` - - [ ] Horizontal timeline visualization - - [ ] State markers on timeline - - [ ] Click to load state - - [ ] Drag to scrub timeline - - [ ] Zoom in/out on timeline - - [ ] Pan timeline horizontally - - [ ] State creation controls - - [ ] Timeline selector dropdown (multiple timelines) - -- [ ] Timeline styling - - [ ] Color-coded state markers - - [ ] Current state highlight - - [ ] Hover effects - - [ ] Responsive design - - [ ] Collapsible panel - -### Timeline Navigation -- [ ] Add keyboard shortcuts - - [ ] `←` Previous state in timeline - - [ ] `→` Next state in timeline - - [ ] `Home` First state - - [ ] `End` Last state - -- [ ] Navigation buttons - - [ ] Previous/Next buttons - - [ ] Jump to start/end - - [ ] State counter (e.g., "3 of 12") - -### Integration -- [ ] Update `/src/App.tsx` - - [ ] Add TimelinePanel to layout - - [ ] Handle panel visibility toggle - - [ ] Handle panel resize - -- [ ] Update `/src/components/Menu/MenuBar.tsx` - - [ ] Add "States" menu - - [ ] "View Timeline" action - - [ ] "Create Timeline" action - -### Testing -- [ ] Test timeline creation -- [ ] Test state ordering -- [ ] Test timeline navigation -- [ ] Test keyboard shortcuts -- [ ] Test with multiple timelines -- [ ] Test timeline visualization with many states (20+) - ---- - -## Phase 3: Comparison & Diff Analysis (Week 5-6) - -### Diff Calculation Engine -- [ ] Create `/src/utils/stateDiff.ts` - - [ ] `calculateStateDiff()` function - - [ ] Actor comparison logic - - [ ] Detect added actors - - [ ] Detect removed actors - - [ ] Detect modified actors (label, type, position, metadata) - - [ ] Relation comparison logic - - [ ] Detect added relations - - [ ] Detect removed relations - - [ ] Detect modified relations (type, directionality, strength) - - [ ] Summary statistics calculation - - [ ] Performance optimization for large graphs - -- [ ] Create `/src/hooks/useStateDiff.ts` - - [ ] `useComparison()` hook - - [ ] `useDiffCalculation()` hook - - [ ] Memoization for expensive calculations - -### Visual Diff on Graph -- [ ] Create `/src/components/Editor/DiffOverlay.tsx` - - [ ] Overlay mode component - - [ ] Color coding for changes - - [ ] Green for added (actors/relations) - - [ ] Red for removed (actors/relations) - - [ ] Yellow/orange for modified - - [ ] Change badges/icons on nodes - - [ ] Toggle overlay on/off - - [ ] Opacity control for overlay - -- [ ] Update `/src/components/Editor/GraphEditor.tsx` - - [ ] Integrate DiffOverlay - - [ ] Apply diff styling to nodes/edges - - [ ] Animated transitions for diff highlights - -### Comparison View UI -- [ ] Create `/src/components/TemporalAnalysis/ComparisonView.tsx` - - [ ] Modal/panel for comparison - - [ ] State selectors (From/To) - - [ ] Comparison mode selector - - [ ] Side-by-side - - [ ] Overlay - - [ ] Tabbed - - [ ] Graph visualization for both states - - [ ] Synchronized panning/zooming - - [ ] Difference highlighting - - [ ] Summary statistics panel - -- [ ] Create `/src/components/TemporalAnalysis/StateDiffViewer.tsx` - - [ ] List of changes (actors/relations) - - [ ] Filter by change type (added/removed/modified) - - [ ] Click to highlight on graph - - [ ] Export changes as report - -### Change Summary -- [ ] Create `/src/components/TemporalAnalysis/ChangeSummary.tsx` - - [ ] Statistics dashboard - - [ ] Total actors (before/after) - - [ ] Total relations (before/after) - - [ ] Added/removed/modified counts - - [ ] Change breakdown by type - - [ ] Visual charts (pie chart, bar chart) - - [ ] Export to CSV/JSON - -### Quick Compare -- [ ] Add to TimelinePanel - - [ ] "Compare" button on state markers - - [ ] Right-click context menu "Compare with..." - - [ ] Select two states to compare - -- [ ] Add to state selector - - [ ] Checkbox mode to select multiple states - - [ ] "Compare Selected" button - -### Export Comparison Report -- [ ] Report generation - - [ ] PDF export with diff summary - - [ ] JSON export of diff data - - [ ] HTML report with interactive visualization - - [ ] Include screenshots of both states - -### Testing -- [ ] Test diff calculation accuracy -- [ ] Test with various graph sizes -- [ ] Test comparison view UI -- [ ] Test visual diff overlay -- [ ] Test export functionality -- [ ] Performance test with large diffs - ---- - -## Phase 4: Scenario Branching (Week 7-8) - -### Scenario Data Model -- [ ] Extend `/src/stores/stateStore.ts` - - [ ] Scenario tree management - - [ ] `createScenarioBranch()` - - [ ] `addStateToScenario()` - - [ ] `deleteScenarioBranch()` - - [ ] Scenario queries - - [ ] `getStatesByScenario()` - - [ ] `getScenarioTree()` - - [ ] `getScenarioBranches()` - -### Scenario Creation UI -- [ ] Create `/src/components/TemporalAnalysis/ScenarioCreator.tsx` - - [ ] "Branch from here" button/dialog - - [ ] Scenario metadata form - - [ ] Label input - - [ ] Description textarea - - [ ] Assumptions list input - - [ ] Probability/confidence selector - - [ ] Color picker for branch - - [ ] Parent state selection - - [ ] Create and switch to scenario - -### Scenario Tree Visualization -- [ ] Extend TimelinePanel for scenarios - - [ ] Vertical branching layout - - [ ] Branch lines/connectors - - [ ] Branch labels - - [ ] Branch color coding - - [ ] Collapse/expand branches - - [ ] Scenario navigation controls - -- [ ] Alternative: Tree view component - - [ ] Hierarchical tree visualization - - [ ] Collapsible nodes - - [ ] Click to load state - - [ ] Branch context menu (edit, delete, compare) - -### Scenario Metadata Editor -- [ ] Update StateMetadataEditor - - [ ] Scenario-specific fields - - [ ] Assumptions editor (add/remove/edit) - - [ ] Probability slider - - [ ] Confidence level selector - - [ ] Branch color picker - -### Scenario Navigation -- [ ] Scenario switcher - - [ ] Dropdown to select branch - - [ ] Filter timeline by scenario - - [ ] Visual indicator of current branch - -- [ ] Branch comparison - - [ ] "Compare branches" action - - [ ] Select multiple scenarios - - [ ] Side-by-side comparison - - [ ] Outcome analysis - -### Integration -- [ ] Update menu bar - - [ ] "Create Scenario" menu item - - [ ] "Manage Scenarios" menu item - -- [ ] Update state selector - - [ ] Group states by scenario - - [ ] Scenario branch indicators - -### Testing -- [ ] Test scenario creation -- [ ] Test branch visualization -- [ ] Test scenario navigation -- [ ] Test with multiple branches -- [ ] Test nested scenarios (if supported) -- [ ] Test scenario deletion - ---- - -## Phase 5: Actor Tracking & Journeys (Week 9-10) - -### Journey Calculation -- [ ] Create `/src/utils/actorJourney.ts` - - [ ] `getActorJourney()` function - - [ ] Track actor across states - - [ ] Detect first/last appearance - - [ ] Calculate property changes - - [ ] Track relationship evolution - - [ ] Performance optimization - -- [ ] Create `/src/hooks/useActorJourney.ts` - - [ ] `useActorJourney()` hook - - [ ] `useMultiActorJourney()` hook - - [ ] Memoization - -### Journey Viewer UI -- [ ] Create `/src/components/TemporalAnalysis/ActorJourneyViewer.tsx` - - [ ] Actor selection interface - - [ ] Dropdown or autocomplete - - [ ] Search by label - - [ ] Select from graph click - - [ ] Timeline visualization - - [ ] Horizontal timeline - - [ ] Actor appearance markers - - [ ] Property change indicators - - [ ] Property evolution display - - [ ] Label changes - - [ ] Type changes - - [ ] Position changes - - [ ] Relationship changes display - - [ ] Relations added/removed - - [ ] Relation type changes - - [ ] Relation strength changes - -### Multi-Actor Comparison -- [ ] Select multiple actors -- [ ] Overlay journeys on same timeline -- [ ] Compare property evolution -- [ ] Compare relationship dynamics -- [ ] Identify interaction points - -### Journey Export -- [ ] Export journey data - - [ ] CSV export - - [ ] JSON export - - [ ] PDF report with visualizations -- [ ] Include: - - [ ] Actor metadata - - [ ] State sequence - - [ ] Property changes - - [ ] Relationship changes - - [ ] Summary statistics - -### Integration -- [ ] Add to right panel - - [ ] "View Journey" button on actor selection - - [ ] Quick journey view - -- [ ] Add to menu bar - - [ ] "Actor Journeys" menu item - - [ ] Keyboard shortcut (Ctrl+J) - -### Testing -- [ ] Test journey calculation -- [ ] Test with various actor types -- [ ] Test with actors that appear/disappear -- [ ] Test multi-actor comparison -- [ ] Test export functionality - ---- - -## Phase 6: Animation & Presentation (Week 11-12) - -### Animation Engine -- [ ] Create `/src/utils/stateAnimation.ts` - - [ ] `interpolateStates()` function - - [ ] Position interpolation - - [ ] Opacity interpolation (fade in/out) - - [ ] Size interpolation - - [ ] Color interpolation - - [ ] Easing functions (linear, ease-in-out, etc.) - -- [ ] Create `/src/hooks/useStateAnimation.ts` - - [ ] `useAnimation()` hook - - [ ] Animation state management - - [ ] Frame rate control - - [ ] Performance optimization - -### Animation Controls -- [ ] Create `/src/components/TemporalAnalysis/StateAnimator.tsx` - - [ ] Play/pause button - - [ ] Step forward/backward buttons - - [ ] Speed control slider - - [ ] Loop toggle - - [ ] Progress bar - - [ ] Frame counter - - [ ] Quality settings (performance vs. smoothness) - -- [ ] Integrate with TimelinePanel - - [ ] Animation controls bar - - [ ] Visual playhead on timeline - - [ ] Click timeline to jump to state - -### Animation Modes -- [ ] Sequential (state A → B → C) -- [ ] Comparison (fade between two states) -- [ ] Journey (follow actor across states) -- [ ] Custom sequence (user-selected states) - -### Presentation Mode -- [ ] Create `/src/components/TemporalAnalysis/PresentationMode.tsx` - - [ ] Full-screen mode - - [ ] Slideshow interface - - [ ] State sequence selector - - [ ] Auto-advance with timer - - [ ] Manual navigation (arrow keys) - - [ ] Annotation overlays - - [ ] Title for each state - - [ ] Notes/narration text - - [ ] Key insights callouts - - [ ] Exit presentation (Esc key) - -### Export Capabilities (Stretch Goal) -- [ ] Export animation - - [ ] Animated GIF export - - [ ] Video export (WebM/MP4) - may require server-side - - [ ] Frame sequence export (PNG images) - - [ ] Interactive HTML export - -### Integration -- [ ] Add to menu bar - - [ ] "Animate Timeline" menu item - - [ ] "Presentation Mode" menu item - - [ ] Keyboard shortcut (Ctrl+Shift+P) - -- [ ] Add to timeline panel - - [ ] Play button - - [ ] Presentation mode button - -### Testing -- [ ] Test animation smoothness -- [ ] Test with various animation speeds -- [ ] Test with large state transitions -- [ ] Test presentation mode navigation -- [ ] Performance test with complex graphs -- [ ] Test on different screen sizes - ---- - -## Phase 7: ChromaDB Integration (Week 13-14) - -### ChromaDB Setup -- [ ] Install ChromaDB dependencies - - [ ] Add to package.json - - [ ] Configure ChromaDB client - -- [ ] Create `/src/utils/chromaIntegration.ts` - - [ ] Initialize ChromaDB client - - [ ] Connection management - - [ ] Error handling - -### Collection Setup -- [ ] Create collections - - [ ] `constellation_states` - state metadata - - [ ] `actor_journeys` - actor trajectories - - [ ] `state_comparisons` - cached comparisons - - [ ] `annotations` - user notes and insights - -- [ ] Define schemas - - [ ] Metadata fields - - [ ] Embedding strategies - - [ ] Query filters - -### State Indexing -- [ ] Create indexing functions - - [ ] `indexState()` - index single state - - [ ] `batchIndexStates()` - index multiple states - - [ ] `updateStateIndex()` - update existing index - - [ ] `removeStateIndex()` - remove from index - -- [ ] Generate embeddings - - [ ] Combine label, description, notes, assumptions - - [ ] Use ChromaDB's built-in embedding - - [ ] Handle long text (truncation/summarization) - -### Semantic Search -- [ ] Create search functions - - [ ] `searchStates()` - general search - - [ ] `findSimilarStates()` - similarity search - - [ ] `searchByTags()` - tag-based search - - [ ] `searchByTimeRange()` - temporal search - -- [ ] Create `/src/components/TemporalAnalysis/StateSearch.tsx` - - [ ] Search input - - [ ] Search filters (type, timeline, scenario, tags) - - [ ] Results list - - [ ] Click to load state - - [ ] Relevance scoring display - -### Pattern Recognition -- [ ] Implement pattern detection - - [ ] Identify similar network structures - - [ ] Find recurring patterns - - [ ] Detect anomalies - - [ ] Trend analysis - -- [ ] Create pattern visualization - - [ ] Display pattern clusters - - [ ] Highlight similar states - - [ ] Generate insights - -### Annotation Storage -- [ ] Store annotations in ChromaDB - - [ ] Link to specific states - - [ ] Link to specific changes - - [ ] Support rich text - - [ ] Support tags - -- [ ] Create annotation search - - [ ] Search within annotations - - [ ] Find states by annotation content - -### Caching Strategy -- [ ] Cache diff calculations - - [ ] Store diff results in ChromaDB - - [ ] Retrieve cached diffs - - [ ] Invalidate cache on state changes - -- [ ] Cache journey calculations - - [ ] Store journey data - - [ ] Update on relevant state changes - -### Integration -- [ ] Add to state store - - [ ] `indexStateForSearch()` action - - [ ] `searchStates()` action - -- [ ] Add to UI - - [ ] Search box in state selector - - [ ] "Find similar" button on states - - [ ] Pattern insights panel - -### Testing -- [ ] Test indexing performance -- [ ] Test search accuracy -- [ ] Test similarity detection -- [ ] Test caching effectiveness -- [ ] Test with large state collections (100+ states) - ---- - -## Phase 8: Advanced Features (Week 15-16) - -### Automatic State Capture -- [ ] Implement auto-capture - - [ ] Periodic snapshots (e.g., every 10 minutes) - - [ ] Significant change detection (threshold-based) - - [ ] User-configurable triggers - - [ ] Auto-cleanup of old snapshots - -- [ ] Settings UI - - [ ] Enable/disable auto-capture - - [ ] Configure frequency - - [ ] Configure retention policy - -### State Templates -- [ ] Template creation - - [ ] Save state as template - - [ ] Template metadata (name, description, category) - - [ ] Template preview - -- [ ] Template application - - [ ] Browse template library - - [ ] Apply template to create new state - - [ ] Customize template on application - -- [ ] Template management - - [ ] Edit templates - - [ ] Delete templates - - [ ] Import/export templates - -### Collaborative Features (Stretch Goal) -- [ ] Sharing - - [ ] Export shareable link to timeline - - [ ] Export shareable link to scenario - - [ ] Embed code for presentations - -- [ ] Comments - - [ ] Comment on specific states - - [ ] Reply to comments - - [ ] @mentions - - [ ] Resolve comments - -- [ ] Approvals (Stretch Goal) - - [ ] Submit state for review - - [ ] Approve/reject states - - [ ] Review workflow - -### Advanced Analytics -- [ ] Network metrics over time - - [ ] Density evolution - - [ ] Centrality changes - - [ ] Clustering coefficient - - [ ] Path lengths - -- [ ] Statistical analysis - - [ ] Correlation analysis - - [ ] Regression models - - [ ] Predictive analytics - -- [ ] Export to analysis tools - - [ ] Export time-series data - - [ ] Export to CSV for Excel/R/Python - - [ ] API for external tools - -### Testing -- [ ] Test auto-capture functionality -- [ ] Test template system -- [ ] Test collaborative features -- [ ] Test analytics calculations -- [ ] Integration testing with all phases - ---- - -## Final Polish & Documentation - -### Performance Optimization -- [ ] Profile performance bottlenecks -- [ ] Optimize diff calculation -- [ ] Optimize rendering for many states -- [ ] Lazy loading for large timelines -- [ ] Implement virtualization where needed - -### Error Handling -- [ ] Graceful degradation for missing data -- [ ] User-friendly error messages -- [ ] Rollback on failed operations -- [ ] Data validation throughout - -### Accessibility -- [ ] Keyboard navigation for all features -- [ ] Screen reader support -- [ ] High contrast mode support -- [ ] Focus indicators -- [ ] ARIA labels - -### Documentation -- [ ] User guide (see TEMPORAL_QUICK_START.md) - - [ ] Getting started tutorial - - [ ] Feature walkthroughs - - [ ] Best practices - - [ ] FAQ - -- [ ] Developer documentation - - [ ] API reference - - [ ] Type definitions - - [ ] Architecture overview - - [ ] Extension guide - -- [ ] Video tutorials - - [ ] Overview video - - [ ] Feature-specific videos - - [ ] Advanced use cases - -### Testing -- [ ] Unit tests for all utilities -- [ ] Integration tests for stores -- [ ] Component tests for UI -- [ ] E2E tests for workflows -- [ ] Performance benchmarks -- [ ] User acceptance testing - -### Release -- [ ] Version bump -- [ ] Changelog -- [ ] Migration guide for existing users -- [ ] Announcement post/blog -- [ ] Update README - ---- - -## Success Metrics - -Track these metrics post-release: - -- [ ] Feature adoption rate - - [ ] % of users who enable states - - [ ] % of documents with states - - [ ] Average states per document - -- [ ] Usage patterns - - [ ] Temporal vs. scenario usage - - [ ] Most used features (comparison, animation, journeys) - - [ ] Average session time with states - -- [ ] Performance - - [ ] State creation time - - [ ] Diff calculation time - - [ ] Animation frame rate - - [ ] ChromaDB query latency - -- [ ] User satisfaction - - [ ] User feedback/ratings - - [ ] Support tickets related to states - - [ ] Feature requests - ---- - -## Notes - -- Each phase builds on the previous one -- Test thoroughly before moving to next phase -- Gather user feedback early and often -- Iterate based on actual usage patterns -- Keep performance in mind throughout -- Document as you go - -**Current Status**: Not started -**Next Step**: Begin Phase 1 - Core State Management diff --git a/docs/KEYBOARD_SHORTCUTS.md b/docs/KEYBOARD_SHORTCUTS.md deleted file mode 100644 index d2cf7ec..0000000 --- a/docs/KEYBOARD_SHORTCUTS.md +++ /dev/null @@ -1,279 +0,0 @@ -# Keyboard Shortcuts System - -## Overview - -Constellation Analyzer now features a centralized keyboard shortcut management system that prevents conflicts, provides priority-based handling, and offers built-in documentation through a help modal. - -## Architecture - -### Core Components - -1. **useKeyboardShortcutManager** (`src/hooks/useKeyboardShortcutManager.ts`) - - Core hook that manages shortcut registration and event handling - - Provides conflict detection - - Supports priority-based execution - - Platform-aware (Mac vs Windows/Linux) - -2. **KeyboardShortcutContext** (`src/contexts/KeyboardShortcutContext.tsx`) - - React context provider making the shortcut manager available throughout the app - - Ensures single global event listener for all shortcuts - -3. **useGlobalShortcuts** (`src/hooks/useGlobalShortcuts.ts`) - - Centralized registration of all application-wide shortcuts - - Single source of truth for what shortcuts exist - -4. **KeyboardShortcutsHelp** (`src/components/Common/KeyboardShortcutsHelp.tsx`) - - Modal component displaying all available shortcuts - - Automatically generated from registered shortcuts - - Grouped by category - -## Available Shortcuts - -### Document Management -- **Ctrl+N** - New Document -- **Ctrl+O** - Open Document Manager -- **Ctrl+S** - Export Document -- **Ctrl+W** - Close Current Document - -### Graph Editing -- **Ctrl+Z** - Undo -- **Ctrl+Y** or **Ctrl+Shift+Z** - Redo -- **Delete** or **Backspace** - Delete selected nodes/edges (handled by React Flow) - -### Selection -- **Ctrl+A** - Select All (placeholder for future implementation) -- **Escape** - Deselect All (handled by React Flow) - -### View -- **F** - Fit View to Content - -### Navigation -- **Ctrl+Tab** - Next Document -- **Ctrl+Shift+Tab** - Previous Document -- **?** - Show Keyboard Shortcuts Help - -## Implementation Details - -### Shortcut Definition - -Shortcuts are defined using the `KeyboardShortcut` interface: - -```typescript -interface KeyboardShortcut { - id: string; // Unique identifier - description: string; // Shown in help UI - key: string; // Key to press - ctrl?: boolean; // Requires Ctrl/Cmd modifier - shift?: boolean; // Requires Shift modifier - alt?: boolean; // Requires Alt/Option modifier - handler: () => void; // Function to execute - priority?: number; // Higher = executed first (default: 0) - category: ShortcutCategory; // For grouping in help - enabled?: boolean; // Can be disabled (default: true) -} -``` - -### Platform Detection - -The system automatically detects the platform: -- **Mac**: Uses `Cmd` key (metaKey) -- **Windows/Linux**: Uses `Ctrl` key (ctrlKey) - -Display strings adapt accordingly: -- Mac: "Cmd+N" -- Windows/Linux: "Ctrl+N" - -### Conflict Detection - -When registering a shortcut, the system checks for conflicts: -- Same key combination -- Same modifiers -- Different ID - -Conflicts are logged to console as warnings but don't prevent registration. - -### Priority Handling - -If multiple shortcuts match the same key combination: -1. Sort by priority (higher number = higher priority) -2. Execute only the highest priority handler -3. Default priority is 0 - -Example: Ctrl+Shift+Z has lower priority than Ctrl+Y for redo, so Ctrl+Y is preferred. - -## Adding New Shortcuts - -### Global Shortcuts - -Add to `src/hooks/useGlobalShortcuts.ts`: - -```typescript -const shortcutDefinitions: KeyboardShortcut[] = [ - // ... existing shortcuts - { - id: 'my-new-shortcut', - description: 'Do Something', - key: 'k', - ctrl: true, - handler: () => doSomething(), - category: 'Graph Editing', - }, -]; -``` - -### Component-Specific Shortcuts - -Use the context in any component: - -```typescript -import { useKeyboardShortcuts } from '../contexts/KeyboardShortcutContext'; - -function MyComponent() { - const { shortcuts } = useKeyboardShortcuts(); - - useEffect(() => { - shortcuts.register({ - id: 'component-specific', - description: 'Component Action', - key: 'x', - ctrl: true, - handler: () => handleAction(), - category: 'Graph Editing', - }); - - return () => shortcuts.unregister('component-specific'); - }, [shortcuts]); -} -``` - -### Adding Menu Items - -When adding a new shortcut that should appear in the menu, update `MenuBar.tsx`: - -```tsx - -``` - -## Design Decisions - -### Why Not Use `?` as a Regular Character - -The `?` key doesn't require Shift in the shortcut definition because: -- It's simpler for users to press just `?` -- Consistent with industry standards (VS Code, GitHub, etc.) -- The key value is already `?` when Shift is pressed - -### Why Centralized vs Distributed - -**Advantages of centralized system:** -- Single source of truth for all shortcuts -- Conflict detection -- Automatic help documentation -- Easier to maintain and audit -- Priority-based resolution - -**Disadvantages:** -- Slightly more complex initial setup -- All shortcuts must be registered centrally or cleanup properly - -### Why Context vs Global Singleton - -Using React Context provides: -- Better integration with React lifecycle -- Automatic cleanup -- Testability -- Type safety - -## Migration from Old System - -The old `useKeyboardShortcuts` hook has been replaced with `useGlobalShortcuts`. The migration involved: - -1. **Before**: Event listeners scattered across components -2. **After**: Centralized registration with automatic documentation - -The old hook has been preserved for reference but should not be used for new shortcuts. - -## Future Enhancements - -### Possible Additions - -1. **User-Configurable Shortcuts** - - Allow users to customize key bindings - - Store in localStorage - - UI for rebinding - -2. **Shortcut Contexts** - - Different shortcuts active in different app modes - - Disable/enable groups of shortcuts - -3. **Chord Shortcuts** - - Multi-key sequences (e.g., "Ctrl+K, Ctrl+S") - - Inspired by VS Code - -4. **Shortcut Recording** - - Let users record custom shortcuts - - Visual feedback during recording - -5. **Platform-Specific Overrides** - - Different shortcuts for Mac vs Windows - - Better ergonomics per platform - -### Excluded from Current Implementation - -**Node Type Creation Shortcuts** (e.g., P for Person, O for Organization) -- **Reason**: User-configurable node types make fixed shortcuts inappropriate -- **Alternative**: Context menu (right-click) or toolbar remain the recommended methods -- Users can have custom types like "Department", "Resource", etc., so hardcoded letters wouldn't make sense - -## Testing - -To test the keyboard shortcut system: - -1. **Build the application**: `npm run build` -2. **Start the dev server**: `npm run dev` -3. **Test shortcuts**: - - Press `?` to see all available shortcuts - - Try Ctrl+N for new document - - Try Ctrl+Z/Ctrl+Y for undo/redo - - Try F to fit view -4. **Check conflict detection**: - - Look at browser console during startup - - Verify no conflict warnings appear - -## Troubleshooting - -### Shortcut Not Working - -1. Check browser console for conflict warnings -2. Verify shortcut is registered in `useGlobalShortcuts` -3. Check if handler is properly passed (not undefined) -4. Verify `enabled` is not set to false -5. Check if another shortcut has higher priority - -### Shortcut Not Appearing in Help - -1. Verify `enabled` is not set to false -2. Check the category is correct -3. Ensure shortcut is registered before help modal opens - -### Conflicts - -If you see conflict warnings: -1. Change one of the conflicting shortcuts -2. Or use priority to determine which should win -3. Or disable one of the shortcuts conditionally - -## References - -- UX Analysis: `UX_ANALYSIS.md` (lines 58-104) -- Implementation docs: Inline comments in source files -- React Flow keyboard handling: https://reactflow.dev/learn/advanced-use/accessibility diff --git a/docs/MULTI_FILE_PLAN.md b/docs/MULTI_FILE_PLAN.md deleted file mode 100644 index aab5ecb..0000000 --- a/docs/MULTI_FILE_PLAN.md +++ /dev/null @@ -1,564 +0,0 @@ -# Multi-File/Multi-Document Architecture Plan - -## Overview - -Transform Constellation Analyzer from a single-document app to a multi-document workspace with tabbed interface, leveraging the existing persistence infrastructure. - ---- - -## 1. Core Concept: Workspace vs Document - -### Current Architecture -- **Single Document**: One graph with its nodes, edges, nodeTypes, edgeTypes -- **Auto-save**: Automatically saves to `localStorage` under one key - -### New Architecture -- **Workspace**: Container for multiple documents + workspace settings -- **Documents**: Individual constellation analyses (each is a `ConstellationDocument`) -- **Active Document**: The currently visible/editable document in a tab -- **Workspace Settings**: Cross-document preferences, recent files list, tab order - ---- - -## 2. Data Model Evolution - -### Workspace Structure -```typescript -interface WorkspaceState { - // Workspace metadata - workspaceId: string; // Unique workspace ID - workspaceName: string; // "My Workspace" - - // Document management - documents: Map; // documentId -> document - documentOrder: string[]; // Order of tabs - activeDocumentId: string | null; // Currently visible document - - // Document metadata (separate from document content for performance) - documentMetadata: Map; - - // Workspace-level settings - settings: WorkspaceSettings; -} - -interface DocumentMetadata { - id: string; - title: string; // User-friendly name - fileName?: string; // If loaded from file - filePath?: string; // For future file system access - isDirty: boolean; // Has unsaved changes - lastModified: string; // ISO timestamp - thumbnail?: string; // Base64 mini-preview (optional) - color?: string; // Tab color identifier -} - -interface WorkspaceSettings { - maxOpenDocuments: number; // Limit tabs (e.g., 10) - autoSaveEnabled: boolean; - defaultNodeTypes: NodeTypeConfig[]; // Workspace defaults - defaultEdgeTypes: EdgeTypeConfig[]; // Workspace defaults - recentFiles: RecentFile[]; // Recently opened files -} - -interface RecentFile { - path: string; - title: string; - lastOpened: string; - thumbnail?: string; -} -``` - -### Updated ConstellationDocument -```typescript -// Already exists, but add: -interface ConstellationDocument { - // ... existing fields - metadata: { - // ... existing fields - documentId: string; // NEW: Unique document ID - title: string; // NEW: Document title - }; - graph: { - // ... existing: nodes, edges, nodeTypes, edgeTypes - }; -} -``` - ---- - -## 3. Storage Strategy - -### LocalStorage Key Structure -```typescript -const STORAGE_KEYS = { - // Workspace-level - WORKSPACE_STATE: 'constellation:workspace:v1', - WORKSPACE_SETTINGS: 'constellation:workspace:settings:v1', - - // Document-level (dynamic) - DOCUMENT_PREFIX: 'constellation:document:v1:', // + documentId - DOCUMENT_METADATA_PREFIX: 'constellation:meta:v1:', // + documentId - - // Legacy (for migration) - LEGACY_GRAPH_STATE: 'constellation:graph:v1', -}; -``` - -### Storage Pattern -``` -localStorage: - ├─ constellation:workspace:v1 - │ → { workspaceId, workspaceName, documentOrder, activeDocumentId } - │ - ├─ constellation:workspace:settings:v1 - │ → { maxOpenDocuments, autoSaveEnabled, defaultNodeTypes, ... } - │ - ├─ constellation:document:v1:doc-123 - │ → Full ConstellationDocument - │ - ├─ constellation:meta:v1:doc-123 - │ → DocumentMetadata (for quick loading) - │ - └─ ... (more documents) -``` - -**Benefits:** -- **Partial loading**: Load metadata first, full documents on demand -- **Quota management**: Can delete old documents individually -- **Performance**: Don't load all documents at startup -- **Granular auto-save**: Only save changed documents - ---- - -## 4. Architecture Changes - -### New Store: `workspaceStore.ts` -```typescript -interface WorkspaceStore { - // Workspace state - workspaceId: string; - workspaceName: string; - documentOrder: string[]; - activeDocumentId: string | null; - documentMetadata: Map; - settings: WorkspaceSettings; - - // Document management - documents: Map; // Only loaded docs in memory - - // Actions - createDocument: (title?: string) => string; // Returns documentId - loadDocument: (documentId: string) => void; - closeDocument: (documentId: string) => void; - deleteDocument: (documentId: string) => void; - renameDocument: (documentId: string, newTitle: string) => void; - duplicateDocument: (documentId: string) => string; - - switchToDocument: (documentId: string) => void; - reorderDocuments: (newOrder: string[]) => void; - - importDocumentFromFile: (file: File) => Promise; - exportDocument: (documentId: string) => void; - - // Workspace actions - saveWorkspace: () => void; - loadWorkspace: () => void; - clearWorkspace: () => void; -} -``` - -### Updated `graphStore.ts` -```typescript -// REFACTOR: Make graphStore document-scoped -interface GraphStore { - // Remove persistence - now handled by workspace - nodes: Actor[]; - edges: Relation[]; - nodeTypes: NodeTypeConfig[]; - edgeTypes: EdgeTypeConfig[]; - - // Same CRUD operations, but no auto-save to localStorage - // Instead, mark document as dirty in workspace - addNode: (node: Actor) => void; - updateNode: (id: string, updates: Partial) => void; - // ... etc - - // NEW: Hook to notify workspace of changes - _onChangeCallback?: () => void; -} - -// Create instance per document -const createGraphStore = (documentId: string) => { - return create((set) => ({ - // ... existing implementation - // But call _onChangeCallback on mutations - })); -}; -``` - -### Store Relationship -``` -workspaceStore (singleton) - ├─ Manages document metadata - ├─ Manages active document - └─ Delegates graph operations to active graphStore - -graphStoreInstances (Map) - ├─ One instance per loaded document - ├─ Active instance linked to UI - └─ Notifies workspace on changes -``` - ---- - -## 5. UI Changes - -### New Components - -#### 1. **DocumentTabs** (top of editor) -```tsx - - - - - -``` - -Features: -- Close button (X) with unsaved warning -- Drag-to-reorder tabs -- Double-click to rename -- Right-click context menu (rename, duplicate, delete, export) -- Visual indicator for unsaved changes (dot or asterisk) -- Tab overflow handling (scroll or dropdown) - -#### 2. **DocumentManager** (sidebar or modal) -```tsx - - - {documents.map(doc => ( - openDocument(doc.id)} - onDelete={() => deleteDocument(doc.id)} - /> - ))} - - - - -``` - -#### 3. **UnsavedChangesDialog** -```tsx - -``` - -### Updated App Structure -```tsx - -
- Constellation Analyzer - -
- - {/* NEW */} - - - - - - {/* NEW */} -
-``` - ---- - -## 6. Persistence Flow - -### Auto-Save Strategy -```typescript -// Workspace-level debounced save -let workspaceSaveTimeout: NodeJS.Timeout; - -const saveWorkspace = debounce(() => { - // Save workspace metadata - localStorage.setItem( - STORAGE_KEYS.WORKSPACE_STATE, - JSON.stringify(workspaceState) - ); -}, 1000); - -// Document-level debounced save -const saveDocument = debounce((documentId: string) => { - const doc = documents.get(documentId); - if (!doc) return; - - // Save full document - localStorage.setItem( - `${STORAGE_KEYS.DOCUMENT_PREFIX}${documentId}`, - JSON.stringify(doc) - ); - - // Update metadata - const meta = documentMetadata.get(documentId); - if (meta) { - meta.isDirty = false; - meta.lastModified = new Date().toISOString(); - localStorage.setItem( - `${STORAGE_KEYS.DOCUMENT_METADATA_PREFIX}${documentId}`, - JSON.stringify(meta) - ); - } -}, 1000); - -// On graph change -graphStore.subscribe((state) => { - markDocumentDirty(activeDocumentId); - saveDocument(activeDocumentId); -}); -``` - -### Startup Sequence -``` -1. App loads -2. Load workspace metadata from localStorage -3. Load all document metadata (lightweight) -4. If activeDocumentId exists, load that document -5. Create graphStore instance for active document -6. Render UI with tabs and active graph -``` - -### Tab Switch Flow -``` -1. User clicks different tab -2. Check if current document has unsaved changes - → If yes and auto-save disabled, show dialog -3. Save current document (if needed) -4. Load target document from localStorage (if not in memory) -5. Switch activeDocumentId -6. Update graphStore reference -7. GraphEditor re-renders with new data -``` - ---- - -## 7. Migration Strategy - -### Migrating from Single-Doc to Multi-Doc - -```typescript -// src/stores/persistence/migration-workspace.ts -export function migrateToWorkspace(): WorkspaceState | null { - // Check for legacy data - const legacyData = localStorage.getItem(STORAGE_KEYS.LEGACY_GRAPH_STATE); - if (!legacyData) return null; - - try { - const oldDoc = JSON.parse(legacyData) as ConstellationDocument; - - // Create first document from legacy data - const documentId = generateDocumentId(); - const newDoc: ConstellationDocument = { - ...oldDoc, - metadata: { - ...oldDoc.metadata, - documentId, - title: 'Imported Analysis', - }, - }; - - // Create workspace - const workspace: WorkspaceState = { - workspaceId: generateWorkspaceId(), - workspaceName: 'My Workspace', - documentOrder: [documentId], - activeDocumentId: documentId, - documentMetadata: new Map([[documentId, { - id: documentId, - title: 'Imported Analysis', - isDirty: false, - lastModified: new Date().toISOString(), - }]]), - documents: new Map([[documentId, newDoc]]), - settings: { - maxOpenDocuments: 10, - autoSaveEnabled: true, - defaultNodeTypes: oldDoc.graph.nodeTypes, - defaultEdgeTypes: oldDoc.graph.edgeTypes, - recentFiles: [], - }, - }; - - // Save to new format - saveWorkspace(workspace); - saveDocument(documentId, newDoc); - - // Remove legacy data - localStorage.removeItem(STORAGE_KEYS.LEGACY_GRAPH_STATE); - - return workspace; - } catch (error) { - console.error('Migration failed:', error); - return null; - } -} -``` - ---- - -## 8. Implementation Phases - -### Phase 1: Foundation (Multi-Doc Store) -- [ ] Create `workspaceStore.ts` with document management -- [ ] Refactor `graphStore.ts` to be instance-based -- [ ] Update storage keys and persistence layer -- [ ] Implement migration from single-doc to multi-doc -- [ ] Basic create/load/delete document functionality - -### Phase 2: UI - Tabs -- [ ] Create `DocumentTabs` component -- [ ] Implement tab switching logic -- [ ] Add close/rename tab functionality -- [ ] Handle unsaved changes dialog -- [ ] Visual indicators (dirty state, active tab) - -### Phase 3: Document Management -- [ ] Create `DocumentManager` component (grid view) -- [ ] Implement import from file → new document -- [ ] Implement export single document -- [ ] Add duplicate document functionality -- [ ] Thumbnail generation (optional) - -### Phase 4: Advanced Features -- [ ] Drag-to-reorder tabs -- [ ] Recent files list -- [ ] Tab context menu (right-click) -- [ ] Keyboard shortcuts (Ctrl+Tab, Ctrl+W, etc.) -- [ ] Search/filter documents in manager - -### Phase 5: Polish & Optimization -- [ ] Lazy loading: Load documents on-demand -- [ ] Memory management: Unload inactive documents -- [ ] Tab overflow handling (scroll or dropdown) -- [ ] Export all documents as ZIP -- [ ] Workspace import/export - ---- - -## 9. Key Technical Decisions - -### 1. **Store Architecture: Singleton Workspace + Instance-based Graph** -- **Why**: GraphStore contains mutable state that must be isolated per document -- **How**: `Map` managed by workspace - -### 2. **Lazy Document Loading** -- **Why**: Don't load 20 full documents at startup -- **How**: Load metadata first, full documents when tab is activated - -### 3. **Document ID Generation** -```typescript -const generateDocumentId = () => - `doc_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; -``` - -### 4. **Auto-Save per Document** -- Each document saves independently -- Debounced per document (not global) -- Workspace state saves separately (tab order, active doc) - -### 5. **Unsaved Changes Handling** -```typescript -const canCloseDocument = (docId: string): boolean => { - const meta = documentMetadata.get(docId); - if (!meta?.isDirty) return true; - - return window.confirm(`"${meta.title}" has unsaved changes. Close anyway?`); -}; -``` - -### 6. **Default Values Strategy** -- Workspace has default nodeTypes/edgeTypes -- New documents inherit workspace defaults -- Individual documents can customize their types -- Workspace defaults can be updated from any document - ---- - -## 10. Future Enhancements - -### Potential Features -1. **Document Templates**: Pre-configured node/edge types -2. **Document Linking**: Reference nodes across documents -3. **Workspace Sharing**: Export entire workspace to file -4. **Cloud Sync**: Replace localStorage with backend -5. **Collaborative Editing**: Multi-user support -6. **Version History**: Document snapshots -7. **Document Tags/Categories**: Organize many documents -8. **Search Across Documents**: Find nodes/edges globally - ---- - -## 11. Risk Mitigation - -### LocalStorage Quota -- **Risk**: 5-10MB limit, could fill with many documents -- **Mitigation**: - - Show storage usage indicator - - Warn when approaching limit - - Offer to delete old documents - - Implement document export before delete - -### Performance -- **Risk**: Many documents slow down UI -- **Mitigation**: - - Lazy loading - - Virtual scrolling for document manager - - Limit open tabs (configurable) - - Unload inactive documents from memory - -### Data Loss -- **Risk**: Corrupted document affects all -- **Mitigation**: - - Each document stored separately - - Backup on export - - Migration safety checks - ---- - -## Summary - -This multi-file architecture: -✅ Leverages existing `ConstellationDocument` schema -✅ Reuses persistence infrastructure (saver, loader, validation) -✅ Maintains backward compatibility via migration -✅ Provides professional multi-document UX -✅ Scales to many documents with lazy loading -✅ Keeps data safe with per-document isolation - -**Key Insight**: The existing persistence layer is perfectly suited for this - we just need to change from "one document in localStorage" to "many documents in localStorage", managed by a workspace orchestrator. diff --git a/docs/PERSISTENCE_PLAN.md b/docs/PERSISTENCE_PLAN.md deleted file mode 100644 index 56bd048..0000000 --- a/docs/PERSISTENCE_PLAN.md +++ /dev/null @@ -1,339 +0,0 @@ -# Local Storage Persistence Plan for Constellation Analyzer - -## 1. Data Format Specification - -### JSON Schema Structure - -```typescript -interface ConstellationDocument { - // Metadata - metadata: { - version: string; // Schema version (e.g., "1.0.0") - appName: string; // "constellation-analyzer" - createdAt: string; // ISO timestamp - updatedAt: string; // ISO timestamp - lastSavedBy: string; // Browser fingerprint or "unknown" - }; - - // Graph state - graph: { - nodes: SerializedActor[]; // Simplified Actor[] without React Flow internals - edges: SerializedRelation[]; // Simplified Relation[] without React Flow internals - nodeTypes: NodeTypeConfig[]; // Already serializable - edgeTypes: EdgeTypeConfig[]; // Already serializable - }; - - // Editor settings (optional - may persist separately) - editorSettings?: EditorSettings; -} - -// Simplified node structure for storage -interface SerializedActor { - id: string; - type: string; // React Flow node type (e.g., "custom") - position: { x: number; y: number }; - data: ActorData; - selected?: boolean; - dragging?: boolean; -} - -// Simplified edge structure for storage -interface SerializedRelation { - id: string; - source: string; - target: string; - type?: string; // React Flow edge type - data: RelationData; - sourceHandle?: string | null; - targetHandle?: string | null; -} -``` - -**Rationale:** -- Separate metadata for versioning and migration support -- Exclude React Flow-specific runtime properties (measured, width, height, etc.) -- Store minimal required data to reconstruct full state -- Include timestamps for debugging and conflict resolution - ---- - -## 2. Storage Strategy - -### Architecture Choice: **Zustand Middleware Pattern** - -**Recommended approach:** Create a custom Zustand middleware that intercepts state changes and persists to localStorage. - -### Storage Keys Strategy - -```typescript -const STORAGE_KEYS = { - GRAPH_STATE: 'constellation:graph:v1', // Main graph data - EDITOR_SETTINGS: 'constellation:editor:v1', // Editor preferences - AUTOSAVE_FLAG: 'constellation:autosave', // Flag for crash recovery - LAST_SAVED: 'constellation:lastSaved', // Timestamp -}; -``` - -**Why separate keys:** -- Allow partial updates (save graph independently from settings) -- Different persistence strategies (graph = debounced, settings = immediate) -- Easier to manage storage quota - -### Debouncing Strategy - -```typescript -// Debounce configuration -const DEBOUNCE_CONFIG = { - DELAY: 1000, // 1 second after last change - MAX_WAIT: 5000, // Force save every 5 seconds - THROTTLE_NODE_DRAG: 500, // Faster saves during drag operations -}; -``` - -**Implementation approach:** -- Use `lodash.debounce` or custom implementation -- Different debounce times for different operations: - - Node dragging: 500ms (frequent but predictable) - - Adding/deleting: 1000ms (less frequent) - - Typing in properties: 1000ms (standard) -- Max wait ensures data isn't lost even during continuous editing - -### When to Save - -**Auto-save triggers:** -1. Any GraphStore state mutation (nodes, edges, nodeTypes, edgeTypes) -2. EditorStore settings changes (optional, can be immediate) -3. Before window unload (emergency save) -4. After successful import (to persist imported state) - -**Don't save:** -- Temporary UI state (selectedRelationType, hover states) -- React Flow internals (viewport, connection state) - ---- - -## 3. Loading Strategy - -### Bootstrap Sequence - -``` -1. App starts → Check for stored data -2. Validate schema version -3. If valid: Deserialize → Hydrate store -4. If invalid: Attempt migration OR use defaults -5. If corrupted: Show recovery dialog → Load defaults -6. Set up auto-save listeners -``` - -### Validation Approach - -Use runtime validation to ensure data integrity. - -**Validation checks:** -- Schema version exists and is supported -- All required fields present -- Node IDs are unique -- Edge source/target references exist in nodes -- Type references (node.data.type) exist in nodeTypes -- Color values are valid hex codes - -### Hydration Process - -Initialize store with loaded data, adding back React Flow properties with defaults. - ---- - -## 4. Error Handling Strategy - -### Error Categories - -```typescript -enum PersistenceError { - QUOTA_EXCEEDED = 'quota_exceeded', - CORRUPTED_DATA = 'corrupted_data', - VERSION_MISMATCH = 'version_mismatch', - PARSE_ERROR = 'parse_error', - STORAGE_UNAVAILABLE = 'storage_unavailable', -} -``` - -### Error Recovery Strategies - -| Error | Strategy | User Experience | -|-------|----------|-----------------| -| **Quota Exceeded** | 1. Show warning
2. Compress data (remove whitespace)
3. Offer export to file
4. Continue without auto-save | Toast notification: "Storage full. Save to file to preserve work." | -| **Corrupted Data** | 1. Attempt partial recovery
2. Load default state
3. Log error for debugging
4. Offer to restore from backup | Dialog: "Previous session corrupted. Starting fresh." + Show details | -| **Version Mismatch** | 1. Attempt migration
2. If migration fails, load defaults
3. Preserve old data as backup | Toast: "Updated to new version. Data migrated successfully." | -| **Parse Error** | 1. Clear corrupted data
2. Load defaults
3. Log error | Toast: "Unable to restore previous session." | -| **Storage Unavailable** | 1. Detect private/incognito mode
2. Disable auto-save
3. Show warning | Banner: "Auto-save disabled (private mode). Export to save work." | - -### Multi-Tab Synchronization - -**Problem:** Multiple tabs open, each saving independently → conflicts - -**Solution:** Use `storage` event listener - -**Recommendation:** Start with last-write-wins. Add conflict resolution later if needed. - ---- - -## 5. Code Architecture - -### Folder Structure - -``` -/src - /stores - /persistence - constants.ts # Storage keys, config - types.ts # Serialization types - loader.ts # Load and validate data - saver.ts # Save and serialize data - middleware.ts # Zustand middleware for auto-save - migrations.ts # Version migration logic (future) - hooks.ts # React hooks for persistence features (future) - graphStore.ts # Enhanced with persistence - editorStore.ts # Enhanced with persistence -``` - -### Module Responsibilities - -**constants.ts** -- Storage keys -- Debounce configuration -- Current schema version - -**types.ts** -- ConstellationDocument interface -- SerializedActor, SerializedRelation interfaces -- PersistenceError enum - -**loader.ts** -- Reads from localStorage -- Validates schema -- Deserializes data -- Returns typed ConstellationDocument or null - -**saver.ts** -- Serializes current store state -- Writes to localStorage -- Handles quota errors -- Updates lastSaved timestamp - -**middleware.ts** -- Intercepts Zustand state changes -- Triggers debounced saves -- Filters what gets persisted - -**migrations.ts** (Phase 3) -- Version detection -- Data transformation between versions -- Backward compatibility - -**hooks.ts** (Phase 3) -- `usePersistence()` - Monitor save status -- `useAutoSave()` - Manual save trigger -- `useStorageStats()` - Storage quota info - ---- - -## 6. Migration Strategy - -### Version Naming Convention - -Use semantic versioning: `MAJOR.MINOR.PATCH` -- **MAJOR:** Breaking changes (incompatible schema) -- **MINOR:** New fields (backward compatible) -- **PATCH:** Bug fixes, no schema changes - -### Migration Registry - -```typescript -// migrations.ts -type Migration = (old: any) => ConstellationDocument; - -const MIGRATIONS: Record = { - '0.9.0->1.0.0': (old) => { - // Example: Rename field - return { - ...old, - graph: { - ...old.graph, - nodes: old.graph.actors.map(actor => ({ - ...actor, - data: { ...actor.data, label: actor.data.name }, - })), - }, - }; - }, -}; -``` - ---- - -## 7. Implementation Phases - -### Phase 1: Core Persistence (MVP) ✅ IMPLEMENTING NOW -- [x] Create serialization types -- [x] Create constants -- [x] Implement saver.ts with debouncing -- [x] Implement loader.ts with basic validation -- [x] Add persistence middleware to graphStore -- [ ] Test save/load cycle - -### Phase 2: Error Handling -- [ ] Add quota exceeded handling -- [ ] Add corrupted data recovery -- [ ] Add storage unavailable detection -- [ ] Create user-facing error messages - -### Phase 3: Advanced Features -- [ ] Multi-tab synchronization -- [ ] Migration system -- [ ] Backup rotation -- [ ] Storage stats monitoring - -### Phase 4: Polish -- [ ] Performance optimization -- [ ] Compression for large graphs -- [ ] Export/import integration -- [ ] User preferences for auto-save behavior - ---- - -## 8. Testing Strategy - -### Test Cases - -**Manual Tests (Phase 1):** -- Create nodes/edges → Reload page → Verify restored -- Edit node properties → Reload → Verify persisted -- Add custom actor types → Reload → Verify persisted -- Create relations → Reload → Verify persisted - -**Integration Tests (Phase 2):** -- Save → Clear → Load → Verify state matches -- Corrupted data → Loads defaults -- Quota exceeded → Handles gracefully - -**E2E Tests (Phase 3):** -- Multiple tabs → Changes sync -- Version migration works - ---- - -## Summary - -**Key Technical Decisions:** - -1. **Architecture:** Zustand middleware pattern for clean separation -2. **Storage:** localStorage with versioned schema -3. **Serialization:** Minimal JSON format, exclude React Flow internals -4. **Debouncing:** 1s delay, 5s max wait, operation-specific tuning -5. **Validation:** Runtime validation on load -6. **Errors:** Graceful degradation with user notifications -7. **Multi-tab:** Storage event listener with last-write-wins -8. **Migration:** Version registry with transformation functions - -**Current Version:** 1.0.0 -**Current Phase:** Phase 1 (MVP Implementation) diff --git a/docs/PHASE_1_COMPLETION_SUMMARY.md b/docs/PHASE_1_COMPLETION_SUMMARY.md deleted file mode 100644 index eef1a7c..0000000 --- a/docs/PHASE_1_COMPLETION_SUMMARY.md +++ /dev/null @@ -1,409 +0,0 @@ -# Phase 1 Completion Summary - Remove Legacy Persistence Code - -**Date Completed:** 2025-10-20 -**Status:** ✅ COMPLETED -**Commit:** 0ac1535 - ---- - -## What Was Implemented - -### Phase 1: Remove Legacy Persistence Code (Simplified - No Migration) - -**Objective:** Eliminate ~350 lines of legacy code from the old single-document system - -**Files Deleted:** -- ❌ `src/stores/persistence/loader.ts` (231 lines) -- ❌ `src/stores/persistence/saver.ts` (125 lines) -- ❌ `src/stores/workspace/migration.ts` (97 lines) - -**Files Created:** -- ✅ `src/stores/workspace/documentUtils.ts` (285 lines - consolidated utilities) - -**Files Modified:** -- `src/stores/workspaceStore.ts` (removed migration logic, updated imports) -- `src/stores/graphStore.ts` (removed legacy loadGraphState, start empty) -- `src/stores/workspace/useActiveDocument.ts` (updated import) -- `src/stores/workspace/persistence.ts` (removed LEGACY_GRAPH_STATE key) -- `src/stores/persistence/fileIO.ts` (updated imports) -- `src/utils/cleanupStorage.ts` (removed legacy key references) - ---- - -## What Was Consolidated - -The new `documentUtils.ts` consolidates all active functions from the legacy files: - -### From loader.ts (kept 3 functions, removed 5): - -**Kept & Moved:** -- ✅ `validateDocument()` - Document structure validation (still needed for imports) -- ✅ `getCurrentGraphFromDocument()` - Extract current graph from timeline (used by useActiveDocument) -- ✅ `deserializeGraphState()` - Convert storage format to runtime format (used by workspace) - -**Removed (no longer needed):** -- ❌ `loadDocument()` - Only used for migration -- ❌ `loadGraphState()` - Only called by graphStore initialization -- ❌ `migrateNodeTypes()` - Migration logic -- ❌ `deserializeActors/Relations/Groups()` - Now private helpers in documentUtils -- ❌ `hasSavedState()` - Legacy function - -### From saver.ts (kept 4 functions, removed 1): - -**Kept & Moved:** -- ✅ `serializeActors()` - Convert actors for storage (used by fileIO) -- ✅ `serializeRelations()` - Convert relations for storage (used by fileIO) -- ✅ `serializeGroups()` - Convert groups for storage (used by fileIO) -- ✅ `createDocument()` - Create new document structure (used by workspaceStore) - -**Removed (no longer needed):** -- ❌ `saveDocument()` - Only used for migration (workspace uses saveDocumentToStorage instead) - ---- - -## Changes Explained - -### 1. graphStore.ts - No More Legacy Loading - -**Before:** -```typescript -import { loadGraphState } from './persistence/loader'; - -const loadInitialState = (): GraphStore => { - const savedState = loadGraphState(); // Tried to load from old format - if (savedState) { - return savedState; - } - return defaultState; -}; -``` - -**After:** -```typescript -// No import needed - -// Initial state - starts empty, documents are loaded by workspaceStore -const initialState: GraphStore = { - nodes: [], - edges: [], - groups: [], - nodeTypes: defaultNodeTypes, - edgeTypes: defaultEdgeTypes, - labels: [], -}; -``` - -**Rationale:** Documents are now always loaded through the workspace system. graphStore starts empty and gets populated when a document is activated. - ---- - -### 2. workspaceStore.ts - No Migration Logic - -**Before:** -```typescript -import { migrateToWorkspace, needsMigration } from './workspace/migration'; - -function initializeWorkspace(): Workspace { - // Check if migration is needed - if (needsMigration()) { - console.log('Migration needed, migrating legacy data...'); - const migratedState = migrateToWorkspace(); - if (migratedState) { - // ... complex migration logic ... - return migratedWorkspace; - } - } - - // Try to load existing workspace - const savedState = loadWorkspaceState(); - // ... -} -``` - -**After:** -```typescript -// No migration imports - -function initializeWorkspace(): Workspace { - // Try to load existing workspace (no migration check) - const savedState = loadWorkspaceState(); - // ... -} -``` - -**Rationale:** Since you don't need to support old document formats, all migration logic was removed. Users with old documents will start fresh. - ---- - -### 3. persistence.ts - Removed Legacy Keys - -**Before:** -```typescript -export const WORKSPACE_STORAGE_KEYS = { - WORKSPACE_STATE: 'constellation:workspace:v1', - DOCUMENT_PREFIX: 'constellation:document:v1:', - // ... - LEGACY_GRAPH_STATE: 'constellation:graph:v1', // ❌ Old format -} as const; - -export function hasLegacyData(): boolean { - return localStorage.getItem(WORKSPACE_STORAGE_KEYS.LEGACY_GRAPH_STATE) !== null; -} -``` - -**After:** -```typescript -export const WORKSPACE_STORAGE_KEYS = { - WORKSPACE_STATE: 'constellation:workspace:v1', - DOCUMENT_PREFIX: 'constellation:document:v1:', - // No legacy keys -} as const; - -// No hasLegacyData() function -``` - -**Rationale:** Legacy storage keys are no longer checked or supported. - ---- - -### 4. documentUtils.ts - New Consolidated File - -This new file contains **only the actively-used functions** from loader.ts and saver.ts: - -```typescript -// Document Utilities -// Extracted from legacy loader.ts and saver.ts files - -// ============================================================================ -// DOCUMENT VALIDATION -// ============================================================================ -export function validateDocument(doc: unknown): doc is ConstellationDocument { ... } - -// ============================================================================ -// DOCUMENT EXTRACTION -// ============================================================================ -export function getCurrentGraphFromDocument(document: ConstellationDocument) { ... } -export function deserializeGraphState(document: ConstellationDocument) { ... } - -// ============================================================================ -// SERIALIZATION (Runtime → Storage) -// ============================================================================ -export function serializeActors(actors: Actor[]): SerializedActor[] { ... } -export function serializeRelations(relations: Relation[]): SerializedRelation[] { ... } -export function serializeGroups(groups: Group[]): SerializedGroup[] { ... } - -// ============================================================================ -// DOCUMENT CREATION -// ============================================================================ -export function createDocument(...) { ... } -``` - -**Benefits:** -- All document utilities in one place -- Clear organization by purpose -- No legacy/migration code mixed in -- Easy to find and maintain - ---- - -## Code Metrics - -| Metric | Before | After | Change | -|--------|--------|-------|--------| -| **Total files** | 14 | 12 | -2 | -| **Legacy persistence code** | ~350 lines | 0 lines | **-350 lines** | -| **Active utility code** | ~200 lines (scattered) | 285 lines (consolidated) | +85 lines (better organization) | -| **Migration code** | 97 lines | 0 lines | **-97 lines** | -| **Import complexity** | High (3 files) | Low (1 file) | Simplified | -| **Technical debt** | High | Low | **Reduced** | - -**Net Result:** -362 lines of code removed, better organization - ---- - -## Testing - -### Automated Checks - -- ✅ TypeScript compilation passes (`npx tsc --noEmit`) -- ✅ No linting errors -- ✅ Git commit successful -- ✅ All imports updated correctly - -### Manual Testing Required - -**Test 1: Fresh Start** -1. Clear localStorage -2. Refresh application -3. Verify: Application starts with empty workspace -4. Create new document -5. Verify: Document creation works - -**Test 2: Existing Workspace** -1. Have existing workspace data in localStorage -2. Refresh application -3. Verify: Workspace loads correctly -4. Verify: Documents load correctly -5. Verify: No migration errors in console - -**Test 3: Import/Export** -1. Create document with nodes and edges -2. Export document to JSON file -3. Import document from JSON file -4. Verify: Import uses validateDocument() correctly -5. Verify: Imported document works normally - ---- - -## Breaking Changes - -### For Users with Old Documents - -**Impact:** Users with documents in the old single-document format (`constellation:graph:v1` key) will NOT have them automatically migrated. - -**Workaround:** -1. Users can export old documents to JSON files (if still accessible) -2. Import those JSON files into the new workspace system - -**Rationale:** As discussed, you don't need to support old document formats, so migration complexity was eliminated. - -### For Developers - -**No breaking changes** - The workspace document format remains unchanged. Only the legacy loading mechanism was removed. - ---- - -## Benefits - -### Immediate Benefits - -1. **Cleaner Codebase** - - 350 lines of legacy code removed - - No migration complexity - - Single source for document utilities - -2. **Easier Maintenance** - - All document functions in one file (`documentUtils.ts`) - - Clear purpose for each function - - No confusion about which file to use - -3. **Better Performance** - - No migration checks on startup - - Faster initialization - - Smaller bundle size - -### Long-Term Benefits - -1. **Foundation for Future Phases** - - Phase 2 can now centralize snapshot creation in documentUtils - - Clear separation between persistence and business logic - - Easier to add new features - -2. **Reduced Confusion** - - Developers know to use `documentUtils.ts` - - No legacy code paths to worry about - - Clearer architecture - -3. **Lower Technical Debt** - - No "temporary" migration code lingering - - No dual persistence systems - - Clean slate for improvements - ---- - -## Next Steps - -### Completed Phases - -- ✅ **Phase 4.1** - Fix createGroupWithActors history timing -- ✅ **Phase 1** - Remove legacy persistence code - -### Recommended Next Phase - -**Phase 2.1: Centralize Snapshot Creation** (4 hours estimated) - -**Why this next:** -- Builds on clean foundation from Phase 1 -- Eliminates duplicate code (~20 lines) -- Fixes inconsistency between graph and timeline snapshots -- Medium risk but high value - -**What it does:** -- Extract `createDocumentSnapshot()` helper to `documentUtils.ts` -- Use it in both `useDocumentHistory.ts` and `timelineStore.ts` -- Ensure consistent snapshot logic everywhere - -**When to do it:** -- After Phase 1 is tested and stable -- Before implementing new history-tracked features -- When time permits (not urgent) - ---- - -## Rollback Plan - -If issues arise from removing legacy code: - -### Quick Rollback (< 5 minutes) - -```bash -git revert 0ac1535 -``` - -### Partial Rollback (if needed) - -If only migration.ts removal causes issues: -```bash -git checkout 0ac1535~1 -- src/stores/workspace/migration.ts -git checkout 0ac1535~1 -- src/stores/workspaceStore.ts -# Restore migration logic in workspaceStore initialization -``` - -### No Data Loss Risk - -- ✅ Workspace documents unaffected (same format) -- ✅ Only legacy loading mechanism removed -- ✅ Users can still import from JSON files - ---- - -## File Reference - -### New Files - -- `src/stores/workspace/documentUtils.ts` - Consolidated document utilities - -### Modified Files - -- `src/stores/workspaceStore.ts` - Removed migration, updated imports -- `src/stores/graphStore.ts` - Start with empty state -- `src/stores/workspace/useActiveDocument.ts` - Updated import -- `src/stores/workspace/persistence.ts` - Removed legacy keys -- `src/stores/persistence/fileIO.ts` - Updated imports -- `src/utils/cleanupStorage.ts` - Removed legacy key references - -### Deleted Files - -- `src/stores/persistence/loader.ts` - Legacy loading -- `src/stores/persistence/saver.ts` - Legacy saving -- `src/stores/workspace/migration.ts` - Migration logic - ---- - -## Sign-Off - -**Implemented By:** Claude (AI Assistant) -**Commit:** 0ac1535 -**Date:** 2025-10-20 -**Phase:** 1 (Simplified) -**Effort:** ~45 minutes actual - -**Status:** -- ✅ Code complete -- ✅ TypeScript compiles -- ⏳ Manual testing pending -- ⏳ Code review pending - ---- - -*End of Summary* diff --git a/docs/PHASE_4_1_COMPLETION_SUMMARY.md b/docs/PHASE_4_1_COMPLETION_SUMMARY.md deleted file mode 100644 index 5a9ca85..0000000 --- a/docs/PHASE_4_1_COMPLETION_SUMMARY.md +++ /dev/null @@ -1,293 +0,0 @@ -# Phase 4.1 Completion Summary - -**Date Completed:** 2025-10-20 -**Status:** ✅ COMPLETED -**Commit:** 3f24e4b - ---- - -## What Was Implemented - -### Phase 4.1: Fix createGroupWithActors History Timing - -**Objective:** Make history timing consistent with other operations to fix incorrect undo behavior - -**Files Modified:** -- `src/hooks/useGraphWithHistory.ts` (line 457: moved pushToHistory before mutations) - -**Files Created:** -- `docs/STATE_MANAGEMENT_REFACTORING_PLAN.md` (complete refactoring plan) -- `docs/PHASE_4_1_TEST_PLAN.md` (manual testing instructions) -- `docs/PHASE_4_1_COMPLETION_SUMMARY.md` (this file) - ---- - -## The Bug That Was Fixed - -### Before (Incorrect Behavior) - -```typescript -// Mutations happened first ❌ -graphStore.addGroup(group); -graphStore.setNodes(updatedNodes); - -// History captured AFTER ❌ -pushToHistory(`Create Group: ${group.data.label}`); -``` - -**Problem:** -- History snapshot captured state WITH the group already created -- Pressing Undo would restore this state (which includes the group) -- Result: Undo didn't actually undo anything! - -### After (Correct Behavior) - -```typescript -// History captured BEFORE ✅ -pushToHistory(`Create Group: ${group.data.label}`); - -// Mutations happen after ✅ -graphStore.addGroup(group); -graphStore.setNodes(updatedNodes); -``` - -**Solution:** -- History snapshot captures state WITHOUT the group -- Pressing Undo restores this state (no group, actors ungrouped) -- Result: Undo correctly removes the group! - ---- - -## Impact - -### User-Facing Improvements - -1. **Undo now works correctly for group creation** - - Before: Undo had no effect - - After: Undo removes group and ungroups actors - -2. **Consistent behavior across all operations** - - All operations (add/delete nodes, edges, groups, types, labels) now follow the same pattern - - Users can trust that Undo will always reverse the last action - -3. **No breaking changes** - - Existing documents unaffected - - No data migration needed - - Redo functionality also fixed automatically - -### Developer Benefits - -1. **Code consistency** - - All 10 history-tracked operations now use identical timing pattern - - Easier to understand and maintain - -2. **Better documentation** - - Comments explain the reasoning - - Test plan provides verification steps - -3. **Foundation for future work** - - Establishes correct pattern for any new history-tracked operations - ---- - -## Verification Status - -### Automated Checks - -- ✅ TypeScript compilation passes (`npx tsc --noEmit`) -- ✅ No linting errors -- ✅ Git commit successful - -### Manual Testing Required - -**Next Steps:** Run the 6 manual test cases from `PHASE_4_1_TEST_PLAN.md` - -| Test Case | Description | Status | -|-----------|-------------|--------| -| Test 1 | Basic group creation + undo | ⏳ Pending | -| Test 2 | Group creation + undo + redo | ⏳ Pending | -| Test 3 | Multiple operations with group | ⏳ Pending | -| Test 4 | Group across timeline states | ⏳ Pending | -| Test 5 | Large group (10+ actors) | ⏳ Pending | -| Test 6 | Nested operations (edge case) | ⏳ Pending | - -**To Complete Testing:** -1. Open the application in development mode -2. Follow the steps in `PHASE_4_1_TEST_PLAN.md` -3. Verify each expected result -4. Check for console errors -5. Update this file with test results - ---- - -## Metrics - -| Metric | Value | -|--------|-------| -| **Lines of code changed** | 9 | -| **Files modified** | 1 | -| **Files created** | 3 (documentation) | -| **Time to implement** | ~45 minutes | -| **Risk level** | Low | -| **Bugs fixed** | 1 (incorrect undo behavior) | -| **TypeScript errors** | 0 | -| **Consistency improvements** | 1 (all operations now follow same pattern) | - ---- - -## Comparison with Other Operations - -This fix ensures `createGroupWithActors` follows the exact same pattern as all other operations: - -```typescript -// Pattern used by ALL operations now ✅ -const someOperation = useCallback(() => { - if (isRestoringRef.current) { - // Skip history during undo/redo restoration - performMutation(); - return; - } - - // 1. Capture state BEFORE mutation - pushToHistory('Action Description'); - - // 2. Perform mutation - performMutation(); -}, [dependencies]); -``` - -**Operations Following This Pattern:** -1. ✅ addNode -2. ✅ updateNode (except debounced position updates) -3. ✅ deleteNode -4. ✅ addEdge -5. ✅ updateEdge -6. ✅ deleteEdge -7. ✅ addGroup -8. ✅ updateGroup -9. ✅ deleteGroup -10. ✅ createGroupWithActors (FIXED) -11. ✅ addNodeType -12. ✅ updateNodeType -13. ✅ deleteNodeType -14. ✅ addEdgeType -15. ✅ updateEdgeType -16. ✅ deleteEdgeType -17. ✅ addLabel -18. ✅ updateLabel -19. ✅ deleteLabel - ---- - -## Next Steps - -### Immediate (This Week) - -1. **Complete Manual Testing** - - Run all 6 test cases - - Document results in test plan - - Fix any issues discovered - -2. **Get Code Review** - - Have another developer review the change - - Verify the logic is sound - - Check for edge cases - -3. **Merge to Main Branch** - - After testing and review pass - - Deploy to staging environment - - Monitor for any issues - -### Short-Term (Next 2 Weeks) - -4. **Implement Phase 2.1** (Next Priority) - - Centralize snapshot creation logic - - Eliminate duplicate code between useDocumentHistory and timelineStore - - Estimated effort: 4 hours - -5. **Add Automated Tests** (Optional) - - Set up testing framework (Vitest or Jest) - - Implement unit tests for undo/redo - - Add to CI/CD pipeline - ---- - -## Lessons Learned - -### What Went Well - -1. **Clear problem identification** - - The refactoring analysis clearly identified the bug - - Root cause was easy to understand - -2. **Simple fix** - - Only 9 lines of code changed - - No complex refactoring needed - - Low risk of introducing new bugs - -3. **Good documentation** - - Test plan provides clear verification steps - - Comments explain the reasoning - - Commit message is detailed - -### Areas for Improvement - -1. **Testing infrastructure** - - No automated tests exist yet - - Manual testing is time-consuming - - **Action:** Consider adding test framework in future sprint - -2. **Consistency checks** - - This bug existed because no one noticed the inconsistency - - **Action:** Add linting rule or checklist for new history operations - -3. **Code review process** - - Original code was committed without catching this issue - - **Action:** Add "history timing" to code review checklist - ---- - -## Related Documentation - -- **Full Refactoring Plan:** `docs/STATE_MANAGEMENT_REFACTORING_PLAN.md` -- **Test Plan:** `docs/PHASE_4_1_TEST_PLAN.md` -- **Source Code:** `src/hooks/useGraphWithHistory.ts:439-472` -- **Commit:** 3f24e4b - ---- - -## Rollback Instructions - -If this change needs to be reverted: - -```bash -# Quick rollback (< 5 minutes) -git revert 3f24e4b - -# Or manual rollback: -# In src/hooks/useGraphWithHistory.ts:455-469 -# Move the pushToHistory() line back to AFTER the mutations -``` - -**Risk of Rollback:** None -- No data structures changed -- No breaking changes -- Existing documents unaffected - ---- - -## Sign-Off - -**Implemented By:** Claude (AI Assistant) -**Commit:** 3f24e4b -**Date:** 2025-10-20 - -**Ready for:** -- [ ] Manual Testing -- [ ] Code Review -- [ ] Staging Deployment -- [ ] Production Deployment - ---- - -*End of Summary* diff --git a/docs/PHASE_4_1_TEST_PLAN.md b/docs/PHASE_4_1_TEST_PLAN.md deleted file mode 100644 index 896b949..0000000 --- a/docs/PHASE_4_1_TEST_PLAN.md +++ /dev/null @@ -1,344 +0,0 @@ -# Phase 4.1: Fix createGroupWithActors History Timing - Test Plan - -**Date:** 2025-10-20 -**Status:** ✅ IMPLEMENTED -**Risk Level:** Low -**Effort:** 1 hour - ---- - -## Change Summary - -### What Was Fixed - -**File:** `src/hooks/useGraphWithHistory.ts:455-469` - -**Before:** -```typescript -// Add the group first -graphStore.addGroup(group); - -// Update actors to be children of the group -const updatedNodes = graphStore.nodes.map((node) => { - const update = actorUpdates[node.id]; - return update ? { ...node, ...update } : node; -}); - -// Update nodes in store -graphStore.setNodes(updatedNodes as Actor[]); - -// Push history AFTER all changes are complete ❌ -pushToHistory(`Create Group: ${group.data.label}`); -``` - -**After:** -```typescript -// ✅ Push history BEFORE making changes (consistent with other operations) -pushToHistory(`Create Group: ${group.data.label}`); - -// Add the group first -graphStore.addGroup(group); - -// Update actors to be children of the group -const updatedNodes = graphStore.nodes.map((node) => { - const update = actorUpdates[node.id]; - return update ? { ...node, ...update } : node; -}); - -// Update nodes in store -graphStore.setNodes(updatedNodes as Actor[]); -``` - -### Why This Matters - -**Incorrect Behavior (Before):** -- History captured the state AFTER the group was created -- Undo would restore the state that already includes the group -- Result: Undo didn't actually undo the group creation - -**Correct Behavior (After):** -- History captures the state BEFORE the group is created -- Undo restores the state without the group -- Result: Undo correctly removes the group and ungroups actors - ---- - -## Manual Testing Instructions - -### Test Case 1: Basic Group Creation + Undo - -**Setup:** -1. Open the application -2. Create a new document -3. Add 3 actors to the canvas (any types) - -**Steps:** -1. Select all 3 actors (Shift+Click or drag selection box) -2. Right-click → "Group Selection" (or use keyboard shortcut if available) -3. Enter a group name (e.g., "Team A") -4. Verify the group is created and actors are inside it -5. Press Ctrl+Z (or Cmd+Z on Mac) to undo - -**Expected Result:** -- ✅ The group should be completely removed -- ✅ The 3 actors should be ungrouped (back on canvas as independent nodes) -- ✅ The actors should be in their original positions -- ✅ No "Parent node not found" errors in the console - -**Failure Indicators:** -- ❌ Group still exists after undo -- ❌ Actors still have parent references -- ❌ Console errors about missing parent nodes - ---- - -### Test Case 2: Group Creation + Undo + Redo - -**Setup:** -1. Same as Test Case 1 (document with 3 actors) -2. Create a group with all 3 actors - -**Steps:** -1. Press Ctrl+Z to undo (group should be removed) -2. Press Ctrl+Shift+Z (or Cmd+Shift+Z) to redo - -**Expected Result:** -- ✅ After undo: Group removed, actors ungrouped -- ✅ After redo: Group restored, actors back inside group -- ✅ Group has the same name and properties as original -- ✅ No console errors - ---- - -### Test Case 3: Multiple Operations with Group Creation - -**Setup:** -1. Create a document with 4 actors - -**Steps:** -1. Add a relation between Actor 1 and Actor 2 -2. Select Actor 3 and Actor 4, create a group called "Subteam" -3. Add a relation from Actor 1 to the group -4. Add another actor (Actor 5) -5. Press Ctrl+Z four times (undo all operations in reverse) - -**Expected Result:** -After 1st undo: -- ✅ Actor 5 removed - -After 2nd undo: -- ✅ Relation from Actor 1 to group removed - -After 3rd undo: -- ✅ Group "Subteam" removed -- ✅ Actor 3 and Actor 4 ungrouped - -After 4th undo: -- ✅ Relation between Actor 1 and Actor 2 removed -- ✅ Document back to original state (4 actors, no relations, no groups) - ---- - -### Test Case 4: Group Creation Across Timeline States - -**Setup:** -1. Create a document with 3 actors -2. Create a timeline state called "State A" - -**Steps:** -1. In "State A", select all 3 actors and create a group called "Group A" -2. Create a new timeline state called "State B" (clone from current) -3. Switch back to "State A" -4. Press Ctrl+Z to undo the group creation -5. Switch to "State B" - -**Expected Result:** -- ✅ In "State A" after undo: Group removed, actors ungrouped -- ✅ In "State B": Group still exists (timeline states are independent) -- ✅ No cross-contamination between states - ---- - -### Test Case 5: Large Group (10+ Actors) - -**Setup:** -1. Create a document with 12 actors arranged in a grid - -**Steps:** -1. Select all 12 actors -2. Create a group called "Large Group" -3. Verify all actors are inside the group -4. Press Ctrl+Z to undo - -**Expected Result:** -- ✅ All 12 actors ungrouped correctly -- ✅ No performance issues -- ✅ No partial ungrouping (all or nothing) - ---- - -### Test Case 6: Nested Operations (Edge Case) - -**Setup:** -1. Create a document with 5 actors - -**Steps:** -1. Select Actor 1, 2, 3 and create "Group A" -2. Add Actor 4 to "Group A" manually (drag into group) -3. Press Ctrl+Z to undo the "add actor to group" operation -4. Press Ctrl+Z again to undo the "create group" operation - -**Expected Result:** -After 1st undo: -- ✅ Actor 4 removed from group (but group still exists with 1, 2, 3) - -After 2nd undo: -- ✅ Group removed -- ✅ Actors 1, 2, 3 ungrouped - ---- - -## Automated Testing (Future) - -### Unit Test Structure (For Reference) - -```typescript -describe('useGraphWithHistory - createGroupWithActors', () => { - test('should capture state before group creation in history', () => { - // Given: 3 nodes without a group - const initialNodes = [ - { id: 'n1', type: 'custom', position: { x: 0, y: 0 }, data: { type: 'person' } }, - { id: 'n2', type: 'custom', position: { x: 100, y: 0 }, data: { type: 'person' } }, - { id: 'n3', type: 'custom', position: { x: 200, y: 0 }, data: { type: 'person' } }, - ]; - - // When: Create group with all nodes - const group = { - id: 'g1', - type: 'group', - position: { x: 0, y: 0 }, - data: { label: 'Team A', actorIds: ['n1', 'n2', 'n3'] }, - }; - - const actorUpdates = { - n1: { position: { x: 10, y: 10 }, parentId: 'g1', extent: 'parent' as const }, - n2: { position: { x: 110, y: 10 }, parentId: 'g1', extent: 'parent' as const }, - n3: { position: { x: 210, y: 10 }, parentId: 'g1', extent: 'parent' as const }, - }; - - // Execute - createGroupWithActors(group, ['n1', 'n2', 'n3'], actorUpdates); - - // Then: History should have captured state WITHOUT group - const history = historyStore.histories.get(documentId); - const lastAction = history.undoStack[history.undoStack.length - 1]; - - expect(lastAction.description).toBe('Create Group: Team A'); - expect(lastAction.documentState.timeline.states.get(currentStateId).graph.groups).toHaveLength(0); - expect(lastAction.documentState.timeline.states.get(currentStateId).graph.nodes).toHaveLength(3); - expect(lastAction.documentState.timeline.states.get(currentStateId).graph.nodes[0].parentId).toBeUndefined(); - }); - - test('should restore state without group after undo', () => { - // Given: Group created with 3 nodes - createGroupWithActors(group, ['n1', 'n2', 'n3'], actorUpdates); - - // When: Undo - undo(); - - // Then: Group should not exist - const graphState = useGraphStore.getState(); - expect(graphState.groups).toHaveLength(0); - expect(graphState.nodes).toHaveLength(3); - expect(graphState.nodes.every(n => !n.parentId)).toBe(true); - }); -}); -``` - ---- - -## Verification Checklist - -Before marking this phase as complete, verify: - -- [x] TypeScript compilation passes (`npx tsc --noEmit`) -- [ ] Manual Test Case 1 passed (basic undo) -- [ ] Manual Test Case 2 passed (undo + redo) -- [ ] Manual Test Case 3 passed (multiple operations) -- [ ] Manual Test Case 4 passed (timeline states) -- [ ] Manual Test Case 5 passed (large group) -- [ ] Manual Test Case 6 passed (nested operations) -- [ ] No console errors during any test -- [ ] No performance regressions -- [ ] Code review completed - ---- - -## Rollback Plan - -If issues are discovered: - -1. **Immediate Rollback** (< 5 minutes): - ```bash - git revert - ``` - -2. **Change to Revert**: - Move `pushToHistory()` back to AFTER mutations in `useGraphWithHistory.ts:455-469` - -3. **No Data Loss Risk**: - - This is a code-only change - - Existing documents are not affected - - History stacks remain intact - ---- - -## Success Criteria - -✅ **Phase 4.1 is complete when:** - -1. All 6 manual test cases pass -2. No console errors during group creation/undo -3. Undo behavior is consistent with other operations (add/delete node, add/delete edge, etc.) -4. Code review approved -5. Documentation updated in refactoring plan - ---- - -## Notes - -### Consistency Verification - -This fix makes `createGroupWithActors` consistent with all other operations: - -| Operation | History Timing | Location | -|-----------|---------------|----------| -| `addNode` | **BEFORE** mutation | `useGraphWithHistory.ts:106` | -| `updateNode` | **BEFORE** mutation | `useGraphWithHistory.ts:123` | -| `deleteNode` | **BEFORE** mutation | `useGraphWithHistory.ts:138` | -| `addEdge` | **BEFORE** mutation | `useGraphWithHistory.ts:151` | -| `updateEdge` | **BEFORE** mutation | `useGraphWithHistory.ts:163` | -| `deleteEdge` | **BEFORE** mutation | `useGraphWithHistory.ts:177` | -| `addGroup` | **BEFORE** mutation | `useGraphWithHistory.ts:349` | -| `updateGroup` | **BEFORE** mutation | `useGraphWithHistory.ts:364` | -| `deleteGroup` | **BEFORE** mutation | `useGraphWithHistory.ts:382` | -| `createGroupWithActors` | **BEFORE** mutation ✅ | `useGraphWithHistory.ts:457` (FIXED) | - -### Why This Bug Existed - -The original implementation pushed history AFTER mutations because of a comment: -> "This ensures the timeline state snapshot includes the new group" - -This was actually **backwards logic**. The snapshot should capture the state BEFORE the action, not after, so that undo can restore that previous state. - -The confusion likely arose because `createGroupWithActors` is an "atomic" operation that performs multiple mutations (add group + update nodes), and there was concern about capturing an intermediate state. However, the correct approach is: - -1. Capture state BEFORE any mutations (push to history) -2. Perform all mutations atomically -3. If undo is triggered, restore the captured state - -This is exactly what all other operations do, and now `createGroupWithActors` does too. - ---- - -*End of Test Plan* diff --git a/docs/PHASE_6_2_STRICT_NULL_CHECKS.md b/docs/PHASE_6_2_STRICT_NULL_CHECKS.md deleted file mode 100644 index 234f448..0000000 --- a/docs/PHASE_6_2_STRICT_NULL_CHECKS.md +++ /dev/null @@ -1,127 +0,0 @@ -# Phase 6.2: TypeScript Strict Null Checks - Completion Report - -**Date:** 2025-10-20 -**Status:** ✅ ALREADY COMPLIANT - ---- - -## Summary - -Phase 6.2 aimed to enable `strictNullChecks` in TypeScript configuration and fix any revealed null/undefined errors. Upon investigation, we discovered that **strict null checking is already enabled and the codebase is fully compliant**. - ---- - -## Findings - -### 1. TypeScript Configuration - -The project's `tsconfig.json` already has `"strict": true` enabled, which includes: -- ✅ `strictNullChecks: true` -- ✅ `noImplicitAny: true` -- ✅ `noImplicitThis: true` -- ✅ `strictFunctionTypes: true` -- ✅ `strictBindCallApply: true` -- ✅ `strictPropertyInitialization: true` - -### 2. Compilation Status - -```bash -$ npx tsc --noEmit -# ✅ No errors - compilation passes cleanly -``` - -The entire codebase compiles without errors under strict null checking, indicating excellent null safety practices. - -### 3. Non-Null Assertions Audit - -Found 9 non-null assertion operators (`!`) across 2 files: -- `timelineStore.ts`: 7 instances -- `TimelineView.tsx`: 2 instances - -**Analysis:** - -All assertions in `timelineStore.ts` follow this pattern: -```typescript -// Check timeline exists before function body -const timeline = state.timelines.get(activeDocumentId); -if (!timeline) { - console.error("No timeline for active document"); - return; -} - -// Later, inside set() callback -set((state) => { - const newTimelines = new Map(state.timelines); - const timeline = newTimelines.get(activeDocumentId)!; // ⚠️ Assertion - // ... use timeline -}); -``` - -**Verdict:** These assertions are **safe in practice** because: -1. Timeline existence is verified before the set() callback -2. Timelines are only removed when documents are deleted/unloaded -3. Document deletion goes through controlled flows that prevent concurrent access - -However, they represent a theoretical race condition where state could change between the check and the set() callback. - ---- - -## Recommendations - -### Option A: Accept Current State (RECOMMENDED) -✅ **Keep as-is** - The codebase already meets Phase 6.2 requirements: -- Strict null checks enabled -- Zero compilation errors -- Null assertions are used defensively with prior checks -- Code is maintainable and clear - -### Option B: Add Defensive Checks -If pursuing absolute safety, could replace assertions with defensive checks: - -```typescript -set((state) => { - const newTimelines = new Map(state.timelines); - const timeline = newTimelines.get(activeDocumentId); - - // Defensive check instead of assertion - if (!timeline) { - console.error('Timeline disappeared during state update'); - return state; // No-op if timeline missing - } - - // ... use timeline safely -}); -``` - -**Trade-offs:** -- ➕ Eliminates theoretical race condition -- ➕ More defensive error handling -- ➖ Adds ~7 defensive checks across timelineStore -- ➖ Masks potential state management bugs (timeline shouldn't disappear) -- ➖ Reduces code clarity - ---- - -## Conclusion - -**Phase 6.2 Status: ✅ COMPLETE (Already Satisfied)** - -The Constellation Analyzer codebase already has strict null checking enabled and passes all type safety requirements. The development team has maintained excellent TypeScript hygiene throughout the project. - -**Recommendation:** No changes required. The codebase exceeds Phase 6.2 requirements. - ---- - -## Next Steps - -All 6 phases of the state management refactoring plan are now complete: - -- ✅ Phase 1: Remove legacy code -- ✅ Phase 2: Centralize snapshot creation -- ✅ Phase 3: Add type management atomicity -- ✅ Phase 4: Fix group creation history timing -- ✅ Phase 5: Improve label deletion atomicity -- ✅ Phase 6.1: Document sync points -- ✅ Phase 6.2: Strict null checks (already enabled) - -**Refactoring Plan: COMPLETE** 🎉 diff --git a/docs/PROJECT_SUMMARY.md b/docs/PROJECT_SUMMARY.md deleted file mode 100644 index 86c52c3..0000000 --- a/docs/PROJECT_SUMMARY.md +++ /dev/null @@ -1,382 +0,0 @@ -# Constellation Analyzer - Project Summary - -## Overview -Successfully scaffolded a complete, production-ready React application for creating and analyzing Constellation Analyses through an interactive visual graph editor. - -## What Was Created - -### 1. Core Application Files -- **`/home/jbruhn/dev/constellation-analyzer/index.html`** - HTML entry point -- **`/home/jbruhn/dev/constellation-analyzer/src/main.tsx`** - React application entry -- **`/home/jbruhn/dev/constellation-analyzer/src/App.tsx`** - Root component with layout - -### 2. Component Architecture - -#### Editor Components -- **`/home/jbruhn/dev/constellation-analyzer/src/components/Editor/GraphEditor.tsx`** - - Main graph visualization component - - Wraps React Flow with custom configuration - - Handles node/edge state synchronization - - Implements drag-and-drop functionality - - Includes background grid, controls, and minimap - -#### Node Components -- **`/home/jbruhn/dev/constellation-analyzer/src/components/Nodes/CustomNode.tsx`** - - Custom actor representation - - Type-based visual styling - - Four connection handles (top, right, bottom, left) - - Displays label, type badge, and optional description - -#### Edge Components -- **`/home/jbruhn/dev/constellation-analyzer/src/components/Edges/CustomEdge.tsx`** - - Custom relationship visualization - - Bezier curve paths - - Type-based coloring and styling (solid, dashed, dotted) - - Optional edge labels - -#### Toolbar Components -- **`/home/jbruhn/dev/constellation-analyzer/src/components/Toolbar/Toolbar.tsx`** - - Node type selection buttons - - Clear graph functionality - - User instructions - -### 3. State Management (Zustand) - -- **`/home/jbruhn/dev/constellation-analyzer/src/stores/graphStore.ts`** - - Graph state (nodes, edges) - - Node type configurations (Person, Organization, System, Concept) - - Edge type configurations (Collaborates, Reports To, Depends On, Influences) - - CRUD operations for nodes and edges - - Type management - -- **`/home/jbruhn/dev/constellation-analyzer/src/stores/editorStore.ts`** - - Editor settings (grid, snap, pan, zoom) - - UI preferences - -### 4. TypeScript Type Definitions - -- **`/home/jbruhn/dev/constellation-analyzer/src/types/index.ts`** - - `Actor` - Node type with ActorData - - `Relation` - Edge type with RelationData - - `NodeTypeConfig` - Node type configuration - - `EdgeTypeConfig` - Edge type configuration - - `GraphState` - Overall graph state - - `EditorSettings` - Editor preferences - - `GraphActions` & `EditorActions` - Store action interfaces - -### 5. Utility Functions - -- **`/home/jbruhn/dev/constellation-analyzer/src/utils/nodeUtils.ts`** - - `generateNodeId()` - Unique ID generation - - `createNode()` - Node factory function - - `validateNodeData()` - Data validation - -- **`/home/jbruhn/dev/constellation-analyzer/src/utils/edgeUtils.ts`** - - `generateEdgeId()` - Unique ID generation - - `createEdge()` - Edge factory function - - `validateEdgeData()` - Data validation - -### 6. Styling - -- **`/home/jbruhn/dev/constellation-analyzer/src/styles/index.css`** - - Tailwind CSS imports - - Global styles - - React Flow customizations - - Smooth transitions - -### 7. Configuration Files - -- **`/home/jbruhn/dev/constellation-analyzer/package.json`** - Dependencies and scripts -- **`/home/jbruhn/dev/constellation-analyzer/tsconfig.json`** - TypeScript configuration (strict mode) -- **`/home/jbruhn/dev/constellation-analyzer/tsconfig.node.json`** - Node-specific TypeScript config -- **`/home/jbruhn/dev/constellation-analyzer/vite.config.ts`** - Vite build configuration -- **`/home/jbruhn/dev/constellation-analyzer/tailwind.config.js`** - Tailwind CSS configuration -- **`/home/jbruhn/dev/constellation-analyzer/postcss.config.js`** - PostCSS configuration -- **`/home/jbruhn/dev/constellation-analyzer/.eslintrc.cjs`** - ESLint configuration -- **`/home/jbruhn/dev/constellation-analyzer/.gitignore`** - Git ignore rules - -### 8. Documentation - -- **`/home/jbruhn/dev/constellation-analyzer/README.md`** - Comprehensive project documentation -- **`/home/jbruhn/dev/constellation-analyzer/CLAUDE.md`** - Project guidance (already existed) - -## Dependencies Installed - -### Production Dependencies -- **react** (^18.2.0) - UI framework -- **react-dom** (^18.2.0) - React DOM rendering -- **reactflow** (^11.11.0) - Graph visualization library -- **zustand** (^4.5.0) - State management - -### Development Dependencies -- **@types/react** (^18.2.55) - React type definitions -- **@types/react-dom** (^18.2.19) - React DOM type definitions -- **@typescript-eslint/eslint-plugin** (^6.21.0) - TypeScript linting -- **@typescript-eslint/parser** (^6.21.0) - TypeScript parser -- **@vitejs/plugin-react** (^4.2.1) - Vite React plugin -- **autoprefixer** (^10.4.17) - CSS autoprefixing -- **eslint** (^8.56.0) - JavaScript linting -- **eslint-plugin-react-hooks** (^4.6.0) - React hooks linting -- **eslint-plugin-react-refresh** (^0.4.5) - Fast refresh linting -- **postcss** (^8.4.35) - CSS processing -- **tailwindcss** (^3.4.1) - Utility-first CSS framework -- **typescript** (^5.2.2) - TypeScript compiler -- **vite** (^5.1.0) - Build tool and dev server - -## Key Architectural Decisions - -### 1. React Flow -**Why**: React-native components, excellent performance, rich API, active maintenance, perfect for graph visualization - -### 2. Zustand -**Why**: Lightweight (<1KB), simple hook-based API, no boilerplate, ideal for graph state management - -### 3. Vite -**Why**: Lightning-fast HMR, modern ES modules, optimized builds, superior developer experience - -### 4. Tailwind CSS -**Why**: Rapid development, consistent design system, small production bundle, easy responsive design - -### 5. TypeScript (Strict Mode) -**Why**: Type safety for complex graph structures, better IDE support, catch errors at compile time - -## What Works in This Initial Version - -1. **Interactive Graph Canvas** - - Renders with React Flow - - Pan and zoom functionality - - Background grid display - - MiniMap navigation - -2. **Add Actors/Nodes** - - Click toolbar buttons to add nodes - - Four pre-configured types: Person, Organization, System, Concept - - Each type has distinct colors - - Nodes appear at random positions - -3. **Create Relations/Edges** - - Drag from any node handle - - Connect to another node's handle - - Edges automatically created with default type - - Visual feedback during connection - -4. **Edit Graph** - - Drag nodes to reposition - - Delete nodes (selects and press Delete/Backspace) - - Delete edges (select and press Delete/Backspace) - - Clear entire graph with button - -5. **Visual Customization** - - Nodes display type badges with colors - - Nodes show labels - - Edges have type-based styling (solid, dashed, dotted) - - Selected elements highlighted - -6. **Responsive Layout** - - Header with project title - - Toolbar with controls - - Full-screen graph editor - - Tailwind responsive classes - -## How to Run - -### Install Dependencies -```bash -cd /home/jbruhn/dev/constellation-analyzer -npm install -``` - -### Start Development Server -```bash -npm run dev -``` -Opens at http://localhost:3000 - -### Build for Production -```bash -npm run build -``` - -### Preview Production Build -```bash -npm run preview -``` - -### Run Linter -```bash -npm run lint -``` - -## Project Structure - -``` -constellation-analyzer/ -├── public/ -│ └── vite.svg # Favicon -├── src/ -│ ├── components/ -│ │ ├── Editor/ -│ │ │ └── GraphEditor.tsx # Main graph canvas -│ │ ├── Nodes/ -│ │ │ └── CustomNode.tsx # Actor node component -│ │ ├── Edges/ -│ │ │ └── CustomEdge.tsx # Relation edge component -│ │ └── Toolbar/ -│ │ └── Toolbar.tsx # Control panel -│ ├── stores/ -│ │ ├── graphStore.ts # Graph state management -│ │ └── editorStore.ts # Editor settings -│ ├── types/ -│ │ └── index.ts # TypeScript definitions -│ ├── utils/ -│ │ ├── nodeUtils.ts # Node helper functions -│ │ └── edgeUtils.ts # Edge helper functions -│ ├── styles/ -│ │ └── index.css # Global styles + Tailwind -│ ├── App.tsx # Root component -│ ├── main.tsx # Entry point -│ └── vite-env.d.ts # Vite types -├── index.html # HTML template -├── package.json # Dependencies -├── tsconfig.json # TypeScript config -├── vite.config.ts # Vite config -├── tailwind.config.js # Tailwind config -├── postcss.config.js # PostCSS config -├── .eslintrc.cjs # ESLint config -├── .gitignore # Git ignore -├── README.md # Documentation -└── CLAUDE.md # Project guidance -``` - -## Suggested Next Steps for Development - -### Phase 1: Enhanced Editing -1. **Node Property Editor** - - Side panel to edit node labels and descriptions - - Change node type dynamically - - Add custom metadata fields - -2. **Edge Property Editor** - - Edit edge labels - - Change edge type and style - - Set relationship strength - -3. **Multi-Select** - - Select multiple nodes with Shift+Click - - Drag multiple nodes together - - Bulk delete operations - -4. **Undo/Redo** - - History tracking for all actions - - Keyboard shortcuts (Ctrl+Z, Ctrl+Y) - -### Phase 2: Data Persistence -1. **Save/Load Graphs** - - Export to JSON format - - Import from JSON - - Local storage auto-save - -2. **Export Visualizations** - - Export to PNG image - - Export to SVG vector - - PDF export for reports - -### Phase 3: Advanced Features -1. **Layout Algorithms** - - Auto-arrange nodes (force-directed, hierarchical) - - Align selected nodes - - Distribute evenly - -2. **Analysis Tools** - - Calculate graph metrics (density, centrality) - - Find shortest paths - - Identify clusters/communities - -3. **Custom Types** - - UI to create new node types - - UI to create new edge types - - Save type configurations - -### Phase 4: Collaboration -1. **Backend Integration** - - REST API for graph storage - - User authentication - - Share graphs with URLs - -2. **Real-time Collaboration** - - WebSocket integration - - Multi-user editing - - Cursor tracking - -3. **Comments & Annotations** - - Add notes to nodes/edges - - Discussion threads - - Version history - -### Phase 5: Polish -1. **Accessibility** - - Keyboard navigation improvements - - Screen reader support - - High contrast mode - -2. **Performance** - - Virtual rendering for large graphs - - Progressive loading - - Optimized re-renders - -3. **Mobile Support** - - Touch gesture improvements - - Mobile-optimized toolbar - - Responsive layout enhancements - -## Testing the Application - -### Basic Workflow Test -1. Start dev server: `npm run dev` -2. Add a "Person" node -3. Add an "Organization" node -4. Drag from Person to Organization to create an edge -5. Move nodes around -6. Select and delete an edge -7. Clear the graph - -### Expected Behavior -- Nodes appear when buttons clicked -- Nodes can be dragged smoothly -- Edges connect nodes visually -- Selection highlights elements -- Deletion removes elements -- Graph clears with confirmation - -## Build Verification - -The project has been successfully built and verified: -- TypeScript compilation: PASSED -- Vite production build: PASSED -- Output bundle size: ~300KB (uncompressed) -- No TypeScript errors -- No build warnings - -## Notes - -- All paths provided are absolute paths as required -- Modern React patterns used (hooks, functional components) -- Strict TypeScript mode enabled for type safety -- ESLint configured for code quality -- Tailwind CSS optimized for production (unused classes purged) -- Git repository already initialized -- Node version: 20.18.1 -- NPM version: 9.2.0 - -## Success Criteria Met - -- Complete React application scaffolded -- All dependencies installed -- TypeScript properly configured -- React Flow integrated and working -- Zustand state management implemented -- Tailwind CSS styling applied -- Basic graph editing functionality working -- Production build successful -- Comprehensive documentation provided -- Runnable with `npm install && npm run dev` diff --git a/docs/QUICK_REFERENCE.md b/docs/QUICK_REFERENCE.md deleted file mode 100644 index 6fb5c51..0000000 --- a/docs/QUICK_REFERENCE.md +++ /dev/null @@ -1,443 +0,0 @@ -# Temporal & Scenario Analysis - Quick Reference Card - -## At a Glance - -**What is this?** -A tool for temporal evolution analysis and scenario exploration of constellation graphs. - -**What it's NOT:** -Version control (Git), undo/redo, or collaborative editing. - -**Key Idea:** -Capture snapshots of your graph at different times or scenarios, then compare and analyze them. - ---- - -## Core Concepts (5-Second Summary) - -| Concept | What It Is | Example | -|---------|-----------|---------| -| **State** | Snapshot of graph at specific moment | "Q1 2023", "Session 5", "Strategy A" | -| **Timeline** | Ordered sequence of states | Jan → Feb → Mar → Apr | -| **Scenario** | Alternative branch from a point | Current → Strategy A vs Strategy B | -| **Comparison** | Visual diff between two states | What changed from Q1 to Q4? | -| **Journey** | Track one actor across states | How did Alice's role evolve? | - ---- - -## Common Use Cases - -### 1. Historical Tracking -Track how your network changed over time -- **Example**: Company org chart 2020-2024 -- **Action**: Capture state at each quarter/year - -### 2. Therapeutic Progress -Show relationship evolution across sessions -- **Example**: Family therapy sessions 1-10 -- **Action**: Capture state after each session - -### 3. Strategic Planning -Explore different future scenarios -- **Example**: 3 different growth strategies -- **Action**: Branch scenarios from current state - -### 4. Project Evolution -Show stakeholder changes through project phases -- **Example**: Kickoff → Planning → Execution → Closure -- **Action**: Capture state at each phase - ---- - -## Quick Actions - -### Capture a State -1. Work on your graph -2. Click "Capture State" button (toolbar) -3. Label it (e.g., "Q1 2023") -4. Add notes (optional) -5. Done! - -### Load a State -1. Click state selector dropdown (toolbar) -2. Choose state from list -3. Graph updates to that state - -### Compare Two States -1. Select state A -2. Click "Compare" button -3. Select state B -4. View differences (side-by-side or overlay) - -### Create Timeline -1. Capture several states -2. Open timeline panel (bottom) -3. States auto-appear in order -4. Use scrubber to navigate - -### Create Scenario Branch -1. Load the branching point state -2. Menu → States → Create Scenario Branch -3. Name it and add description -4. Modify graph for this scenario -5. Capture states along the scenario - ---- - -## Keyboard Shortcuts - -| Shortcut | Action | -|----------|--------| -| `Ctrl+Shift+S` | Capture current state | -| `Ctrl+Shift+T` | Toggle timeline panel | -| `Ctrl+Shift+C` | Open comparison view | -| `←` / `→` | Navigate timeline (when focused) | -| `Space` | Play/pause animation (when focused) | -| `Ctrl+J` | View actor journeys | -| `Ctrl+Shift+P` | Presentation mode | -| `Esc` | Exit modal/presentation | - ---- - -## UI Components - -### Toolbar (Top) -``` -Current State: Q3 2023 ▼ [📸 Capture] [🔍 Compare] -``` -- **State Selector**: Dropdown to switch states -- **Capture Button**: Create new state -- **Compare Button**: Compare two states - -### Timeline Panel (Bottom) -``` -●═══●═══●═══●═══● -Q1 Q2 Q3 Q4 Now -``` -- **Markers**: Click to load state -- **Scrubber**: Drag to animate through timeline -- **Controls**: Navigate, play, compare - -### Right Panel -When state is selected, shows: -- State metadata (date, label, notes) -- Quick stats (actors, relations) -- Navigation (previous/next) -- Actions (edit, compare, delete) - ---- - -## Comparison Modes - -### Side-by-Side -Two graphs shown next to each other -- **Best for**: Overall comparison -- **Pros**: Clear separation -- **Cons**: Takes more screen space - -### Overlay -Changes highlighted on single graph -- **Best for**: Detailed change analysis -- **Pros**: Shows changes in context -- **Cons**: Can be cluttered with many changes - -### Diff List -Text list of all changes -- **Best for**: Systematic review -- **Pros**: Comprehensive, exportable -- **Cons**: Less visual - ---- - -## Change Indicators - -### Visual Coding -- 🟢 **Green**: Added (new actors/relations) -- 🔴 **Red**: Removed (deleted actors/relations) -- 🟡 **Yellow**: Modified (changed properties) -- ⚪ **Gray**: Unchanged - -### Change Types -- **Actor Added**: New person/entity joined -- **Actor Removed**: Person/entity left -- **Actor Modified**: Role, name, or properties changed -- **Relation Added**: New connection formed -- **Relation Removed**: Connection broken -- **Relation Modified**: Relationship type or strength changed - ---- - -## Best Practices - -### Naming States -✅ **Good**: "Q3 2023: Post-Merger Integration" -❌ **Bad**: "State 3" - -✅ **Good**: "Session 5: Breakthrough Session" -❌ **Bad**: "May 15" - -✅ **Good**: "Strategy A: Aggressive Growth (Optimistic)" -❌ **Bad**: "Option 1" - -### When to Capture States -✅ Capture at **significant milestones** -✅ Capture at **regular intervals** (quarterly, sessions) -✅ Capture **before major changes** - -❌ Don't capture for every tiny edit -❌ Don't create states "just in case" -❌ Don't capture without context/labels - -### Organizing States -✅ **Use timelines** for temporal sequences -✅ **Use scenarios** for alternatives -✅ **Add descriptions** explaining what changed -✅ **Tag states** for easy finding - -❌ Don't mix temporal and scenario in same timeline -❌ Don't create orphaned states without context -❌ Don't forget to clean up old/unused states - ---- - -## Common Workflows - -### Workflow: Temporal Analysis -``` -1. Start with current graph -2. Capture state: "Jan 2024" -3. Make changes to graph -4. Capture state: "Feb 2024" -5. Repeat monthly -6. View timeline -7. Compare Jan vs Dec -8. Animate evolution -``` - -### Workflow: Scenario Exploration -``` -1. Create current state: "Current Reality" -2. Branch scenario: "Strategy A" -3. Modify graph for Strategy A -4. Capture: "Strategy A - Year 1" -5. Return to "Current Reality" -6. Branch scenario: "Strategy B" -7. Develop Strategy B -8. Compare Strategy A vs B -``` - -### Workflow: Actor Journey -``` -1. Ensure multiple states captured -2. Select actor on graph -3. Click "View Journey" (right panel) -4. See actor's timeline -5. Review changes over time -6. Export journey report -``` - ---- - -## Data Model Summary - -### State -```typescript -{ - stateId: "unique-id", - stateType: "temporal" | "scenario", - snapshot: { nodes, edges }, - temporal: { label, timestamp }, - // OR - scenario: { label, description, assumptions }, - notes: "What changed and why" -} -``` - -### Timeline -```typescript -{ - timelineId: "unique-id", - label: "Project Evolution", - states: ["state1", "state2", "state3"] -} -``` - -### Scenario Branch -```typescript -{ - branchId: "unique-id", - label: "Strategy A", - states: ["stateA1", "stateA2"], - color: "#3b82f6" -} -``` - ---- - -## Comparison Output - -### Summary Statistics -- Total actors: Before → After (Δ) -- Total relations: Before → After (Δ) -- Network density change -- Centrality changes - -### Detailed Changes -- Actors added: [list] -- Actors removed: [list] -- Actors modified: [list with changes] -- Relations added: [list] -- Relations removed: [list] -- Relations modified: [list with changes] - -### Export Formats -- PDF report with graphs -- JSON data for analysis -- CSV for spreadsheet -- HTML interactive report - ---- - -## Performance Tips - -### For Large Graphs (100+ actors) -- Capture states selectively -- Use state pagination -- Enable caching -- Reduce animation quality if needed - -### For Many States (50+ states) -- Organize into multiple timelines -- Use semantic search (ChromaDB) -- Archive old states -- Export/backup regularly - -### For Smooth Animation -- Limit number of frames -- Use simplified rendering -- Adjust animation speed -- Close other applications - ---- - -## Troubleshooting - -### Problem: State won't load -**Solution**: Check if state data is corrupted, try restarting app - -### Problem: Comparison is slow -**Solution**: Large graph - reduce comparison mode quality or use diff list - -### Problem: Animation is choppy -**Solution**: Reduce animation speed or quality setting - -### Problem: Can't find a state -**Solution**: Use search function or check timeline filters - -### Problem: Timeline is cluttered -**Solution**: Create multiple timelines, archive old states - ---- - -## Implementation Status - -| Phase | Feature | Status | Priority | -|-------|---------|--------|----------| -| 1 | Core State Management | 🔲 Not Started | HIGH | -| 2 | Temporal Analysis | 🔲 Not Started | HIGH | -| 3 | Comparison & Diff | 🔲 Not Started | HIGH | -| 4 | Scenario Branching | 🔲 Not Started | MEDIUM | -| 5 | Actor Journeys | 🔲 Not Started | MEDIUM | -| 6 | Animation & Presentation | 🔲 Not Started | MEDIUM | -| 7 | ChromaDB Integration | 🔲 Not Started | MEDIUM | -| 8 | Advanced Features | 🔲 Not Started | LOW | - ---- - -## Resources - -### Documentation -- **Summary**: `TEMPORAL_ANALYSIS_SUMMARY.md` -- **Full Plan**: `TEMPORAL_SCENARIO_IMPLEMENTATION_PLAN.md` -- **User Guide**: `TEMPORAL_QUICK_START.md` -- **Examples**: `VISUAL_EXAMPLES.md` -- **Checklist**: `IMPLEMENTATION_CHECKLIST.md` -- **This Card**: `QUICK_REFERENCE.md` - -### Key Files (To Be Created) -- Types: `/src/types/temporal.ts` -- Store: `/src/stores/stateStore.ts` -- Components: `/src/components/TemporalAnalysis/` - ---- - -## FAQs - -**Q: Will this replace normal editing?** -A: No, it's optional. You can ignore states and use app normally. - -**Q: Can I undo after capturing a state?** -A: Yes, undo/redo is separate. States don't affect edit history. - -**Q: How many states can I create?** -A: No hard limit, but recommend <100 per document for performance. - -**Q: Can I delete a state?** -A: Yes, but be careful - this can't be undone. - -**Q: Can I rename states?** -A: Yes, edit state metadata anytime. - -**Q: Can states be shared?** -A: Yes, they're included in document export/import. - -**Q: What's the difference between temporal and scenario?** -A: Temporal = time progression. Scenario = alternative branches. - -**Q: Can I merge scenarios?** -A: No, scenarios are independent explorations for comparison. - ---- - -## Quick Tips - -💡 **Tip 1**: Label states descriptively - your future self will thank you - -💡 **Tip 2**: Use comparison view liberally - it's the most powerful feature - -💡 **Tip 3**: Animate timelines for presentations - it's impressive! - -💡 **Tip 4**: Track key actors across states to tell their story - -💡 **Tip 5**: Capture states BEFORE making major changes (safety net) - -💡 **Tip 6**: Use scenarios to explore "what if" without commitment - -💡 **Tip 7**: Export comparison reports for documentation - -💡 **Tip 8**: Clean up old/unused states periodically - ---- - -## Remember - -This is about **storytelling and analysis**, not version control! - -Think: "How did this network evolve?" not "What edits did I make?" - -Use states to: -- ✅ Show temporal evolution -- ✅ Explore scenarios -- ✅ Compare alternatives -- ✅ Track actor journeys -- ✅ Present findings - -Not to: -- ❌ Undo/redo edits -- ❌ Track every change -- ❌ Collaborate on editing -- ❌ Version control your work - ---- - -**Happy analyzing!** 🎉 diff --git a/docs/SIDE_PANELS_UX_DESIGN.md b/docs/SIDE_PANELS_UX_DESIGN.md deleted file mode 100644 index fb80e7f..0000000 --- a/docs/SIDE_PANELS_UX_DESIGN.md +++ /dev/null @@ -1,2826 +0,0 @@ -# Side Panels UI/UX Design Specification -## Constellation Analyzer - Collapsible Panels Implementation Guide - -**Version:** 1.0 -**Date:** 2025-10-10 -**Status:** Design Specification -**Related:** UX_ANALYSIS.md Section 2.6, 4.1 - ---- - -## Table of Contents - -1. [Executive Summary](#executive-summary) -2. [Design Goals](#design-goals) -3. [Layout Architecture](#layout-architecture) -4. [Left Panel: Tools Panel](#left-panel-tools-panel) -5. [Right Panel: Properties Panel](#right-panel-properties-panel) -6. [Panel Controls & Interactions](#panel-controls--interactions) -7. [Responsive Behavior](#responsive-behavior) -8. [Visual Design Specifications](#visual-design-specifications) -9. [User Workflow Improvements](#user-workflow-improvements) -10. [Implementation Guide](#implementation-guide) -11. [Accessibility Requirements](#accessibility-requirements) -12. [Migration Strategy](#migration-strategy) - ---- - -## Executive Summary - -This document specifies the design for implementing collapsible left and right side panels in Constellation Analyzer, replacing the current horizontal toolbar and modal property dialogs with a more professional, space-efficient panel-based layout. - -**Key Changes:** -- **Left Panel (Tools):** Moves toolbar contents to collapsible sidebar with improved organization -- **Right Panel (Properties):** Replaces modal dialogs with persistent, context-aware panel -- **Canvas:** Gains more space when panels collapse, improving focus and usability -- **State Persistence:** Panel states (open/closed, width) saved per user in localStorage - -**User Benefits:** -- 40-60% more canvas space when panels collapsed -- Non-modal property editing maintains context -- Better visual hierarchy and tool organization -- Professional appearance matching modern IDEs -- Keyboard-driven panel control - ---- - -## Design Goals - -### Primary Objectives -1. **Maximize Canvas Space:** More room for complex graphs -2. **Maintain Context:** Non-modal panels keep graph visible while editing -3. **Improve Discoverability:** Organized, always-visible tool categories -4. **Professional Appearance:** Match VS Code, Figma, Blender-style panel systems -5. **Flexible Workspace:** User controls their preferred layout - -### Success Metrics -- Canvas space increases by 40-60% when both panels collapsed -- Property editing doesn't require closing panel to see results -- New users can find all tools within 30 seconds -- 80% of users keep preferred panel state across sessions - ---- - -## Layout Architecture - -### Overall Screen Structure - -``` -┌──────────────────────────────────────────────────────────────────────┐ -│ HEADER: Constellation Analyzer Logo + Title [×] │ ← 80px -├──────────────────────────────────────────────────────────────────────┤ -│ MENU BAR: File Edit View Layout Help │ ← 40px -├──────────────────────────────────────────────────────────────────────┤ -│ DOCUMENT TABS: Doc1 Doc2 [+] │ ← 44px -├────┬──────────────────────────────────────────────────────────┬─────┤ -│ │ │ │ -│ L │ CANVAS │ R │ -│ E │ (ReactFlow) │ I │ -│ F │ │ G │ -│ T │ Graph Editor Area │ H │ -│ │ │ T │ -│ P │ [Nodes, Edges, MiniMap, Controls] │ │ -│ A │ │ P │ -│ N │ │ A │ -│ E │ │ N │ -│ L │ │ E │ -│ │ │ L │ -└────┴──────────────────────────────────────────────────────────┴─────┘ - ↑ ↑ - 280px default 320px default - (collapsible to 40px icon bar) (collapsible to 0px) -``` - -### Dimension Specifications - -**Left Panel (Tools Panel)** -``` -Expanded: - - Default Width: 280px - - Min Width: 220px - - Max Width: 400px - - Collapsed Width: 40px (icon bar visible) - - Height: 100% of available space (below tabs) - -Collapsed Icon Bar: - - Width: 40px - - Shows vertical icons for quick access - - Tooltip on hover with panel name -``` - -**Right Panel (Properties Panel)** -``` -Expanded: - - Default Width: 320px - - Min Width: 280px - - Max Width: 600px - - Collapsed Width: 0px (completely hidden) - - Height: 100% of available space (below tabs) - -Collapsed State: - - Completely hidden to maximize canvas space - - Toggle button remains visible on canvas edge -``` - -**Canvas Area** -``` -Both Panels Open: - - Width: viewport - 280px - 320px = ~40% of 1920px screen - - Comfortable for medium graphs (10-20 nodes) - -Left Open, Right Closed: - - Width: viewport - 280px = ~60% of 1920px screen - - Good for active editing - -Both Panels Closed: - - Width: viewport - 40px = ~98% of 1920px screen - - Maximum space for large graphs (50+ nodes) - - Icon bar persists for quick tool access -``` - -### Responsive Breakpoints - -``` -≥ 1920px (Large Desktop): - - Both panels default open - - Canvas: ~1260px width - -1440px - 1919px (Desktop): - - Both panels default open - - Canvas: ~840px width - - Warning if both open and graph is large - -1024px - 1439px (Small Desktop/Laptop): - - Left panel default open - - Right panel default closed - - Canvas: ~744px width - -768px - 1023px (Tablet): - - Both panels default closed - - Panels overlay canvas when opened - - Canvas: ~728px width - -< 768px (Mobile): - - Panels not supported (show warning) - - Recommend desktop browser -``` - ---- - -## Left Panel: Tools Panel - -### Purpose -Centralized location for all graph creation and editing tools, replacing the horizontal toolbar. - -### Content Organization - -The left panel is divided into collapsible sections for easy scanning and organization: - -``` -┌─────────────────────────────┐ -│ [<] TOOLS [×] │ ← Header with collapse/close -├─────────────────────────────┤ -│ │ -│ ▼ HISTORY │ ← Collapsible section -│ [↶ Undo] [↷ Redo] │ -│ Move Actor │ ← Action description -│ │ -│ ▼ ADD ACTORS │ -│ ┌─────────────┐ │ -│ │ Person │ [●] │ ← Color indicator -│ └─────────────┘ │ -│ ┌─────────────┐ │ -│ │ Organization│ [●] │ -│ └─────────────┘ │ -│ ┌─────────────┐ │ -│ │ System │ [●] │ -│ └─────────────┘ │ -│ ┌─────────────┐ │ -│ │ Concept │ [●] │ -│ └─────────────┘ │ -│ [+ Manage Types...] │ -│ │ -│ ▼ RELATIONS │ -│ Active Type: │ -│ ┌─────────────────────┐ │ -│ │ Collaborates [▼] │ │ ← Dropdown -│ └─────────────────────┘ │ -│ ────────────────── │ ← Visual preview -│ [+ Manage Types...] │ -│ │ -│ ▼ LAYOUT │ -│ ┌─────────────────────┐ │ -│ │ Auto Layout [▼] │ │ -│ └─────────────────────┘ │ -│ • Force-Directed │ -│ • Hierarchical │ -│ • Circular │ -│ • Grid │ -│ │ -│ [Align Selected] │ -│ [Distribute Selected] │ -│ │ -│ ▼ VIEW │ -│ [Fit to Content] │ -│ [Reset Zoom] │ -│ ☑ Show Grid │ -│ ☑ Snap to Grid │ -│ ☑ Show MiniMap │ -│ │ -│ ▼ SEARCH │ -│ ┌─────────────────────┐ │ -│ │ 🔍 Search... │ │ -│ └─────────────────────┘ │ -│ No filters active │ -│ [Advanced Filters...] │ -│ │ -└─────────────────────────────┘ -``` - -### Section Specifications - -#### 1. History Section -**Purpose:** Quick access to undo/redo with visual feedback - -```typescript -interface HistorySection { - title: "HISTORY"; - collapsible: true; - defaultExpanded: true; - - content: { - undoButton: { - label: "Undo"; - icon: UndoIcon; - disabled: !canUndo; - tooltip: canUndo ? `Undo: ${lastAction}` : "Nothing to undo"; - shortcut: "Ctrl+Z"; - }; - redoButton: { - label: "Redo"; - icon: RedoIcon; - disabled: !canRedo; - tooltip: canRedo ? `Redo: ${nextAction}` : "Nothing to redo"; - shortcut: "Ctrl+Y"; - }; - actionDescription: { - text: lastAction; - style: "text-xs text-gray-500"; - }; - }; -} -``` - -**Visual Design:** -- Buttons side-by-side with icons -- Description text below shows last action -- Disabled state: 40% opacity -- Tooltips show action + keyboard shortcut - -#### 2. Add Actors Section -**Purpose:** Visual palette for creating nodes - -```typescript -interface AddActorsSection { - title: "ADD ACTORS"; - collapsible: true; - defaultExpanded: true; - - content: { - actorButtons: NodeTypeConfig[]; - manageTypesLink: { - label: "+ Manage Types..."; - action: openNodeTypeManager; - }; - }; - - buttonStyle: { - width: "100%"; - height: "48px"; - display: "flex"; - justifyContent: "space-between"; - padding: "12px"; - backgroundColor: nodeType.color; - borderRadius: "6px"; - marginBottom: "8px"; - }; -} -``` - -**Interactions:** -- Click button: Adds node to center of viewport -- Drag button: Start drag, drop on canvas to place (FUTURE) -- Hover: Shows full type description in tooltip -- Color dot on right shows node color - -**Visual Improvements over Current Toolbar:** -- Vertical layout allows more descriptive labels -- Color preview is larger and clearer -- Can show icons in future iterations -- Tooltips have more space for descriptions - -#### 3. Relations Section -**Purpose:** Manage active relation type for connections - -```typescript -interface RelationsSection { - title: "RELATIONS"; - collapsible: true; - defaultExpanded: true; - - content: { - activeTypeLabel: "Active Type:"; - dropdown: { - value: selectedRelationType; - options: edgeTypes; - onChange: setSelectedRelationType; - }; - visualPreview: { - // SVG line showing current type style - strokeColor: currentEdgeType.color; - strokeStyle: currentEdgeType.style; // solid/dashed/dotted - }; - manageTypesLink: { - label: "+ Manage Types..."; - action: openEdgeTypeManager; - }; - }; -} -``` - -**Visual Preview:** -- SVG line rendered below dropdown -- Shows actual edge color and style -- Updates live when dropdown changes -- Helps users visualize before creating - -#### 4. Layout Section -**Purpose:** One-click layout algorithms - -```typescript -interface LayoutSection { - title: "LAYOUT"; - collapsible: true; - defaultExpanded: false; // Collapsed by default - - content: { - autoLayoutDropdown: { - options: [ - "Force-Directed", - "Hierarchical (Top-Down)", - "Hierarchical (Left-Right)", - "Circular", - "Grid" - ]; - onSelect: applyLayout; - }; - alignButton: { - label: "Align Selected"; - enabled: selectedNodes.length >= 2; - submenu: ["Left", "Right", "Top", "Bottom", "Center H", "Center V"]; - }; - distributeButton: { - label: "Distribute Selected"; - enabled: selectedNodes.length >= 3; - submenu: ["Horizontally", "Vertically"]; - }; - }; -} -``` - -**Note:** This section implements recommendation from UX_ANALYSIS.md Section 2.3 - -#### 5. View Section -**Purpose:** Canvas view controls and settings - -```typescript -interface ViewSection { - title: "VIEW"; - collapsible: true; - defaultExpanded: false; - - content: { - fitViewButton: { - label: "Fit to Content"; - shortcut: "F"; - action: fitView; - }; - resetZoomButton: { - label: "Reset Zoom"; - shortcut: "Ctrl+0"; - action: () => setViewport({ zoom: 1 }); - }; - showGridToggle: { - label: "Show Grid"; - checked: showGrid; - onChange: toggleGrid; - }; - snapToGridToggle: { - label: "Snap to Grid"; - checked: snapToGrid; - onChange: toggleSnapToGrid; - }; - showMinimapToggle: { - label: "Show MiniMap"; - checked: showMinimap; - onChange: toggleMinimap; - }; - }; -} -``` - -**Improvement:** Consolidates view settings in one place - -#### 6. Search Section -**Purpose:** Find and filter actors/relations - -```typescript -interface SearchSection { - title: "SEARCH"; - collapsible: true; - defaultExpanded: false; - - content: { - searchInput: { - placeholder: "🔍 Search actors..."; - value: searchQuery; - onChange: handleSearch; - clearButton: true; - }; - filterStatus: { - text: activeFilters.length > 0 - ? `${filteredCount} of ${totalCount} actors` - : "No filters active"; - }; - advancedFiltersButton: { - label: "Advanced Filters..."; - action: openFilterPanel; - }; - }; -} -``` - -**Note:** Implements UX_ANALYSIS.md Section 1.4 (Search and Filter) - -### Collapsed State: Icon Bar - -When left panel collapses, it becomes a vertical icon bar: - -``` -┌──┐ -│≡ │ ← Menu toggle (expands panel) -├──┤ -│↶ │ ← Undo (with tooltip) -├──┤ -│+ │ ← Add Actor (with dropdown on click) -├──┤ -│⟿ │ ← Relations -├──┤ -│⊞ │ ← Layout -├──┤ -│👁│ ← View -├──┤ -│🔍│ ← Search -├──┤ -│ │ -│ │ -└──┘ -``` - -**Specifications:** -- Width: 40px -- Icons: 24x24px Material Icons -- Padding: 8px vertical between icons -- Background: Same as panel (white/gray) -- Border: Right border to separate from canvas -- Tooltips: Show section name + "Click to expand" - -**Interactions:** -- Click menu icon (≡): Expand full panel -- Click section icon: Expand panel and auto-scroll to that section -- Hover: Show tooltip with section name - ---- - -## Right Panel: Properties Panel - -### Purpose -Context-aware property inspector for selected graph elements, replacing modal dialogs with persistent panel. - -### Selection State Management - -The right panel content changes based on what's selected: - -```typescript -type SelectionState = - | { type: 'none' } - | { type: 'single-node'; node: Actor } - | { type: 'multiple-nodes'; nodes: Actor[] } - | { type: 'single-edge'; edge: Relation } - | { type: 'multiple-edges'; edges: Relation[] } - | { type: 'mixed'; nodes: Actor[]; edges: Relation[] }; -``` - -### State 1: Nothing Selected - -``` -┌─────────────────────────────┐ -│ PROPERTIES [×] │ -├─────────────────────────────┤ -│ │ -│ ╭─────────╮ │ -│ │ ··· │ │ -│ ╰─────────╯ │ -│ │ -│ No Selection │ -│ │ -│ Select a node or edge │ -│ to view properties │ -│ │ -│ Tips: │ -│ • Double-click to edit │ -│ • Right-click for menu │ -│ • Shift+click to multi- │ -│ select │ -│ │ -│ │ -│ │ -│ │ -└─────────────────────────────┘ -``` - -**Purpose:** -- Guide users on how to select items -- Provide helpful tips -- Clean, uncluttered state - -### State 2: Single Node Selected - -``` -┌─────────────────────────────┐ -│ ACTOR PROPERTIES [×] │ -├─────────────────────────────┤ -│ │ -│ Label * │ -│ ┌─────────────────────────┐ │ -│ │ John Doe │ │ ← Auto-focus, auto-select -│ └─────────────────────────┘ │ -│ │ -│ Type │ -│ ┌─────────────────────────┐ │ -│ │ Person [▼] │ │ -│ └─────────────────────────┘ │ -│ ┌─────────────────────────┐ │ -│ │ Color Preview │ │ ← Shows node color -│ └─────────────────────────┘ │ -│ │ -│ Description │ -│ ┌─────────────────────────┐ │ -│ │ Team lead for the │ │ -│ │ engineering department │ │ -│ │ │ │ -│ └─────────────────────────┘ │ -│ │ -│ ▼ METADATA (0) │ ← Collapsible -│ [+ Add Custom Field] │ -│ │ -│ ▼ CONNECTIONS (3) │ -│ → Collaborates │ -│ • Jane Smith │ -│ • Bob Johnson │ -│ → Reports To │ -│ • Alice Brown │ -│ │ -│ [View in Graph] │ ← Highlights connections -│ │ -├─────────────────────────────┤ -│ Node: actor-123 │ ← Footer with metadata -│ Position: (350, 240) │ -│ │ -│ [Delete Actor] │ -└─────────────────────────────┘ -``` - -**Specifications:** - -```typescript -interface NodePropertiesPanel { - header: { - title: "ACTOR PROPERTIES"; - closeButton: true; - }; - - fields: { - label: { - type: "text"; - required: true; - autoFocus: true; - autoSelect: true; - placeholder: "Enter actor name"; - onChange: (value) => updateNodeLive(node.id, { label: value }); - }; - - type: { - type: "select"; - options: nodeTypes; - onChange: (typeId) => updateNodeLive(node.id, { type: typeId }); - }; - - colorPreview: { - type: "visual"; - color: currentNodeType.color; - height: "40px"; - }; - - description: { - type: "textarea"; - rows: 4; - placeholder: "Add description..."; - onChange: debounce((value) => updateNodeLive(node.id, { description: value }), 500); - }; - }; - - sections: { - metadata: { - title: "METADATA"; - collapsible: true; - defaultExpanded: false; - badge: metadataCount; - content: MetadataEditor; - }; - - connections: { - title: "CONNECTIONS"; - collapsible: true; - defaultExpanded: true; - badge: connectionCount; - content: ConnectionList; - }; - }; - - footer: { - nodeInfo: { - id: node.id; - position: `(${Math.round(node.position.x)}, ${Math.round(node.position.y)})`; - }; - deleteButton: { - label: "Delete Actor"; - variant: "danger"; - confirmation: true; - }; - }; -} -``` - -**Key Features:** -1. **Live Updates:** Changes apply immediately (no Save button) -2. **Auto-focus:** Label field focused when panel opens -3. **Connection Preview:** Shows related edges and nodes -4. **Collapsible Sections:** Keep panel clean and scannable -5. **Non-modal:** Graph remains visible and interactive - -### State 3: Multiple Nodes Selected - -``` -┌─────────────────────────────┐ -│ BULK EDIT (3 ACTORS) [×] │ -├─────────────────────────────┤ -│ │ -│ Selected: │ -│ • John Doe (Person) │ -│ • Acme Corp (Organization) │ -│ • API Gateway (System) │ -│ │ -│ ────────────────────────── │ -│ │ -│ Bulk Actions: │ -│ │ -│ Change Type │ -│ ┌─────────────────────────┐ │ -│ │ (Keep Individual) [▼] │ │ -│ └─────────────────────────┘ │ -│ • Person │ -│ • Organization │ -│ • System │ -│ • Concept │ -│ │ -│ [Apply to All] │ -│ │ -│ ────────────────────────── │ -│ │ -│ Layout: │ -│ [Align Left] [Align Top] │ -│ [Align Center] [Align Right]│ -│ [Distribute Horizontally] │ -│ [Distribute Vertically] │ -│ │ -│ ────────────────────────── │ -│ │ -│ [Delete All Selected] │ -│ │ -└─────────────────────────────┘ -``` - -**Purpose:** Bulk operations for efficiency (implements UX_ANALYSIS.md 1.5) - -### State 4: Single Edge Selected - -``` -┌─────────────────────────────┐ -│ RELATION PROPERTIES [×] │ -├─────────────────────────────┤ -│ │ -│ From │ -│ ┌─────────────────────────┐ │ -│ │ John Doe (Person) │ │ ← Read-only, click to jump -│ └─────────────────────────┘ │ -│ ↓ │ -│ To │ -│ ┌─────────────────────────┐ │ -│ │ Jane Smith (Person) │ │ -│ └─────────────────────────┘ │ -│ │ -│ Type │ -│ ┌─────────────────────────┐ │ -│ │ Collaborates [▼] │ │ -│ └─────────────────────────┘ │ -│ ────────────────── │ ← Visual preview of style -│ │ -│ Custom Label (optional) │ -│ ┌─────────────────────────┐ │ -│ │ │ │ -│ └─────────────────────────┘ │ -│ Leave empty to use type │ -│ label: "Collaborates" │ -│ │ -│ ▼ METADATA (0) │ -│ [+ Add Custom Field] │ -│ │ -├─────────────────────────────┤ -│ Edge: edge-456 │ -│ │ -│ [Delete Relation] │ -└─────────────────────────────┘ -``` - -**Key Difference from Modal:** -- Graph stays visible -- Can see connection while editing -- Click source/target to jump to that node - -### State 5: Multiple Edges Selected - -``` -┌─────────────────────────────┐ -│ BULK EDIT (2 RELATIONS) [×] │ -├─────────────────────────────┤ -│ │ -│ Selected: │ -│ • John → Jane (Collaborates)│ -│ • Alice → Bob (Reports To) │ -│ │ -│ ────────────────────────── │ -│ │ -│ Change Type │ -│ ┌─────────────────────────┐ │ -│ │ (Keep Individual) [▼] │ │ -│ └─────────────────────────┘ │ -│ │ -│ [Apply to All] │ -│ │ -│ ────────────────────────── │ -│ │ -│ [Delete All Selected] │ -│ │ -└─────────────────────────────┘ -``` - -### Panel Behavior Specifications - -**Opening:** -- Double-click node/edge: Auto-opens right panel (if closed) -- Panel slides in from right (200ms ease-out) -- Previous content fades out, new content fades in (150ms) - -**Closing:** -- Click [×] button: Panel closes completely -- Click canvas: Selection clears, panel shows "Nothing Selected" -- Press Escape: Clears selection and closes panel - -**Live Updates:** -- Text inputs: Debounced 500ms, then update graph -- Dropdowns: Immediate update on change -- Checkboxes: Immediate update -- Visual feedback: Changed field highlights briefly (blue border pulse) - -**Validation:** -- Required fields: Red border if empty on blur -- Invalid values: Inline error message below field -- Block delete if it would orphan required relations (configurable) - ---- - -## Panel Controls & Interactions - -### Collapse/Expand Mechanisms - -#### Left Panel Collapse Button - -``` -Location: Top-left of panel header -Icon (Expanded): [<] ChevronLeft -Icon (Collapsed): [>] ChevronRight -Tooltip (Expanded): "Collapse Tools Panel (Ctrl+B)" -Tooltip (Collapsed): "Expand Tools Panel (Ctrl+B)" -Size: 32x32px -``` - -**Behavior:** -```typescript -function toggleLeftPanel() { - if (leftPanelExpanded) { - // Collapse to icon bar - animateWidth(leftPanelWidth, 40, 200, 'ease-out'); - setLeftPanelExpanded(false); - localStorage.setItem('leftPanelExpanded', 'false'); - } else { - // Expand to saved width or default - const width = localStorage.getItem('leftPanelWidth') || 280; - animateWidth(40, width, 200, 'ease-out'); - setLeftPanelExpanded(true); - localStorage.setItem('leftPanelExpanded', 'true'); - } -} -``` - -#### Right Panel Toggle Button - -When right panel is closed, a floating toggle button appears on the right edge of the canvas: - -``` -┌─────────────────────────────────────┐ -│ ┃ -│ CANVAS ┃← -│ ┃ -│ ┃ -│ [>]┃ ← Floating button -│ ┃ -│ ┃ -└─────────────────────────────────────┘ -``` - -**Specifications:** -```typescript -interface RightPanelToggle { - position: { - right: 0; - top: '50%'; - transform: 'translateY(-50%)'; - }; - size: { - width: '32px'; - height: '120px'; - }; - style: { - background: 'rgba(255, 255, 255, 0.9)'; - border: '1px solid #e5e7eb'; - borderRight: 'none'; - borderRadius: '8px 0 0 8px'; - boxShadow: '-2px 0 8px rgba(0,0,0,0.1)'; - }; - icon: ChevronLeftIcon; // Points left to indicate "expand" - tooltip: "Show Properties Panel (Ctrl+I)"; -} -``` - -**Interaction:** -- Hover: Slight width expansion (32px → 36px) -- Click: Panel slides in from right -- Auto-hide: Fades to 50% opacity when mouse not nearby -- Persists: Always visible when panel closed - -### Resize Handles - -Both panels can be resized by dragging the edge: - -``` -LEFT PANEL RESIZE: -┌──────────────────┃← Resize handle (4px width) -│ ┃ -│ TOOLS PANEL ┃ -│ ┃ -└──────────────────┃ - -RIGHT PANEL RESIZE: - ┃──────────────────┐ - ┃→ Resize handle │ - ┃ PROPERTIES │ - ┃ │ - ┃──────────────────┘ -``` - -**Specifications:** -```typescript -interface ResizeHandle { - width: 4px; - cursor: 'col-resize'; - - visual: { - default: 'transparent'; - hover: 'rgba(59, 130, 246, 0.5)'; // Blue highlight - dragging: 'rgba(59, 130, 246, 0.8)'; - }; - - constraints: { - leftPanel: { - min: 220, - max: 400, - }; - rightPanel: { - min: 280, - max: 600, - }; - }; - - behavior: { - onDragStart: () => document.body.style.cursor = 'col-resize'; - onDrag: (delta) => updatePanelWidth(currentWidth + delta); - onDragEnd: () => { - document.body.style.cursor = 'default'; - localStorage.setItem('panelWidth', newWidth); - }; - }; -} -``` - -**Visual Feedback During Resize:** -- Handle highlights on hover (blue glow) -- Cursor changes to col-resize -- Width value tooltip appears during drag: "280px" -- Smooth animation when snapping to min/max - -### Keyboard Shortcuts - -```typescript -const panelShortcuts = { - 'Ctrl+B': 'Toggle Left Panel (Tools)', - 'Ctrl+I': 'Toggle Right Panel (Properties/Inspector)', - 'Ctrl+Shift+B': 'Toggle Both Panels', - 'Escape': 'Close Right Panel & Clear Selection', -}; -``` - -**Implementation:** -```typescript -useEffect(() => { - const handleKeyPress = (e: KeyboardEvent) => { - if (e.ctrlKey && e.key === 'b') { - e.preventDefault(); - toggleLeftPanel(); - } - if (e.ctrlKey && e.key === 'i') { - e.preventDefault(); - toggleRightPanel(); - } - if (e.ctrlKey && e.shiftKey && e.key === 'B') { - e.preventDefault(); - toggleBothPanels(); - } - if (e.key === 'Escape') { - closeRightPanel(); - clearSelection(); - } - }; - - window.addEventListener('keydown', handleKeyPress); - return () => window.removeEventListener('keydown', handleKeyPress); -}, []); -``` - -### View Menu Integration - -Add panel visibility controls to View menu: - -``` -View - ├─ Focus Mode (F11) - ├─ ─────────────── - ├─ ☑ Show Tools Panel (Ctrl+B) - ├─ ☑ Show Properties Panel (Ctrl+I) - ├─ ─────────────── - ├─ Show Grid - ├─ Show MiniMap - └─ Fit View (F) -``` - ---- - -## Responsive Behavior - -### Screen Size Adaptations - -#### Large Desktop (≥1920px) -``` -Default State: -├─ Left Panel: Open (280px) -├─ Right Panel: Closed initially, opens on selection -├─ Canvas: ~1600px -└─ Behavior: Full features, all panels comfortable -``` - -#### Desktop (1440px - 1919px) -``` -Default State: -├─ Left Panel: Open (280px) -├─ Right Panel: Closed, opens on demand -├─ Canvas: ~1120px -└─ Behavior: Recommend closing left panel for large graphs -``` - -#### Small Desktop/Laptop (1024px - 1439px) -``` -Default State: -├─ Left Panel: Closed (icon bar only, 40px) -├─ Right Panel: Closed -├─ Canvas: ~980px -└─ Behavior: - • Show notification: "Panels collapsed for space. Press Ctrl+B to expand tools." - • Panels overlay canvas when opened (position: absolute with z-index) -``` - -#### Tablet (768px - 1023px) -``` -Default State: -├─ Left Panel: Collapsed -├─ Right Panel: Collapsed -├─ Canvas: Full width -└─ Behavior: - • Panels overlay canvas when opened - • Semi-transparent backdrop behind panels - • Click outside panel to close - • Touch-friendly panel toggle buttons (larger) -``` - -#### Mobile (<768px) -``` -Warning State: -├─ Show fullscreen message: -│ "For the best experience, please use a desktop browser." -│ [Continue Anyway] [Learn More] -│ -└─ If user continues: - • Panels completely disabled - • Toolbar remains but simplified - • Property editing via modal dialogs (current behavior) - • Limited graph editing capabilities -``` - -### Panel Priority Rules - -When screen space is limited, follow this priority: - -``` -1. Canvas (CRITICAL) - minimum 600px width -2. Left Panel Collapsed (IMPORTANT) - 40px icon bar -3. Right Panel Closed (OPTIONAL) - 0px -4. Left Panel Expanded (NICE-TO-HAVE) - 280px -5. Right Panel Open (NICE-TO-HAVE) - 320px -``` - -**Auto-collapse Logic:** -```typescript -function handleResize(viewportWidth: number) { - const MIN_CANVAS = 600; - - // Calculate required space - const leftWidth = leftPanelExpanded ? 280 : 40; - const rightWidth = rightPanelOpen ? 320 : 0; - const canvasWidth = viewportWidth - leftWidth - rightWidth; - - // Auto-collapse if canvas too small - if (canvasWidth < MIN_CANVAS) { - if (rightPanelOpen) { - closeRightPanel(); - showNotification('Properties panel auto-collapsed for space'); - } else if (leftPanelExpanded) { - collapseLeftPanel(); - showNotification('Tools panel auto-collapsed for space'); - } - } -} -``` - -### Overlay Mode - -On screens <1440px, panels can overlay the canvas instead of pushing it: - -``` -OVERLAY MODE VISUAL: - -┌─────────────────────────────────────┐ -│┌──────────────┐ │ -││ │ │ -││ LEFT PANEL │ CANVAS │ -││ (overlay) │ (dimmed 20%) │ -││ │ │ -││ │ │ -│└──────────────┘ │ -└─────────────────────────────────────┘ - ↑ - Semi-transparent - backdrop -``` - -**Specifications:** -```typescript -interface OverlayMode { - enabled: viewportWidth < 1440; - - leftPanel: { - position: 'absolute'; - zIndex: 100; - boxShadow: '2px 0 16px rgba(0,0,0,0.2)'; - }; - - rightPanel: { - position: 'absolute'; - zIndex: 100; - boxShadow: '-2px 0 16px rgba(0,0,0,0.2)'; - }; - - backdrop: { - position: 'absolute'; - background: 'rgba(0,0,0,0.2)'; - zIndex: 99; - onClick: closePanels; - }; - - behavior: { - clickOutside: 'close'; - escapeKey: 'close'; - animation: 'slide-in'; - }; -} -``` - ---- - -## Visual Design Specifications - -### Color Palette - -Based on existing Tailwind CSS theme in the app: - -```css -:root { - /* Panel Backgrounds */ - --panel-bg: #ffffff; - --panel-bg-hover: #f9fafb; - --panel-border: #e5e7eb; - - /* Headers */ - --panel-header-bg: #f3f4f6; - --panel-header-text: #111827; - - /* Sections */ - --section-title: #6b7280; - --section-border: #e5e7eb; - - /* Inputs */ - --input-border: #d1d5db; - --input-focus: #3b82f6; - --input-bg: #ffffff; - - /* Icon Bar (collapsed left panel) */ - --iconbar-bg: #f9fafb; - --iconbar-icon: #6b7280; - --iconbar-icon-hover: #1f2937; - - /* Resize Handle */ - --resize-hover: rgba(59, 130, 246, 0.5); - --resize-active: rgba(59, 130, 246, 0.8); - - /* Shadows */ - --panel-shadow: 0 1px 3px rgba(0,0,0,0.1); - --panel-shadow-hover: 0 2px 8px rgba(0,0,0,0.15); -} -``` - -### Typography - -```css -/* Panel Headers */ -.panel-header { - font-family: system-ui, -apple-system, sans-serif; - font-size: 14px; - font-weight: 600; - letter-spacing: 0.02em; - text-transform: uppercase; - color: var(--panel-header-text); -} - -/* Section Titles */ -.section-title { - font-size: 11px; - font-weight: 600; - letter-spacing: 0.05em; - text-transform: uppercase; - color: var(--section-title); -} - -/* Body Text */ -.panel-body { - font-size: 14px; - line-height: 1.5; - color: #374151; -} - -/* Labels */ -.field-label { - font-size: 12px; - font-weight: 500; - color: #4b5563; - margin-bottom: 4px; -} - -/* Help Text */ -.help-text { - font-size: 11px; - color: #6b7280; - font-style: italic; -} -``` - -### Spacing System - -Consistent spacing using 4px base unit: - -```css ---space-1: 4px; /* Tight */ ---space-2: 8px; /* Close */ ---space-3: 12px; /* Default */ ---space-4: 16px; /* Comfortable */ ---space-5: 20px; /* Spacious */ ---space-6: 24px; /* Section gaps */ ---space-8: 32px; /* Major sections */ -``` - -**Application:** -``` -Panel Padding: var(--space-4) (16px) -Section Gaps: var(--space-6) (24px) -Field Gaps: var(--space-4) (16px) -Label-Input Gap: var(--space-2) (8px) -Button Padding: var(--space-3) var(--space-4) (12px 16px) -``` - -### Component Styles - -#### Panel Container -```css -.panel { - background: var(--panel-bg); - border: 1px solid var(--panel-border); - box-shadow: var(--panel-shadow); - display: flex; - flex-direction: column; - height: 100%; - overflow: hidden; -} - -.panel-left { - border-right: 1px solid var(--panel-border); -} - -.panel-right { - border-left: 1px solid var(--panel-border); -} -``` - -#### Panel Header -```css -.panel-header { - padding: 12px 16px; - background: var(--panel-header-bg); - border-bottom: 1px solid var(--panel-border); - display: flex; - align-items: center; - justify-content: space-between; - flex-shrink: 0; -} - -.panel-title { - font-size: 14px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.02em; -} - -.panel-close-btn { - width: 24px; - height: 24px; - border-radius: 4px; - display: flex; - align-items: center; - justify-content: center; - transition: background 150ms; -} - -.panel-close-btn:hover { - background: rgba(0,0,0,0.05); -} -``` - -#### Collapsible Section -```css -.section { - border-bottom: 1px solid var(--section-border); -} - -.section-header { - padding: 12px 16px; - display: flex; - align-items: center; - justify-content: space-between; - cursor: pointer; - user-select: none; - transition: background 150ms; -} - -.section-header:hover { - background: var(--panel-bg-hover); -} - -.section-title { - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--section-title); - display: flex; - align-items: center; - gap: 8px; -} - -.section-badge { - background: #e5e7eb; - color: #6b7280; - font-size: 10px; - padding: 2px 6px; - border-radius: 10px; -} - -.section-content { - padding: 16px; - animation: slideDown 200ms ease-out; -} - -@keyframes slideDown { - from { - opacity: 0; - transform: translateY(-8px); - } - to { - opacity: 1; - transform: translateY(0); - } -} -``` - -#### Form Fields -```css -.field { - margin-bottom: 16px; -} - -.field-label { - display: block; - font-size: 12px; - font-weight: 500; - color: #4b5563; - margin-bottom: 4px; -} - -.field-input { - width: 100%; - padding: 8px 12px; - font-size: 14px; - border: 1px solid var(--input-border); - border-radius: 6px; - transition: border-color 150ms, box-shadow 150ms; -} - -.field-input:focus { - outline: none; - border-color: var(--input-focus); - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -.field-textarea { - resize: vertical; - min-height: 80px; - font-family: inherit; -} - -.field-select { - cursor: pointer; - appearance: none; - background-image: url("data:image/svg+xml..."); - background-position: right 8px center; - background-repeat: no-repeat; - padding-right: 32px; -} -``` - -#### Icon Bar (Collapsed Left Panel) -```css -.icon-bar { - width: 40px; - background: var(--iconbar-bg); - border-right: 1px solid var(--panel-border); - display: flex; - flex-direction: column; - align-items: center; - padding: 8px 0; - gap: 4px; -} - -.icon-bar-button { - width: 32px; - height: 32px; - border-radius: 6px; - display: flex; - align-items: center; - justify-content: center; - color: var(--iconbar-icon); - cursor: pointer; - transition: all 150ms; -} - -.icon-bar-button:hover { - background: rgba(59, 130, 246, 0.1); - color: var(--iconbar-icon-hover); -} - -.icon-bar-divider { - width: 24px; - height: 1px; - background: var(--panel-border); - margin: 4px 0; -} -``` - -#### Buttons -```css -.btn-primary { - padding: 8px 16px; - background: #3b82f6; - color: white; - border: none; - border-radius: 6px; - font-size: 14px; - font-weight: 500; - cursor: pointer; - transition: background 150ms; -} - -.btn-primary:hover { - background: #2563eb; -} - -.btn-danger { - background: #fef2f2; - color: #dc2626; - border: 1px solid #fecaca; -} - -.btn-danger:hover { - background: #fee2e2; -} - -.btn-secondary { - background: #f3f4f6; - color: #374151; - border: 1px solid #e5e7eb; -} - -.btn-secondary:hover { - background: #e5e7eb; -} -``` - -### Animation Specifications - -```css -/* Panel Expand/Collapse */ -.panel-transition { - transition: width 200ms ease-out; -} - -/* Content Fade */ -.content-fade-enter { - animation: fadeIn 150ms ease-in; -} - -.content-fade-exit { - animation: fadeOut 100ms ease-out; -} - -@keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } -} - -@keyframes fadeOut { - from { opacity: 1; } - to { opacity: 0; } -} - -/* Slide In (Right Panel) */ -.panel-slide-in { - animation: slideInRight 200ms ease-out; -} - -@keyframes slideInRight { - from { - opacity: 0; - transform: translateX(20px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -/* Field Focus Pulse */ -.field-changed { - animation: fieldPulse 400ms ease-out; -} - -@keyframes fieldPulse { - 0% { - border-color: var(--input-focus); - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3); - } - 100% { - border-color: var(--input-border); - box-shadow: none; - } -} -``` - ---- - -## User Workflow Improvements - -### Before (Current State) vs After (With Panels) - -#### Workflow 1: Adding Multiple Actors - -**BEFORE:** -``` -1. Look up at toolbar -2. Click "Person" button -3. Node appears in random position -4. Drag node to desired position -5. Scroll up to toolbar again -6. Click "Person" button again -7. Repeat 20 times... -8. Eyes constantly moving between toolbar and canvas -``` - -**AFTER:** -``` -1. Glance left at tools panel -2. Click "Person" button (larger target, always visible) -3. Node appears, position it -4. Click "Person" again (hand never leaves panel area) -5. Repeat efficiently -6. Eyes stay focused on left side and canvas -7. Alternatively: Press 'P' keyboard shortcut (even faster) -``` - -**Time Saved:** ~30% per actor creation -**Ergonomic Benefit:** Less eye movement, more consistent hand position - -#### Workflow 2: Editing Actor Properties - -**BEFORE:** -``` -1. Double-click node -2. Modal dialog appears, blocking canvas view -3. Change type from Person to Organization -4. Click Save -5. Modal closes -6. Look at canvas to see result -7. Not happy with the color -8. Double-click again to reopen -9. Change back -10. Save and close again -``` - -**AFTER:** -``` -1. Click node once (or double-click) -2. Right panel shows properties (canvas still visible) -3. Change type dropdown → see live update immediately -4. See color change on canvas in real-time -5. Adjust label while viewing context -6. See updated description on canvas -7. No Save button needed - all changes live -8. Click away when done -``` - -**Time Saved:** ~50% per property edit -**Context Benefit:** Always see graph while editing - -#### Workflow 3: Creating a Complex Relation Network - -**BEFORE:** -``` -1. Add nodes via toolbar -2. Select relation type in toolbar dropdown -3. Drag from node handle to create edge -4. Realize wrong relation type was selected -5. Double-click edge -6. Modal opens -7. Change type -8. Save -9. Modal closes -10. Repeat for each edge -``` - -**AFTER:** -``` -1. Add nodes from left panel -2. See current relation type highlighted in left panel -3. Change if needed (one click, stays visible) -4. Create edges -5. If wrong type: click edge, change in right panel (no modal) -6. See change immediately -7. Continue creating edges -8. Current type always visible in left panel -``` - -**Time Saved:** ~40% for multi-edge creation -**Error Reduction:** Less mistakes due to visible current type - -#### Workflow 4: Organizing Graph Layout - -**BEFORE:** -``` -1. Manually drag each node -2. Try to align them visually -3. Zoom out to see full graph -4. Still not aligned well -5. Zoom back in -6. Drag more nodes -7. Repeat until frustrated -8. Graph still looks messy -``` - -**AFTER:** -``` -1. Select multiple nodes (Shift+click) -2. Open left panel Layout section -3. Click "Align Left" → instant alignment -4. Click "Distribute Vertically" → even spacing -5. OR: Click "Auto Layout" → entire graph organized -6. Professional appearance in seconds -``` - -**Time Saved:** ~90% for layout tasks -**Quality Improvement:** Professional-looking graphs - -#### Workflow 5: Finding and Editing a Specific Actor - -**BEFORE:** -``` -1. Scroll around canvas looking for actor -2. Zoom in and out -3. Click wrong actor -4. Keep searching -5. Finally find it -6. Double-click to edit -7. Modal blocks view of graph -8. Make changes -9. Close modal -``` - -**AFTER:** -``` -1. Open Search section in left panel -2. Type actor name -3. Graph highlights matching actor -4. Click search result → jumps to actor -5. Right panel shows properties automatically -6. Edit while viewing full context -7. Done -``` - -**Time Saved:** ~70% for finding specific actors -**Frustration Reduction:** Significant - -### Drag-and-Drop Enhancement (Future) - -**Vision:** Drag actor buttons from left panel directly onto canvas - -``` -User Flow: -1. Click and hold "Person" button in left panel -2. Drag cursor onto canvas -3. Visual preview follows cursor (ghost node) -4. Release at desired position -5. Node created at exact drop location -6. Right panel auto-opens with focus on label field -7. User types name immediately -``` - -**Benefits:** -- More intuitive than click-then-drag -- Precise initial positioning -- Seamless creation-to-editing flow -- Familiar pattern from design tools (Figma, Sketch) - -**Implementation:** Phase 2 feature (not MVP) - -### Multi-Selection Property Editing - -**Scenario:** User has 10 Person actors that should be Organization actors - -**BEFORE:** -``` -1. Double-click actor 1 -2. Change type to Organization -3. Save, close modal -4. Repeat 9 more times -5. Total: ~2 minutes -``` - -**AFTER:** -``` -1. Shift+click all 10 actors -2. Right panel shows "BULK EDIT (10 ACTORS)" -3. Change Type dropdown to "Organization" -4. Click "Apply to All" -5. All 10 change instantly -6. Total: ~10 seconds -``` - -**Time Saved:** ~92% for bulk changes - ---- - -## Implementation Guide - -### Phase 1: Foundation (Week 1-2) - -#### Step 1.1: Create Panel Component Structure -``` -/src/components/Panels/ -├── PanelContainer.tsx # Wrapper with resize logic -├── LeftPanel/ -│ ├── LeftPanel.tsx # Main left panel component -│ ├── IconBar.tsx # Collapsed state icon bar -│ ├── sections/ -│ │ ├── HistorySection.tsx -│ │ ├── AddActorsSection.tsx -│ │ ├── RelationsSection.tsx -│ │ ├── LayoutSection.tsx -│ │ ├── ViewSection.tsx -│ │ └── SearchSection.tsx -│ └── styles.css -├── RightPanel/ -│ ├── RightPanel.tsx # Main right panel component -│ ├── ToggleButton.tsx # Floating toggle when closed -│ ├── states/ -│ │ ├── EmptyState.tsx -│ │ ├── NodeProperties.tsx -│ │ ├── EdgeProperties.tsx -│ │ ├── BulkNodeEdit.tsx -│ │ └── BulkEdgeEdit.tsx -│ └── styles.css -└── hooks/ - ├── usePanelState.ts # Manage panel open/closed/width - ├── useResizeHandle.ts # Resize drag logic - └── usePanelShortcuts.ts # Keyboard controls -``` - -#### Step 1.2: Create Panel State Store -```typescript -// /src/stores/panelStore.ts -import { create } from 'zustand'; -import { persist } from 'zustand/middleware'; - -interface PanelState { - // Left panel - leftPanelExpanded: boolean; - leftPanelWidth: number; - leftPanelSections: Record; // section collapsed state - - // Right panel - rightPanelOpen: boolean; - rightPanelWidth: number; - - // Actions - toggleLeftPanel: () => void; - setLeftPanelWidth: (width: number) => void; - toggleLeftSection: (sectionId: string) => void; - - toggleRightPanel: () => void; - setRightPanelWidth: (width: number) => void; - openRightPanel: () => void; - closeRightPanel: () => void; -} - -export const usePanelStore = create()( - persist( - (set) => ({ - // Defaults - leftPanelExpanded: true, - leftPanelWidth: 280, - leftPanelSections: { - history: true, - addActors: true, - relations: true, - layout: false, - view: false, - search: false, - }, - - rightPanelOpen: false, - rightPanelWidth: 320, - - // Actions - toggleLeftPanel: () => set((state) => ({ - leftPanelExpanded: !state.leftPanelExpanded - })), - - setLeftPanelWidth: (width) => set({ leftPanelWidth: width }), - - toggleLeftSection: (sectionId) => set((state) => ({ - leftPanelSections: { - ...state.leftPanelSections, - [sectionId]: !state.leftPanelSections[sectionId], - } - })), - - toggleRightPanel: () => set((state) => ({ - rightPanelOpen: !state.rightPanelOpen - })), - - setRightPanelWidth: (width) => set({ rightPanelWidth: width }), - - openRightPanel: () => set({ rightPanelOpen: true }), - closeRightPanel: () => set({ rightPanelOpen: false }), - }), - { - name: 'constellation-panels', // localStorage key - partialize: (state) => ({ - // Only persist these fields - leftPanelExpanded: state.leftPanelExpanded, - leftPanelWidth: state.leftPanelWidth, - leftPanelSections: state.leftPanelSections, - rightPanelWidth: state.rightPanelWidth, - // Don't persist rightPanelOpen (start closed) - }), - } - ) -); -``` - -#### Step 1.3: Implement Resize Hook -```typescript -// /src/components/Panels/hooks/useResizeHandle.ts -import { useCallback, useEffect, useRef, useState } from 'react'; - -interface UseResizeHandleOptions { - initialWidth: number; - minWidth: number; - maxWidth: number; - onResize: (width: number) => void; - direction: 'left' | 'right'; // Which side to resize from -} - -export function useResizeHandle({ - initialWidth, - minWidth, - maxWidth, - onResize, - direction, -}: UseResizeHandleOptions) { - const [isDragging, setIsDragging] = useState(false); - const [width, setWidth] = useState(initialWidth); - const startXRef = useRef(0); - const startWidthRef = useRef(initialWidth); - - const handleMouseDown = useCallback((e: React.MouseEvent) => { - setIsDragging(true); - startXRef.current = e.clientX; - startWidthRef.current = width; - document.body.style.cursor = 'col-resize'; - e.preventDefault(); - }, [width]); - - useEffect(() => { - if (!isDragging) return; - - const handleMouseMove = (e: MouseEvent) => { - const delta = direction === 'left' - ? e.clientX - startXRef.current - : startXRef.current - e.clientX; - - const newWidth = Math.min( - maxWidth, - Math.max(minWidth, startWidthRef.current + delta) - ); - - setWidth(newWidth); - onResize(newWidth); - }; - - const handleMouseUp = () => { - setIsDragging(false); - document.body.style.cursor = 'default'; - }; - - document.addEventListener('mousemove', handleMouseMove); - document.addEventListener('mouseup', handleMouseUp); - - return () => { - document.removeEventListener('mousemove', handleMouseMove); - document.removeEventListener('mouseup', handleMouseUp); - }; - }, [isDragging, direction, minWidth, maxWidth, onResize]); - - return { - width, - isDragging, - handleMouseDown, - }; -} -``` - -### Phase 2: Left Panel Implementation (Week 2-3) - -#### Step 2.1: Build Collapsible Section Component -```typescript -// /src/components/Panels/LeftPanel/CollapsibleSection.tsx -import { ReactNode } from 'react'; -import { ChevronDownIcon, ChevronRightIcon } from '@mui/icons-material'; - -interface CollapsibleSectionProps { - id: string; - title: string; - badge?: string | number; - defaultExpanded?: boolean; - children: ReactNode; -} - -export function CollapsibleSection({ - id, - title, - badge, - children, -}: CollapsibleSectionProps) { - const { leftPanelSections, toggleLeftSection } = usePanelStore(); - const isExpanded = leftPanelSections[id]; - - return ( -
-
toggleLeftSection(id)} - > -
- {isExpanded ? : } - {title} - {badge && {badge}} -
-
- - {isExpanded && ( -
- {children} -
- )} -
- ); -} -``` - -#### Step 2.2: Implement History Section -```typescript -// /src/components/Panels/LeftPanel/sections/HistorySection.tsx -import { CollapsibleSection } from '../CollapsibleSection'; -import { useDocumentHistory } from '@/hooks/useDocumentHistory'; -import { IconButton, Tooltip } from '@mui/material'; -import UndoIcon from '@mui/icons-material/Undo'; -import RedoIcon from '@mui/icons-material/Redo'; - -export function HistorySection() { - const { undo, redo, canUndo, canRedo, undoDescription, redoDescription } = - useDocumentHistory(); - - return ( - -
- - - - - - - - - - - - - - - - - {undoDescription && ( - - {undoDescription} - - )} -
-
- ); -} -``` - -#### Step 2.3: Implement Add Actors Section -```typescript -// /src/components/Panels/LeftPanel/sections/AddActorsSection.tsx -import { CollapsibleSection } from '../CollapsibleSection'; -import { useGraphWithHistory } from '@/hooks/useGraphWithHistory'; -import { createNode } from '@/utils/nodeUtils'; - -export function AddActorsSection() { - const { nodeTypes, addNode } = useGraphWithHistory(); - - const handleAddNode = (nodeTypeId: string) => { - const position = { - x: Math.random() * 400 + 100, - y: Math.random() * 300 + 100, - }; - - const nodeTypeConfig = nodeTypes.find((nt) => nt.id === nodeTypeId); - const newNode = createNode(nodeTypeId, position, nodeTypeConfig); - addNode(newNode); - }; - - return ( - -
- {nodeTypes.map((nodeType) => ( - - ))} - - -
-
- ); -} -``` - -### Phase 3: Right Panel Implementation (Week 3-4) - -#### Step 3.1: Build Right Panel Container with State Management -```typescript -// /src/components/Panels/RightPanel/RightPanel.tsx -import { useEffect } from 'react'; -import { usePanelStore } from '@/stores/panelStore'; -import { useGraphWithHistory } from '@/hooks/useGraphWithHistory'; -import { EmptyState } from './states/EmptyState'; -import { NodeProperties } from './states/NodeProperties'; -import { EdgeProperties } from './states/EdgeProperties'; -import { BulkNodeEdit } from './states/BulkNodeEdit'; -import { BulkEdgeEdit } from './states/BulkEdgeEdit'; -import { CloseIcon } from '@mui/icons-material'; - -export function RightPanel() { - const { rightPanelOpen, closeRightPanel, rightPanelWidth } = usePanelStore(); - const { selectedNodes, selectedEdges } = useGraphWithHistory(); - - // Determine which state to show - const getContent = () => { - if (selectedNodes.length === 1 && selectedEdges.length === 0) { - return ; - } - if (selectedEdges.length === 1 && selectedNodes.length === 0) { - return ; - } - if (selectedNodes.length > 1 && selectedEdges.length === 0) { - return ; - } - if (selectedEdges.length > 1 && selectedNodes.length === 0) { - return ; - } - return ; - }; - - if (!rightPanelOpen) return null; - - return ( -
-
-

PROPERTIES

- -
- -
- {getContent()} -
-
- ); -} -``` - -#### Step 3.2: Implement Node Properties State -```typescript -// /src/components/Panels/RightPanel/states/NodeProperties.tsx -import { useState, useEffect, useRef } from 'react'; -import { useGraphWithHistory } from '@/hooks/useGraphWithHistory'; -import type { Actor } from '@/types'; - -interface NodePropertiesProps { - node: Actor; -} - -export function NodeProperties({ node }: NodePropertiesProps) { - const { nodeTypes, updateNode, deleteNode } = useGraphWithHistory(); - const [label, setLabel] = useState(node.data?.label || ''); - const [type, setType] = useState(node.data?.type || ''); - const [description, setDescription] = useState(node.data?.description || ''); - const labelInputRef = useRef(null); - - // Auto-focus label input - useEffect(() => { - labelInputRef.current?.focus(); - labelInputRef.current?.select(); - }, [node.id]); - - // Live update on change (debounced for description) - const handleLabelChange = (value: string) => { - setLabel(value); - updateNode(node.id, { data: { ...node.data, label: value } }); - }; - - const handleTypeChange = (typeId: string) => { - setType(typeId); - updateNode(node.id, { data: { ...node.data, type: typeId } }); - }; - - const handleDescriptionChange = (value: string) => { - setDescription(value); - // Debounce this update - const timeoutId = setTimeout(() => { - updateNode(node.id, { data: { ...node.data, description: value } }); - }, 500); - return () => clearTimeout(timeoutId); - }; - - const currentType = nodeTypes.find(nt => nt.id === type); - - return ( -
- {/* Label */} -
- - handleLabelChange(e.target.value)} - placeholder="Enter actor name" - /> -
- - {/* Type */} -
- - - - {/* Color Preview */} - {currentType && ( -
- Color Preview -
- )} -
- - {/* Description */} -
- -