From 28f8224284922a295543fe266820664dd049309c Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Sat, 11 Oct 2025 22:00:34 +0200 Subject: [PATCH] feat: add timeline system for multi-state constellation analysis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements a comprehensive timeline system that enables documents to contain multiple constellation states with branching timelines. This allows users to create different versions of their analysis for temporal evolution, alternative scenarios, or what-if analysis. Core Features: - Timeline store managing multiple states per document with branching structure - Visual timeline panel with React Flow-based state graph visualization - State management: create, switch, rename, duplicate (parallel/series), delete - Per-state undo/redo history (max 50 actions per state) - Context menu for timeline node operations - Collapsible timeline panel (always visible, moved toolbar to panel header) Architecture Changes: - Document structure: removed top-level graph field, states now only in timeline - Global types: nodeTypes and edgeTypes are now global per document, not per state - State graphs: only contain nodes and edges, types inherited from document - Persistence: full timeline serialization/deserialization with all states - History system: converted from document-level to per-state independent stacks Timeline Components: - TimelineView: main timeline visualization with state nodes and edges - BottomPanel: collapsible container with timeline controls in header - StateNode: custom node component showing state info and active indicator - CreateStateDialog: dialog for creating new timeline states - RenameStateDialog: dialog for renaming existing states - Context menu: right-click operations (rename, duplicate parallel/series, delete) Document Management: - Documents always have timeline (initialized with root state on creation) - Timeline persisted with document in localStorage - Export/import includes complete timeline with all states - Migration support for legacy single-state documents Store Updates: - timelineStore: manages timelines, states, and timeline operations - historyStore: per-state history with independent undo/redo stacks - workspaceStore: saves/loads timeline data, handles global types - panelStore: added timeline panel visibility state - useActiveDocument: syncs timeline state with graph editor Context Menu Improvements: - Smart viewport edge detection to prevent overflow - Click-outside detection for React Flow panes - Consistent styling across application Files Added: - src/types/timeline.ts - Timeline type definitions - src/stores/timelineStore.ts - Timeline state management - src/components/Timeline/TimelineView.tsx - Main timeline component - src/components/Timeline/BottomPanel.tsx - Timeline panel container - src/components/Timeline/StateNode.tsx - State node visualization - src/components/Timeline/CreateStateDialog.tsx - State creation dialog - src/components/Timeline/RenameStateDialog.tsx - State rename dialog Files Removed: - src/stores/persistence/middleware.ts - Obsolete persistence middleware Documentation: - Added comprehensive timeline feature documentation - Implementation checklists and quick reference guides - Temporal analysis concepts and UX guidelines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- IMPLEMENTATION_CHECKLIST.md | 755 ++++++++++ QUICK_REFERENCE.md | 443 ++++++ TEMPORAL_ANALYSIS_SUMMARY.md | 602 ++++++++ TEMPORAL_QUICK_START.md | 462 ++++++ TEMPORAL_SCENARIO_IMPLEMENTATION_PLAN.md | 1311 ++++++++++++++++ TEMPORAL_SCENARIO_UX_CONCEPT.md | 1149 ++++++++++++++ UX_CONCEPT_MULTI_VERSION_GRAPH.md | 1318 +++++++++++++++++ VISUAL_EXAMPLES.md | 916 ++++++++++++ src/App.tsx | 119 +- src/components/Editor/ContextMenu.tsx | 38 + src/components/Timeline/BottomPanel.tsx | 151 ++ src/components/Timeline/CreateStateDialog.tsx | 99 ++ src/components/Timeline/RenameStateDialog.tsx | 79 + src/components/Timeline/StateNode.tsx | 99 ++ src/components/Timeline/TimelineView.tsx | 371 +++++ src/hooks/useDocumentHistory.ts | 166 +-- src/stores/graphStore.ts | 14 +- src/stores/historyStore.ts | 190 ++- src/stores/panelStore.ts | 35 + src/stores/persistence/fileIO.ts | 42 +- src/stores/persistence/loader.ts | 113 +- src/stores/persistence/middleware.ts | 41 - src/stores/persistence/saver.ts | 132 +- src/stores/persistence/types.ts | 21 +- src/stores/timelineStore.ts | 568 +++++++ src/stores/workspace/migration.ts | 5 +- src/stores/workspace/useActiveDocument.ts | 38 +- src/stores/workspaceStore.ts | 100 +- src/types/timeline.ts | 100 ++ 29 files changed, 8978 insertions(+), 499 deletions(-) create mode 100644 IMPLEMENTATION_CHECKLIST.md create mode 100644 QUICK_REFERENCE.md create mode 100644 TEMPORAL_ANALYSIS_SUMMARY.md create mode 100644 TEMPORAL_QUICK_START.md create mode 100644 TEMPORAL_SCENARIO_IMPLEMENTATION_PLAN.md create mode 100644 TEMPORAL_SCENARIO_UX_CONCEPT.md create mode 100644 UX_CONCEPT_MULTI_VERSION_GRAPH.md create mode 100644 VISUAL_EXAMPLES.md create mode 100644 src/components/Timeline/BottomPanel.tsx create mode 100644 src/components/Timeline/CreateStateDialog.tsx create mode 100644 src/components/Timeline/RenameStateDialog.tsx create mode 100644 src/components/Timeline/StateNode.tsx create mode 100644 src/components/Timeline/TimelineView.tsx delete mode 100644 src/stores/persistence/middleware.ts create mode 100644 src/stores/timelineStore.ts create mode 100644 src/types/timeline.ts diff --git a/IMPLEMENTATION_CHECKLIST.md b/IMPLEMENTATION_CHECKLIST.md new file mode 100644 index 0000000..611a1a8 --- /dev/null +++ b/IMPLEMENTATION_CHECKLIST.md @@ -0,0 +1,755 @@ +# 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/QUICK_REFERENCE.md b/QUICK_REFERENCE.md new file mode 100644 index 0000000..6fb5c51 --- /dev/null +++ b/QUICK_REFERENCE.md @@ -0,0 +1,443 @@ +# 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/TEMPORAL_ANALYSIS_SUMMARY.md b/TEMPORAL_ANALYSIS_SUMMARY.md new file mode 100644 index 0000000..656e23f --- /dev/null +++ b/TEMPORAL_ANALYSIS_SUMMARY.md @@ -0,0 +1,602 @@ +# Temporal & Scenario Analysis - Implementation Summary + +## Overview + +This document provides a high-level summary of the revised multi-graph implementation plan, now correctly framed as a **temporal and scenario analysis** tool for constellation analyses. + +--- + +## Key Correction: NOT Version Control + +### Previous Misunderstanding +The initial approach treated this as a version control system (like Git for graphs), focusing on: +- Commits and checkouts +- Branching for collaboration +- Merge operations +- Edit history + +### Corrected Understanding +This is actually a **temporal and scenario analysis tool** focused on: +- **Temporal evolution**: How constellations change over time +- **Scenario exploration**: Comparing alternative futures +- **Comparison analysis**: Visual diff and change tracking +- **Storytelling**: Presenting network dynamics + +--- + +## Core Use Cases + +### 1. Historical/Temporal Analysis +**Example**: Track how a team evolved from 2020 to 2024 +- Capture states at key time points (quarters, years, milestones) +- Navigate through timeline to see evolution +- Compare early vs. late states +- Identify inflection points and trends + +### 2. Therapeutic/Session-Based Tracking +**Example**: Family therapist tracking constellation across 10 sessions +- Capture state after each therapy session +- Track relationship changes over time +- Visualize progress and breakthroughs +- Compare initial vs. final states + +### 3. Strategic Scenario Planning +**Example**: Explore three different organizational strategies +- Start from current state +- Branch into multiple scenarios (Strategy A, B, C) +- Develop each scenario independently +- Compare outcomes side-by-side +- Present findings to stakeholders + +### 4. Project Evolution +**Example**: Stakeholder network from project kickoff to closure +- Capture states at project phases +- Track key stakeholders across phases +- Animate evolution for presentations +- Generate actor journey reports + +--- + +## Key Features + +### Phase 1: Core State Management (Weeks 1-2) +**Status**: Not started +**Priority**: HIGH - Foundation + +- Capture current graph as a "state" (snapshot) +- Load states to view at different points +- Basic state metadata (label, notes) +- Simple state selector dropdown + +**Deliverable**: Users can create and switch between states + +### Phase 2: Temporal Analysis (Weeks 3-4) +**Status**: Not started +**Priority**: HIGH - Core use case + +- Timeline management (ordered sequence of states) +- Timeline panel UI with scrubber +- Temporal metadata (dates, sequence numbers) +- Timeline navigation (previous/next) + +**Deliverable**: Users can create temporal sequences and navigate through time + +### Phase 3: Comparison & Diff (Weeks 5-6) +**Status**: Not started +**Priority**: HIGH - Key analytical feature + +- Diff calculation engine +- Visual diff overlay on graph +- Comparison view (side-by-side) +- Change summary panel +- Export comparison reports + +**Deliverable**: Users can compare states and see visual differences + +### Phase 4: Scenario Branching (Weeks 7-8) +**Status**: Not started +**Priority**: MEDIUM + +- Scenario data model and tree structure +- "Branch from here" UI +- Scenario tree visualization +- Scenario comparison + +**Deliverable**: Users can create and explore alternative scenarios + +### Phase 5: Actor Journeys (Weeks 9-10) +**Status**: Not started +**Priority**: MEDIUM + +- Track specific actors across states +- Journey visualization +- Property and relationship evolution +- Export actor journey reports + +**Deliverable**: Users can follow individual actors through time + +### Phase 6: Animation & Presentation (Weeks 11-12) +**Status**: Not started +**Priority**: MEDIUM + +- Smooth transitions between states +- Animation controls (play/pause/speed) +- Presentation mode (full-screen slideshow) +- Export animations (stretch goal) + +**Deliverable**: Users can animate evolution and present findings + +### Phase 7: ChromaDB Integration (Weeks 13-14) +**Status**: Not started +**Priority**: MEDIUM + +- Index states in ChromaDB +- Semantic search for states +- Pattern recognition +- Annotation storage and search + +**Deliverable**: Users can search and analyze state history semantically + +### Phase 8: Advanced Features (Weeks 15-16) +**Status**: Not started +**Priority**: LOW + +- Auto-capture states +- State templates +- Collaborative features (stretch) +- Advanced analytics + +--- + +## Data Model Changes + +### New Type: `AnalysisState` +Represents the constellation at a specific moment: +```typescript +interface AnalysisState { + stateId: string; + stateType: 'temporal' | 'scenario'; + snapshot: { + nodes: SerializedActor[]; + edges: SerializedRelation[]; + }; + temporal?: TemporalMetadata; // For time-based states + scenario?: ScenarioMetadata; // For scenario branches + relationships: StateRelationship[]; // Links to other states + notes?: string; + createdAt: string; +} +``` + +### Updated: `ConstellationDocument` +Existing documents get optional state support: +```typescript +interface ConstellationDocument { + metadata: { ... }; + graph: { ... }; // Current working graph + states?: { + stateList: AnalysisState[]; + currentStateId: string | null; + timelines: Timeline[]; + scenarioTrees: ScenarioTree[]; + settings: { ... }; + }; +} +``` + +### New Type: `Timeline` +Ordered sequence of temporal states: +```typescript +interface Timeline { + timelineId: string; + label: string; + states: string[]; // Ordered state IDs + displaySettings: { ... }; +} +``` + +### New Type: `ScenarioTree` +Hierarchical structure of branched scenarios: +```typescript +interface ScenarioTree { + rootStateId: string; + branches: Array<{ + branchId: string; + label: string; + states: string[]; + color?: string; + }>; +} +``` + +--- + +## Architecture Changes + +### New Store: `stateStore.ts` +Manages all state-related operations: +- Create/read/update/delete states +- Timeline management +- Scenario branch management +- State comparison +- Actor journey tracking +- ChromaDB integration + +### New Components: `TemporalAnalysis/` +``` +/src/components/TemporalAnalysis/ +├── TimelinePanel.tsx # Bottom panel with timeline +├── StateSelector.tsx # Dropdown to select states +├── ComparisonView.tsx # Side-by-side comparison +├── StateDiffViewer.tsx # List of changes +├── StateMetadataEditor.tsx # Edit state metadata +├── ActorJourneyViewer.tsx # Track actor across states +├── StateAnimator.tsx # Animation controls +└── PresentationMode.tsx # Full-screen slideshow +``` + +### Updated Components +- **Toolbar**: Add state controls (capture, current state indicator) +- **BottomPanel**: Integrate TimelinePanel +- **RightPanel**: Add state history section +- **MenuBar**: Add "States" menu +- **GraphEditor**: Support diff overlay and state loading + +--- + +## Terminology Changes + +| Old Term (Version Control) | New Term (Temporal/Scenario) | +|----------------------------|------------------------------| +| Version | State / Timepoint / Scenario | +| Commit | Capture State / Create Snapshot | +| Checkout | Load State / View State | +| Branch | Create Scenario Branch | +| Version History | Timeline / State History | +| Version Graph | Timeline / Scenario Tree | +| Diff | Comparison / Change Analysis | +| Merge | N/A (not applicable) | +| Revert | Restore State | + +--- + +## User Workflows + +### Workflow 1: Create Temporal Sequence +1. Work on graph normally +2. At key milestone, click "Capture State" +3. Label it (e.g., "Q1 2023") +4. Continue editing graph +5. Capture next state (e.g., "Q2 2023") +6. Repeat for all time points +7. View timeline panel to see sequence +8. Use scrubber to navigate + +### Workflow 2: Compare Two States +1. Select first state (e.g., "Q1 2023") +2. Click "Compare" button +3. Select second state (e.g., "Q4 2023") +4. View side-by-side comparison +5. See highlighted changes +6. Review change summary +7. Export comparison report + +### Workflow 3: Create Scenario Branch +1. Load the state to branch from (e.g., "Current") +2. Click "Create Scenario Branch" +3. Name it (e.g., "Strategy A") +4. Add description and assumptions +5. Modify graph for this scenario +6. Capture states along scenario +7. Return to branching point +8. Create alternative scenario (e.g., "Strategy B") +9. Compare scenarios + +### Workflow 4: Track Actor Journey +1. Select actor on graph +2. Click "View Journey" in right panel +3. See timeline of actor appearances +4. Review property changes over time +5. Examine relationship evolution +6. Export journey report + +### Workflow 5: Animate & Present +1. Create timeline with multiple states +2. Click "Animate" button +3. Adjust animation speed +4. Play animation (smooth transitions) +5. Enter presentation mode (full-screen) +6. Navigate through slideshow +7. Present to stakeholders + +--- + +## ChromaDB Integration Strategy + +### Collections + +**1. State Metadata Collection** +- Index state descriptions, notes, assumptions +- Enable semantic search ("Find states about merger") +- Support tag-based filtering + +**2. Actor Journey Collection** +- Store actor trajectories +- Enable actor-centric queries +- Track relationship evolution + +**3. Comparison Cache Collection** +- Cache expensive diff calculations +- Speed up repeated comparisons +- Store change summaries + +**4. Annotation Collection** +- Store user notes and insights +- Link to specific states or changes +- Enable annotation search + +### Use Cases + +1. **Semantic Search**: "Find all states related to organizational restructuring" +2. **Pattern Recognition**: "Find states similar to current state" +3. **Actor Tracking**: "Find all states where Alice appears" +4. **Change Analysis**: "Find states with significant network changes" +5. **Insight Discovery**: "Search annotations for mentions of 'conflict'" + +--- + +## Implementation Priority + +### Must-Have (MVP) +1. Phase 1: Core State Management +2. Phase 2: Temporal Analysis +3. Phase 3: Comparison & Diff + +These three phases provide core value: +- Users can capture states at different times +- Navigate through temporal sequences +- Compare and analyze differences + +### Should-Have +4. Phase 4: Scenario Branching +5. Phase 5: Actor Journeys +6. Phase 6: Animation & Presentation + +These add significant analytical and storytelling power. + +### Nice-to-Have +7. Phase 7: ChromaDB Integration +8. Phase 8: Advanced Features + +These enhance but aren't essential for core functionality. + +--- + +## Success Metrics + +### Feature Adoption +- Percentage of documents with states enabled +- Average number of states per document +- Temporal vs. scenario usage ratio + +### User Engagement +- Time spent in comparison view +- Number of comparisons per session +- Animation playback frequency +- Actor journey queries + +### Performance +- State creation time < 500ms +- Diff calculation time < 1s +- Animation frame rate > 30fps +- ChromaDB query latency < 200ms + +### User Satisfaction +- Qualitative feedback +- Feature requests +- Support tickets +- User testimonials + +--- + +## Migration Strategy + +### Existing Documents +- All existing documents continue to work +- States are optional (`states?` property) +- No breaking changes + +### Enabling States +Users can enable temporal analysis for any document: +1. Click "Enable Temporal Analysis" in menu +2. System captures current graph as initial state +3. Creates default timeline +4. User can now create additional states + +### Export/Import +- Export includes all states (if present) +- Import preserves state structure +- Backward compatible with old exports + +--- + +## Documentation Plan + +### User Documentation +- **Quick Start Guide**: `TEMPORAL_QUICK_START.md` (created) +- **Use Case Examples**: Included in quick start +- **Best Practices**: Included in quick start +- **Video Tutorials**: To be created + +### Developer Documentation +- **Implementation Plan**: `TEMPORAL_SCENARIO_IMPLEMENTATION_PLAN.md` (created) +- **Implementation Checklist**: `IMPLEMENTATION_CHECKLIST.md` (created) +- **Visual Examples**: `VISUAL_EXAMPLES.md` (created) +- **API Reference**: To be created with code +- **Type Definitions**: To be created in code + +### Design Documentation +- **Visual wireframes**: Included in plan and examples +- **Interaction patterns**: Included in examples +- **Component hierarchy**: Included in plan + +--- + +## Common Pitfalls to Avoid + +### 1. Creating Too Many States +**Problem**: State for every tiny change +**Solution**: Capture states only at significant milestones + +### 2. Poor State Naming +**Problem**: "State 1", "State 2" with no context +**Solution**: Use descriptive labels with date/context + +### 3. Not Using Comparison +**Problem**: Just switching between states without analysis +**Solution**: Actively use comparison view to identify changes + +### 4. Mixing Temporal and Scenario +**Problem**: Scenarios and time in same timeline +**Solution**: Keep temporal timelines and scenario branches separate + +### 5. Neglecting Metadata +**Problem**: States without descriptions or notes +**Solution**: Always add context (date, description, key changes) + +### 6. Performance Issues with Large Graphs +**Problem**: Slow diff calculation or animation +**Solution**: Implement caching, Web Workers, progressive rendering + +--- + +## Next Steps + +### Immediate (This Week) +1. Review and approve this revised plan +2. Set up project board/tracker +3. Create initial type definitions +4. Begin Phase 1 implementation + +### Short-Term (Next 2-4 Weeks) +1. Complete Phase 1 (Core State Management) +2. Begin Phase 2 (Temporal Analysis) +3. Create example documents for testing +4. Gather early user feedback + +### Medium-Term (Next 2-3 Months) +1. Complete Phases 2-3 (Temporal + Comparison) +2. Begin Phase 4 (Scenario Branching) +3. User testing with real use cases +4. Iterate based on feedback + +### Long-Term (3-6 Months) +1. Complete Phases 4-6 (Scenarios, Journeys, Animation) +2. ChromaDB integration (Phase 7) +3. Advanced features (Phase 8) +4. Production release + +--- + +## Files Created + +This revision has created the following documentation: + +1. **TEMPORAL_SCENARIO_IMPLEMENTATION_PLAN.md** (24KB) + - Complete technical implementation plan + - Data models and type definitions + - Component architecture + - Phase-by-phase breakdown + - ChromaDB integration details + - Algorithms and utilities + +2. **TEMPORAL_QUICK_START.md** (16KB) + - User-focused guide + - Core concepts explained + - Common use cases with examples + - Workflow walkthroughs + - Best practices + - FAQ + +3. **IMPLEMENTATION_CHECKLIST.md** (15KB) + - Granular task breakdown + - Phase-by-phase checklist + - Testing requirements + - Documentation tasks + - Success metrics + +4. **VISUAL_EXAMPLES.md** (20KB) + - Concrete visual examples + - 8 detailed scenarios + - UI mockups and wireframes + - Interaction patterns + - Before/after visualizations + +5. **TEMPORAL_ANALYSIS_SUMMARY.md** (This document) + - High-level overview + - Quick reference + - Links to detailed docs + +--- + +## Questions & Answers + +### Q: Is this replacing the existing document system? +**A**: No, it's an optional enhancement. Documents work fine without states. + +### Q: Can I use this for undo/redo? +**A**: No, states are for temporal/scenario analysis, not edit history. Undo/redo is separate. + +### Q: How many states can a document have? +**A**: No hard limit, but recommend <100 for performance. We'll implement pagination for large collections. + +### Q: Will this work with existing documents? +**A**: Yes, fully backward compatible. Enable states when you need them. + +### Q: Can I export just the timeline? +**A**: Yes, you can export specific timelines or scenario branches independently. + +### Q: How does this differ from version control? +**A**: Version control tracks edit history for recovery. This tracks temporal evolution and scenarios for analysis and storytelling. + +### Q: What about collaborative editing? +**A**: That's a separate feature. States can be shared but editing is still single-user. + +### Q: Can I animate between any two states? +**A**: Yes, animation works between any pair of states, not just sequential ones. + +--- + +## Resources + +### Documentation +- Main plan: `TEMPORAL_SCENARIO_IMPLEMENTATION_PLAN.md` +- User guide: `TEMPORAL_QUICK_START.md` +- Checklist: `IMPLEMENTATION_CHECKLIST.md` +- Examples: `VISUAL_EXAMPLES.md` + +### Code (To Be Created) +- Types: `/src/types/temporal.ts` +- Store: `/src/stores/stateStore.ts` +- Components: `/src/components/TemporalAnalysis/` +- Utils: `/src/utils/stateDiff.ts`, `stateAnimation.ts`, etc. + +### External Resources +- ChromaDB docs: https://docs.trychroma.com/ +- React Flow (for graph): https://reactflow.dev/ +- Zustand (state management): https://github.com/pmndrs/zustand + +--- + +## Conclusion + +This revised implementation plan transforms Constellation Analyzer from a static graph editor into a powerful temporal and scenario analysis tool. By correctly framing this as storytelling and analysis (not version control), we enable users to: + +1. **Understand change**: Track how networks evolve over time +2. **Explore alternatives**: Compare different possible futures +3. **Analyze dynamics**: Identify patterns, trends, and inflection points +4. **Communicate insights**: Present findings with animation and comparison + +The phased approach ensures we deliver value incrementally while building toward a comprehensive solution. Starting with core state management and temporal analysis provides immediate utility, while later phases add sophisticated analytical and presentation capabilities. + +**Ready to begin implementation!** diff --git a/TEMPORAL_QUICK_START.md b/TEMPORAL_QUICK_START.md new file mode 100644 index 0000000..cf5a8cd --- /dev/null +++ b/TEMPORAL_QUICK_START.md @@ -0,0 +1,462 @@ +# Temporal & Scenario Analysis - Quick Start Guide + +## What This Is (And Isn't) + +### This IS: +- A tool for **temporal evolution analysis** (how constellations change over time) +- A tool for **scenario exploration** (exploring alternative futures) +- A **comparison and analysis** framework (visualizing differences) +- A **storytelling platform** (presenting network dynamics) + +### This Is NOT: +- Version control for your work (not Git for graphs) +- Undo/redo functionality (that's separate) +- Collaborative editing (that's separate) +- A backup system (save your documents!) + +--- + +## Core Concepts + +### 1. States +A **state** is a snapshot of your constellation at a specific moment in time or scenario. Think of it as a photograph of your network. + +**Two types:** +- **Temporal States**: Time-based snapshots (e.g., "Q1 2023", "Session 5", "Post-Merger") +- **Scenario States**: Alternative futures (e.g., "Strategy A", "Pessimistic Case", "Option 2") + +### 2. Timelines +A **timeline** is an ordered sequence of temporal states showing evolution over time. + +**Example Timeline:** +``` +2020 → 2021 → 2022 → 2023 → Projected 2024 +``` + +### 3. Scenarios +A **scenario** is a branch from a specific point to explore "what if" alternatives. + +**Example Scenario Tree:** +``` +Current State + ├→ Strategy A → Quarter 2 → Quarter 3 + ├→ Strategy B → Quarter 2 + └→ Strategy C → Quarter 2 → Quarter 3 → Quarter 4 +``` + +### 4. Comparisons +**Comparison** shows the differences between any two states with visual highlighting: +- Green: Added actors/relations +- Red: Removed actors/relations +- Yellow: Modified actors/relations + +### 5. Actor Journeys +An **actor journey** tracks a specific actor across multiple states to see how they evolve. + +--- + +## Common Use Cases + +### Use Case 1: Historical Analysis +**Scenario**: You want to show how a team's structure evolved over a year. + +**Steps:** +1. Create states for each quarter: Q1, Q2, Q3, Q4 +2. Set temporal metadata (dates or sequence) +3. Add these states to a timeline +4. Use timeline scrubber to navigate through time +5. Compare Q1 vs Q4 to see total change + +**Result**: You can present the evolution story and identify key inflection points. + +--- + +### Use Case 2: Therapeutic Progress +**Scenario**: A therapist tracking a patient's family constellation across sessions. + +**Steps:** +1. Capture state after each session: "Session 1", "Session 5", "Session 10" +2. Track specific family members (actors) across sessions +3. Compare early vs. late sessions to show progress +4. Create animation showing relationship evolution + +**Result**: Visual evidence of therapeutic progress and relationship changes. + +--- + +### Use Case 3: Strategic Planning +**Scenario**: Exploring three different organizational restructuring options. + +**Steps:** +1. Capture current state: "Current Org Structure" +2. Create three scenario branches: + - "Option A: Consolidation" + - "Option B: Decentralization" + - "Option C: Hybrid" +3. Develop each scenario with different actor configurations +4. Compare all three scenarios side-by-side +5. Present findings to leadership + +**Result**: Clear visual comparison of strategic alternatives. + +--- + +### Use Case 4: Project Evolution +**Scenario**: Tracking how a project's stakeholder network changes from kickoff to completion. + +**Steps:** +1. Create timeline: "Kickoff" → "Planning" → "Execution" → "Closure" +2. Capture state at each phase +3. Track key stakeholders across all phases +4. Generate actor journey reports for executives +5. Animate the evolution for presentation + +**Result**: Compelling narrative of project dynamics and stakeholder engagement. + +--- + +## Key Features + +### Feature Matrix + +| Feature | Phase | Priority | Use Case | +|---------|-------|----------|----------| +| **Capture State** | 1 | HIGH | Create snapshots of current graph | +| **Load State** | 1 | HIGH | Switch between different states | +| **Timeline View** | 2 | HIGH | Navigate temporal sequences | +| **Temporal Metadata** | 2 | HIGH | Label states with dates/periods | +| **Compare States** | 3 | HIGH | Side-by-side comparison | +| **Visual Diff** | 3 | HIGH | Highlight changes on graph | +| **Change Summary** | 3 | HIGH | Statistics and change lists | +| **Scenario Branching** | 4 | MEDIUM | Create alternative futures | +| **Scenario Tree** | 4 | MEDIUM | Visualize branches | +| **Actor Journey** | 5 | MEDIUM | Track actors across states | +| **State Animation** | 6 | MEDIUM | Smooth transitions | +| **Presentation Mode** | 6 | MEDIUM | Full-screen slideshow | +| **Semantic Search** | 7 | MEDIUM | Find states by description | +| **Pattern Recognition** | 7 | MEDIUM | Identify similar states | + +--- + +## User Interface Overview + +### 1. Main Toolbar +``` +┌─────────────────────────────────────────────────────────┐ +│ [File] [Edit] [View] [States] [Help] │ +│ │ +│ Current State: Q3 2023 ▼ [📸 Capture] [🔍 Compare] │ +└─────────────────────────────────────────────────────────┘ +``` + +**New Controls:** +- **State Selector Dropdown**: Quick switch between states +- **Capture Button**: Create new state from current graph +- **Compare Button**: Open comparison view + +### 2. Bottom Timeline Panel +``` +┌─────────────────────────────────────────────────────────┐ +│ Timeline: Project Evolution [+] State [≡] View │ +├─────────────────────────────────────────────────────────┤ +│ │ +│ ●═══●═══●═══●═══●════┬══●═══● │ +│ Q1 Q2 Q3 Q4 Now │ S1 S2 (Branch A) │ +│ 2023 2023 2023 2023 │ │ +│ └──●═══● (Branch B) │ +│ S1 S2 │ +│ │ +│ [◀] [▶] Navigate [⏯] Animate [⚖] Compare │ +└─────────────────────────────────────────────────────────┘ +``` + +**Features:** +- Horizontal timeline with state markers +- Vertical scenario branches +- Navigation controls +- Animation playback +- Quick compare access + +### 3. Right Panel (When State Selected) +``` +┌─────────────────────────────────────┐ +│ State: Q3 2023 │ +├─────────────────────────────────────┤ +│ Type: Temporal │ +│ Date: 2023-09-30 │ +│ Sequence: 3 of 4 │ +│ │ +│ Actors: 12 │ +│ Relations: 18 │ +│ │ +│ Notes: │ +│ "Significant restructuring after │ +│ merger announcement..." │ +│ │ +│ [Edit Metadata] [Compare] [Delete] │ +│ │ +│ Navigation: │ +│ ← Q2 2023 | Q4 2023 → │ +└─────────────────────────────────────┘ +``` + +--- + +## Keyboard Shortcuts + +| Shortcut | Action | +|----------|--------| +| `Ctrl+Shift+S` | Capture current state | +| `Ctrl+Shift+T` | Open 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 | + +--- + +## Workflow Examples + +### Workflow 1: Creating a Temporal Sequence + +``` +1. Start with your current graph + └→ [Capture State] → "January 2025" + +2. Make changes to graph (add/remove/modify actors) + └→ [Capture State] → "February 2025" + +3. Continue for each time period + └→ [Capture State] → "March 2025" + +4. View timeline panel + └→ See all states in sequence + └→ Use scrubber to navigate + +5. Compare any two states + └→ [Compare] → Select "January" vs "March" + └→ See visual diff + +6. Animate evolution + └→ [Play] → Watch smooth transition +``` + +### Workflow 2: Exploring Scenarios + +``` +1. Load the state you want to branch from + └→ Select "Current State" from dropdown + +2. Create scenario branch + └→ [States Menu] → [Create Scenario Branch] + └→ Name: "Strategy A" + └→ Description: "Aggressive expansion" + └→ Assumptions: ["Funding secured", "Market growth"] + +3. Modify graph for this scenario + └→ Add new actors, relations + +4. Capture states along this scenario + └→ [Capture State] → "Strategy A - Q2" + └→ [Capture State] → "Strategy A - Q3" + +5. Return to branching point + └→ Select "Current State" again + +6. Create alternative scenario + └→ [Create Scenario Branch] → "Strategy B" + └→ Different modifications + +7. Compare scenarios + └→ [Compare] → "Strategy A - Q3" vs "Strategy B - Q3" +``` + +### Workflow 3: Actor Journey Analysis + +``` +1. Ensure you have multiple states captured + +2. Select an actor on the graph + └→ Click actor node + +3. View actor journey + └→ [Right Panel] → [View Journey] + OR + └→ [Ctrl+J] → Select actor from list + +4. Journey viewer shows: + └→ All states where actor appears + └→ Property changes over time + └→ Relationship changes + └→ Position evolution + +5. Export actor report + └→ [Export Journey] → PDF or JSON +``` + +--- + +## Best Practices + +### 1. State Naming +- **Use descriptive labels**: "Q3 2023 Post-Restructuring" not just "State 3" +- **Include context**: "Strategy A: Optimistic Scenario - Year 2" +- **Be consistent**: Use same format for similar states + +### 2. Timeline Organization +- **One timeline per narrative**: Don't mix different stories +- **Logical sequencing**: Ensure temporal order makes sense +- **Manageable length**: Consider breaking very long timelines + +### 3. Scenario Branching +- **Clear branching points**: Choose meaningful divergence points +- **Document assumptions**: Always explain what makes scenarios different +- **Parallel development**: Develop scenario branches to similar time horizons +- **Color coding**: Use colors to distinguish branches visually + +### 4. Comparison Analysis +- **Compare meaningful pairs**: Adjacent states or alternative scenarios +- **Focus on key changes**: Filter by change type if needed +- **Document insights**: Add notes about significant differences +- **Export reports**: Save comparison results for reference + +### 5. Presentation +- **Start with context**: Begin with overview state +- **Show progression**: Use animation for temporal sequences +- **Highlight key changes**: Use comparison view for dramatic differences +- **Tell a story**: Sequence states to create narrative flow + +--- + +## Common Pitfalls to Avoid + +### 1. Too Many States +**Problem**: Creating state for every tiny change clutters timeline +**Solution**: Capture states at significant milestones only + +### 2. Inconsistent Labeling +**Problem**: "Jan", "February 2023", "2023-03-15" in same timeline +**Solution**: Choose format and stick with it + +### 3. Forgetting Metadata +**Problem**: States labeled "State 1", "State 2" with no context +**Solution**: Always add description, date, or sequence info + +### 4. Not Using Comparison +**Problem**: Just switching between states without analyzing differences +**Solution**: Use comparison view to identify and document changes + +### 5. Orphaned Scenarios +**Problem**: Creating scenario branches but not developing them +**Solution**: Either fully develop scenarios or delete incomplete branches + +### 6. Mixing Temporal and Scenario +**Problem**: Putting scenarios and time progression in same timeline +**Solution**: Keep temporal timelines and scenario branches separate + +--- + +## Performance Tips + +### For Large Graphs (100+ actors) +- Capture states selectively (not every change) +- Use diff caching (automatic in ChromaDB integration) +- Limit animation quality for smooth playback +- Consider pagination for very long timelines + +### For Many States (50+ states) +- Organize into multiple timelines by theme +- Use semantic search to find relevant states +- Archive old/unused states +- Export and backup state data regularly + +--- + +## Integration with Existing Features + +### Document System +- Each document can have its own state history +- States are document-specific (not shared across documents) +- Duplicate document includes all states + +### Export/Import +- Export document includes all states +- Import preserves state structure +- Can export specific timeline or scenario branch + +### Undo/Redo +- Undo/redo works on current working graph +- Does NOT affect captured states +- Capturing state does not add to undo history + +--- + +## Next Steps + +### Phase 1: Getting Started (Week 1-2) +1. Implement basic state capture and loading +2. Create simple state selector +3. Test with example document + +### Phase 2: Temporal Features (Week 3-4) +1. Build timeline panel UI +2. Add temporal metadata editor +3. Implement timeline navigation + +### Phase 3: Comparison (Week 5-6) +1. Develop diff algorithm +2. Create comparison view UI +3. Add visual diff overlay + +**Continue with remaining phases as outlined in main implementation plan** + +--- + +## Questions to Consider + +Before implementing, discuss: + +1. **State Limit**: Should we limit number of states per document? +2. **Storage**: IndexedDB for states or localStorage? (Recommend IndexedDB) +3. **Persistence**: Auto-save states or explicit save? +4. **Naming**: Should users be prompted to name states or auto-generate? +5. **Default State**: What happens when document opens - load latest state or working graph? +6. **Branching UI**: Tree view or timeline with vertical branches? +7. **Animation**: Default animation duration and easing function? +8. **Export**: Include states in normal JSON export or separate? + +--- + +## Resources + +- **Main Implementation Plan**: See `TEMPORAL_SCENARIO_IMPLEMENTATION_PLAN.md` for complete technical details +- **Type Definitions**: `/src/types/temporal.ts` (to be created) +- **Store Implementation**: `/src/stores/stateStore.ts` (to be created) +- **UI Components**: `/src/components/TemporalAnalysis/` (to be created) + +--- + +## Success Criteria + +You'll know the implementation is successful when users can: + +1. Capture a state in < 3 clicks +2. Navigate timeline intuitively +3. Immediately see differences when comparing states +4. Create and understand scenario branches +5. Animate evolution smoothly +6. Find specific states quickly (with ChromaDB) +7. Present findings effectively to stakeholders + +--- + +## Support + +For questions about this implementation: +- Review the main implementation plan for technical details +- Check type definitions for data structures +- Examine workflow examples for common patterns +- Test with real use cases early and often + +**Remember**: This is about storytelling and analysis, not version control! diff --git a/TEMPORAL_SCENARIO_IMPLEMENTATION_PLAN.md b/TEMPORAL_SCENARIO_IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..15e5a82 --- /dev/null +++ b/TEMPORAL_SCENARIO_IMPLEMENTATION_PLAN.md @@ -0,0 +1,1311 @@ +# Temporal & Scenario Analysis Implementation Plan + +## Executive Summary + +This implementation plan transforms Constellation Analyzer into a powerful tool for **temporal evolution analysis** and **scenario exploration**. This is NOT a version control system - it's a storytelling and analytical tool that helps users: + +1. **Track temporal evolution**: Show how constellations change over time (historical or projected) +2. **Explore scenarios**: Branch from any point to explore alternative futures +3. **Compare and analyze**: Visualize differences between states +4. **Present findings**: Create compelling narratives about network dynamics + +### Key ChromaDB Integration Opportunities + +ChromaDB should be leveraged for: +- **State metadata search**: Find states by description, time period, or scenario assumptions +- **Actor trajectory analysis**: Track how specific actors evolve across states +- **Pattern recognition**: Identify similar states or recurring patterns +- **Annotation storage**: Store and retrieve analytical notes about changes +- **Comparison results**: Cache diff calculations for quick retrieval + +--- + +## 1. Revised Data Model + +### 1.1 Core Types (New File: `/src/types/temporal.ts`) + +```typescript +/** + * Type of analysis state + */ +export type StateType = 'temporal' | 'scenario'; + +/** + * Temporal metadata for time-based states + */ +export interface TemporalMetadata { + // Absolute date/time + timestamp?: string; // ISO 8601 format + + // Relative ordering + sequenceNumber?: number; + + // Human-readable labels + label: string; // e.g., "Q1 2023", "Session 5", "Post-Merger" + + // Period information + periodStart?: string; + periodEnd?: string; + + // Display properties + displayFormat?: 'date' | 'sequence' | 'label'; +} + +/** + * Scenario metadata for branched alternatives + */ +export interface ScenarioMetadata { + // Scenario identification + label: string; // e.g., "Strategy A", "Pessimistic Outlook" + + // Analytical context + description: string; + assumptions: string[]; // Key assumptions for this scenario + + // Probability/confidence + probability?: number; // 0-1 scale + confidence?: 'high' | 'medium' | 'low'; + + // Visual properties + color?: string; // Color code for this scenario branch +} + +/** + * Relationship between states + */ +export interface StateRelationship { + type: 'temporal-next' | 'temporal-previous' | 'scenario-branch' | 'scenario-parent'; + targetStateId: string; +} + +/** + * Analysis state - represents the constellation at a specific point in time or scenario + */ +export interface AnalysisState { + // Identity + stateId: string; + stateType: StateType; + + // Graph snapshot + snapshot: { + nodes: SerializedActor[]; + edges: SerializedRelation[]; + // Note: nodeTypes and edgeTypes inherited from document + }; + + // Metadata based on type + temporal?: TemporalMetadata; + scenario?: ScenarioMetadata; + + // Relationships + relationships: StateRelationship[]; + + // Annotations and analysis + notes?: string; + tags?: string[]; + + // Tracking + createdAt: string; + createdBy?: string; + + // ChromaDB integration + embeddingId?: string; // ID in ChromaDB for semantic search +} + +/** + * Diff between two states + */ +export interface StateDiff { + fromStateId: string; + toStateId: string; + + // Actor changes + actorsAdded: SerializedActor[]; + actorsRemoved: SerializedActor[]; + actorsModified: Array<{ + actorId: string; + changes: { + label?: { from: string; to: string }; + type?: { from: string; to: string }; + position?: { from: { x: number; y: number }; to: { x: number; y: number } }; + metadata?: { from: any; to: any }; + }; + }>; + + // Relation changes + relationsAdded: SerializedRelation[]; + relationsRemoved: SerializedRelation[]; + relationsModified: Array<{ + relationId: string; + changes: { + type?: { from: string; to: string }; + directionality?: { from: string; to: string }; + strength?: { from: number; to: number }; + }; + }>; + + // Summary statistics + summary: { + totalActorChanges: number; + totalRelationChanges: number; + networkDensityChange?: number; + centralityChanges?: Map; // Actor ID -> centrality delta + }; +} + +/** + * Actor journey - tracks a specific actor across multiple states + */ +export interface ActorJourney { + actorId: string; + label: string; + + // Appearances in different states + appearances: Array<{ + stateId: string; + stateLabel: string; + actor: SerializedActor; + timestamp?: string; + }>; + + // Summary + firstAppearance: string; // State ID + lastAppearance: string; // State ID + appearanceCount: number; +} + +/** + * Timeline - ordered sequence of temporal states + */ +export interface Timeline { + timelineId: string; + label: string; + description?: string; + + // Ordered states + states: string[]; // State IDs in temporal order + + // Display settings + displaySettings: { + showGrid: boolean; + snapToInterval?: 'day' | 'week' | 'month' | 'quarter' | 'year'; + autoLayout: boolean; + }; +} + +/** + * Scenario tree - hierarchical structure of branched scenarios + */ +export interface ScenarioTree { + rootStateId: string; + branches: Array<{ + branchId: string; + label: string; + states: string[]; // State IDs in this branch + color?: string; + }>; +} +``` + +### 1.2 Updated Document Type + +```typescript +// Update to ConstellationDocument in /src/stores/persistence/types.ts +export interface ConstellationDocument { + metadata: { + version: string; + appName: string; + createdAt: string; + updatedAt: string; + lastSavedBy: string; + documentId?: string; + title?: string; + + // NEW: Multi-state support + supportsStates?: boolean; // Feature flag + }; + + // Current/working graph state + graph: { + nodes: SerializedActor[]; + edges: SerializedRelation[]; + nodeTypes: NodeTypeConfig[]; + edgeTypes: EdgeTypeConfig[]; + }; + + // NEW: Temporal and scenario states + states?: { + // All states + stateList: AnalysisState[]; + + // Current active state + currentStateId: string | null; + + // Organization + timelines: Timeline[]; + scenarioTrees: ScenarioTree[]; + + // Settings + settings: { + enableAutoDiff: boolean; + showChangeIndicators: boolean; + defaultStateType: StateType; + }; + }; +} +``` + +--- + +## 2. Component Architecture + +### 2.1 New Components + +#### `/src/components/TemporalAnalysis/` (New Directory) + +**TimelinePanel.tsx** +- Bottom panel for timeline/scenario visualization +- Horizontal axis: temporal progression +- Vertical axis: scenario branches +- Interactive timeline scrubber +- State creation and navigation controls + +**StateSelector.tsx** +- Dropdown/modal for selecting states +- Filter by type (temporal/scenario) +- Search by label, description, tags +- Visual tree/timeline representation + +**ComparisonView.tsx** +- Side-by-side or overlay comparison of two states +- Diff highlighting (added/removed/modified) +- Toggle between different comparison modes +- Export comparison reports + +**StateDiffViewer.tsx** +- Visual representation of changes between states +- Color-coded change indicators +- Summary statistics panel +- Drill-down into specific changes + +**StateMetadataEditor.tsx** +- Edit temporal/scenario metadata +- Set labels, timestamps, descriptions +- Manage assumptions for scenarios +- Add tags and notes + +**ActorJourneyViewer.tsx** +- Track specific actors across states +- Timeline visualization of actor evolution +- Relationship changes over time +- Export actor-specific reports + +**StateAnimator.tsx** +- Smooth transitions between states +- Configurable animation speed +- Morph visualization (actors moving, appearing, disappearing) +- Playback controls (play, pause, step forward/back) + +**PresentationMode.tsx** +- Slideshow through states +- Full-screen mode +- Annotations and narration +- Export as video/animated GIF + +#### `/src/components/Panels/` (Updates to Existing) + +**BottomPanel.tsx** (Replace or Extend) +- Integrate TimelinePanel +- Collapsible/expandable +- Drag to resize height +- State management controls + +**RightPanel.tsx** (Update) +- Add "State History" section +- Show current state metadata +- Quick comparison tools +- State navigation shortcuts + +### 2.2 Component Hierarchy + +``` +App +├── MenuBar +│ └── States Menu (new) +│ ├── Create State +│ ├── View Timeline +│ ├── Compare States +│ └── Manage Scenarios +├── Toolbar +│ └── State Controls (new) +│ ├── Current State Indicator +│ ├── Quick State Switch +│ └── Create Snapshot Button +├── GraphEditor +│ └── State Overlay (new) +│ ├── Change Indicators +│ └── Diff Highlighting +├── BottomPanel +│ └── TimelinePanel (new) +│ ├── Timeline View +│ ├── Scenario Tree View +│ └── State Creation Controls +└── Modals + ├── ComparisonView (new) + ├── StateMetadataEditor (new) + ├── ActorJourneyViewer (new) + └── PresentationMode (new) +``` + +--- + +## 3. Store Architecture + +### 3.1 New Store: `stateStore.ts` + +```typescript +// /src/stores/stateStore.ts + +import { create } from 'zustand'; +import type { AnalysisState, StateDiff, Timeline, ScenarioTree, StateType } from '../types/temporal'; + +interface StateStore { + // State management + states: Map; + currentStateId: string | null; + + // Organization + timelines: Timeline[]; + scenarioTrees: ScenarioTree[]; + + // Actions + createState: ( + type: StateType, + snapshot: { nodes: SerializedActor[]; edges: SerializedRelation[] }, + metadata: Partial | Partial, + parentStateId?: string + ) => string; + + loadState: (stateId: string) => void; + deleteState: (stateId: string) => void; + updateStateMetadata: (stateId: string, updates: Partial) => void; + + // Timeline management + createTimeline: (label: string, stateIds: string[]) => string; + addStateToTimeline: (timelineId: string, stateId: string, position?: number) => void; + removeStateFromTimeline: (timelineId: string, stateId: string) => void; + reorderTimeline: (timelineId: string, newOrder: string[]) => void; + + // Scenario management + createScenarioBranch: (parentStateId: string, label: string) => string; + addStateToScenario: (branchId: string, stateId: string) => void; + + // Comparison and analysis + compareStates: (stateId1: string, stateId2: string) => StateDiff; + getActorJourney: (actorId: string, stateIds?: string[]) => ActorJourney; + + // Navigation + getNextState: (currentStateId: string, type?: 'temporal' | 'scenario') => string | null; + getPreviousState: (currentStateId: string) => string | null; + getStatesByTimeline: (timelineId: string) => AnalysisState[]; + getStatesByScenario: (branchId: string) => AnalysisState[]; + + // ChromaDB integration + indexStateForSearch: (stateId: string) => Promise; + searchStates: (query: string) => Promise; +} +``` + +### 3.2 Updated `workspaceStore.ts` + +Add state-related actions: +```typescript +interface WorkspaceActions { + // ... existing actions ... + + // State operations + captureCurrentState: (type: StateType, metadata: any) => string; + restoreState: (stateId: string) => void; + exportStatesTimeline: (timelineId: string) => void; + importStatesTimeline: () => Promise; +} +``` + +--- + +## 4. Implementation Phases + +### Phase 1: Core State Management (Week 1-2) +**Priority: HIGH - Foundation for all features** + +1. **Data Model Setup** + - Create `/src/types/temporal.ts` + - Update `ConstellationDocument` type + - Add migration for existing documents + +2. **Basic State Store** + - Implement `stateStore.ts` + - Basic CRUD operations for states + - Simple state switching + +3. **Snapshot Functionality** + - Capture current graph as state + - Store state metadata + - Load state back to graph + +4. **UI Integration** + - Add "Capture State" button to toolbar + - Basic state selector dropdown + - Current state indicator + +**Deliverable**: Users can create snapshots and switch between them + +--- + +### Phase 2: Temporal Analysis (Week 3-4) +**Priority: HIGH - Core use case** + +1. **Timeline Management** + - Create Timeline data structure + - Timeline CRUD operations + - State ordering and sequencing + +2. **Timeline Panel UI** + - Bottom panel with timeline visualization + - Horizontal timeline scrubber + - State markers with labels + - Click to load state + +3. **Temporal Metadata** + - Date/time picker for states + - Sequence number assignment + - Period labeling + +4. **Timeline Navigation** + - Previous/Next state navigation + - Keyboard shortcuts (← →) + - Timeline auto-scrolling + +**Deliverable**: Users can create temporal sequences and navigate through time + +--- + +### Phase 3: Comparison & Diff Analysis (Week 5-6) +**Priority: HIGH - Key analytical feature** + +1. **Diff Calculation Engine** + - Implement `compareStates()` function + - Actor-level diff detection + - Relation-level diff detection + - Change summarization + +2. **Visual Diff Indicators** + - Overlay mode on graph + - Color coding (green=added, red=removed, yellow=modified) + - Change badges/icons + - Animated transitions + +3. **Comparison View Component** + - Side-by-side state comparison + - Synchronized panning/zooming + - Difference highlighting + - Export comparison report + +4. **Change Summary Panel** + - Statistics dashboard + - List of changes + - Filter by change type + - Jump to changed actors + +**Deliverable**: Users can compare any two states and see visual differences + +--- + +### Phase 4: Scenario Branching (Week 7-8) +**Priority: MEDIUM - Advanced feature** + +1. **Scenario Data Model** + - Scenario tree structure + - Branch metadata management + - Parent-child relationships + +2. **Scenario Creation UI** + - "Branch from here" action + - Scenario metadata form + - Assumptions editor + - Probability/confidence inputs + +3. **Scenario Tree Visualization** + - Vertical branching in timeline panel + - Branch color coding + - Branch labels and descriptions + - Interactive branch selection + +4. **Scenario Navigation** + - Switch between branches + - Compare scenario outcomes + - Merge/delete branches + +**Deliverable**: Users can create and explore alternative scenarios + +--- + +### Phase 5: Actor Tracking & Journeys (Week 9-10) +**Priority: MEDIUM - Valuable analysis tool** + +1. **Journey Calculation** + - Track actor across states + - Detect appearances/disappearances + - Calculate trajectory metrics + - Relationship evolution tracking + +2. **Journey Viewer UI** + - Actor selection interface + - Timeline visualization of actor + - Relationship changes display + - Export actor report + +3. **Multi-Actor Comparison** + - Compare multiple actor journeys + - Network position changes + - Relationship dynamics + +**Deliverable**: Users can follow specific actors through time/scenarios + +--- + +### Phase 6: Animation & Presentation (Week 11-12) +**Priority: MEDIUM - Storytelling feature** + +1. **State Animation Engine** + - Smooth transitions between states + - Actor movement interpolation + - Fade in/out for added/removed actors + - Configurable animation speed + +2. **Animation Controls** + - Play/pause/step controls + - Speed adjustment + - Loop options + - Auto-play through timeline + +3. **Presentation Mode** + - Full-screen slideshow + - Narration/annotation overlay + - Custom sequence selection + - Export as video/GIF (stretch goal) + +**Deliverable**: Users can create animated presentations of their analyses + +--- + +### Phase 7: ChromaDB Integration (Week 13-14) +**Priority: MEDIUM - Enhanced search and analysis** + +1. **State Indexing** + - Index state metadata in ChromaDB + - Include descriptions, assumptions, notes + - Tag-based organization + - Semantic embeddings + +2. **Semantic Search** + - Search states by description + - Find similar states + - Query by temporal criteria + - Tag-based filtering + +3. **Pattern Recognition** + - Identify recurring patterns + - Find similar network structures + - Anomaly detection + - Trend analysis + +4. **Annotation Storage** + - Store analytical notes in ChromaDB + - Link notes to specific changes + - Search annotations + - Generate analysis reports + +**Deliverable**: Users can semantically search and analyze their state history + +--- + +### Phase 8: Advanced Features (Week 15-16) +**Priority: LOW - Nice-to-have enhancements** + +1. **Automatic State Capture** + - Periodic auto-snapshots + - Significant change detection + - Configurable triggers + +2. **State Templates** + - Save state as template + - Apply template to create new state + - Template library + +3. **Collaborative Features** + - Share timelines/scenarios + - Comment on states + - Approval workflows + +4. **Advanced Analytics** + - Network metrics over time + - Predictive modeling + - Statistical analysis + - Export to analysis tools + +--- + +## 5. File Structure + +``` +/src +├── types/ +│ ├── index.ts (existing) +│ └── temporal.ts (NEW) +│ +├── stores/ +│ ├── workspaceStore.ts (update) +│ ├── stateStore.ts (NEW) +│ ├── panelStore.ts (existing) +│ └── toastStore.ts (existing) +│ +├── components/ +│ ├── TemporalAnalysis/ (NEW) +│ │ ├── TimelinePanel.tsx +│ │ ├── StateSelector.tsx +│ │ ├── ComparisonView.tsx +│ │ ├── StateDiffViewer.tsx +│ │ ├── StateMetadataEditor.tsx +│ │ ├── ActorJourneyViewer.tsx +│ │ ├── StateAnimator.tsx +│ │ └── PresentationMode.tsx +│ │ +│ ├── Panels/ (existing, update) +│ │ ├── LeftPanel.tsx +│ │ ├── RightPanel.tsx (update) +│ │ └── BottomPanel.tsx (update or replace) +│ │ +│ ├── Toolbar/ (existing, update) +│ │ └── StateControls.tsx (NEW) +│ │ +│ └── Menu/ (existing, update) +│ └── StatesMenu.tsx (NEW) +│ +├── utils/ +│ ├── stateComparison.ts (NEW) +│ ├── stateDiff.ts (NEW) +│ ├── stateAnimation.ts (NEW) +│ └── chromaIntegration.ts (NEW) +│ +├── hooks/ +│ ├── useStateManagement.ts (NEW) +│ ├── useStateDiff.ts (NEW) +│ ├── useStateAnimation.ts (NEW) +│ └── useActorJourney.ts (NEW) +│ +└── contexts/ + └── StateContext.tsx (NEW, optional) +``` + +--- + +## 6. Terminology Guide + +### Old (Version Control) → New (Temporal/Scenario Analysis) + +| Old Term | New Term | Context | +|----------|----------|---------| +| Version | State / Timepoint / Scenario | General | +| Commit | Capture State / Create Snapshot | Action | +| Checkout | Load State / View State | Action | +| Branch | Create Scenario Branch | Action | +| Version History | Timeline / State History | View | +| Version Graph | Timeline / Scenario Tree | Visualization | +| Diff | Comparison / Change Analysis | Analysis | +| Merge | N/A (not applicable) | - | +| Revert | Restore State | Action | + +--- + +## 7. UI/UX Wireframes + +### 7.1 Bottom Timeline Panel + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Timeline: Project Evolution [+] Create State [≡] View │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ ●─────●─────●─────●─────●────────┬──●────●────● │ │ +│ │ Q1 Q2 Q3 Q4 Current │ B1 B2 B3 │ │ +│ │ 2023 2023 2023 2023 (2024) │ (Strat A) │ │ +│ │ │ │ │ +│ │ └──●────● │ │ +│ │ C1 C2 │ │ +│ │ (Strat B) │ │ +│ └──────────────────────────────────────────────────────────┘ │ +│ │ +│ Timeline: ◀ ▶ Scenarios: ▲ ▼ Compare: [Select Two] │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 7.2 State Selector Dropdown + +``` +┌────────────────────────────────────┐ +│ Current State: Q4 2023 ▼ │ +├────────────────────────────────────┤ +│ Recent States: │ +│ ● Current (Unsaved) │ +│ ● Q4 2023 │ +│ ● Q3 2023 │ +│ │ +│ Timelines: │ +│ 📅 Project Evolution (5 states) │ +│ 📅 Historical (3 states) │ +│ │ +│ Scenarios: │ +│ 🌿 Strategy A Branch (3 states) │ +│ 🌿 Strategy B Branch (2 states) │ +│ │ +│ [View All States] [New State] │ +└────────────────────────────────────┘ +``` + +### 7.3 Comparison View + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Compare States [×] │ +├─────────────────────────────────────────────────────────────────┤ +│ From: Q3 2023 ▼ To: Q4 2023 ▼ Mode: Side-by │ +├──────────────────────────────┬──────────────────────────────────┤ +│ │ │ +│ State: Q3 2023 │ State: Q4 2023 │ +│ │ │ +│ [Graph Visualization] │ [Graph Visualization] │ +│ │ │ +│ Actors: 12 │ Actors: 15 (+3) │ +│ Relations: 18 │ Relations: 22 (+4) │ +│ │ │ +├──────────────────────────────┴──────────────────────────────────┤ +│ Changes Summary: │ +│ Added: 3 actors, 4 relations │ +│ Removed: 0 actors, 0 relations │ +│ Modified: 2 actors (properties changed) │ +│ │ +│ [View Detailed Changes] [Export Report] │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 8. Key Algorithms + +### 8.1 State Diff Calculation + +```typescript +// /src/utils/stateDiff.ts + +export function calculateStateDiff( + fromState: AnalysisState, + toState: AnalysisState +): StateDiff { + const diff: StateDiff = { + fromStateId: fromState.stateId, + toStateId: toState.stateId, + actorsAdded: [], + actorsRemoved: [], + actorsModified: [], + relationsAdded: [], + relationsRemoved: [], + relationsModified: [], + summary: { + totalActorChanges: 0, + totalRelationChanges: 0, + }, + }; + + // Create lookup maps + const fromActors = new Map( + fromState.snapshot.nodes.map(n => [n.id, n]) + ); + const toActors = new Map( + toState.snapshot.nodes.map(n => [n.id, n]) + ); + + // Find added actors + toState.snapshot.nodes.forEach(actor => { + if (!fromActors.has(actor.id)) { + diff.actorsAdded.push(actor); + } + }); + + // Find removed actors + fromState.snapshot.nodes.forEach(actor => { + if (!toActors.has(actor.id)) { + diff.actorsRemoved.push(actor); + } + }); + + // Find modified actors + fromState.snapshot.nodes.forEach(fromActor => { + const toActor = toActors.get(fromActor.id); + if (toActor) { + const changes = detectActorChanges(fromActor, toActor); + if (Object.keys(changes).length > 0) { + diff.actorsModified.push({ + actorId: fromActor.id, + changes, + }); + } + } + }); + + // Similar logic for relations... + + // Calculate summary + diff.summary.totalActorChanges = + diff.actorsAdded.length + + diff.actorsRemoved.length + + diff.actorsModified.length; + + return diff; +} + +function detectActorChanges( + fromActor: SerializedActor, + toActor: SerializedActor +): any { + const changes: any = {}; + + if (fromActor.data.label !== toActor.data.label) { + changes.label = { + from: fromActor.data.label, + to: toActor.data.label, + }; + } + + if (fromActor.data.type !== toActor.data.type) { + changes.type = { + from: fromActor.data.type, + to: toActor.data.type, + }; + } + + if ( + fromActor.position.x !== toActor.position.x || + fromActor.position.y !== toActor.position.y + ) { + changes.position = { + from: fromActor.position, + to: toActor.position, + }; + } + + return changes; +} +``` + +### 8.2 Actor Journey Tracking + +```typescript +// /src/utils/actorJourney.ts + +export function getActorJourney( + actorId: string, + states: AnalysisState[] +): ActorJourney { + const appearances = states + .map(state => { + const actor = state.snapshot.nodes.find(n => n.id === actorId); + if (actor) { + return { + stateId: state.stateId, + stateLabel: state.temporal?.label || state.scenario?.label || 'Unknown', + actor, + timestamp: state.temporal?.timestamp, + }; + } + return null; + }) + .filter(Boolean); + + return { + actorId, + label: appearances[0]?.actor.data.label || 'Unknown', + appearances, + firstAppearance: appearances[0]?.stateId || '', + lastAppearance: appearances[appearances.length - 1]?.stateId || '', + appearanceCount: appearances.length, + }; +} +``` + +### 8.3 State Animation Interpolation + +```typescript +// /src/utils/stateAnimation.ts + +export function interpolateStates( + fromState: AnalysisState, + toState: AnalysisState, + progress: number // 0 to 1 +): { nodes: Actor[]; edges: Relation[] } { + // For each actor, interpolate position and properties + const interpolatedNodes = toState.snapshot.nodes.map(toNode => { + const fromNode = fromState.snapshot.nodes.find(n => n.id === toNode.id); + + if (!fromNode) { + // Actor being added - fade in + return { + ...toNode, + style: { + opacity: progress, + }, + }; + } + + // Interpolate position + const x = fromNode.position.x + (toNode.position.x - fromNode.position.x) * progress; + const y = fromNode.position.y + (toNode.position.y - fromNode.position.y) * progress; + + return { + ...toNode, + position: { x, y }, + }; + }); + + // Handle actors being removed (fade out) + fromState.snapshot.nodes.forEach(fromNode => { + const existsInTo = toState.snapshot.nodes.find(n => n.id === fromNode.id); + if (!existsInTo) { + interpolatedNodes.push({ + ...fromNode, + style: { + opacity: 1 - progress, + }, + }); + } + }); + + // Similar logic for edges... + + return { nodes: interpolatedNodes, edges: interpolatedEdges }; +} +``` + +--- + +## 9. ChromaDB Integration Details + +### 9.1 Collections Structure + +```typescript +// State metadata collection +const stateMetadataCollection = { + name: 'constellation_states', + metadata: { + description: 'Analysis states with temporal and scenario metadata', + }, +}; + +// Documents to index: +{ + id: stateId, + embedding: [/* vector from description + notes + assumptions */], + metadata: { + documentId: string, + stateType: 'temporal' | 'scenario', + label: string, + timestamp?: string, + tags: string[], + actorCount: number, + relationCount: number, + }, + document: `${label} ${description} ${notes} ${assumptions.join(' ')}`, +} + +// Actor journey collection +const actorJourneyCollection = { + name: 'actor_journeys', + metadata: { + description: 'Actor trajectories across states', + }, +}; + +// Comparison results collection (caching) +const comparisonCacheCollection = { + name: 'state_comparisons', + metadata: { + description: 'Cached comparison results', + }, +}; +``` + +### 9.2 Query Examples + +```typescript +// Find states by semantic search +async function searchStates(query: string): Promise { + const results = await chromaClient.query({ + collection: 'constellation_states', + queryTexts: [query], + nResults: 10, + }); + + // Retrieve full states from store + return results.ids[0].map(id => stateStore.states.get(id)); +} + +// Find similar states (pattern recognition) +async function findSimilarStates(stateId: string): Promise { + const state = stateStore.states.get(stateId); + const description = generateStateDescription(state); + + const results = await chromaClient.query({ + collection: 'constellation_states', + queryTexts: [description], + nResults: 5, + where: { + documentId: state.documentId, + stateId: { $ne: stateId }, // Exclude self + }, + }); + + return results.ids[0].map(id => stateStore.states.get(id)); +} + +// Track actor mentions across states +async function findStatesWithActor(actorLabel: string): Promise { + const results = await chromaClient.query({ + collection: 'constellation_states', + queryTexts: [actorLabel], + nResults: 20, + where: { + tags: { $contains: 'actor:' + actorLabel }, + }, + }); + + return results.ids[0].map(id => stateStore.states.get(id)); +} +``` + +--- + +## 10. Testing Strategy + +### 10.1 Unit Tests + +- State creation and serialization +- Diff calculation accuracy +- Actor journey tracking +- Timeline ordering +- Scenario branching logic + +### 10.2 Integration Tests + +- State store integration with workspace store +- ChromaDB indexing and retrieval +- UI component interactions +- Animation performance + +### 10.3 User Acceptance Tests + +- Create temporal sequence (historical and projected) +- Create scenario branches +- Compare two states visually +- Track actor through timeline +- Animate transition between states +- Export comparison report +- Search states semantically + +--- + +## 11. Migration Strategy + +### 11.1 Existing Documents + +Documents without state support will continue to work normally. The `states` property in `ConstellationDocument` is optional. + +### 11.2 Enabling States for Existing Documents + +Users can "enable" temporal analysis for any document: + +```typescript +function enableStatesForDocument(documentId: string) { + const doc = workspaceStore.documents.get(documentId); + if (!doc) return; + + // Create initial state from current graph + const initialState: AnalysisState = { + stateId: generateStateId(), + stateType: 'temporal', + snapshot: { + nodes: doc.graph.nodes, + edges: doc.graph.edges, + }, + temporal: { + label: 'Initial State', + timestamp: new Date().toISOString(), + sequenceNumber: 0, + }, + relationships: [], + notes: 'Initial state captured when enabling temporal analysis', + createdAt: new Date().toISOString(), + }; + + doc.states = { + stateList: [initialState], + currentStateId: initialState.stateId, + timelines: [{ + timelineId: generateTimelineId(), + label: 'Main Timeline', + states: [initialState.stateId], + displaySettings: { + showGrid: true, + autoLayout: true, + }, + }], + scenarioTrees: [], + settings: { + enableAutoDiff: true, + showChangeIndicators: true, + defaultStateType: 'temporal', + }, + }; + + doc.metadata.supportsStates = true; + saveDocumentToStorage(documentId, doc); +} +``` + +--- + +## 12. Performance Considerations + +### 12.1 State Storage + +- Store states in separate IndexedDB entries (not inline with document) +- Lazy load states on demand +- Implement state pagination for documents with many states + +### 12.2 Diff Calculation + +- Cache diff results in ChromaDB +- Compute diffs in Web Worker for large graphs +- Implement incremental diff for sequential states + +### 12.3 Animation + +- Use requestAnimationFrame for smooth animations +- Implement frame skipping for performance +- Provide quality/performance toggle + +### 12.4 ChromaDB + +- Batch index operations +- Implement indexing queue +- Cache frequently accessed queries +- Use metadata filtering to reduce embedding computations + +--- + +## 13. Future Enhancements (Post-MVP) + +### 13.1 Advanced Analytics + +- Network density over time +- Centrality metrics evolution +- Clustering coefficient changes +- Community detection across states + +### 13.2 Predictive Modeling + +- Extrapolate future states +- Trend analysis +- Anomaly detection +- Pattern-based predictions + +### 13.3 Collaborative Features + +- Share timelines with team +- Comment threads on specific changes +- Approval workflows for scenarios +- Real-time collaboration + +### 13.4 Export Capabilities + +- Export as video/GIF +- Generate PowerPoint presentations +- PDF reports with comparison analysis +- Interactive HTML export + +### 13.5 Integration + +- Import time-series data from external sources +- API for programmatic state creation +- Webhook triggers for auto-capture +- Integration with project management tools + +--- + +## 14. Success Metrics + +### 14.1 Feature Adoption + +- % of documents with states enabled +- Average number of states per document +- Timeline vs. scenario usage ratio + +### 14.2 User Engagement + +- Time spent in comparison view +- Number of comparisons per session +- Animation playback usage +- Actor journey queries + +### 14.3 Performance + +- State creation time < 500ms +- Diff calculation time < 1s for typical graphs +- Animation frame rate > 30fps +- ChromaDB query latency < 200ms + +--- + +## 15. Documentation Plan + +### 15.1 User Guide + +- "Getting Started with Temporal Analysis" +- "Creating and Managing Timelines" +- "Exploring Scenarios" +- "Comparing States Effectively" +- "Tracking Actor Journeys" +- "Presentation Mode Tutorial" + +### 15.2 Developer Documentation + +- State data model reference +- API documentation for stateStore +- ChromaDB integration guide +- Custom animation plugins +- Extension points for advanced analytics + +--- + +## Conclusion + +This implementation plan transforms Constellation Analyzer from a static graph editor into a powerful temporal and scenario analysis tool. By focusing on storytelling, comparison, and analysis rather than version control, we enable users to: + +1. **Understand evolution**: Track how networks change over time +2. **Explore alternatives**: Branch and compare different scenarios +3. **Analyze dynamics**: Identify patterns and trends +4. **Communicate insights**: Present findings effectively + +The phased approach ensures we deliver value incrementally while building toward a comprehensive solution. ChromaDB integration adds semantic search and pattern recognition capabilities that elevate the tool beyond simple visualization. + +**Recommended Starting Point**: Begin with Phase 1 (Core State Management) and Phase 2 (Temporal Analysis) as these provide immediate value and establish the foundation for all subsequent features. diff --git a/TEMPORAL_SCENARIO_UX_CONCEPT.md b/TEMPORAL_SCENARIO_UX_CONCEPT.md new file mode 100644 index 0000000..52f8aab --- /dev/null +++ b/TEMPORAL_SCENARIO_UX_CONCEPT.md @@ -0,0 +1,1149 @@ +# Temporal & Scenario Analysis - UX Concept (Revised) + +## Executive Summary + +This document outlines the UX concept for multi-graph capabilities in Constellation Analyzer, focusing on **temporal evolution** and **scenario exploration**. This is NOT a version control system - it's a tool for understanding how constellations change over time and exploring alternative futures. + +**Core Use Cases:** +- Track relationship evolution across time periods (historical analysis) +- Explore alternative scenarios and strategic options (what-if analysis) +- Compare different states side-by-side (comparative analysis) +- Present evolution to stakeholders (storytelling and presentation) + +--- + +## 1. Terminology & Mental Model + +### 1.1 Recommended Terms + +After careful consideration, we recommend a **dual-mode terminology** that adapts to user context: + +#### Primary Terms: +- **State** - A single snapshot of the constellation at a point in time or scenario +- **Timeline** - The collection of states with temporal/branching relationships +- **Evolution** - The progression of states over time +- **Branch** - A divergence point where scenarios split from a common state + +#### Context-Specific Labels: +The system should allow users to set their analysis mode: + +**Temporal Mode** (for time-based analysis): +- States are called "Timepoints" +- Labels show dates/periods: "January 2024", "Q2 2024", "6 months post-merger" +- Navigation emphasizes chronological progression +- Visual: Timeline with chronological markers + +**Scenario Mode** (for strategic exploration): +- States are called "Scenarios" +- Labels show scenario names: "Aggressive Growth", "Conservative Approach", "Status Quo" +- Navigation emphasizes comparison and alternatives +- Visual: Decision tree or parallel tracks + +**Hybrid Mode** (combines both): +- Temporal progression WITH scenario branches at key points +- Example: "Pre-merger" → "Option A: Quick Integration" vs "Option B: Gradual Transition" +- Visual: Timeline with branching points + +### 1.2 Mental Model + +Users should think of this feature as: +> "A timeline of my constellation's evolution, with the ability to explore 'what-if' branches at any point" + +**Metaphor:** Think of a photo album with multiple timelines - you can flip through one person's life chronologically, or compare different life paths they could have taken at decision points. + +**Not like:** Git branches, version control, or undo/redo history + +--- + +## 2. UI Architecture + +### 2.1 Layout Overview + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Header: Constellation Analyzer │ +├─────────────────────────────────────────────────────────────┤ +│ Menu Bar │ +├─────────────────────────────────────────────────────────────┤ +│ Document Tabs │ +├─────────────────────────────────────────────────────────────┤ +│ ┌──────┐ Current State Indicator │ +│ │ ICON │ "Jan 2024" or "Scenario A: Aggressive Growth" │ +│ └──────┘ [Timeline Controls] [Compare] [Present] │ +├──────────┬──────────────────────────────────┬──────────────┤ +│ Left │ │ Right │ +│ Panel │ Graph Canvas │ Panel │ +│ (Tools) │ │ (Properties) │ +│ │ │ │ +├──────────┴──────────────────────────────────┴──────────────┤ +│ Timeline Panel │ +│ [═══●═══●═══●═══●═══] Timeline scrubber │ +│ [Visual timeline with states and branches] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 2.2 Current State Indicator (New Component) + +**Location:** Between toolbar and main canvas, full width +**Purpose:** Always show which state user is viewing/editing + +**Design:** +``` +┌────────────────────────────────────────────────────────────────┐ +│ 📍 Current State: January 2024 │ +│ ← Q4 2023 | [Edit State Info] | Feb 2024 → │ +│ Branch: Main Timeline │ +└────────────────────────────────────────────────────────────────┘ +``` + +**Features:** +- Large, clear state name/label +- Navigation arrows to previous/next state in sequence +- Branch indicator if applicable +- Quick edit button for state metadata +- Visual connection to timeline panel below + +### 2.3 Timeline Panel (Bottom Panel - Redesigned) + +**Default State:** Collapsed (shows only scrubber bar) +**Expanded State:** Shows full timeline visualization + +#### Collapsed View (40px height): +``` +┌────────────────────────────────────────────────────────────────┐ +│ ▲ Timeline [═══●═══●═══●═══●═══] [+] [⚙] [Expand] │ +└────────────────────────────────────────────────────────────────┘ +``` +- Mini timeline scrubber with state markers +- Current state highlighted +- Click markers to jump to state +- Quick add state button +- Settings for timeline display +- Expand/collapse toggle + +#### Expanded View (200-400px height, resizable): +``` +┌────────────────────────────────────────────────────────────────┐ +│ Timeline View: [Linear] [Tree] [Matrix] [+] [⚙] [▼] │ +├────────────────────────────────────────────────────────────────┤ +│ │ +│ Temporal Mode (Linear): │ +│ ─────●─────────●──────────●──────────●─────────●──── │ +│ Jan 24 Mar 24 Jun 24 Sep 24 Dec 24 │ +│ "Setup" "Growth" "Stable" "Expansion" "Future" │ +│ │ +│ Scenario Mode (Tree): │ +│ ┌──●── "Aggressive" ───●── "Year 2" │ +│ │ "High Risk" "Scale Up" │ +│ ──●───────●──────● │ +│ "Now" "Setup" │ "Decision Point" │ +│ │ │ +│ └──●── "Conservative" ───●── "Stable" │ +│ "Low Risk" "Maintain" │ +│ │ +│ Hybrid Mode (Matrix): │ +│ Shows both temporal and scenario dimensions │ +│ │ +└────────────────────────────────────────────────────────────────┘ +``` + +**Visualization Modes:** + +1. **Linear Timeline** (Temporal): + - Horizontal timeline with evenly spaced or date-proportional spacing + - State cards with labels, dates, descriptions + - Visual continuity indicators (lines/arrows) + - Current state highlighted with distinct styling + - Hover shows preview thumbnail + +2. **Tree View** (Scenarios): + - Branch visualization showing decision points + - Parent-child relationships clear + - Color coding by branch/scenario family + - Collapsible branches + - Labels emphasize scenario names over dates + +3. **Matrix View** (Hybrid): + - Horizontal axis: Time progression + - Vertical tracks: Different scenario branches + - Grid layout showing all parallel scenarios at each timepoint + - Quick comparison across scenarios + +**Interactions:** +- Click state to navigate to it +- Drag to reorder (with confirmation) +- Right-click for context menu +- Double-click to edit metadata +- Drag timeline area to scroll +- Pinch/zoom to adjust spacing + +--- + +## 3. Core Workflows + +### 3.1 Creating a New State + +**From Current State:** + +1. User clicks [+ New State] button in timeline panel or current state indicator +2. Dialog appears: + ``` + ┌─────────────────────────────────────────┐ + │ Create New State │ + ├─────────────────────────────────────────┤ + │ ○ Continue Timeline │ + │ Next step in temporal progression │ + │ │ + │ ○ Branch into Scenario │ + │ Alternative possibility from here │ + ├─────────────────────────────────────────┤ + │ Label: [_____________________] │ + │ │ + │ ○ Temporal Mode │ + │ Date/Period: [Jan 2024 ▼] │ + │ │ + │ ○ Scenario Mode │ + │ Description: [____________] │ + │ │ + │ Starting Point: │ + │ ⦿ Clone current state │ + │ ○ Clean slate │ + │ │ + │ [Cancel] [Create State] │ + └─────────────────────────────────────────┘ + ``` + +3. User fills in: + - **Label:** Required name for the state + - **Type:** Continue timeline (sequential) or branch (alternative) + - **Mode:** Temporal (with date) or Scenario (with description) + - **Starting point:** Clone current actors/relations or start empty + +4. New state is created and becomes the active state +5. Timeline panel updates to show new state in context + +**Quick Actions:** +- "Next Month" button (temporal): Creates next sequential timepoint +- "What If?" button (scenario): Creates branch from current state +- Keyboard shortcut: Ctrl+Shift+N + +### 3.2 Navigating Between States + +**Timeline Panel:** +- Click any state marker to jump to it +- Use scrubber to drag through states (with animation) +- Arrow keys when timeline focused: ← → navigate chronologically + +**Current State Indicator:** +- Previous/Next arrows for sequential navigation +- Dropdown showing all states with search/filter + +**Keyboard Shortcuts:** +- Alt + Left/Right Arrow: Previous/Next state +- Alt + Number: Jump to state (1-9) +- Alt + B: Show branch menu + +**Animation:** +- When navigating between states, show smooth transition +- Actors that exist in both: morph positions/properties +- Actors only in old state: fade out +- Actors only in new state: fade in +- Configurable animation speed or instant toggle + +### 3.3 Comparing States + +Users need powerful comparison tools for analysis. + +#### Side-by-Side Comparison + +1. Click [Compare] button in toolbar +2. Comparison mode activates: + ``` + ┌────────────────────────────────────────────────────────┐ + │ Compare Mode: [State A ▼] vs [State B ▼] [Exit] │ + ├──────────────────────────┬─────────────────────────────┤ + │ │ │ + │ State A: Jan 2024 │ State B: Jun 2024 │ + │ "Initial Setup" │ "After Growth" │ + │ │ │ + │ [Graph visualization] │ [Graph visualization] │ + │ │ │ + └──────────────────────────┴─────────────────────────────┘ + ``` + +3. Features: + - Synchronized zoom/pan (toggle) + - Highlight differences + - Shared actors connected with visual lines + - Color coding: Added (green), Removed (red), Changed (yellow) + - Comparison metrics panel (optional) + +#### Overlay/Diff Mode + +1. From comparison mode, toggle to overlay +2. Single canvas shows both states: + - State A actors: Semi-transparent + - State B actors: Full opacity + - Movement arrows showing position changes + - New/removed actors clearly marked + - Toggle layers on/off + +#### Difference Report + +1. Click [Generate Report] in comparison mode +2. Automatic analysis shows: + - Actors added/removed + - Relations added/removed + - Property changes + - Network metrics comparison (density, centrality, etc.) + - Export as PDF/HTML + +### 3.4 Animation Between States + +**Presentation Mode** (for stakeholder demos): + +1. Click [Present] button +2. Presentation interface appears: + ``` + ┌──────────────────────────────────────────────────────────┐ + │ ╔═════════════╗ │ + │ ║ ◄ ▐▐ ► ║ [Exit Presentation] │ + │ ╚═════════════╝ │ + ├──────────────────────────────────────────────────────────┤ + │ │ + │ Full-screen Graph View │ + │ │ + │ State: January 2024 │ + │ "Initial Team Structure" │ + │ │ + │ [Actors and relations displayed] │ + │ │ + └──────────────────────────────────────────────────────────┘ + ``` + +3. Features: + - Full-screen mode (ESC to exit) + - Play/pause automated progression through states + - Adjustable animation speed + - State titles and descriptions display as subtitles + - Voice-over text field (optional annotations read aloud) + - Export as video/animated GIF + +4. Presentation Controls: + - Left/Right arrows: Previous/Next state + - Space: Play/Pause animation + - Number keys: Jump to state + - Escape: Exit presentation mode + +**Auto-play Settings:** +- Duration per state: 3s / 5s / 10s / Custom +- Animation speed: Slow / Medium / Fast / Instant +- Pause on branches (show decision point) + +### 3.5 Editing State Metadata + +**State Information Dialog:** + +Right-click state in timeline → "Edit State Info" + +``` +┌─────────────────────────────────────────┐ +│ State Information │ +├─────────────────────────────────────────┤ +│ Label: [January 2024____________] │ +│ │ +│ Mode: ⦿ Temporal ○ Scenario │ +│ │ +│ [Temporal] │ +│ Date: [2024-01-15___] │ +│ Period: [Q1 2024_________] │ +│ │ +│ Description: │ +│ [Initial team structure │ +│ after reorganization...] │ +│ │ +│ Tags: [planning] [actual] [+] │ +│ │ +│ Parent State: ← Q4 2023 │ +│ Next State: → February 2024 │ +│ │ +│ Color: [🎨 Blue] │ +│ │ +│ Notes: │ +│ [Additional context for │ +│ presenters and analysts...] │ +│ │ +│ [Delete State] [Save] │ +└─────────────────────────────────────────┘ +``` + +**Fields:** +- **Label:** Display name (required) +- **Mode:** Temporal or Scenario +- **Date/Period:** For temporal states +- **Description:** Rich text description +- **Tags:** Categorization and filtering +- **Color:** Custom color for timeline visualization +- **Notes:** Presenter notes (shown in presentation mode) + +--- + +## 4. Visual Design Recommendations + +### 4.1 Timeline Visualization + +**State Cards in Timeline:** +``` +┌────────────────────┐ +│ January 2024 │ ← Large, readable label +│ ┌──────────────┐ │ +│ │ [preview] │ │ ← Small graph thumbnail (optional) +│ └──────────────┘ │ +│ Initial Setup │ ← Short description +│ 📅 2024-01-15 │ ← Date/metadata +│ 👥 12 actors │ ← Quick stats +└────────────────────┘ + ● ← Connection point + │ +``` + +**Current State Highlighting:** +- Distinct color (blue/accent color) +- Subtle glow or border +- Larger card size +- Animated pulse (subtle) + +**Branch Visualization:** +``` +Main Timeline +─────●─────●─────●───── + │ + ├──●──● "Scenario A" + │ + └──●──● "Scenario B" +``` +- Branch lines with different colors +- Clear labels at divergence points +- Hierarchy preserved visually + +### 4.2 Color Coding System + +**By Branch/Timeline:** +- Main timeline: Blue +- Scenario branches: Purple, Orange, Green, etc. +- Auto-assign colors, user can override + +**By State Type:** +- Temporal (historical): Darker shades +- Temporal (projected): Lighter shades, dashed borders +- Scenarios: Solid colors, distinct from temporal + +**By Status:** +- Current state: Highlighted accent color +- Past states: Full color +- Future states: Semi-transparent +- Draft/incomplete: Gray with icon + +### 4.3 Comparison View Design + +**Side-by-Side:** +- Vertical divider with drag handle +- Synchronized indicators (when zoom/pan synced) +- Difference highlights: + - Added actors: Green outline with + badge + - Removed actors: Red outline with - badge (ghost in removed state) + - Changed actors: Yellow outline + - Unchanged actors: Normal appearance + +**Overlay/Diff:** +- Layer opacity controls +- Toggle visibility per layer +- Color shifts to indicate temporal position +- Movement arrows with directional animation + +### 4.4 Animation Design + +**Smooth Transitions:** +- Actor movement: Bezier curve paths (not straight lines) +- Fade timing: Staggered (not all at once) +- Duration: 300-800ms (configurable) +- Easing: Ease-in-out for natural feel + +**Attention Direction:** +- Highlight changes sequentially +- Narration overlay option +- Pause points at key changes + +--- + +## 5. Advanced Features + +### 5.1 Diff Analysis Tools + +**Change Detection:** +- Automatic identification of: + - New actors (not in previous state) + - Removed actors + - Moved actors (position change >threshold) + - Re-typed actors (type change) + - New relations + - Removed relations + - Changed relation properties + +**Visualization:** +- Diff panel (bottom or right) +- List of changes with categories +- Click to highlight in graph +- Filter by change type + +**Example:** +``` +┌─────────────────────────────────────────┐ +│ Changes: Jan 2024 → Jun 2024 │ +├─────────────────────────────────────────┤ +│ ✅ Actors Added (3) │ +│ + Alice Johnson (Team Lead) │ +│ + Bob Chen (Designer) │ +│ + Carol Smith (Engineer) │ +│ │ +│ ❌ Actors Removed (1) │ +│ - David Lee (Former Manager) │ +│ │ +│ 🔄 Relations Changed (5) │ +│ ≈ Alice → Bob: "supervises" │ +│ ≈ Carol → Bob: "collaborates" │ +│ ... │ +│ │ +│ 📊 Network Metrics │ +│ Density: 0.45 → 0.52 (+15%) │ +│ Avg Connections: 3.2 → 4.1 │ +└─────────────────────────────────────────┘ +``` + +### 5.2 Actor Tracking Across Time + +**Follow Actor Feature:** + +1. Right-click actor → "Track Across States" +2. Actor timeline appears: + ``` + ┌─────────────────────────────────────────┐ + │ Tracking: Alice Johnson │ + ├─────────────────────────────────────────┤ + │ │ + │ Jan 24 Mar 24 Jun 24 Sep 24 │ + │ ●─────────●─────────●─────────● │ + │ │ │ │ │ │ + │ Junior Team Senior Manager │ + │ Dev Lead Dev │ + │ │ + │ Connections: 2 → 5 → 8 → 12 │ + │ Centrality: 0.1 → 0.3 → 0.5 → 0.7 │ + └─────────────────────────────────────────┘ + ``` + +3. Shows: + - Actor presence in each state + - Property changes over time + - Relationship changes + - Metrics evolution + - Graph of metrics over time + +**Use Cases:** +- Track individual's career progression +- Monitor key stakeholder involvement +- Analyze relationship building patterns +- Identify critical transition points + +### 5.3 Scenario Comparison Matrix + +**For Strategic Planning:** + +``` +┌───────────────────────────────────────────────────────────┐ +│ Scenario Comparison Matrix │ +├───────────┬─────────────┬─────────────┬─────────────┬────┤ +│ │ Aggressive │ Conservative│ Status Quo │...│ +├───────────┼─────────────┼─────────────┼─────────────┼────┤ +│ Team Size │ 25 actors │ 15 actors │ 12 actors │ │ +│ Density │ 0.65 │ 0.42 │ 0.38 │ │ +│ Leaders │ 4 │ 2 │ 1 │ │ +│ Risk │ High │ Medium │ Low │ │ +│ Cost │ $$$ │ $$ │ $ │ │ +├───────────┴─────────────┴─────────────┴─────────────┴────┤ +│ [View] [View] [View] [View] │ +└───────────────────────────────────────────────────────────┘ +``` + +- Compare metrics across scenarios +- Custom metric definitions +- Export to spreadsheet +- Visual heatmap of differences + +### 5.4 Smart Suggestions + +**AI-Assisted Analysis:** + +- "Key changes between states" summary +- "Critical actors in this period" identification +- "Potential issues" detection (isolated actors, broken connections) +- "Similar patterns" from other states +- "Recommended next state" based on patterns + +(Note: Requires AI integration, future feature) + +### 5.5 Export & Reporting + +**Export Options:** + +1. **Timeline Report (PDF/HTML):** + - All states with graphs + - Annotations and descriptions + - Change summaries + - Metrics evolution charts + - Professional formatting + +2. **Presentation Deck:** + - PowerPoint/Google Slides + - One slide per state + - Transition animations + - Speaker notes from state notes + +3. **Video/Animation:** + - MP4 export of presentation mode + - Configurable duration and transitions + - Voiceover from notes (text-to-speech) + - Watermark and branding options + +4. **Data Export:** + - CSV with state metadata + - JSON with full state data + - Graph metrics per state + - Change log + +--- + +## 6. Data Model + +### 6.1 State Structure + +```typescript +interface ConstellationState { + id: string; + label: string; + description?: string; + + // Mode and temporal information + mode: 'temporal' | 'scenario'; + date?: string; // ISO date for temporal states + period?: string; // e.g., "Q1 2024", "6 months post-merger" + + // Graph data + nodes: Actor[]; + edges: Relation[]; + nodeTypes: NodeTypeConfig[]; + edgeTypes: EdgeTypeConfig[]; + + // Relationships to other states + parentStateId?: string; // State this was created from + childStateIds: string[]; // States created from this one + branchName?: string; // For organizing branches + + // Metadata + tags: string[]; + color?: string; // Custom color for timeline + notes?: string; // Presenter notes + thumbnail?: string; // Base64 or URL to preview image + + // Timestamps + createdAt: string; + updatedAt: string; +} +``` + +### 6.2 Timeline Structure + +```typescript +interface Timeline { + id: string; + documentId: string; // Link to parent document + + states: ConstellationState[]; + + // Timeline metadata + name: string; + description?: string; + defaultMode: 'temporal' | 'scenario' | 'hybrid'; + + // View preferences + viewMode: 'linear' | 'tree' | 'matrix'; + sortBy: 'date' | 'created' | 'custom'; + + // Presentation settings + presentationSettings: { + autoPlayDuration: number; // seconds per state + animationSpeed: 'slow' | 'medium' | 'fast' | 'instant'; + showDescriptions: boolean; + pauseOnBranches: boolean; + }; +} +``` + +### 6.3 Document Structure Update + +```typescript +interface Document { + // Existing fields... + id: string; + name: string; + + // Add timeline reference + timelineId?: string; + currentStateId?: string; // Active state being edited + + // Legacy support: documents without timeline + // use existing nodes/edges directly + nodes?: Actor[]; + edges?: Relation[]; + nodeTypes?: NodeTypeConfig[]; + edgeTypes?: EdgeTypeConfig[]; +} +``` + +**Migration Strategy:** +- Existing documents: Continue to work without timeline +- User can "Enable Timeline" to convert to multi-state +- First state created automatically from current graph +- Backwards compatible + +--- + +## 7. User Stories & Use Cases + +### 7.1 Organizational Consultant + +**Scenario:** Track team dynamics over 6-month transformation + +**Story:** +> "As an organizational consultant, I want to document how team relationships evolved during a restructuring so that I can present the transformation to leadership and identify success factors." + +**Workflow:** +1. Create document: "Sales Team Transformation" +2. Set to Temporal Mode +3. Create state "January 2024 - Before Restructure" + - Add all team members + - Map current reporting relationships +4. Create state "March 2024 - Mid-transition" + - Update structure, add new manager + - Show matrix relationships forming +5. Create state "June 2024 - New Structure" + - Final organizational structure + - New collaboration patterns +6. Use Compare mode to show before/after +7. Generate presentation showing evolution +8. Present to leadership with animated transitions + +**Key Features Used:** +- Temporal mode with dates +- State-to-state animation +- Comparison view +- Presentation mode +- Change detection + +### 7.2 Strategic Planner + +**Scenario:** Evaluate three alternative market entry strategies + +**Story:** +> "As a strategic planner, I want to model different partnership scenarios so that our executive team can compare approaches and make an informed decision." + +**Workflow:** +1. Create document: "APAC Market Entry 2025" +2. Set to Scenario Mode +3. Create base state "Current Partnerships - Q4 2024" + - Map existing partner ecosystem +4. Branch into three scenarios: + - "Scenario A: Direct Entry" (build own team) + - "Scenario B: Strategic Acquisition" (acquire local player) + - "Scenario C: JV Partnership" (joint venture) +5. Model each scenario: + - Different actor sets (internal vs. external) + - Different relationship patterns + - Add cost/risk tags +6. Use Comparison Matrix to evaluate: + - Team size, relationship density + - Risk factors, dependencies +7. Present all three to executive team +8. Decision made, continue developing chosen scenario + +**Key Features Used:** +- Scenario mode with branches +- State cloning with modifications +- Comparison matrix +- Scenario-specific metadata +- Multi-scenario presentation + +### 7.3 Family Therapist + +**Scenario:** Track family system changes through therapy + +**Story:** +> "As a family therapist, I want to visualize how family relationships evolve over our sessions so that family members can see their progress and understand the changes they've made." + +**Workflow:** +1. Create document: "Johnson Family System" +2. Set to Temporal Mode +3. Create state "Session 1 - Initial Intake" + - Map family members + - Show conflict patterns (red edges) + - Identify isolated members +4. Create state "Session 5 - After First Intervention" + - Update relationship types + - Show new communication patterns +5. Create state "Session 10 - Current State" + - Document improvements + - Identify remaining work areas +6. Show animated progression to family +7. Highlight positive changes +8. Discuss next goals based on visual + +**Key Features Used:** +- Temporal progression +- Relationship type tracking +- Visual storytelling +- Change highlighting +- Actor tracking (family member journeys) + +### 7.4 Researcher - Historical Analysis + +**Scenario:** Study political alliance formation pre-WWI + +**Story:** +> "As a historian, I want to map how European alliances shifted from 1900-1914 so that I can analyze the path to war and teach students about alliance dynamics." + +**Workflow:** +1. Create document: "European Alliances 1900-1914" +2. Set to Temporal Mode +3. Create states for key years: + - "1900 - Post-Victorian Era" + - "1904 - Entente Cordiale" + - "1907 - Triple Entente Formed" + - "1912 - Balkan Wars Impact" + - "1914 - Pre-War System" +4. For each state: + - Add nations as actors + - Map alliances as relations + - Annotate with historical context +5. Create "What-If" branches: + - "If Britain remained neutral" + - "If Russia-Japan alliance continued" +6. Use presentation mode for lectures +7. Generate report with analysis + +**Key Features Used:** +- Historical timeline +- Dense annotations +- Counterfactual scenarios (branches) +- Educational presentation +- Export for publication + +### 7.5 Product Manager - Stakeholder Mapping + +**Scenario:** Plan stakeholder engagement for product launch + +**Story:** +> "As a product manager, I want to model how our stakeholder ecosystem will evolve from beta to GA launch so that we can plan our engagement strategy and identify key influencers at each stage." + +**Workflow:** +1. Create document: "Product X Launch Stakeholders" +2. Set to Temporal Mode +3. Create states: + - "Current - Pre-announcement" + - "Beta Program - Month 1" + - "Beta Program - Month 3" + - "GA Launch" + - "3 Months Post-GA" +4. For each state: + - Add/remove stakeholders + - Update engagement levels (edge strength) + - Mark key influencers +5. Track specific stakeholders across timeline +6. Identify where to focus engagement efforts +7. Share with marketing team + +**Key Features Used:** +- Temporal planning +- Actor tracking +- Relationship strength evolution +- Strategic planning view +- Team collaboration + +--- + +## 8. Implementation Priorities + +### Phase 1: Core Functionality (MVP) +**Goal:** Basic multi-state support + +- [ ] State data model and storage +- [ ] Create/switch between states +- [ ] Simple timeline panel (linear view) +- [ ] Current state indicator +- [ ] Basic state metadata editing +- [ ] Timeline navigation + +**Success Criteria:** +- User can create multiple states +- User can switch between states +- Changes are saved per-state +- UI clearly shows current state + +### Phase 2: Temporal & Scenario Modes +**Goal:** Differentiate use cases + +- [ ] Temporal vs. Scenario mode selection +- [ ] Date/period fields for temporal states +- [ ] Branch creation from states +- [ ] Tree view in timeline panel +- [ ] State cloning options +- [ ] Basic comparison (side-by-side) + +**Success Criteria:** +- User can track temporal progression +- User can branch into scenarios +- User can compare two states side-by-side +- Timeline visualizes branches clearly + +### Phase 3: Analysis & Comparison +**Goal:** Power user features + +- [ ] Advanced comparison (overlay/diff) +- [ ] Change detection and highlighting +- [ ] Actor tracking across states +- [ ] Comparison matrix for scenarios +- [ ] Metrics evolution charts +- [ ] Difference reports + +**Success Criteria:** +- User can analyze changes between states +- User can track individual actors over time +- User can generate comparison reports + +### Phase 4: Presentation & Export +**Goal:** Storytelling and sharing + +- [ ] Presentation mode +- [ ] State-to-state animation +- [ ] Auto-play with timing controls +- [ ] Export timeline as PDF +- [ ] Export as PowerPoint +- [ ] Video/GIF export +- [ ] Presenter notes support + +**Success Criteria:** +- User can present evolution to stakeholders +- User can export for external sharing +- Animations are smooth and professional + +### Phase 5: Advanced Features +**Goal:** Enterprise and research use + +- [ ] Smart suggestions/AI analysis +- [ ] Custom metrics tracking +- [ ] Advanced filtering and search +- [ ] Collaborative editing per state +- [ ] Template scenarios +- [ ] Import/export timeline formats + +--- + +## 9. Design Patterns & Best Practices + +### 9.1 State Management + +**Immutability:** +- Each state is independent snapshot +- Editing state A doesn't affect state B +- Changes are isolated to active state + +**Auto-save:** +- Changes save immediately to current state +- No "save state" button needed +- Visual indicator when saving + +**Conflicts:** +- Not applicable (no concurrent editing of same state) +- But consider: collaborative scenarios in future + +### 9.2 Performance Considerations + +**Large Timelines:** +- Lazy load state data +- Only active state + neighbors in memory +- Thumbnails for preview +- Virtualized timeline rendering + +**Complex Graphs:** +- Same optimization as single-state graphs +- Cache state thumbnails +- Progressive loading in comparison mode + +### 9.3 User Guidance + +**First-time Use:** +- Onboarding tour explaining temporal/scenario concepts +- Template timelines for common use cases +- Example documents with states + +**Inline Help:** +- Tooltips explaining state operations +- Context-sensitive help in timeline panel +- Visual cues for branch points + +### 9.4 Accessibility + +**Keyboard Navigation:** +- All timeline operations keyboard-accessible +- State switcher with keyboard +- Presentation mode keyboard controls + +**Screen Readers:** +- Announce state changes +- Describe timeline structure +- Alternative text for visualizations + +**Visual Design:** +- High contrast for current state +- Clear focus indicators +- Colorblind-safe highlighting + +--- + +## 10. Measuring Success + +### 10.1 User Metrics + +**Engagement:** +- Percentage of users who create 2+ states +- Average states per document +- Time spent in comparison mode +- Presentation mode usage + +**Feature Adoption:** +- Temporal mode vs. Scenario mode usage +- Branch creation frequency +- Actor tracking usage +- Export/presentation frequency + +### 10.2 User Feedback + +**Qualitative:** +- User interviews with consultants, strategists +- Usability testing of timeline panel +- Feedback on comparison features +- Presentation mode effectiveness + +**Quantitative:** +- NPS score for timeline feature +- Feature satisfaction ratings +- Bug reports and friction points + +### 10.3 Success Indicators + +**Good Signs:** +- Users naturally create states without prompting +- Users share presentations with stakeholders +- Users request advanced comparison features +- Users migrate existing documents to timelines + +**Warning Signs:** +- Users confused about state vs. document +- Timeline panel rarely used +- Users try to use as version control +- High abandonment of multi-state documents + +--- + +## 11. Future Enhancements + +### 11.1 Collaborative Scenarios + +**Multi-user Planning:** +- Different team members model different scenarios +- Merge scenarios for discussion +- Voting/commenting on scenario states + +### 11.2 Dynamic States + +**Live Data Integration:** +- States update from external data sources +- Real-time stakeholder maps +- Automated state creation from events + +### 11.3 AI-Powered Insights + +**Predictive Modeling:** +- "Likely evolution" suggestions +- Pattern matching from similar timelines +- Risk assessment across scenarios +- Anomaly detection + +### 11.4 Advanced Visualizations + +**3D Timeline:** +- Time as Z-axis +- Spatial evolution visualization +- VR presentation mode + +**Network Metrics Over Time:** +- Animated metric dashboards +- Trend analysis +- Correlation detection + +--- + +## Conclusion + +This revised UX concept positions Constellation Analyzer's multi-graph feature as a powerful tool for **temporal analysis** and **scenario exploration**, not version control. + +**Key Differentiators:** +1. **Dual-mode design**: Temporal and Scenario modes for different use cases +2. **Comparison-first**: Multiple ways to compare and analyze states +3. **Presentation-ready**: Built for storytelling and stakeholder communication +4. **Evolution tracking**: Follow actors and relationships over time +5. **Strategic planning**: Model and evaluate alternative futures + +**Next Steps:** +1. Validate terminology with target users (consultants, strategists, researchers) +2. Create interactive prototype of timeline panel +3. User test comparison and presentation modes +4. Prioritize Phase 1 features for development +5. Build example use cases for each user persona + +--- + +## Appendix: Alternative Terminology Considered + +### Timeline Terms: +- ✅ Timeline - Clear, intuitive +- ❌ History - Implies past only +- ❌ Evolution - Too biological +- ❌ Journey - Too narrative-focused +- ❌ Progression - Good but less common + +### State Terms: +- ✅ State - Neutral, technical but clear +- ✅ Snapshot - Intuitive but implies static +- ❌ Version - Too version-control-like +- ❌ Instance - Too technical +- ❌ Frame - Too video-like +- ❌ Point - Too minimal + +### Branch Terms: +- ✅ Branch - Familiar from trees/git +- ✅ Scenario - Clear for alternatives +- ❌ Fork - Too git-specific +- ❌ Alternative - Too wordy +- ❌ Path - Could work but less clear + +**Final Decision:** Adaptive terminology based on user-selected mode (Temporal vs. Scenario) provides the best user experience. diff --git a/UX_CONCEPT_MULTI_VERSION_GRAPH.md b/UX_CONCEPT_MULTI_VERSION_GRAPH.md new file mode 100644 index 0000000..aa49d67 --- /dev/null +++ b/UX_CONCEPT_MULTI_VERSION_GRAPH.md @@ -0,0 +1,1318 @@ +# Multi-Version Graph Feature - UX Concept Document + +## Executive Summary + +This document outlines a comprehensive UX design for adding multi-version graph support to Constellation Analyzer. The feature enables users to maintain multiple versions of a constellation graph within a single document, with a branching model similar to version control systems but adapted for visual graph editing. + +**Design Philosophy**: Balance power-user capabilities with approachable, intuitive interactions for users unfamiliar with traditional version control. + +--- + +## 1. User Mental Model + +### Core Metaphor: "Snapshots with Branches" + +**Primary Terminology** (domain-specific, not git-like): +- **Snapshot** - A saved state of the graph at a specific point in time +- **Branch** - A diverging path of development from a snapshot +- **Timeline** - The visual representation of snapshot history +- **Active Snapshot** - The currently displayed and editable version + +**Why Not Git Terminology?** +While the underlying model resembles git, using terms like "commit," "branch," and "merge" may intimidate users unfamiliar with version control. Our target users are analysts and researchers, not developers. + +**User Mental Model Narrative**: +> "I'm working on a constellation analysis. As I explore different scenarios, I can create snapshots of my work. Each snapshot preserves the exact state of actors and relations at that moment. Later, I can branch off from any snapshot to explore alternative arrangements without losing my original work. The timeline shows me the evolution of my analysis." + +### Key Mental Model Principles: + +1. **Non-destructive editing** - Creating a new snapshot never destroys previous work +2. **Exploration-friendly** - Easy to try alternative configurations +3. **Clear lineage** - Visual representation shows how snapshots relate +4. **Lightweight snapshots** - Creating a snapshot is quick and low-friction +5. **Safety first** - Difficult to accidentally delete work + +--- + +## 2. Bottom Panel Design + +### 2.1 Layout & Positioning + +**Position**: Bottom edge of the graph editor (below the ReactFlow canvas) + +**Dimensions**: +- Default height: 200px +- Minimum height: 120px (collapsed state showing only controls) +- Maximum height: 50% of viewport height +- Resizable via drag handle at top edge + +**States**: +1. **Fully Visible** (200px) - Timeline graph fully visible with snapshot details +2. **Collapsed** (120px) - Timeline visible but compressed, minimal details +3. **Hidden** (0px, optional) - Panel completely hidden, toggle via View menu or keyboard + +**Behavior**: +- Panel remains visible by default when document has multiple snapshots +- For documents with single snapshot, panel auto-hides but can be shown +- Resize handle appears on hover at top edge (3px hit area, visual indicator) +- Double-click resize handle to toggle between default and collapsed heights + +### 2.2 Visual Structure + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ ┌─┐ Snapshot Timeline [Search] [?] [Filter▾] [−] [×] │ ← Header (32px) +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ◯───◯───◯───┬───◯ │ +│ │ │ │ │ │ │ ← Timeline Graph +│ S1 S2 S3 │ S5 (Active) │ (Variable height) +│ │ │ +│ └───◯ │ +│ S4 │ +│ │ +├─────────────────────────────────────────────────────────────────┤ +│ Snapshot S5: "Exploring team structure" • Modified 2 min ago │ ← Details Footer (36px) +│ [Create Snapshot] [Branch from S3▾] [⋮ More Actions] │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 2.3 Header Controls + +**Left side**: +- **Timeline icon** + "Snapshot Timeline" title +- Subtle visual indicator of total snapshot count (e.g., "(7 snapshots)") + +**Right side controls**: +- **Search field** - Filter snapshots by name/description +- **Help icon (?)** - Opens tooltip explaining snapshot concepts +- **Filter dropdown** - Show/hide specific branches, date ranges +- **Collapse button (−)** - Collapses panel to minimal height +- **Hide button (×)** - Hides panel completely + +### 2.4 Timeline Visualization + +**Layout**: Horizontal flow, left to right (earliest to newest) + +**Visual Elements**: + +**Snapshot Node**: +``` + ◯ ← Hollow circle (16px) for inactive snapshots + ● ← Filled circle (20px) for active snapshot + + Colors: + - Default: Gray (#94a3b8) + - Active: Blue (#3b82f6) + - Hover: Darker shade + - Selected: Ring around node +``` + +**Connection Lines**: +- Solid lines (2px) connecting parent to child +- Bezier curves for branching paths +- Color matches the branch (subtle color coding) + +**Labels**: +- Snapshot name appears below node (truncated to 20 chars) +- Timestamp on secondary line (relative: "2h ago", "3 days ago") +- Custom label text wraps to 2 lines max + +**Interactive States**: +1. **Default** - Snapshot visible, clickable +2. **Hover** - Node enlarges slightly (scale 1.1), shows preview tooltip +3. **Active** - Larger node, blue color, subtle glow effect +4. **Selected** - Blue ring around node, details shown in footer +5. **Dimmed** - Snapshots filtered out (opacity 0.3) + +### 2.5 Snapshot Preview Tooltip (on hover) + +``` +┌────────────────────────────────┐ +│ Snapshot: Feature Analysis │ +│ Created: Oct 11, 2025 2:30 PM │ +│ Branch: main │ +│ ───────────────────────────── │ +│ [Thumbnail preview] │ ← 200x120px graph preview +│ 12 actors • 18 relations │ +│ ───────────────────────────── │ +│ Click to view │ +│ Double-click to switch │ +└────────────────────────────────┘ +``` + +**Preview generation**: +- Cached thumbnail of graph state (rendered on snapshot creation) +- Shows miniature view of node layout +- Updates only when snapshot is modified (if editable) + +### 2.6 Details Footer + +Displays information about selected/active snapshot: + +**Content**: +- Snapshot name (editable inline on click) +- Description text (if set) +- Last modified timestamp +- Statistics: actor count, relation count +- Branch indicator (if multiple branches exist) + +**Action Buttons**: +1. **"Create Snapshot"** - Create new snapshot from current active state +2. **"Branch from..."** - Dropdown to select any snapshot to branch from +3. **"More Actions" (⋮)** - Overflow menu for: + - Rename snapshot + - Edit description + - Delete snapshot + - Compare with another snapshot + - Export snapshot + - View snapshot history + +### 2.7 Handling Large Version Graphs + +**Performance Optimizations** (for 100+ snapshots): + +1. **Virtual Scrolling**: + - Render only visible snapshot nodes (viewport + buffer) + - Smooth horizontal scrolling with momentum + +2. **Timeline Zoom Levels**: + - **Zoomed out** - Dots only, no labels (fit more on screen) + - **Default** - Nodes with names, condensed timestamps + - **Zoomed in** - Full details, larger nodes, preview thumbnails + - Zoom controls: +/− buttons or Ctrl+Scroll + +3. **Minimap** (appears when >20 snapshots): + ``` + ┌─────────────────────────────────────┐ + │ Timeline Minimap: [====····] │ ← Shows full timeline + │ ^current view^ │ + └─────────────────────────────────────┘ + ``` + +4. **Branch Folding**: + - Collapse inactive branches to single node + - "Show 5 hidden snapshots" expandable indicator + - Focus mode: Show only active branch + ancestors + +5. **Search & Filter**: + - Filter by date range slider + - Filter by branch name + - Search by snapshot name/description + - Tag-based filtering (if tags implemented) + +6. **Lazy Loading**: + - Load snapshot metadata only + - Graph data loaded on-demand when switching + - Thumbnail previews loaded progressively + +--- + +## 3. Version Navigation + +### 3.1 Switching Between Snapshots + +**Primary Interaction**: **Double-click** on snapshot node in timeline + +**Rationale**: +- Single-click selects/previews (low commitment) +- Double-click activates (deliberate action) +- Familiar pattern from file explorers + +**Alternative Methods**: +1. Select snapshot → Press Enter +2. Select snapshot → Click "Switch to This Snapshot" button +3. Keyboard shortcuts: Ctrl+[ / Ctrl+] to navigate chronologically +4. Right-click context menu → "Switch to Snapshot" + +### 3.2 Unsaved Changes Handling + +**When attempting to switch with unsaved changes**: + +**Modal Dialog**: +``` +┌──────────────────────────────────────────────┐ +│ ⚠ Unsaved Changes │ +│ │ +│ You have unsaved changes in this snapshot. │ +│ What would you like to do? │ +│ │ +│ ○ Create new snapshot with changes │ +│ ○ Discard changes and switch │ +│ ○ Cancel (stay on current snapshot) │ +│ │ +│ [Cancel] [Proceed →] │ +└──────────────────────────────────────────────┘ +``` + +**Auto-snapshot Option** (in Settings): +- "Automatically create snapshot when switching versions" +- Prompts for name if enabled, or uses auto-generated name +- Default: OFF (to avoid clutter) + +### 3.3 Visual Feedback During Switch + +**Loading State**: +1. Timeline panel: Active node animates to new position +2. Graph editor: Fade out → Brief loading indicator → Fade in new graph +3. Duration: 200-400ms for smooth transition +4. Preserve viewport position if possible (or fit view) + +**Confirmation**: +- Toast notification: "Switched to snapshot: [Name]" +- Active snapshot highlighted in timeline +- Document title updates with snapshot indicator + +### 3.4 Keyboard Navigation + +**Shortcuts**: +- `Ctrl+[` - Previous snapshot (chronologically) +- `Ctrl+]` - Next snapshot +- `Ctrl+Shift+S` - Create new snapshot +- `Ctrl+B` - Toggle timeline panel visibility +- `Ctrl+/` - Focus timeline search +- `Arrow keys` - Navigate between snapshots in timeline (when focused) +- `Enter` - Switch to selected snapshot +- `Escape` - Deselect snapshot, return to active view + +--- + +## 4. Version Creation + +### 4.1 Creating New Snapshots + +**Trigger Methods**: + +1. **Manual Creation**: + - Click "Create Snapshot" button in timeline footer + - Menu: Edit → Create Snapshot + - Keyboard: Ctrl+Shift+S + - Toolbar: Snapshot icon button (camera icon) + +2. **Quick Snapshot Dialog**: +``` +┌──────────────────────────────────────────┐ +│ Create Snapshot │ +│ │ +│ Name: [Exploring team dynamics______] │ +│ │ +│ Description (optional): │ +│ [Added marketing team actors and___] │ +│ [their relations to product team___] │ +│ │ +│ ☐ Create as new branch │ +│ │ +│ [Cancel] [Create Snapshot] │ +└──────────────────────────────────────────┘ +``` + +**Auto-naming**: +- If name left empty, generate: "Snapshot [number]" or "Untitled [timestamp]" +- Show placeholder: "e.g., After adding finance team" +- Timestamp automatically attached to metadata + +**Description field**: +- Optional but encouraged +- Multi-line textarea (3 rows) +- Character limit: 500 chars +- Used in search and timeline tooltips + +### 4.2 Auto-save vs Manual Snapshots + +**Recommended Approach**: **Manual Only** (initially) + +**Rationale**: +- Users maintain control over what constitutes a meaningful version +- Prevents timeline clutter from every minor edit +- More intentional version management + +**Future Enhancement**: Auto-snapshot options (in Settings): +- "Auto-snapshot every N minutes" (disabled by default) +- "Auto-snapshot on significant changes" (e.g., >10 actors added/removed) +- Auto-snapshots marked visually different (hollow squares instead of circles) +- Can be cleaned up via "Compress Timeline" action + +### 4.3 Branching UX + +**Creating a Branch**: + +**Scenario**: User wants to explore alternative from Snapshot 3 while keeping current work + +**Method 1 - Context Menu**: +1. Right-click snapshot in timeline +2. Select "Create Branch from Here" +3. Name branch dialog appears + +**Method 2 - Branch Button**: +1. Select snapshot (single-click) +2. Click "Branch from..." dropdown in footer +3. Shows list of all snapshots with thumbnails +4. Select source snapshot +5. Name dialog appears + +**Branch Naming Dialog**: +``` +┌──────────────────────────────────────────┐ +│ Create Branch from Snapshot 3 │ +│ │ +│ New snapshot will be created from: │ +│ "Initial actor layout" (Oct 11, 2:15pm) │ +│ │ +│ Branch name: [Alternative structure_] │ +│ │ +│ Start with: │ +│ ● Exact copy of source snapshot │ +│ ○ Empty graph (keep only types) │ +│ │ +│ [Cancel] [Create Branch] │ +└──────────────────────────────────────────┘ +``` + +**Visual Branching Indicator**: +- Branch lines diverge with bezier curve from parent +- Different subtle background colors for different branches +- Branch labels in timeline (collapsible) + +**Branch Management**: +- Branches are implicit (no explicit branch objects) +- Branches identified by diverging paths in snapshot graph +- "Main" branch is simply the linear path from first snapshot +- Can rename branches by editing the divergence point snapshot + +--- + +## 5. Version Graph Editing + +### 5.1 Deletion Operations + +**Delete Snapshot**: + +**Trigger**: +- Select snapshot → More Actions (⋮) → Delete Snapshot +- Right-click → Delete +- Select + Delete key + +**Safety Rules**: +1. **Cannot delete active snapshot** - Must switch to another first +2. **Orphan prevention** - If snapshot has children, show warning +3. **Confirmation required** - Always prompt before deletion + +**Confirmation Dialog**: +``` +┌──────────────────────────────────────────┐ +│ ⚠ Delete Snapshot? │ +│ │ +│ This will permanently delete: │ +│ "Feature Analysis v2" │ +│ │ +│ ⚠ Warning: This snapshot has 3 children │ +│ What should happen to child snapshots? │ +│ │ +│ ● Re-parent to previous snapshot │ +│ ○ Delete entire branch (4 snapshots) │ +│ │ +│ ☑ Don't ask again for simple deletions │ +│ │ +│ [Cancel] [Delete Snapshot] │ +└──────────────────────────────────────────┘ +``` + +**Re-parenting**: +- Children automatically connect to deleted snapshot's parent +- Visual animation shows line reconnecting +- Toast: "Snapshot deleted, 3 children re-parented" + +### 5.2 Re-parenting Operations + +**Use Case**: Change the parent of a snapshot (move it to different branch point) + +**Interaction**: +1. Right-click snapshot → "Change Parent..." +2. Timeline enters "re-parent mode" +3. All valid parent snapshots highlight (must be chronologically earlier) +4. Click new parent snapshot +5. Confirmation dialog shows before/after visualization + +**Constraints**: +- New parent must be chronologically before the snapshot being moved +- Cannot create cycles +- Moving a snapshot moves all its descendants + +**Visual Feedback**: +- Dotted line from snapshot to cursor during selection +- Valid drop targets pulse gently +- Invalid targets dimmed with "not allowed" cursor + +### 5.3 Merge Operations (Advanced) + +**Note**: Merging is complex and should be a **later phase** feature + +**If implemented, merge UX**: + +**Trigger**: +1. Select two snapshots (Ctrl+Click) +2. Right-click → "Merge Snapshots..." +3. Merge wizard opens + +**Merge Wizard**: +``` +Step 1: Select merge strategy + ● Manual conflict resolution + ○ Keep all actors from both (auto-merge) + ○ Prefer snapshot A + ○ Prefer snapshot B + +Step 2: Resolve conflicts (if manual) + [Side-by-side diff view] + - Actors only in A (5) → [Keep] [Discard] + - Actors only in B (3) → [Keep] [Discard] + - Modified actors (2) → [Use A] [Use B] [Edit] + +Step 3: Name merged snapshot + Name: [Merged: A + B_________] + +Step 4: Review & Create + [Preview merged graph] + [Create Merged Snapshot] +``` + +**Complexity Warning**: +- Merging graphs is semantically ambiguous +- Better to support "Compare" feature first +- Allow manual recreation by viewing two snapshots side-by-side + +### 5.4 Reordering & Reorganization + +**Timeline Reorganization**: +- Drag-and-drop to reorder snapshots within a linear branch (no re-parenting) +- Useful for organizing exploration snapshots +- Does not change parent-child relationships +- Visual-only reorganization for cleaner timeline + +**Compact View**: +- "Compress Timeline" action in More menu +- Automatically removes auto-snapshots older than X days +- Consolidates linear sequences with no branching +- Shows "Compressed 15 snapshots" indicator + +--- + +## 6. Integration with Existing UI + +### 6.1 Top-Level Current Snapshot Indicator + +**Location**: Document tab bar OR below menu bar + +**Option A - In Document Tab** (Recommended): +``` +┌────────────────────────────────────────────┐ +│ [Document 1 ▾] [Document 2] [+] │ +│ └─ Snapshot: Feature Analysis v2 │ +└────────────────────────────────────────────┘ +``` + +**Option B - Breadcrumb Header**: +``` +┌────────────────────────────────────────────┐ +│ Home > Team Analysis > Snapshot: v2.3 │ +└────────────────────────────────────────────┘ +``` + +**Option C - Status Bar** (below tabs, above toolbar): +``` +┌────────────────────────────────────────────┐ +│ [Document 1 ▾] [Document 2] [+] │ +├────────────────────────────────────────────┤ +│ 📸 Active: Feature Analysis v2 • Oct 11... │ +└────────────────────────────────────────────┘ +``` + +**Interaction**: +- Click indicator → Opens snapshot selector dropdown +- Dropdown shows recent snapshots with quick-switch +- "Manage Snapshots..." option at bottom opens timeline panel + +### 6.2 Left Panel Integration + +**New Section: "Snapshots"** (collapsible, like "Add Actors"): + +``` +┌─────────────────────────┐ +│ ▼ Snapshots │ +├─────────────────────────┤ +│ Current: │ +│ Feature Analysis v2 │ +│ Modified 5 min ago │ +│ │ +│ [Create Snapshot] │ +│ [View Timeline] │ +│ │ +│ Recent: │ +│ • Initial layout │ +│ • Team structure v1 │ +│ • Alternative view │ +└─────────────────────────┘ +``` + +**Benefits**: +- Quick access without opening timeline panel +- Shows current state at a glance +- Recent list for fast switching +- Minimal space when collapsed + +### 6.3 Right Panel Integration + +**When Snapshot Selected** (in timeline, not graph element): + +Right panel shows snapshot metadata: +``` +┌─────────────────────────┐ +│ Snapshot Properties │ +├─────────────────────────┤ +│ Name: │ +│ [Feature Analysis v2__] │ +│ │ +│ Description: │ +│ [Added finance team...] │ +│ │ +│ Created: │ +│ Oct 11, 2025 2:30 PM │ +│ │ +│ Statistics: │ +│ • 12 actors │ +│ • 18 relations │ +│ • Branch: main │ +│ │ +│ Actions: │ +│ [Switch to Snapshot] │ +│ [Branch from Here] │ +│ [Compare with Current] │ +│ [Export Snapshot] │ +│ [Delete Snapshot] │ +└─────────────────────────┘ +``` + +### 6.4 Document-Level Operations + +**Save Behavior**: +- **Ctrl+S** saves changes to **active snapshot** (creates new state) +- Snapshots are immutable after creation (for history integrity) +- "Save" updates the active snapshot's graph data +- Dirty indicator shows unsaved changes in current snapshot + +**Export Behavior**: + +**Export Active Snapshot**: +- File → Export → Current Snapshot +- Exports only active snapshot's graph + +**Export All Snapshots**: +- File → Export → All Snapshots (with timeline) +- Creates .constellation-multi file or ZIP +- Includes full snapshot history and lineage + +**Export Snapshot Range**: +- File → Export → Selected Snapshots... +- Choose snapshots in timeline (multi-select) +- Exports selected portion of timeline + +**Import Behavior**: +- Importing a multi-version file creates new document +- Preserves all snapshot history and relationships +- Can import into existing document as new branch (advanced) + +### 6.5 Menu Bar Updates + +**New Menu Items**: + +**File Menu**: +``` +File +├─ New Document +├─ Open Document... +├─ Save Document Ctrl+S +├─ ─────────────── +├─ Import... +├─ Export ► +│ ├─ Current Snapshot... +│ ├─ All Snapshots... +│ └─ Selected Snapshots... +├─ ─────────────── +├─ Document Manager... +``` + +**Edit Menu**: +``` +Edit +├─ Undo Ctrl+Z +├─ Redo Ctrl+Shift+Z +├─ ─────────────── +├─ Create Snapshot Ctrl+Shift+S +├─ Switch Snapshot ► +│ ├─ Feature Analysis v2 ✓ +│ ├─ Initial layout +│ ├─ Team structure v1 +│ └─ ─────────────── +│ └─ Show All... +├─ ─────────────── +├─ Select All Ctrl+A +``` + +**View Menu**: +``` +View +├─ Zoom In Ctrl++ +├─ Zoom Out Ctrl+- +├─ Fit View Ctrl+0 +├─ ─────────────── +├─ Show Left Panel Ctrl+1 +├─ Show Right Panel Ctrl+2 +├─ Show Timeline Panel Ctrl+B +├─ ─────────────── +├─ Toggle Snapshot Minimap +``` + +--- + +## 7. Edge Cases & Considerations + +### 7.1 Very Large Version Graphs (100+ snapshots) + +**Addressed by**: +1. Virtual scrolling in timeline (Section 2.7) +2. Zoom levels for timeline density +3. Branch folding to hide inactive development +4. Search and filtering capabilities +5. Minimap for navigation +6. "Focus mode" showing only active branch + +**Additional Mitigations**: +- Lazy-load snapshot data (metadata only until needed) +- Paginated snapshot list in dropdown menus +- Archive old branches to separate storage +- "Compact timeline" action to consolidate + +### 7.2 Performance Considerations + +**Switching Between Snapshots**: + +**Challenge**: Loading large graphs (500+ nodes) is slow + +**Solutions**: +1. **Incremental Loading**: + - Load nodes first, edges second + - Render in viewport first, off-screen later + - Progress indicator for large graphs + +2. **Snapshot Diff Loading**: + - Store deltas between snapshots + - Apply changes incrementally when switching similar snapshots + - Full load only when necessary + +3. **Caching Strategy**: + - Keep previous 3 snapshots in memory + - LRU cache for frequently accessed snapshots + - Clear cache on memory pressure + +4. **Background Pre-loading**: + - Pre-load adjacent snapshots in background + - Predict likely next navigation (temporal, branching) + +**Timeline Rendering**: +- Canvas-based rendering for large timelines (>50 snapshots) +- SVG for smaller timelines (better quality, easier interaction) +- Virtual scrolling for snapshot list views + +### 7.3 Snapshot Comparison (Diff View) + +**Feature**: Compare two snapshots side-by-side + +**Trigger**: +- Select snapshot → More Actions → Compare with... +- Select second snapshot from dropdown + +**Comparison View**: +``` +┌─────────────────────────────────────────────────────┐ +│ Compare Snapshots [×] │ +├─────────────────────────────────────────────────────┤ +│ [Snapshot A: v1 ▾] [Snapshot B: v2 ▾] │ +├──────────────────────────┬──────────────────────────┤ +│ │ │ +│ [Graph View A] │ [Graph View B] │ +│ │ │ +├──────────────────────────┴──────────────────────────┤ +│ Differences: │ +│ ✓ Actors: +3 new, -1 removed, 2 modified │ +│ ✓ Relations: +5 new, -2 removed, 1 modified │ +│ │ +│ [Show only differences] [Highlight changes] │ +└─────────────────────────────────────────────────────┘ +``` + +**Diff Highlighting**: +- Added actors: Green border +- Removed actors: Red border with strikethrough +- Modified actors: Yellow border +- Similar visual treatment for relations + +**Diff Summary Panel**: +- List of all changes with drill-down +- Export diff as report +- "Apply changes to active snapshot" action + +### 7.4 Conflict Handling (if merging implemented) + +**Types of Conflicts**: + +1. **Actor Conflicts**: + - Same actor ID with different properties + - Different actors at same position + - Actor type changes + +2. **Relation Conflicts**: + - Same relation with different types + - Relations to deleted actors + - Conflicting directionality + +**Resolution UI**: +- Side-by-side conflict view +- "Keep mine" / "Keep theirs" / "Keep both" / "Manually edit" +- Preview of resolution before applying +- Save resolution as new snapshot + +**Recommendation**: Defer merge functionality to v2, focus on: +- Clear separation of branches +- Easy comparison tools +- Manual integration workflows + +### 7.5 Collaborative Editing (Future) + +**Consideration**: If multiple users edit same document + +**Challenges**: +- Snapshot creation conflicts +- Active snapshot synchronization +- Real-time collaboration on same snapshot + +**Potential Solutions**: +1. **Snapshot-level Locking**: + - Only one user can edit a snapshot at a time + - Others can view or create branches + +2. **Per-User Branches**: + - Each user works on their own branch + - Manual merging when ready + +3. **Operational Transform**: + - Real-time collaborative editing + - Automatic conflict resolution + - Very complex, future consideration + +**Initial Approach**: +- Single-user focus +- Export/import for sharing +- Cloud sync preserves all snapshots + +### 7.6 Undo/Redo Interaction + +**Question**: How does undo/redo interact with snapshots? + +**Recommended Behavior**: + +1. **Undo/Redo operates within active snapshot**: + - Each snapshot has its own undo history + - Switching snapshots preserves undo stack + - Undo stack cleared when switching snapshots (configurable) + +2. **Snapshot Creation in Undo Stack**: + - Creating a snapshot is an undoable action + - Undo after snapshot creation deletes the snapshot + - Redo recreates the snapshot + +3. **Switching Snapshots**: + - Not in undo stack (separate navigation) + - Use snapshot history for navigation + - Prevents undo/redo confusion + +**Settings Option**: +- "Preserve undo history when switching snapshots" (default: OFF) +- When ON, each snapshot maintains separate undo stack +- Increases memory usage + +### 7.7 Data Model Implications + +**Snapshot Storage Structure**: + +```typescript +interface Snapshot { + id: string; + parentId: string | null; // null for root snapshot + name: string; + description?: string; + createdAt: string; + updatedAt: string; + + // Graph state + graphState: { + nodes: Actor[]; + edges: Relation[]; + viewport?: { x: number; y: number; zoom: number }; + }; + + // Metadata + metadata: { + actorCount: number; + relationCount: number; + thumbnail?: string; // Base64 encoded preview + tags?: string[]; + branch?: string; // Optional branch label + }; + + // For delta storage (optimization) + isDelta?: boolean; + deltaFrom?: string; // Parent snapshot ID + deltaOperations?: DeltaOp[]; // Add/remove/modify operations +} + +interface SnapshotGraph { + snapshots: Map; + rootSnapshotId: string; + activeSnapshotId: string; + + // Computed properties + branches?: SnapshotBranch[]; + timeline?: SnapshotNode[]; +} +``` + +**Storage Strategy**: +1. **Full Storage** (simple, initial approach): + - Each snapshot stores complete graph state + - Larger file size, but simpler logic + - Good for <50 snapshots + +2. **Delta Storage** (optimization): + - Store only differences from parent + - Reduces storage for large histories + - Reconstruction required when loading + - Good for >50 snapshots + +3. **Hybrid Approach**: + - Store full state every N snapshots (keyframes) + - Deltas in between + - Balance speed and size + +--- + +## 8. User Workflows & Task Flows + +### 8.1 Common Task: Exploring Alternative Scenarios + +**Scenario**: User wants to try different team structures without losing original + +**Workflow**: +1. User creates initial constellation with 20 actors +2. Clicks "Create Snapshot" → Names it "Initial team structure" +3. Makes modifications (adds 5 actors, removes 2) +4. Realizes this doesn't work well +5. In timeline, double-clicks "Initial team structure" +6. System prompts about unsaved changes → User discards +7. User is back at initial state +8. Right-clicks "Initial team structure" → "Create Branch from Here" +9. Names new branch "Alternative hierarchy" +10. Makes different changes +11. Now has two parallel versions to compare + +**Success Metrics**: +- No confusion about current state +- Easy navigation between versions +- Clear visual representation of branches +- No accidental data loss + +### 8.2 Common Task: Reviewing Evolution Over Time + +**Scenario**: User wants to see how analysis evolved over a week + +**Workflow**: +1. User opens document with 30 snapshots +2. Clicks timeline panel to expand +3. Uses timeline zoom to see all snapshots at once +4. Identifies key milestones visually +5. Clicks through snapshots to see changes +6. Uses comparison view to see specific differences +7. Exports progression as presentation + +**Enhancements**: +- "Playback" mode: Auto-advance through snapshots +- Export as animated GIF or video +- Slide show mode for presentations + +### 8.3 Common Task: Cleaning Up Old Work + +**Scenario**: User has 100+ snapshots, wants to consolidate + +**Workflow**: +1. Opens timeline panel +2. Clicks More Actions → "Compact Timeline" +3. System shows preview of compaction: + - "Will remove 40 auto-snapshots older than 30 days" + - "Will consolidate 15 sequential snapshots with no branches" +4. User confirms +5. Timeline now shows 45 snapshots (much cleaner) +6. Important milestones preserved + +**Safety**: +- Preview before compaction +- Undo compaction (keep deleted snapshots in "trash" for 30 days) +- Never auto-compact without user action + +### 8.4 Common Task: Collaborating with Team Member + +**Scenario**: User wants to share work-in-progress with colleague + +**Workflow**: +1. User exports document with all snapshots +2. Colleague imports document +3. Colleague creates branch "Sarah's suggestions" +4. Makes changes in new branch +5. Exports back to user +6. User imports, sees new branch in timeline +7. User reviews changes, manually integrates good ideas + +**Future Enhancement**: +- Cloud sync with branch visibility +- Comments on snapshots +- Suggested changes workflow + +--- + +## 9. Visual Design Recommendations + +### 9.1 Color Palette + +**Timeline Panel**: +- Background: `#ffffff` (white) +- Panel border: `#e5e7eb` (gray-200) +- Header background: `#f9fafb` (gray-50) + +**Snapshot Nodes**: +- Inactive: `#94a3b8` (slate-400) +- Active: `#3b82f6` (blue-500) +- Hover: `#64748b` (slate-500) +- Selected: `#2563eb` (blue-600) + +**Connection Lines**: +- Default: `#cbd5e1` (slate-300) +- Active path: `#3b82f6` (blue-500) +- Hover: `#94a3b8` (slate-400) + +**Branch Color Coding** (subtle): +- Branch 1: `#3b82f6` (blue) - main branch +- Branch 2: `#10b981` (green) +- Branch 3: `#f59e0b` (amber) +- Branch 4: `#8b5cf6` (purple) +- Branch 5+: Rotate through palette + +### 9.2 Typography + +**Timeline Panel**: +- Panel title: 12px, font-weight: 600, color: `#374151` +- Snapshot names: 11px, font-weight: 500, color: `#1f2937` +- Timestamps: 10px, font-weight: 400, color: `#6b7280` +- Details footer: 11px, font-weight: 400 + +**Snapshot Tooltips**: +- Title: 13px, font-weight: 600 +- Body text: 11px, font-weight: 400 +- Metadata: 10px, font-weight: 400, color: `#6b7280` + +### 9.3 Iconography + +**Timeline Controls**: +- Create snapshot: Camera icon (📸) +- Branch: Git branch icon (🔀) +- Compare: Side-by-side icon (⚖️) +- Delete: Trash icon (🗑️) +- More actions: Vertical ellipsis (⋮) + +**Snapshot States**: +- Active: Filled circle with subtle glow +- Inactive: Hollow circle +- Auto-snapshot: Small square +- Milestone: Star or flag icon overlay + +### 9.4 Animation & Transitions + +**Snapshot Switching**: +- Fade out current graph (150ms) +- Show loading indicator (if >200ms load time) +- Fade in new graph (200ms) +- Timeline node moves with ease-in-out (300ms) + +**Timeline Interactions**: +- Node hover: Scale 1.1, duration 100ms +- Connection line hover: Opacity 1.0, width +1px, duration 150ms +- Branch expand/collapse: Height transition 250ms + +**Panel Resize**: +- Smooth height transition (200ms) +- Snap to collapsed/expanded states + +### 9.5 Accessibility + +**Keyboard Navigation**: +- All timeline controls accessible via keyboard +- Focus indicators clearly visible +- Logical tab order through controls + +**Screen Reader Support**: +- Snapshot nodes: "Snapshot: [name], created [timestamp], [active/inactive]" +- Timeline: "Snapshot timeline with [N] snapshots" +- Actions: Clear button labels and ARIA descriptions + +**Visual Accessibility**: +- Color not sole indicator (use shapes, labels, icons) +- High contrast mode support +- Sufficient text size (minimum 11px) +- Focus indicators: 2px blue outline + +--- + +## 10. Implementation Phases + +### Phase 1: Core Foundation (MVP) +**Goal**: Basic multi-version support with linear timeline + +**Features**: +- Create snapshots manually +- Switch between snapshots +- Linear timeline visualization (no branching) +- Basic snapshot metadata (name, timestamp) +- Unsaved changes handling +- Timeline panel (bottom, resizable) + +**Scope**: Single linear sequence of snapshots, no branching yet + +### Phase 2: Branching Support +**Goal**: Enable parallel exploration paths + +**Features**: +- Create branches from any snapshot +- Branching timeline visualization +- Branch labels and organization +- Re-parenting operations +- Branch color coding + +### Phase 3: Enhanced Navigation +**Goal**: Improve large timeline handling + +**Features**: +- Virtual scrolling for 100+ snapshots +- Timeline zoom levels +- Minimap navigation +- Search and filtering +- Branch folding +- Quick switcher dropdown + +### Phase 4: Comparison & Analysis +**Goal**: Tools for analyzing snapshot differences + +**Features**: +- Side-by-side comparison view +- Diff highlighting +- Export comparison reports +- Snapshot playback mode +- Evolution animations + +### Phase 5: Advanced Features +**Goal**: Power-user capabilities + +**Features**: +- Merge operations (if feasible) +- Timeline compaction +- Snapshot tagging +- Advanced filtering +- Collaborative features (cloud sync) + +--- + +## 11. Success Metrics + +**Usability Metrics**: +- Time to create first snapshot: <30 seconds +- Time to switch between snapshots: <3 seconds (perceived) +- Error rate when branching: <5% +- User comprehension score: >80% understand snapshot concept after 5 minutes + +**Adoption Metrics**: +- % of users creating >1 snapshot: Target 60% +- Average snapshots per document: Target 5-10 +- % of users using branching: Target 30% + +**Performance Metrics**: +- Timeline rendering for 50 snapshots: <500ms +- Snapshot switch time: <1 second for 200-node graph +- Memory usage: <50MB additional for snapshot metadata + +**Quality Metrics**: +- Accidental data loss reports: 0 +- Confusion-related support tickets: <5% of total +- User satisfaction with feature: >4.0/5.0 + +--- + +## 12. Open Questions & Future Considerations + +### Open Questions: + +1. **Should snapshots be immutable after creation?** + - Pro: Preserves history integrity + - Con: Can't fix mistakes in past snapshots + - Recommendation: Immutable, but allow "amend" for last snapshot + +2. **How to handle node type / edge type changes across snapshots?** + - If types are modified, do old snapshots update? + - Recommendation: Types versioned with snapshots (each snapshot has its own type definitions) + +3. **Should viewport position be saved per snapshot?** + - Pro: Contextual viewing + - Con: Can be disorienting + - Recommendation: Configurable, default to "fit view" on switch + +4. **Maximum snapshots per document?** + - Technical limit to prevent performance issues + - Recommendation: 500 snapshots hard limit, warning at 100 + +### Future Enhancements: + +1. **Snapshot Templates**: + - Save snapshot as reusable template + - Apply template structure to new snapshot + +2. **Conditional Snapshots**: + - Auto-create snapshot when specific conditions met + - E.g., "snapshot when >50 actors" + +3. **Snapshot Metadata**: + - Add tags, categories, colors + - Custom metadata fields + - Link to external documentation + +4. **Timeline Views**: + - Calendar view (snapshots by date) + - Tree view (hierarchical) + - Graph view (current visualization) + - List view (table with metadata) + +5. **Export Formats**: + - Export timeline as image + - Export snapshot progression as slides + - Export as git repository (for version control enthusiasts) + +6. **AI-Assisted Features**: + - Auto-suggest snapshot names based on changes + - Detect significant changes warranting snapshot + - Recommend consolidation opportunities + +--- + +## Appendix A: Terminology Glossary + +| Term | Definition | User-Facing? | +|------|------------|--------------| +| Snapshot | A saved state of the graph at a specific moment | Yes | +| Timeline | Visual representation of snapshot history | Yes | +| Branch | A diverging path from a snapshot | Yes | +| Active Snapshot | The currently displayed and editable version | Yes | +| Parent Snapshot | The snapshot from which another was created | Partially (internal) | +| Re-parenting | Changing the parent of a snapshot | No (action-based) | +| Keyframe | Full snapshot stored for performance (not delta) | No (internal) | +| Delta | Incremental changes from parent snapshot | No (internal) | +| Lineage | The ancestral path of a snapshot | Partially (visual only) | + +--- + +## Appendix B: Sample User Scenarios + +### Scenario 1: Academic Researcher +**User**: Dr. Emily, analyzing organizational networks + +**Goal**: Track evolution of team dynamics over semester + +**Usage Pattern**: +- Creates snapshot at start of each week (16 total) +- Reviews progression at end of semester +- Exports timeline as part of research paper +- Branches at week 8 to explore "what if" reorganization + +**Key Features Used**: +- Manual snapshot creation +- Timeline playback +- Comparison view +- Export functionality + +### Scenario 2: Business Analyst +**User**: Marcus, mapping stakeholder relationships + +**Goal**: Present multiple strategic options to leadership + +**Usage Pattern**: +- Creates initial stakeholder map +- Branches into 3 scenarios: "Status Quo", "Reorganization", "External Partnership" +- Develops each branch independently +- Uses comparison to highlight differences +- Exports each branch as separate presentation + +**Key Features Used**: +- Branching +- Branch management +- Comparison +- Export per branch + +### Scenario 3: Software Architect +**User**: Priya, designing system architecture + +**Goal**: Document architectural evolution and decision points + +**Usage Pattern**: +- Creates snapshot for each major design iteration +- Adds detailed descriptions explaining rationale +- Uses tags: "approved", "prototype", "rejected" +- Keeps rejected options for future reference +- Shares with team via exported timeline + +**Key Features Used**: +- Snapshots with descriptions +- Tagging (future feature) +- Historical preservation +- Team sharing + +--- + +## Conclusion + +This UX concept provides a comprehensive foundation for implementing multi-version graph support in Constellation Analyzer. The design balances: + +- **Simplicity** for casual users who want basic versioning +- **Power** for advanced users exploring complex scenarios +- **Clarity** through visual timeline representation +- **Safety** via non-destructive editing and confirmations +- **Performance** through optimizations for large version graphs + +The phased implementation approach allows for iterative development and user feedback integration. Starting with a simple linear timeline (Phase 1) establishes the foundation, while later phases add sophisticated branching and comparison capabilities. + +**Next Steps**: +1. Review and validate concept with stakeholders +2. Create high-fidelity mockups for Phase 1 +3. Develop data model and storage strategy +4. Build Phase 1 prototype +5. Conduct usability testing +6. Iterate based on feedback + +--- + +**Document Version**: 1.0 +**Date**: October 11, 2025 +**Author**: UX Design Team +**Status**: Proposal for Review diff --git a/VISUAL_EXAMPLES.md b/VISUAL_EXAMPLES.md new file mode 100644 index 0000000..66ff022 --- /dev/null +++ b/VISUAL_EXAMPLES.md @@ -0,0 +1,916 @@ +# Temporal & Scenario Analysis - Visual Examples + +This document provides concrete visual examples of how the temporal and scenario analysis features should look and behave. + +--- + +## Example 1: Organizational Evolution (Temporal Analysis) + +### Scenario +A company tracking how its organizational structure changed during a merger over 12 months. + +### Timeline +``` +2023 January → April → July → October → 2024 January +(Pre-Merger) (Integration) (Consolidation) (Restructuring) (New Structure) +``` + +### State 1: January 2023 (Pre-Merger) +``` +Graph: + Company A: 25 employees + - CEO (Alice) + - 3 Department Heads + - 21 Team Members + + Company B: 18 employees + - CEO (Bob) + - 2 Department Heads + - 15 Team Members + + No relations between companies yet +``` + +### State 2: April 2023 (Integration) +``` +Graph: + Merged Company: 43 employees + - Co-CEOs (Alice + Bob) + - 5 Department Heads (3 from A, 2 from B) + - Joint steering committee (6 people) + - 36 Team Members + + New relations: + - Cross-company collaboration edges + - Reporting structure changes +``` + +### State 3: July 2023 (Consolidation) +``` +Graph: + Merged Company: 40 employees (-3 departures) + - Single CEO (Alice, Bob moves to advisory) + - 4 Department Heads (1 department merged) + - 35 Team Members + + Changes: + - Removed: 3 actors (departures) + - Modified: Bob's role and relations + - Added: Advisory board node +``` + +### State 4: October 2023 (Restructuring) +``` +Graph: + Merged Company: 42 employees (+2 new hires) + - CEO (Alice) + - 4 Department Heads (reshuffled) + - 2 new leadership roles + - 36 Team Members + + Changes: + - Added: 2 new strategic roles + - Modified: Several reporting relationships + - Removed: Steering committee (integration complete) +``` + +### State 5: January 2024 (New Structure) +``` +Graph: + Merged Company: 45 employees (+3 new hires) + - CEO (Alice) + - 4 Department Heads (stable) + - Established matrix structure + - 40 Team Members + + Changes: + - Added: Cross-functional teams (new edge types) + - Added: 3 new hires + - Stabilized structure +``` + +### Comparison: State 1 vs State 5 + +**Visual Diff:** +``` +Green (Added): + - 2 new leadership roles + - 5 new team members + - 30+ cross-functional collaboration edges + - Matrix structure edges + +Red (Removed): + - Bob as Co-CEO (moved to advisory) + - 1 department head (consolidation) + - 3 departed employees + - Company B as separate entity + +Yellow (Modified): + - Alice: Title change (Co-CEO → CEO) + - Bob: Role change (Co-CEO → Advisor) + - Multiple reporting relationship changes + - 5 department heads repositioned in hierarchy +``` + +**Summary Statistics:** +- Total actors: 43 → 45 (+2, +4.7%) +- Total relations: 68 → 112 (+44, +64.7%) +- Network density: 0.037 → 0.056 (+51%) +- Average connections per person: 3.2 → 5.0 (+56%) + +### Actor Journey: Bob +``` +Timeline visualization: + +Jan 2023 Apr 2023 Jul 2023 Oct 2023 Jan 2024 + ● ● ● ● ● + CEO Co-CEO Advisor Advisor Advisor +Company B Merged Co Merged Co Merged Co Merged Co + +Relations: + 17 direct reports → 20 → 4 → 2 → 2 + Type: Leadership → Leadership → Advisory → Advisory → Advisory + Position: Center → Center → Periphery → Periphery → Periphery + +Key Changes: + Apr 2023: Became Co-CEO, gained cross-company relations + Jul 2023: Transitioned to advisor, lost most direct reports + Oct 2023+: Stable advisory role +``` + +--- + +## Example 2: Therapeutic Progress (Temporal Analysis) + +### Scenario +Family therapist tracking a family constellation across 10 therapy sessions. + +### Timeline +``` +Session 1 → Session 3 → Session 5 → Session 7 → Session 10 +(Intake) (Early Work) (Breakthrough) (Integration) (Closure) +``` + +### State 1: Session 1 (Intake) +``` +Graph: + Family Members: + - Mother (Sarah) + - Father (John) + - Daughter (Emma, 16) + - Son (Michael, 12) + + Relations: + - Sarah ←conflict→ John (high intensity, red) + - Sarah ←protective→ Emma (strong, dashed) + - John ←distant→ Michael (weak, dotted) + - Emma ←tension→ Michael (medium, orange) + + Notes: "High conflict between parents, children taking sides, + triangulation patterns evident" +``` + +### State 2: Session 3 (Early Work) +``` +Changes from Session 1: + Modified: + - Sarah ↔ John: Conflict intensity reduced (high → medium) + - Sarah → Emma: Protective edge slightly weakened + + Added: + - John ↔ Emma: New communication edge (weak) + + Notes: "Parents beginning to communicate more directly, + Emma less involved in parental conflict" +``` + +### State 3: Session 5 (Breakthrough) +``` +Changes from Session 3: + Modified: + - Sarah ↔ John: Conflict edge changed to "communication" type + - John → Michael: Distant edge strengthened (engagement improving) + + Added: + - Family unit node (representing whole family identity) + - All members connected to family unit + + Notes: "Major breakthrough - parents able to discuss issues + without involving children. Family identity emerging." +``` + +### State 4: Session 7 (Integration) +``` +Changes from Session 5: + Modified: + - Sarah ↔ John: Communication edge strengthened + - Emma ↔ Michael: Tension edge changed to "sibling bond" + + Added: + - John → Emma: Communication edge strengthened + - Sarah → Michael: New supportive edge + + Notes: "Parents functioning as parental team. Sibling + relationship improving. Cross-generational boundaries + clearer." +``` + +### State 5: Session 10 (Closure) +``` +Changes from Session 7: + Modified: + - Sarah ↔ John: Strong partnership edge (blue, solid) + - All parent-child edges balanced and healthy + - Sibling edge strong and positive + + Removed: + - No conflict edges remaining + - Protective/distant edges normalized + + Notes: "Family system stabilized. Healthy boundaries, + effective communication, age-appropriate relationships. + Ready for termination." +``` + +### Animated Visualization + +**Frame-by-frame description:** +``` +Frame 1 (Session 1): + - Actors positioned with visible tension + - Conflict edge pulsing in red + - Protective edge thick and binding + +Frame 10 (Session 3): + - Conflict edge fading to orange + - New communication line appearing (fade in) + - Emma moving slightly away from protective orbit + +Frame 20 (Session 5): + - Family unit node appearing in center (fade in) + - Connections to family unit growing out + - All actors shifting toward center + +Frame 30 (Session 7): + - Sibling edge morphing from orange to blue + - Cross-connections strengthening + - Network becoming more interconnected + +Frame 40 (Session 10): + - All edges now healthy colors (blue, green) + - Balanced positioning + - Strong sense of unity and connection +``` + +### Actor Journey: Emma +``` +Timeline: + +Session 1 Session 3 Session 5 Session 7 Session 10 + ● ● ● ● ● + +Role: +Parentified Transitioning De-triangulated Teen Member Healthy Teen + +Position: +Between Moving out Periphery Appropriate Age-appropriate +parents of middle of conflict teen role teen role + +Key Relations: +Mother: Protective, enmeshed → Lessening → Normal parental → Healthy +Father: Distant → Emerging → Communicating → Connected +Brother: Tension → Neutral → Improving → Sibling bond + +Notes: + Session 1-3: Caught in parental conflict, taking mother's side + Session 5: Breakthrough allowed her to step out of middle + Session 7+: Re-established as teenager, not parent-proxy +``` + +--- + +## Example 3: Strategic Planning (Scenario Analysis) + +### Scenario +Tech startup exploring three growth strategies over 2 years. + +### Current State (Branching Point) +``` +Graph: + Team: 15 people + - 1 CEO + - 2 Co-founders (CTO, CPO) + - 3 Engineers + - 2 Designers + - 3 Sales + - 2 Marketing + - 2 Support + + Product: Single core product + Market: Single vertical +``` + +### Scenario A: Rapid Expansion +``` +Branch: "Strategy A - Rapid Expansion" +Assumptions: + - $5M Series A funding secured + - Aggressive hiring + - Market demand high + - Risk: Operational complexity + +Year 1: + Team: 35 people (+20) + - Added: 2 managers, 8 engineers, 4 sales, 3 marketing, 3 support + - Added: Product expansion team (5 people) + - Added: VP Sales node + +Year 2: + Team: 65 people (+30) + - Added: 3 managers, 15 engineers, 7 sales, 5 marketing + - Added: International team (8 people) + - Added: VP Engineering, VP Marketing nodes + - Network density: High complexity + +Outcome Analysis: + Strengths: Market capture, rapid growth, multiple products + Risks: Management overhead, coordination challenges, burn rate +``` + +### Scenario B: Focused Growth +``` +Branch: "Strategy B - Focused Growth" +Assumptions: + - $2M seed extension + - Selective hiring + - Deep vertical penetration + - Risk: Market saturation + +Year 1: + Team: 22 people (+7) + - Added: 3 engineers, 2 sales, 2 support + - Focus: Core product improvement + - Strengthened: Sales/customer relations + +Year 2: + Team: 30 people (+8) + - Added: 4 engineers, 2 sales, 2 marketing + - Added: Customer success team (3 people) + - Network density: Moderate, well-connected + +Outcome Analysis: + Strengths: Product excellence, customer loyalty, sustainable growth + Risks: Slower growth, single product dependency +``` + +### Scenario C: Pivot to Platform +``` +Branch: "Strategy C - Platform Pivot" +Assumptions: + - $3M funding + - Product architecture change + - Partner ecosystem + - Risk: Technical debt, market confusion + +Year 1: + Team: 28 people (+13) + - Added: Platform team (8 people: 5 engineers, 2 product, 1 architect) + - Added: Partner relations (2 people) + - Added: Developer advocacy (2 people) + - Restructured: Product org → Platform + Ecosystem + +Year 2: + Team: 42 people (+14) + - Added: Partner ecosystem (external nodes) + - Added: Developer community node + - Added: Integration team (5 people) + - Network: Extended beyond company (partners) + +Outcome Analysis: + Strengths: Ecosystem leverage, network effects, scalability + Risks: Complex coordination, dependency on partners +``` + +### Comparison: Scenario A vs B vs C (Year 2) + +**Side-by-side visualization:** +``` +┌──────────────────┬──────────────────┬──────────────────┐ +│ Scenario A │ Scenario B │ Scenario C │ +│ Rapid Expansion │ Focused Growth │ Platform Pivot │ +├──────────────────┼──────────────────┼──────────────────┤ +│ Team: 65 people │ Team: 30 people │ Team: 42 people │ +│ 5 VPs │ 3 Managers │ 4 Managers │ +│ High complexity │ Moderate density │ Extended network │ +│ │ │ │ +│ [Dense graph] │ [Tight graph] │ [Extended graph] │ +│ Many nodes │ Fewer nodes │ External nodes │ +│ Hierarchical │ Flat structure │ Hub-and-spoke │ +│ │ │ │ +│ Burn: High │ Burn: Low │ Burn: Medium │ +│ Revenue: High │ Revenue: Medium │ Revenue: Variable│ +│ Risk: Medium │ Risk: Low │ Risk: High │ +└──────────────────┴──────────────────┴──────────────────┘ +``` + +**Comparison metrics:** +``` +Metric Scenario A Scenario B Scenario C +───────────────────────────────────────────────────────────── +Team Size 65 30 42 +Management Layers 4 2 3 +Network Density 0.089 0.156 0.112* +Avg Connections/Person 5.8 4.7 6.3* +External Connections 5 8 24 +Products 3 1 Platform +Revenue Potential $15M $5M $8M +Risk Score 7/10 3/10 8/10 + +* Including external partner nodes +``` + +### Actor Journey: CTO (Across All Scenarios) + +**Scenario A (Rapid Expansion):** +``` +Current → Year 1 → Year 2 + +Role: + CTO (Hands-on) → VP Engineering → CTO (Strategic) + +Direct Reports: + 3 → 8 → 23 + +Focus: + Architecture → Team building → Organization leadership + +Network Position: + Central-technical → Central-management → Central-strategic + +Note: Increasingly removed from code, focus on scaling organization +``` + +**Scenario B (Focused Growth):** +``` +Current → Year 1 → Year 2 + +Role: + CTO (Hands-on) → CTO (Hands-on) → CTO (Technical Lead) + +Direct Reports: + 3 → 6 → 10 + +Focus: + Architecture → Product excellence → Technical depth + +Network Position: + Central-technical → Central-technical → Central-technical + +Note: Remains hands-on, deep technical involvement, leads by example +``` + +**Scenario C (Platform Pivot):** +``` +Current → Year 1 → Year 2 + +Role: + CTO (Hands-on) → CTO + Chief Architect → CTO (Ecosystem) + +Direct Reports: + 3 → 8 → 12 + +Focus: + Product → Platform architecture → External integrations + +Network Position: + Central-technical → Central-hub → Hub-to-external + +Note: Shifts to platform thinking, manages internal + external relations +``` + +**Comparison visualization:** +``` + Scenario A Scenario B Scenario C +Year 2: (Strategic) (Technical) (Ecosystem) + +Hands-on ●──────────●──────────●──────────● ●──────────● +Code: None Moderate Some + +Team Size: ●──────────●──────────●──────────● ●──────────● + Huge (23) Small (10) Medium (12) + +External ●──────────●──────────●──────────● ●──────────● +Focus: Low Low High + +Stress: ●──────────●──────────●──────────● ●──────────● + High Low Medium + +Job ●──────────●──────────●──────────● ●──────────● +Satisfaction: Medium High Medium +``` + +--- + +## Example 4: Timeline Scrubber Interaction + +### Visual Design +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Timeline: Company Evolution │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ↓ (You are here) │ +│ ●═══●═══●═══●═══●═══●═══●═══●═══●═══●═══●═══● │ +│ │ │ │ │ │ │ │ │ │ │ │ │ │ +│ J F M A M J J A S O N D │ +│ a e a p a u u u e c o e │ +│ n b r r y n l g p t v c │ +│ │ +│ ◀───────────────────────────────────────────────────────▶ │ +│ [Drag to scrub through timeline] │ +│ │ +│ Speed: [◀] [1x] [▶] Loop: [ ] Auto-play: [ ] │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Interaction States + +**Hover over state marker:** +``` + ● ← Marker highlights + ╱╲ + ╱ ╲ Tooltip appears: + ┌────────────────┐ + │ April 2023 │ + │ Q2 Review │ + │ 12 actors │ + │ 18 relations │ + │ Click to view │ + └────────────────┘ +``` + +**Click state marker:** +``` +Before: After: + ●───●───●───● ●───●───●───● + ↑ ↑ + (You are here) (You are here) + +Graph updates with transition animation: + - Actors fade out/in + - Actors move to new positions + - Relations appear/disappear + - Duration: 500ms +``` + +**Drag scrubber:** +``` +Dragging: + ●═══●═══●═══●═══● + ↑ ↑ ↑ ↑ + (scrubbing through intermediate frames) + + Graph continuously updates + Shows interpolated states + Smooth animation at 30fps +``` + +**Multi-select for comparison:** +``` +Click first state (Shift+Click): + ●═══●═══●═══●═══● + ✓ (selected) + +Click second state: + ●═══●═══●═══●═══● + ✓ ✓ + └───────┘ + (Range highlighted) + +Compare button appears: + [⚖ Compare Selected] +``` + +--- + +## Example 5: Diff Visualization Modes + +### Mode 1: Overlay Mode +``` +Original graph (State A) shown in gray/muted colors +Changes overlaid with highlighting: + + ┌─────────────────────────────────────┐ + │ │ + │ Alice │ + │ (gray) │ + │ │ │ + │ │ │ + │ ┌─┴─┐ │ + │ │ │ │ + │ Bob Carol │ + │ (gray)(gray) │ + │ │ + │ NEW! │ + │ ┌─────┐ │ + │ │ Dave │ ← Green border │ + │ └─────┘ │ + │ │ │ + │ │ ← Green edge │ + │ │ │ + │ Carol │ + │ │ + └─────────────────────────────────────┘ + +Legend: + ■ Green = Added + ■ Red = Removed (shown faded) + ■ Yellow = Modified + ■ Gray = Unchanged +``` + +### Mode 2: Side-by-Side Mode +``` +┌──────────────────────┬──────────────────────┐ +│ State A (Before) │ State B (After) │ +├──────────────────────┼──────────────────────┤ +│ │ │ +│ Alice │ Alice │ +│ │ │ │ │ +│ │ │ │ │ +│ ┌─┴─┐ │ ┌─┴─┐ │ +│ │ │ │ │ │ │ +│ Bob Carol │ Bob Carol │ +│ │ │ │ +│ │ │ │ +│ │ Dave │ +│ │ (green) │ +│ │ │ +│ Actors: 3 │ Actors: 4 │ +│ Relations: 2 │ Relations: 3 │ +└──────────────────────┴──────────────────────┘ + +Synchronized: Zoom and pan linked between both views +``` + +### Mode 3: Diff List View +``` +┌─────────────────────────────────────────────────────────┐ +│ Changes: State A → State B │ +├─────────────────────────────────────────────────────────┤ +│ │ +│ ADDED (1 actor, 1 relation) │ +│ ✓ Dave (Person) │ +│ ✓ Carol → Dave (Collaboration) │ +│ │ +│ REMOVED (0) │ +│ (none) │ +│ │ +│ MODIFIED (1 actor) │ +│ ○ Carol │ +│ • Position: (120, 80) → (180, 100) │ +│ • Connections: 1 → 2 │ +│ │ +│ UNCHANGED (2 actors, 2 relations) │ +│ [Collapse to hide] │ +│ │ +│ Summary: │ +│ Total changes: 3 │ +│ Actors affected: 2 (50%) │ +│ Relations affected: 1 (33%) │ +│ Network density change: +15% │ +└─────────────────────────────────────────────────────────┘ +``` + +### Mode 4: Animated Diff +``` +Animation sequence (10 frames, 2 seconds total): + +Frame 0 (State A): + Alice, Bob, Carol visible + +Frame 3: + Dave fades in (opacity 0.3) + +Frame 5: + Dave fully visible + New edge starts growing from Carol + +Frame 7: + Edge fully connected + Carol moves to new position (interpolated) + +Frame 10 (State B): + Final state + Highlighting fades out over 1 second + +Visual cues during animation: + - New elements pulse briefly + - Removed elements fade with red glow + - Modified elements highlighted during change +``` + +--- + +## Example 6: Actor Journey Visualization + +### Journey Timeline View +``` +Actor: Sarah Chen +Timeframe: January 2023 - December 2023 + +┌─────────────────────────────────────────────────────────────────┐ +│ │ +│ Jan Feb Mar Apr May Jun Jul Aug Sep │ +│ ●──────●──────●──────●──────●──────●──────●──────●──────● │ +│ │ │ │ │ │ │ │ │ │ │ +│ │ │ │ │ │ │ │ │ │ │ +│ Eng Eng Sr.Eng Sr.Eng Lead Lead Manager Manager Manager│ +│ │ +│ Connections: 3→3→4→5→6→7→8→10→12 │ +│ │ +│ Key Events: │ +│ Mar: Promotion to Senior Engineer │ +│ May: Promoted to Team Lead │ +│ Jul: Became Engineering Manager │ +│ Sep: Team expanded significantly │ +│ │ +│ Property Changes: │ +│ • Title changed: 3 times │ +│ • Direct reports: 0→0→2→2→4→4→8→8→8 │ +│ • Position: Center-left → Center (more central) │ +│ │ +│ Relationship Evolution: │ +│ • Peer relationships: 3→3→4→4→3→3→2→2→1 (declining) │ +│ • Managerial relations: 0→0→0→2→4→4→8→10→12 (growing) │ +│ • Cross-team relations: 0→0→1→2→3→5→6→8→10 (growing) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Journey Graph View +``` +Visual representation of Sarah's network evolution: + +January (Starting point): + [Sarah] + │ + ┌────┼────┐ + │ │ │ + [Tom][Ann][Lee] + (peers) + +May (Became Team Lead): + [Sarah] ← Now has direct reports + │ + ┌────┼────┬────┐ + │ │ │ │ + [Tom][Ann][Lee][New] + ↓ ↓ + [Jr1][Jr2] + +September (Engineering Manager): + [Sarah] + │ + ┌────────┼────────┐ + │ │ │ + [Lead1][Lead2][Lead3] + │ │ │ + ┌─┼─┐ ┌─┼─┐ ┌─┼─┐ + [T][T] [T][T] [T][T] + +Network metrics: + Betweenness centrality: 0.05 → 0.15 → 0.42 (×8.4 increase) + Degree centrality: 0.12 → 0.18 → 0.35 (×2.9 increase) + Closeness centrality: 0.25 → 0.32 → 0.48 (×1.9 increase) +``` + +--- + +## Example 7: Scenario Branching Visualization + +### Tree View +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Scenario Tree: Strategic Planning │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ Current State │ +│ (Jan 2024) │ +│ ● │ +│ │ │ +│ ┌──────────────┼──────────────┐ │ +│ │ │ │ │ +│ Scenario A Scenario B Scenario C │ +│ (Rapid Expand) (Focused) (Platform) │ +│ ● ● ● │ +│ │ │ │ │ +│ ┌────┼────┐ ┌────┴────┐ ┌────┴────┐ │ +│ │ │ │ │ │ │ │ │ +│ Q2 Q3 Q4 Q2 Q3 Q2 Q3 │ +│ ● ● ● ● ● ● ● │ +│ │ │ │ +│ Q4-A1 │ │ +│ ● ┌────┴────┐ │ +│ │ │ │ │ +│ Q4-A2 Q4-C1 Q4-C2 │ +│ ● ● ● │ +│ │ +│ Colors: │ +│ ● Blue = Scenario A branch │ +│ ● Green = Scenario B branch │ +│ ● Purple = Scenario C branch │ +│ ● Gray = Current reality │ +│ │ +│ [Expand All] [Collapse All] [Compare Scenarios] │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Timeline View with Branches +``` +┌─────────────────────────────────────────────────────────────────┐ +│ │ +│ ●─────●─────● Scenario A │ +│ / Q2 Q3 Q4 (Rapid) │ +│ / │ +│ ●────────Current────────● │ +│ Jan 2024 │ │ +│ \ │ +│ ●────● Scenario B │ +│ Q2 Q3 (Focused) │ +│ │ +│ ●─────●─────●─────● Scenario C │ +│ / Q2 Q3 Q4-C1 Q4-C2 (Platform) │ +│ / │ +│ / │ +│ ● │ +│ │ +│ Hover on branch to see: │ +│ • Scenario assumptions │ +│ • Key metrics comparison │ +│ • Probability/confidence │ +│ • Notes and rationale │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Example 8: Presentation Mode + +### Full-Screen Slideshow +``` +┌───────────────────────────────────────────────────────────────────┐ +│ │ +│ [F to toggle fullscreen] │ +│ │ +│ ┌───────────────────────────────────────────────────────────┐ │ +│ │ │ │ +│ │ Organizational Evolution │ │ +│ │ 2023-2024 │ │ +│ │ │ │ +│ │ │ │ +│ │ [Graph Visual] │ │ +│ │ │ │ +│ │ │ │ +│ │ │ │ +│ │ │ │ +│ │ Key Insights: │ │ +│ │ • Team grew by 80% over 12 months │ │ +│ │ • Network density increased significantly │ │ +│ │ • Leadership structure matured │ │ +│ │ │ │ +│ │ State 3 of 5 │ │ +│ └───────────────────────────────────────────────────────────┘ │ +│ │ +│ [◀ Previous] [⏸ Pause] [▶ Next] [Esc Exit] │ +│ │ +└───────────────────────────────────────────────────────────────────┘ + +Navigation: + - Arrow keys to navigate + - Space to play/pause + - Esc to exit presentation mode + - Number keys to jump to slide + - 'R' to restart from beginning +``` + +--- + +## Summary + +These examples demonstrate: + +1. **Temporal Evolution**: How organizations, families, and systems change over time +2. **Scenario Exploration**: Comparing alternative futures with different assumptions +3. **Visual Comparison**: Multiple ways to see and understand differences +4. **Actor Tracking**: Following individuals through changes +5. **Interactive Timeline**: Scrubbing, clicking, and animating through states +6. **Presentation**: Telling compelling stories with data + +All visualizations should be: +- **Interactive**: Click, drag, hover for more information +- **Animated**: Smooth transitions between states +- **Informative**: Rich metadata and context +- **Exportable**: Save as images, videos, or reports +- **Responsive**: Work on different screen sizes + +The goal is to make temporal and scenario analysis intuitive, visual, and powerful for storytelling and analysis. diff --git a/src/App.tsx b/src/App.tsx index 7239975..bfbe9a2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,6 +3,7 @@ import { ReactFlowProvider, useReactFlow } from "reactflow"; import GraphEditor from "./components/Editor/GraphEditor"; import LeftPanel from "./components/Panels/LeftPanel"; import RightPanel from "./components/Panels/RightPanel"; +import BottomPanel from "./components/Timeline/BottomPanel"; import DocumentTabs from "./components/Workspace/DocumentTabs"; import Toolbar from "./components/Toolbar/Toolbar"; import MenuBar from "./components/Menu/MenuBar"; @@ -41,7 +42,7 @@ import type { ExportOptions } from "./utils/graphExport"; function AppContent() { const { undo, redo } = useDocumentHistory(); const { activeDocumentId } = useWorkspaceStore(); - const { leftPanelVisible, rightPanelVisible } = usePanelStore(); + const { leftPanelVisible, rightPanelVisible, bottomPanelVisible } = usePanelStore(); const { handleNewDocument, NewDocumentDialog } = useCreateDocument(); const [showDocumentManager, setShowDocumentManager] = useState(false); const [showKeyboardHelp, setShowKeyboardHelp] = useState(false); @@ -141,63 +142,71 @@ function AppContent() { {/* Toolbar */} {activeDocumentId && } - {/* Main content area with side panels */} -
- {/* Left Panel */} - {leftPanelVisible && activeDocumentId && ( - { - setSelectedNode(null); - setSelectedEdge(null); - }} - onAddNode={addNodeCallback || undefined} - /> - )} - - {/* Center: Graph Editor */} -
- { - setSelectedNode(node); - // Only clear edge if we're setting a node (not clearing) - if (node) { - setSelectedEdge(null); - } - }} - onEdgeSelect={(edge) => { - setSelectedEdge(edge); - // Only clear node if we're setting an edge (not clearing) - if (edge) { + {/* Main content area with side panels and bottom panel */} +
+ {/* Top section: Left panel, graph editor, right panel */} +
+ {/* Left Panel */} + {leftPanelVisible && activeDocumentId && ( + { setSelectedNode(null); - } - }} - onAddNodeRequest={( - callback: ( - nodeTypeId: string, - position?: { x: number; y: number }, - ) => void, - ) => setAddNodeCallback(() => callback)} - onExportRequest={( - callback: ( - format: "png" | "svg", - options?: ExportOptions, - ) => Promise, - ) => setExportCallback(() => callback)} - /> + setSelectedEdge(null); + }} + onAddNode={addNodeCallback || undefined} + /> + )} + + {/* Center: Graph Editor */} +
+ { + setSelectedNode(node); + // Only clear edge if we're setting a node (not clearing) + if (node) { + setSelectedEdge(null); + } + }} + onEdgeSelect={(edge) => { + setSelectedEdge(edge); + // Only clear node if we're setting an edge (not clearing) + if (edge) { + setSelectedNode(null); + } + }} + onAddNodeRequest={( + callback: ( + nodeTypeId: string, + position?: { x: number; y: number }, + ) => void, + ) => setAddNodeCallback(() => callback)} + onExportRequest={( + callback: ( + format: "png" | "svg", + options?: ExportOptions, + ) => Promise, + ) => setExportCallback(() => callback)} + /> +
+ + {/* Right Panel */} + {rightPanelVisible && activeDocumentId && ( + { + setSelectedNode(null); + setSelectedEdge(null); + }} + /> + )}
- {/* Right Panel */} - {rightPanelVisible && activeDocumentId && ( - { - setSelectedNode(null); - setSelectedEdge(null); - }} - /> + {/* Bottom Panel (Timeline) - show when bottomPanelVisible and there's an active document */} + {bottomPanelVisible && activeDocumentId && ( + )}
diff --git a/src/components/Editor/ContextMenu.tsx b/src/components/Editor/ContextMenu.tsx index d6921a0..47bdfe2 100644 --- a/src/components/Editor/ContextMenu.tsx +++ b/src/components/Editor/ContextMenu.tsx @@ -56,6 +56,44 @@ const ContextMenu = ({ x, y, sections, onClose }: Props) => { }; }, [onClose]); + // Adjust position to prevent overflow + useEffect(() => { + if (menuRef.current) { + const rect = menuRef.current.getBoundingClientRect(); + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; + + let adjustedX = x; + let adjustedY = y; + + // Check right edge overflow + if (rect.right > viewportWidth) { + adjustedX = viewportWidth - rect.width - 10; + } + + // Check bottom edge overflow + if (rect.bottom > viewportHeight) { + adjustedY = viewportHeight - rect.height - 10; + } + + // Check left edge overflow + if (adjustedX < 10) { + adjustedX = 10; + } + + // Check top edge overflow + if (adjustedY < 10) { + adjustedY = 10; + } + + // Apply adjusted position + if (adjustedX !== x || adjustedY !== y) { + menuRef.current.style.left = `${adjustedX}px`; + menuRef.current.style.top = `${adjustedY}px`; + } + } + }, [x, y]); + return (
{ + const { + bottomPanelHeight, + bottomPanelCollapsed, + setBottomPanelHeight, + collapseBottomPanel, + expandBottomPanel, + } = usePanelStore(); + + const { activeDocumentId } = useWorkspaceStore(); + const { timelines, getAllStates } = useTimelineStore(); + + const [isResizing, setIsResizing] = useState(false); + const [showCreateState, setShowCreateState] = useState(false); + + const hasTimeline = activeDocumentId ? timelines.has(activeDocumentId) : false; + const currentState = hasTimeline ? getAllStates().find(s => { + const timeline = timelines.get(activeDocumentId!); + return timeline && s.id === timeline.currentStateId; + }) : null; + + const handleMouseDown = useCallback((e: React.MouseEvent) => { + e.preventDefault(); + setIsResizing(true); + }, []); + + const handleMouseMove = useCallback( + (e: MouseEvent) => { + if (!isResizing) return; + + const windowHeight = window.innerHeight; + const newHeight = windowHeight - e.clientY; + + const clampedHeight = Math.max( + PANEL_CONSTANTS.MIN_BOTTOM_HEIGHT, + Math.min(PANEL_CONSTANTS.MAX_BOTTOM_HEIGHT, newHeight) + ); + + setBottomPanelHeight(clampedHeight); + }, + [isResizing, setBottomPanelHeight] + ); + + const handleMouseUp = useCallback(() => { + setIsResizing(false); + }, []); + + // Add/remove event listeners for resize + useEffect(() => { + if (isResizing) { + window.addEventListener('mousemove', handleMouseMove); + window.addEventListener('mouseup', handleMouseUp); + return () => { + window.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('mouseup', handleMouseUp); + }; + } + }, [isResizing, handleMouseMove, handleMouseUp]); + + const displayHeight = bottomPanelCollapsed + ? PANEL_CONSTANTS.COLLAPSED_BOTTOM_HEIGHT + : bottomPanelHeight; + + return ( +
+ {/* Resize Handle */} + {!bottomPanelCollapsed && ( +
+ )} + + {/* Header */} +
+
+

Timeline

+ + {/* Current State Indicator */} + {currentState && !bottomPanelCollapsed && ( + <> + +
+ Current: + {currentState.label} +
+ + )} +
+ +
+ {/* Timeline Controls - Only show when expanded */} + {!bottomPanelCollapsed && activeDocumentId && hasTimeline && ( + <> + + setShowCreateState(true)} size="small"> + + + + + + + )} + + {/* Collapse/Expand Button */} + + + {bottomPanelCollapsed ? : } + + +
+
+ + {/* Create State Dialog */} + setShowCreateState(false)} + /> + + {/* Content - Only show when not collapsed */} + {!bottomPanelCollapsed && ( +
+ +
+ )} +
+ ); +}; + +export default BottomPanel; diff --git a/src/components/Timeline/CreateStateDialog.tsx b/src/components/Timeline/CreateStateDialog.tsx new file mode 100644 index 0000000..41537d9 --- /dev/null +++ b/src/components/Timeline/CreateStateDialog.tsx @@ -0,0 +1,99 @@ +import React, { useState } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + TextField, + Button, + FormControlLabel, + Checkbox, +} from '@mui/material'; +import { useTimelineStore } from '../../stores/timelineStore'; + +interface CreateStateDialogProps { + open: boolean; + onClose: () => void; +} + +/** + * Dialog for creating a new constellation state + */ +const CreateStateDialog: React.FC = ({ open, onClose }) => { + const [label, setLabel] = useState(''); + const [description, setDescription] = useState(''); + const [cloneFromCurrent, setCloneFromCurrent] = useState(true); + + const { createState } = useTimelineStore(); + + const handleCreate = () => { + if (!label.trim()) return; + + createState(label.trim(), description.trim() || undefined, cloneFromCurrent); + + // Reset form + setLabel(''); + setDescription(''); + setCloneFromCurrent(true); + onClose(); + }; + + const handleClose = () => { + // Reset form on cancel + setLabel(''); + setDescription(''); + setCloneFromCurrent(true); + onClose(); + }; + + return ( + + Create New State + +
+ setLabel(e.target.value)} + placeholder="e.g., 'January 2024' or 'Strategy A'" + helperText="Give this state a descriptive name" + /> + + setDescription(e.target.value)} + placeholder="Optional notes about this state..." + /> + + setCloneFromCurrent(e.target.checked)} + /> + } + label="Clone current graph (uncheck for empty state)" + /> +
+
+ + + + +
+ ); +}; + +export default CreateStateDialog; diff --git a/src/components/Timeline/RenameStateDialog.tsx b/src/components/Timeline/RenameStateDialog.tsx new file mode 100644 index 0000000..9ca4606 --- /dev/null +++ b/src/components/Timeline/RenameStateDialog.tsx @@ -0,0 +1,79 @@ +import React, { useState, useEffect } from 'react'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + TextField, +} from '@mui/material'; + +interface RenameStateDialogProps { + open: boolean; + currentLabel: string; + onClose: () => void; + onRename: (newLabel: string) => void; +} + +/** + * RenameStateDialog - Dialog for renaming timeline states + */ +const RenameStateDialog: React.FC = ({ + open, + currentLabel, + onClose, + onRename, +}) => { + const [label, setLabel] = useState(currentLabel); + + // Update label when currentLabel changes + useEffect(() => { + setLabel(currentLabel); + }, [currentLabel]); + + const handleRename = () => { + if (label.trim()) { + onRename(label.trim()); + onClose(); + } + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.preventDefault(); + handleRename(); + } + }; + + return ( + + Rename State + + setLabel(e.target.value)} + onKeyDown={handleKeyDown} + placeholder="Enter state label" + /> + + + + + + + ); +}; + +export default RenameStateDialog; diff --git a/src/components/Timeline/StateNode.tsx b/src/components/Timeline/StateNode.tsx new file mode 100644 index 0000000..361efb5 --- /dev/null +++ b/src/components/Timeline/StateNode.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import { Handle, Position, NodeProps } from 'reactflow'; +import type { ConstellationState } from '../../types/timeline'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; + +interface StateNodeData { + state: ConstellationState; + isCurrent: boolean; +} + +/** + * StateNode - Custom node for timeline visualization + */ +const StateNode: React.FC> = ({ data, selected }) => { + const { state, isCurrent } = data; + + // Format date if present + const dateStr = state.metadata?.date + ? new Date(state.metadata.date).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + }) + : null; + + // Get custom color or default + const color = state.metadata?.color || '#3b82f6'; + + return ( +
+ {/* Handles for connections */} + + + + {/* Content */} +
+ {isCurrent && ( + + )} +
+
+ {state.label} +
+ {dateStr && ( +
{dateStr}
+ )} + {state.description && ( +
+ {state.description} +
+ )} + {state.metadata?.tags && state.metadata.tags.length > 0 && ( +
+ {state.metadata.tags.slice(0, 2).map((tag) => ( + + {tag} + + ))} + {state.metadata.tags.length > 2 && ( + + +{state.metadata.tags.length - 2} + + )} +
+ )} +
+
+
+ ); +}; + +export default StateNode; diff --git a/src/components/Timeline/TimelineView.tsx b/src/components/Timeline/TimelineView.tsx new file mode 100644 index 0000000..6e51962 --- /dev/null +++ b/src/components/Timeline/TimelineView.tsx @@ -0,0 +1,371 @@ +import React, { useMemo, useCallback, useState } from 'react'; +import ReactFlow, { + Background, + Controls, + NodeTypes, + Node, + Edge, + useNodesState, + useEdgesState, + BackgroundVariant, + ReactFlowProvider, +} from 'reactflow'; +import 'reactflow/dist/style.css'; +import { useTimelineStore } from '../../stores/timelineStore'; +import { useWorkspaceStore } from '../../stores/workspaceStore'; +import StateNode from './StateNode'; +import ContextMenu from '../Editor/ContextMenu'; +import RenameStateDialog from './RenameStateDialog'; +import EditIcon from '@mui/icons-material/Edit'; +import FileCopyIcon from '@mui/icons-material/FileCopy'; +import CallSplitIcon from '@mui/icons-material/CallSplit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import type { ConstellationState, StateId } from '../../types/timeline'; + +/** + * Layout states in a horizontal timeline with branches + */ +function layoutStates( + states: ConstellationState[], + currentStateId: StateId, + rootStateId: StateId +): { nodes: Node[]; edges: Edge[] } { + const horizontalSpacing = 200; + const verticalSpacing = 100; + + // Build parent-child relationships + const children = new Map(); + states.forEach((state) => { + if (state.parentStateId) { + if (!children.has(state.parentStateId)) { + children.set(state.parentStateId, []); + } + children.get(state.parentStateId)!.push(state.id); + } + }); + + // BFS to assign horizontal positions (level in tree) + const levels = new Map(); + const queue: StateId[] = [rootStateId]; + levels.set(rootStateId, 0); + + while (queue.length > 0) { + const stateId = queue.shift()!; + const level = levels.get(stateId)!; + + const childStates = children.get(stateId) || []; + childStates.forEach((childId) => { + if (!levels.has(childId)) { + levels.set(childId, level + 1); + queue.push(childId); + } + }); + } + + // Assign vertical lanes for branches + const lanes = new Map(); + let currentLane = 0; + + function assignLanes(stateId: StateId, lane: number) { + lanes.set(stateId, lane); + + const childStates = children.get(stateId) || []; + if (childStates.length === 0) return; + + // First child continues in same lane + assignLanes(childStates[0], lane); + + // Additional children get new lanes + for (let i = 1; i < childStates.length; i++) { + currentLane++; + assignLanes(childStates[i], currentLane); + } + } + + assignLanes(rootStateId, 0); + + // Create nodes + const nodes: Node[] = states.map((state) => { + const level = levels.get(state.id) || 0; + const lane = lanes.get(state.id) || 0; + + return { + id: state.id, + type: 'stateNode', + position: { + x: level * horizontalSpacing, + y: lane * verticalSpacing, + }, + data: { + state, + isCurrent: state.id === currentStateId, + }, + }; + }); + + // Create edges + const edges: Edge[] = []; + states.forEach((state) => { + if (state.parentStateId) { + edges.push({ + id: `${state.parentStateId}-${state.id}`, + source: state.parentStateId, + target: state.id, + type: 'smoothstep', + animated: state.id === currentStateId, + style: { + strokeWidth: state.id === currentStateId ? 3 : 2, + stroke: state.id === currentStateId ? '#10b981' : '#9ca3af', + }, + }); + } + }); + + return { nodes, edges }; +} + +/** + * TimelineViewInner - Inner component with React Flow + */ +const TimelineViewInner: React.FC = () => { + const activeDocumentId = useWorkspaceStore((state) => state.activeDocumentId); + const { timelines, switchToState, updateState, duplicateState, duplicateStateAsChild, deleteState } = useTimelineStore(); + + const timeline = activeDocumentId ? timelines.get(activeDocumentId) : null; + + // Context menu state + const [contextMenu, setContextMenu] = useState<{ + x: number; + y: number; + stateId: string; + } | null>(null); + + // Rename dialog state + const [renameDialog, setRenameDialog] = useState<{ + stateId: string; + currentLabel: string; + } | null>(null); + + // Get all states + const states = useMemo(() => { + if (!timeline) return []; + return Array.from(timeline.states.values()); + }, [timeline]); + + // Layout nodes and edges + const { nodes: layoutNodes, edges: layoutEdges } = useMemo(() => { + if (!timeline || states.length === 0) { + return { nodes: [], edges: [] }; + } + + return layoutStates(states, timeline.currentStateId, timeline.rootStateId); + }, [states, timeline]); + + // React Flow state + const [nodes, setNodes, onNodesChange] = useNodesState(layoutNodes); + const [edges, setEdges, onEdgesChange] = useEdgesState(layoutEdges); + + // Update when layout changes + React.useEffect(() => { + setNodes(layoutNodes); + setEdges(layoutEdges); + }, [layoutNodes, layoutEdges, setNodes, setEdges]); + + // Handle node click - switch to state + const handleNodeClick = useCallback( + (_event: React.MouseEvent, node: Node) => { + switchToState(node.id); + setContextMenu(null); // Close context menu if open + }, + [switchToState] + ); + + // Handle pane click - close context menu + const handlePaneClick = useCallback(() => { + if (contextMenu) { + setContextMenu(null); + } + }, [contextMenu]); + + // Handle node context menu + const handleNodeContextMenu = useCallback( + (event: React.MouseEvent, node: Node) => { + event.preventDefault(); + setContextMenu({ + x: event.clientX, + y: event.clientY, + stateId: node.id, + }); + }, + [] + ); + + // Context menu actions + const handleRenameFromMenu = useCallback(() => { + if (!contextMenu) return; + const state = timeline?.states.get(contextMenu.stateId); + if (state) { + setRenameDialog({ + stateId: contextMenu.stateId, + currentLabel: state.label, + }); + } + setContextMenu(null); + }, [contextMenu, timeline]); + + // Duplicate (Parallel): Creates sibling state with same parent + const handleDuplicateParallelFromMenu = useCallback(() => { + if (!contextMenu) return; + duplicateState(contextMenu.stateId); + setContextMenu(null); + }, [contextMenu, duplicateState]); + + // Duplicate (Series): Creates child state with original as parent + const handleDuplicateSeriesFromMenu = useCallback(() => { + if (!contextMenu) return; + duplicateStateAsChild(contextMenu.stateId); + setContextMenu(null); + }, [contextMenu, duplicateStateAsChild]); + + const handleDeleteFromMenu = useCallback(() => { + if (!contextMenu) return; + deleteState(contextMenu.stateId); + setContextMenu(null); + }, [contextMenu, deleteState]); + + // Rename dialog actions + const handleRename = useCallback( + (newLabel: string) => { + if (renameDialog) { + updateState(renameDialog.stateId, { label: newLabel }); + } + }, + [renameDialog, updateState] + ); + + // Custom node types + const nodeTypes: NodeTypes = useMemo( + () => ({ + stateNode: StateNode, + }), + [] + ); + + if (!timeline) { + return ( +
+
+

No timeline for this document.

+

Create a timeline to manage multiple states.

+
+
+ ); + } + + if (states.length === 0) { + return ( +
+ No states in timeline +
+ ); + } + + return ( +
e.stopPropagation()}> + + + + + + {/* Context Menu */} + {contextMenu && ( + , + onClick: handleRenameFromMenu, + }, + ], + }, + { + title: 'Duplicate', + actions: [ + { + label: 'Duplicate (Parallel)', + icon: , + onClick: handleDuplicateParallelFromMenu, + }, + { + label: 'Duplicate (Series)', + icon: , + onClick: handleDuplicateSeriesFromMenu, + }, + ], + }, + { + actions: [ + { + label: 'Delete', + icon: , + onClick: handleDeleteFromMenu, + }, + ], + }, + ]} + onClose={() => setContextMenu(null)} + /> + )} + + {/* Rename Dialog */} + {renameDialog && ( + setRenameDialog(null)} + onRename={handleRename} + /> + )} +
+ ); +}; + +/** + * TimelineView - Wrapped with its own ReactFlowProvider to avoid conflicts + */ +const TimelineView: React.FC = () => { + return ( + + + + ); +}; + +export default TimelineView; diff --git a/src/hooks/useDocumentHistory.ts b/src/hooks/useDocumentHistory.ts index b3cbc7c..928199a 100644 --- a/src/hooks/useDocumentHistory.ts +++ b/src/hooks/useDocumentHistory.ts @@ -2,23 +2,22 @@ import { useCallback, useEffect } from 'react'; import { useWorkspaceStore } from '../stores/workspaceStore'; import { useHistoryStore } from '../stores/historyStore'; import { useGraphStore } from '../stores/graphStore'; -import type { ConstellationDocument } from '../stores/persistence/types'; -import { createDocument } from '../stores/persistence/saver'; +import { useTimelineStore } from '../stores/timelineStore'; +import type { GraphSnapshot } from '../stores/historyStore'; /** * useDocumentHistory Hook * - * Provides undo/redo functionality for the active document. - * Each document has its own independent history stack (max 50 actions). + * Provides undo/redo functionality for the active timeline state. + * Each timeline state has its own independent history stack (max 50 actions). * - * IMPORTANT: History is per-document. Switching documents maintains separate undo/redo stacks. + * IMPORTANT: History is per-timeline-state. Each state in a document's timeline has completely separate undo/redo stacks. * * Usage: * const { undo, redo, canUndo, canRedo, pushToHistory } = useDocumentHistory(); */ export function useDocumentHistory() { const activeDocumentId = useWorkspaceStore((state) => state.activeDocumentId); - const getActiveDocument = useWorkspaceStore((state) => state.getActiveDocument); const markDocumentDirty = useWorkspaceStore((state) => state.markDocumentDirty); const setNodes = useGraphStore((state) => state.setNodes); @@ -28,175 +27,144 @@ export function useDocumentHistory() { const historyStore = useHistoryStore(); - // Initialize history for active document - useEffect(() => { - if (!activeDocumentId) return; + // Get current timeline state ID + const currentStateId = useTimelineStore((state) => { + if (!activeDocumentId) return null; + const timeline = state.timelines.get(activeDocumentId); + return timeline?.currentStateId || null; + }); - const history = historyStore.histories.get(activeDocumentId); + // Initialize history for active timeline state + useEffect(() => { + if (!currentStateId) return; + + const history = historyStore.histories.get(currentStateId); if (!history) { - const currentDoc = getActiveDocument(); - if (currentDoc) { - historyStore.initializeHistory(activeDocumentId, currentDoc); - } + historyStore.initializeHistory(currentStateId); } - }, [activeDocumentId, historyStore, getActiveDocument]); + }, [currentStateId, historyStore]); /** * Push current graph state to history */ const pushToHistory = useCallback( (description: string) => { - if (!activeDocumentId) { - console.warn('No active document to record action'); + if (!currentStateId) { + console.warn('No active timeline state to record action'); return; } // Read current state directly from store (not from React hooks which might be stale) const currentState = useGraphStore.getState(); - const currentDoc = getActiveDocument(); - if (!currentDoc) { - console.warn('Active document not loaded, attempting to use current graph state'); - // If document isn't loaded yet, create a minimal snapshot from current state - const snapshot: ConstellationDocument = createDocument( - currentState.nodes as never[], - currentState.edges as never[], - currentState.nodeTypes, - currentState.edgeTypes - ); - - // Use minimal metadata - snapshot.metadata = { - documentId: activeDocumentId, - title: 'Untitled', - version: '1.0.0', - appName: 'Constellation Analyzer', - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - lastSavedBy: 'user', - }; - - // Push to history - historyStore.pushAction(activeDocumentId, { - description, - timestamp: Date.now(), - documentState: snapshot, - }); - return; - } - - // Create a snapshot of the current state - const snapshot: ConstellationDocument = createDocument( - currentState.nodes as never[], - currentState.edges as never[], - currentState.nodeTypes, - currentState.edgeTypes - ); - - // Copy metadata from current document - snapshot.metadata = { - ...currentDoc.metadata, - updatedAt: new Date().toISOString(), + // Create a snapshot of the current graph state + const snapshot: GraphSnapshot = { + nodes: currentState.nodes, + edges: currentState.edges, + nodeTypes: currentState.nodeTypes, + edgeTypes: currentState.edgeTypes, }; // Push to history - historyStore.pushAction(activeDocumentId, { + historyStore.pushAction(currentStateId, { description, timestamp: Date.now(), - documentState: snapshot, + graphState: snapshot, }); }, - [activeDocumentId, historyStore, getActiveDocument] + [currentStateId, historyStore] ); /** - * Undo the last action for the active document + * Undo the last action for the active timeline state */ const undo = useCallback(() => { - if (!activeDocumentId) { - console.warn('No active document to undo'); + if (!currentStateId || !activeDocumentId) { + console.warn('No active timeline state to undo'); return; } - const restoredState = historyStore.undo(activeDocumentId); + const restoredState = historyStore.undo(currentStateId); if (restoredState) { // Update graph store with restored state - setNodes(restoredState.graph.nodes as never[]); - setEdges(restoredState.graph.edges as never[]); - setNodeTypes(restoredState.graph.nodeTypes); - setEdgeTypes(restoredState.graph.edgeTypes); + setNodes(restoredState.nodes as never[]); + setEdges(restoredState.edges as never[]); + setNodeTypes(restoredState.nodeTypes as never[]); + setEdgeTypes(restoredState.edgeTypes as never[]); - // Update workspace document - const { documents, saveDocument } = useWorkspaceStore.getState(); - const newDocuments = new Map(documents); - newDocuments.set(activeDocumentId, restoredState); - useWorkspaceStore.setState({ documents: newDocuments }); + // Update the timeline's current state with the restored graph (nodes and edges only) + useTimelineStore.getState().saveCurrentGraph({ + nodes: restoredState.nodes as never[], + edges: restoredState.edges as never[], + }); // Mark document as dirty and trigger auto-save markDocumentDirty(activeDocumentId); // Auto-save after a short delay + const { saveDocument } = useWorkspaceStore.getState(); setTimeout(() => { saveDocument(activeDocumentId); }, 1000); } - }, [activeDocumentId, historyStore, setNodes, setEdges, setNodeTypes, setEdgeTypes, markDocumentDirty]); + }, [currentStateId, activeDocumentId, historyStore, setNodes, setEdges, setNodeTypes, setEdgeTypes, markDocumentDirty]); /** - * Redo the last undone action for the active document + * Redo the last undone action for the active timeline state */ const redo = useCallback(() => { - if (!activeDocumentId) { - console.warn('No active document to redo'); + if (!currentStateId || !activeDocumentId) { + console.warn('No active timeline state to redo'); return; } - const restoredState = historyStore.redo(activeDocumentId); + const restoredState = historyStore.redo(currentStateId); if (restoredState) { // Update graph store with restored state - setNodes(restoredState.graph.nodes as never[]); - setEdges(restoredState.graph.edges as never[]); - setNodeTypes(restoredState.graph.nodeTypes); - setEdgeTypes(restoredState.graph.edgeTypes); + setNodes(restoredState.nodes as never[]); + setEdges(restoredState.edges as never[]); + setNodeTypes(restoredState.nodeTypes as never[]); + setEdgeTypes(restoredState.edgeTypes as never[]); - // Update workspace document - const { documents, saveDocument } = useWorkspaceStore.getState(); - const newDocuments = new Map(documents); - newDocuments.set(activeDocumentId, restoredState); - useWorkspaceStore.setState({ documents: newDocuments }); + // Update the timeline's current state with the restored graph (nodes and edges only) + useTimelineStore.getState().saveCurrentGraph({ + nodes: restoredState.nodes as never[], + edges: restoredState.edges as never[], + }); // Mark document as dirty and trigger auto-save markDocumentDirty(activeDocumentId); // Auto-save after a short delay + const { saveDocument } = useWorkspaceStore.getState(); setTimeout(() => { saveDocument(activeDocumentId); }, 1000); } - }, [activeDocumentId, historyStore, setNodes, setEdges, setNodeTypes, setEdgeTypes, markDocumentDirty]); + }, [currentStateId, activeDocumentId, historyStore, setNodes, setEdges, setNodeTypes, setEdgeTypes, markDocumentDirty]); /** - * Check if undo is available for the active document + * Check if undo is available for the active timeline state */ - const canUndo = activeDocumentId ? historyStore.canUndo(activeDocumentId) : false; + const canUndo = currentStateId ? historyStore.canUndo(currentStateId) : false; /** - * Check if redo is available for the active document + * Check if redo is available for the active timeline state */ - const canRedo = activeDocumentId ? historyStore.canRedo(activeDocumentId) : false; + const canRedo = currentStateId ? historyStore.canRedo(currentStateId) : false; /** * Get the description of the next undo action */ - const undoDescription = activeDocumentId - ? historyStore.getUndoDescription(activeDocumentId) + const undoDescription = currentStateId + ? historyStore.getUndoDescription(currentStateId) : null; /** * Get the description of the next redo action */ - const redoDescription = activeDocumentId - ? historyStore.getRedoDescription(activeDocumentId) + const redoDescription = currentStateId + ? historyStore.getRedoDescription(currentStateId) : null; return { diff --git a/src/stores/graphStore.ts b/src/stores/graphStore.ts index 9e49e17..2d4a724 100644 --- a/src/stores/graphStore.ts +++ b/src/stores/graphStore.ts @@ -8,7 +8,6 @@ import type { RelationData, GraphActions } from '../types'; -import { persistenceMiddleware } from './persistence/middleware'; import { loadGraphState } from './persistence/loader'; import { exportGraphToFile, selectFileForImport } from './persistence/fileIO'; @@ -72,12 +71,11 @@ const loadInitialState = (): GraphStore => { const initialState = loadInitialState(); -export const useGraphStore = create( - persistenceMiddleware((set) => ({ - nodes: initialState.nodes, - edges: initialState.edges, - nodeTypes: initialState.nodeTypes, - edgeTypes: initialState.edgeTypes, +export const useGraphStore = create((set) => ({ + nodes: initialState.nodes, + edges: initialState.edges, + nodeTypes: initialState.nodeTypes, + edgeTypes: initialState.edgeTypes, // Node operations addNode: (node: Actor) => @@ -224,4 +222,4 @@ export const useGraphStore = create( nodeTypes: data.nodeTypes, edgeTypes: data.edgeTypes, }), -}))); +})); diff --git a/src/stores/historyStore.ts b/src/stores/historyStore.ts index b1ca581..9094485 100644 --- a/src/stores/historyStore.ts +++ b/src/stores/historyStore.ts @@ -1,71 +1,75 @@ import { create } from "zustand"; -import type { ConstellationDocument } from "./persistence/types"; import { useGraphStore } from "./graphStore"; +import type { Actor, Relation, NodeTypeConfig, EdgeTypeConfig } from "../types"; /** - * History Store - Per-Document Undo/Redo System + * History Store - Per-Timeline-State Undo/Redo System * - * Each document maintains its own independent history stack with a maximum of 50 actions. + * Each timeline state maintains its own independent history stack with a maximum of 50 actions. * Tracks all reversible operations: node add/delete/move, edge add/delete/edit, type changes. * - * IMPORTANT: History is per-document. Each document has completely separate undo/redo stacks. + * IMPORTANT: History is per-timeline-state. Each state in a document's timeline has completely separate undo/redo stacks. */ +export interface GraphSnapshot { + nodes: Actor[]; + edges: Relation[]; + nodeTypes: NodeTypeConfig[]; + edgeTypes: EdgeTypeConfig[]; +} + export interface HistoryAction { description: string; // Human-readable description (e.g., "Add Person Actor", "Delete Collaborates Relation") timestamp: number; // When the action occurred - documentState: ConstellationDocument; // Complete document state after this action + graphState: GraphSnapshot; // Graph state snapshot (not full document) } -export interface DocumentHistory { +export interface StateHistory { undoStack: HistoryAction[]; // Past states to restore (most recent at end) redoStack: HistoryAction[]; // Future states to restore (most recent at end) } interface HistoryStore { - // Map of documentId -> history (each document has its own independent history) - histories: Map; + // Map of stateId -> history (each timeline state has its own independent history) + histories: Map; - // Max number of actions to keep in history per document + // Max number of actions to keep in history per state maxHistorySize: number; } interface HistoryActions { - // Initialize history for a document - initializeHistory: ( - documentId: string, - initialState: ConstellationDocument, - ) => void; + // Initialize history for a timeline state + initializeHistory: (stateId: string) => void; - // Push a new action onto the document's history stack - pushAction: (documentId: string, action: HistoryAction) => void; + // Push a new action onto the state's history stack + pushAction: (stateId: string, action: HistoryAction) => void; - // Undo the last action for a specific document - undo: (documentId: string) => ConstellationDocument | null; + // Undo the last action for a specific state + undo: (stateId: string) => GraphSnapshot | null; - // Redo the last undone action for a specific document - redo: (documentId: string) => ConstellationDocument | null; + // Redo the last undone action for a specific state + redo: (stateId: string) => GraphSnapshot | null; - // Check if undo is available for a document - canUndo: (documentId: string) => boolean; + // Check if undo is available for a state + canUndo: (stateId: string) => boolean; - // Check if redo is available for a document - canRedo: (documentId: string) => boolean; + // Check if redo is available for a state + canRedo: (stateId: string) => boolean; - // Get the description of the next undo action for a document - getUndoDescription: (documentId: string) => string | null; + // Get the description of the next undo action for a state + getUndoDescription: (stateId: string) => string | null; - // Get the description of the next redo action for a document - getRedoDescription: (documentId: string) => string | null; + // Get the description of the next redo action for a state + getRedoDescription: (stateId: string) => string | null; - // Clear history for a specific document - clearHistory: (documentId: string) => void; + // Clear history for a specific state + clearHistory: (stateId: string) => void; - // Remove history for a document (when document is deleted) - removeHistory: (documentId: string) => void; + // Remove history for a state (when state is deleted) + removeHistory: (stateId: string) => void; // Get history stats for debugging - getHistoryStats: (documentId: string) => { + getHistoryStats: (stateId: string) => { undoCount: number; redoCount: number; } | null; @@ -78,13 +82,13 @@ export const useHistoryStore = create( histories: new Map(), maxHistorySize: MAX_HISTORY_SIZE, - initializeHistory: (documentId: string) => { + initializeHistory: (stateId: string) => { set((state) => { const newHistories = new Map(state.histories); // Only initialize if not already present - if (!newHistories.has(documentId)) { - newHistories.set(documentId, { + if (!newHistories.has(stateId)) { + newHistories.set(stateId, { undoStack: [], redoStack: [], }); @@ -94,30 +98,30 @@ export const useHistoryStore = create( }); }, - pushAction: (documentId: string, action: HistoryAction) => { + pushAction: (stateId: string, action: HistoryAction) => { set((state) => { const newHistories = new Map(state.histories); - const history = newHistories.get(documentId); + const history = newHistories.get(stateId); if (!history) { - console.warn(`History not initialized for document ${documentId}`); + console.warn(`History not initialized for state ${stateId}`); return {}; } console.log("📝 pushAction:", { description: action.description, - actionStateNodes: action.documentState.graph.nodes.length, - actionStateEdges: action.documentState.graph.edges.length, + actionStateNodes: action.graphState.nodes.length, + actionStateEdges: action.graphState.edges.length, currentUndoStackSize: history.undoStack.length, }); - // The action.documentState contains the state BEFORE the action was performed + // The action.graphState contains the state BEFORE the action was performed // We push this to the undo stack so we can restore it if the user clicks undo const newUndoStack = [...history.undoStack]; newUndoStack.push({ description: action.description, timestamp: action.timestamp, - documentState: JSON.parse(JSON.stringify(action.documentState)), // Deep copy + graphState: JSON.parse(JSON.stringify(action.graphState)), // Deep copy }); // Trim undo stack if it exceeds max size @@ -128,7 +132,7 @@ export const useHistoryStore = create( // Clear redo stack when a new action is performed (can't redo after new action) const newRedoStack: HistoryAction[] = []; - newHistories.set(documentId, { + newHistories.set(stateId, { undoStack: newUndoStack, redoStack: newRedoStack, }); @@ -137,20 +141,18 @@ export const useHistoryStore = create( description: action.description, newUndoStackSize: newUndoStack.length, topOfStackNodes: - newUndoStack[newUndoStack.length - 1]?.documentState.graph.nodes - .length, + newUndoStack[newUndoStack.length - 1]?.graphState.nodes.length, topOfStackEdges: - newUndoStack[newUndoStack.length - 1]?.documentState.graph.edges - .length, + newUndoStack[newUndoStack.length - 1]?.graphState.edges.length, }); return { histories: newHistories }; }); }, - undo: (documentId: string) => { + undo: (stateId: string) => { const state = get(); - const history = state.histories.get(documentId); + const history = state.histories.get(stateId); if (!history || history.undoStack.length === 0) { return null; @@ -170,40 +172,33 @@ export const useHistoryStore = create( // Get current state from graphStore and push it to redo stack const currentGraphState = useGraphStore.getState(); - const currentStateSnapshot = { - graph: { - nodes: currentGraphState.nodes, - edges: currentGraphState.edges, - nodeTypes: currentGraphState.nodeTypes, - edgeTypes: currentGraphState.edgeTypes, - }, - metadata: { - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }, - version: "1.0" as const, + const currentStateSnapshot: GraphSnapshot = { + nodes: currentGraphState.nodes, + edges: currentGraphState.edges, + nodeTypes: currentGraphState.nodeTypes, + edgeTypes: currentGraphState.edgeTypes, }; const newRedoStack = [...history.redoStack]; newRedoStack.push({ description: lastAction.description, timestamp: Date.now(), - documentState: JSON.parse(JSON.stringify(currentStateSnapshot)), // Deep copy + graphState: JSON.parse(JSON.stringify(currentStateSnapshot)), // Deep copy }); // Restore the previous state (deep copy) - const restoredState = JSON.parse( - JSON.stringify(lastAction.documentState), + const restoredState: GraphSnapshot = JSON.parse( + JSON.stringify(lastAction.graphState), ); console.log("⏪ after undo:", { - restoredStateNodes: restoredState.graph.nodes.length, - restoredStateEdges: restoredState.graph.edges.length, + restoredStateNodes: restoredState.nodes.length, + restoredStateEdges: restoredState.edges.length, undoStackSize: newUndoStack.length, redoStackSize: newRedoStack.length, }); - newHistories.set(documentId, { + newHistories.set(stateId, { undoStack: newUndoStack, redoStack: newRedoStack, }); @@ -213,9 +208,9 @@ export const useHistoryStore = create( return restoredState; }, - redo: (documentId: string) => { + redo: (stateId: string) => { const state = get(); - const history = state.histories.get(documentId); + const history = state.histories.get(stateId); if (!history || history.redoStack.length === 0) { return null; @@ -229,25 +224,18 @@ export const useHistoryStore = create( // Get current state from graphStore and push it to undo stack const currentGraphState = useGraphStore.getState(); - const currentStateSnapshot = { - graph: { - nodes: currentGraphState.nodes, - edges: currentGraphState.edges, - nodeTypes: currentGraphState.nodeTypes, - edgeTypes: currentGraphState.edgeTypes, - }, - metadata: { - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }, - version: "1.0" as const, + const currentStateSnapshot: GraphSnapshot = { + nodes: currentGraphState.nodes, + edges: currentGraphState.edges, + nodeTypes: currentGraphState.nodeTypes, + edgeTypes: currentGraphState.edgeTypes, }; const newUndoStack = [...history.undoStack]; newUndoStack.push({ description: lastAction.description, timestamp: Date.now(), - documentState: JSON.parse(JSON.stringify(currentStateSnapshot)), // Deep copy + graphState: JSON.parse(JSON.stringify(currentStateSnapshot)), // Deep copy }); // Trim if exceeds max size @@ -256,11 +244,11 @@ export const useHistoryStore = create( } // Restore the future state (deep copy) - const restoredState = JSON.parse( - JSON.stringify(lastAction.documentState), + const restoredState: GraphSnapshot = JSON.parse( + JSON.stringify(lastAction.graphState), ); - newHistories.set(documentId, { + newHistories.set(stateId, { undoStack: newUndoStack, redoStack: newRedoStack, }); @@ -270,21 +258,21 @@ export const useHistoryStore = create( return restoredState; }, - canUndo: (documentId: string) => { + canUndo: (stateId: string) => { const state = get(); - const history = state.histories.get(documentId); + const history = state.histories.get(stateId); return history ? history.undoStack.length > 0 : false; }, - canRedo: (documentId: string) => { + canRedo: (stateId: string) => { const state = get(); - const history = state.histories.get(documentId); + const history = state.histories.get(stateId); return history ? history.redoStack.length > 0 : false; }, - getUndoDescription: (documentId: string) => { + getUndoDescription: (stateId: string) => { const state = get(); - const history = state.histories.get(documentId); + const history = state.histories.get(stateId); if (!history || history.undoStack.length === 0) { return null; @@ -294,9 +282,9 @@ export const useHistoryStore = create( return lastAction.description; }, - getRedoDescription: (documentId: string) => { + getRedoDescription: (stateId: string) => { const state = get(); - const history = state.histories.get(documentId); + const history = state.histories.get(stateId); if (!history || history.redoStack.length === 0) { return null; @@ -306,13 +294,13 @@ export const useHistoryStore = create( return lastAction.description; }, - clearHistory: (documentId: string) => { + clearHistory: (stateId: string) => { set((state) => { const newHistories = new Map(state.histories); - const history = newHistories.get(documentId); + const history = newHistories.get(stateId); if (history) { - newHistories.set(documentId, { + newHistories.set(stateId, { undoStack: [], redoStack: [], }); @@ -322,17 +310,17 @@ export const useHistoryStore = create( }); }, - removeHistory: (documentId: string) => { + removeHistory: (stateId: string) => { set((state) => { const newHistories = new Map(state.histories); - newHistories.delete(documentId); + newHistories.delete(stateId); return { histories: newHistories }; }); }, - getHistoryStats: (documentId: string) => { + getHistoryStats: (stateId: string) => { const state = get(); - const history = state.histories.get(documentId); + const history = state.histories.get(stateId); if (!history) { return null; diff --git a/src/stores/panelStore.ts b/src/stores/panelStore.ts index 3330619..6e59479 100644 --- a/src/stores/panelStore.ts +++ b/src/stores/panelStore.ts @@ -30,25 +30,37 @@ interface PanelState { rightPanelWidth: number; rightPanelCollapsed: boolean; + // Bottom Panel (Timeline) + bottomPanelVisible: boolean; + bottomPanelHeight: number; + bottomPanelCollapsed: boolean; + // Actions toggleLeftPanel: () => void; toggleRightPanel: () => void; setLeftPanelWidth: (width: number) => void; setRightPanelWidth: (width: number) => void; + setBottomPanelHeight: (height: number) => void; toggleLeftPanelSection: (section: keyof PanelState['leftPanelSections']) => void; collapseLeftPanel: () => void; expandLeftPanel: () => void; collapseRightPanel: () => void; expandRightPanel: () => void; + collapseBottomPanel: () => void; + expandBottomPanel: () => void; } const DEFAULT_LEFT_WIDTH = 280; const DEFAULT_RIGHT_WIDTH = 320; +const DEFAULT_BOTTOM_HEIGHT = 200; const MIN_LEFT_WIDTH = 240; const MAX_LEFT_WIDTH = 400; const MIN_RIGHT_WIDTH = 280; const MAX_RIGHT_WIDTH = 500; +const MIN_BOTTOM_HEIGHT = 150; +const MAX_BOTTOM_HEIGHT = 500; const COLLAPSED_LEFT_WIDTH = 40; +const COLLAPSED_BOTTOM_HEIGHT = 40; export const usePanelStore = create()( persist( @@ -70,6 +82,10 @@ export const usePanelStore = create()( rightPanelWidth: DEFAULT_RIGHT_WIDTH, rightPanelCollapsed: false, + bottomPanelVisible: true, // Timeline panel is always visible (can be collapsed but not hidden) + bottomPanelHeight: DEFAULT_BOTTOM_HEIGHT, + bottomPanelCollapsed: false, + // Actions toggleLeftPanel: () => set((state) => ({ @@ -91,6 +107,11 @@ export const usePanelStore = create()( rightPanelWidth: Math.max(MIN_RIGHT_WIDTH, Math.min(MAX_RIGHT_WIDTH, width)), })), + setBottomPanelHeight: (height: number) => + set(() => ({ + bottomPanelHeight: Math.max(MIN_BOTTOM_HEIGHT, Math.min(MAX_BOTTOM_HEIGHT, height)), + })), + toggleLeftPanelSection: (section) => set((state) => ({ leftPanelSections: { @@ -118,6 +139,16 @@ export const usePanelStore = create()( set(() => ({ rightPanelCollapsed: false, })), + + collapseBottomPanel: () => + set(() => ({ + bottomPanelCollapsed: true, + })), + + expandBottomPanel: () => + set(() => ({ + bottomPanelCollapsed: false, + })), }), { name: 'constellation-panel-state', @@ -129,9 +160,13 @@ export const usePanelStore = create()( export const PANEL_CONSTANTS = { DEFAULT_LEFT_WIDTH, DEFAULT_RIGHT_WIDTH, + DEFAULT_BOTTOM_HEIGHT, MIN_LEFT_WIDTH, MAX_LEFT_WIDTH, MIN_RIGHT_WIDTH, MAX_RIGHT_WIDTH, + MIN_BOTTOM_HEIGHT, + MAX_BOTTOM_HEIGHT, COLLAPSED_LEFT_WIDTH, + COLLAPSED_BOTTOM_HEIGHT, }; diff --git a/src/stores/persistence/fileIO.ts b/src/stores/persistence/fileIO.ts index 2e33a4f..0ac9cf6 100644 --- a/src/stores/persistence/fileIO.ts +++ b/src/stores/persistence/fileIO.ts @@ -1,6 +1,6 @@ import type { Actor, Relation, NodeTypeConfig, EdgeTypeConfig } from '../../types'; import type { ConstellationDocument } from './types'; -import { createDocument } from './saver'; +import { createDocument, serializeActors, serializeRelations } from './saver'; import { validateDocument, deserializeGraphState } from './loader'; /** @@ -8,19 +8,12 @@ import { validateDocument, deserializeGraphState } from './loader'; */ /** - * Export current graph state to a JSON file + * Export a complete ConstellationDocument to a JSON file + * Includes all timeline states and metadata */ -export function exportGraphToFile( - nodes: Actor[], - edges: Relation[], - nodeTypes: NodeTypeConfig[], - edgeTypes: EdgeTypeConfig[] -): void { - // Create the document using the existing saver - const doc = createDocument(nodes, edges, nodeTypes, edgeTypes); - +export function exportDocumentToFile(document: ConstellationDocument): void { // Convert to JSON with pretty formatting - const jsonString = JSON.stringify(doc, null, 2); + const jsonString = JSON.stringify(document, null, 2); const blob = new Blob([jsonString], { type: 'application/json' }); const url = URL.createObjectURL(blob); @@ -28,7 +21,9 @@ export function exportGraphToFile( const link = window.document.createElement('a'); link.href = url; const dateStr = new Date().toISOString().slice(0, 10); - link.download = `constellation-analysis-${dateStr}.json`; + const title = document.metadata.title || 'constellation-analysis'; + const sanitizedTitle = title.toLowerCase().replace(/[^a-z0-9]+/g, '-'); + link.download = `${sanitizedTitle}-${dateStr}.json`; // Trigger download window.document.body.appendChild(link); @@ -39,6 +34,27 @@ export function exportGraphToFile( URL.revokeObjectURL(url); } +/** + * Export current graph state to a JSON file + * Creates a new document with a single "Initial State" + */ +export function exportGraphToFile( + nodes: Actor[], + edges: Relation[], + nodeTypes: NodeTypeConfig[], + edgeTypes: EdgeTypeConfig[] +): void { + // Serialize actors and relations + const serializedNodes = serializeActors(nodes); + const serializedEdges = serializeRelations(edges); + + // Create the document using the existing saver + const doc = createDocument(serializedNodes, serializedEdges, nodeTypes, edgeTypes); + + // Use the main export function + exportDocumentToFile(doc); +} + /** * Import graph state from a JSON file */ diff --git a/src/stores/persistence/loader.ts b/src/stores/persistence/loader.ts index 7938e5c..73c5d0a 100644 --- a/src/stores/persistence/loader.ts +++ b/src/stores/persistence/loader.ts @@ -37,70 +37,29 @@ export function validateDocument(doc: unknown): doc is ConstellationDocument { return false; } - // Check graph structure - if (!document.graph || - typeof document.graph !== 'object' || - document.graph === null) { + // Check for global node and edge types + if (!Array.isArray(document.nodeTypes) || !Array.isArray(document.edgeTypes)) { return false; } - const graph = document.graph as Record; - - if (!Array.isArray(graph.nodes) || - !Array.isArray(graph.edges) || - !Array.isArray(graph.nodeTypes) || - !Array.isArray(graph.edgeTypes)) { + // Check timeline structure + if (!document.timeline || + typeof document.timeline !== 'object' || + document.timeline === null) { return false; } - // Validate nodes - for (const node of graph.nodes) { - if (!node || typeof node !== 'object') { - return false; - } - const n = node as Record; - if (!n.id || !n.type || !n.position || !n.data) { - return false; - } - const pos = n.position as Record; - if (typeof pos.x !== 'number' || typeof pos.y !== 'number') { - return false; - } - } - - // Validate edges - for (const edge of graph.edges) { - if (!edge || typeof edge !== 'object') { - return false; - } - const e = edge as Record; - if (!e.id || !e.source || !e.target) { - return false; - } - } - - // Validate node types - for (const nodeType of graph.nodeTypes) { - if (!nodeType || typeof nodeType !== 'object') { - return false; - } - const nt = nodeType as Record; - if (!nt.id || !nt.label || !nt.color) { - return false; - } - } - - // Validate edge types - for (const edgeType of graph.edgeTypes) { - if (!edgeType || typeof edgeType !== 'object') { - return false; - } - const et = edgeType as Record; - if (!et.id || !et.label || !et.color) { - return false; - } + const timeline = document.timeline as Record; + + if (!timeline.states || + typeof timeline.states !== 'object' || + typeof timeline.currentStateId !== 'string' || + typeof timeline.rootStateId !== 'string') { + return false; } + // Timeline validation is sufficient - we'll validate the current state's graph + // when we actually load it return true; } @@ -152,6 +111,35 @@ export function loadDocument(): ConstellationDocument | null { } } +// Get the current graph from a document's timeline +export function getCurrentGraphFromDocument(document: ConstellationDocument): { + nodes: SerializedActor[]; + edges: SerializedRelation[]; + nodeTypes: NodeTypeConfig[]; + edgeTypes: EdgeTypeConfig[]; +} | null { + try { + const { timeline, nodeTypes, edgeTypes } = document; + const currentState = timeline.states[timeline.currentStateId]; + + if (!currentState || !currentState.graph) { + console.error('Current state or graph not found in timeline'); + return null; + } + + // Combine state graph with document types + return { + nodes: currentState.graph.nodes, + edges: currentState.graph.edges, + nodeTypes, + edgeTypes, + }; + } catch (error) { + console.error('Failed to get current graph from document:', error); + return null; + } +} + // Deserialize graph state from a document export function deserializeGraphState(document: ConstellationDocument): { nodes: Actor[]; @@ -160,14 +148,19 @@ export function deserializeGraphState(document: ConstellationDocument): { edgeTypes: EdgeTypeConfig[]; } | null { try { - const nodes = deserializeActors(document.graph.nodes); - const edges = deserializeRelations(document.graph.edges); + const currentGraph = getCurrentGraphFromDocument(document); + if (!currentGraph) { + return null; + } + + const nodes = deserializeActors(currentGraph.nodes); + const edges = deserializeRelations(currentGraph.edges); return { nodes, edges, - nodeTypes: document.graph.nodeTypes, - edgeTypes: document.graph.edgeTypes, + nodeTypes: currentGraph.nodeTypes, + edgeTypes: currentGraph.edgeTypes, }; } catch (error) { console.error('Failed to deserialize graph state:', error); diff --git a/src/stores/persistence/middleware.ts b/src/stores/persistence/middleware.ts deleted file mode 100644 index a258765..0000000 --- a/src/stores/persistence/middleware.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { StateCreator } from 'zustand'; -import type { Actor, Relation, NodeTypeConfig, EdgeTypeConfig } from '../../types'; -import { debouncedSave } from './saver'; -import { DEBOUNCE_CONFIG } from './constants'; - -/** - * Persistence Middleware - Auto-saves graph state to localStorage - * - * This middleware intercepts state changes in the Zustand store and - * triggers debounced saves to localStorage. - */ - - - -export const persistenceMiddleware = < - T extends { - nodes: Actor[]; - edges: Relation[]; - nodeTypes: NodeTypeConfig[]; - edgeTypes: EdgeTypeConfig[]; - } ->( - config: StateCreator -): StateCreator => (set, get, api) => { - const stateCreator = config(set, get, api); - - api.subscribe((state) => { - if (state.nodes && state.edges && state.nodeTypes && state.edgeTypes) { - debouncedSave( - state.nodes, - state.edges, - state.nodeTypes, - state.edgeTypes, - DEBOUNCE_CONFIG.DELAY, - DEBOUNCE_CONFIG.MAX_WAIT - ); - } - }); - - return stateCreator; -}; diff --git a/src/stores/persistence/saver.ts b/src/stores/persistence/saver.ts index 0b00edb..f4132c7 100644 --- a/src/stores/persistence/saver.ts +++ b/src/stores/persistence/saver.ts @@ -1,25 +1,24 @@ -import type { Actor, Relation, NodeTypeConfig, EdgeTypeConfig } from '../../types'; import type { ConstellationDocument, SerializedActor, SerializedRelation } from './types'; +import type { Actor, Relation, NodeTypeConfig, EdgeTypeConfig } from '../../types'; import { STORAGE_KEYS, SCHEMA_VERSION, APP_NAME } from './constants'; /** * Saver - Handles serialization and saving to localStorage */ -// Serialize a single actor (node) for storage -// Excludes transient UI state like selected and dragging -function serializeActor(actor: Actor): SerializedActor { - return { +// Serialize actors for storage (strip React Flow internals) +export function serializeActors(actors: Actor[]): SerializedActor[] { + return actors.map(actor => ({ id: actor.id, - type: actor.type ?? 'default', + type: actor.type || 'custom', // Default to 'custom' if undefined position: actor.position, data: actor.data, - }; + })); } -// Serialize a single relation (edge) for storage -function serializeRelation(relation: Relation): SerializedRelation { - return { +// Serialize relations for storage (strip React Flow internals) +export function serializeRelations(relations: Relation[]): SerializedRelation[] { + return relations.map(relation => ({ id: relation.id, source: relation.source, target: relation.target, @@ -27,37 +26,63 @@ function serializeRelation(relation: Relation): SerializedRelation { data: relation.data, sourceHandle: relation.sourceHandle, targetHandle: relation.targetHandle, - }; + })); } -// Create a complete document from current state +// Generate unique state ID +function generateStateId(): string { + return `state_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; +} + +// Create a complete document from graph data +// Creates a document with a single initial timeline state containing the provided graph export function createDocument( - nodes: Actor[], - edges: Relation[], + nodes: SerializedActor[], + edges: SerializedRelation[], nodeTypes: NodeTypeConfig[], edgeTypes: EdgeTypeConfig[], existingDocument?: ConstellationDocument ): ConstellationDocument { const now = new Date().toISOString(); + const rootStateId = generateStateId(); + // Create the initial timeline state with the provided graph (nodes and edges only) + const initialState = { + id: rootStateId, + label: 'Initial State', + parentStateId: undefined, + graph: { + nodes, + edges, + }, + createdAt: now, + updatedAt: now, + }; + + // Create document with global types and timeline containing the initial state return { metadata: { version: SCHEMA_VERSION, appName: APP_NAME, - createdAt: existingDocument?.metadata.createdAt || now, + createdAt: existingDocument?.metadata?.createdAt || now, updatedAt: now, lastSavedBy: 'browser', }, - graph: { - nodes: nodes.map(serializeActor), - edges: edges.map(serializeRelation), - nodeTypes, - edgeTypes, + nodeTypes, + edgeTypes, + timeline: { + states: { + [rootStateId]: initialState, + }, + currentStateId: rootStateId, + rootStateId: rootStateId, }, }; } -// Save document to localStorage +// Save document to localStorage (legacy function for old single-document system) +// NOTE: This is only used for migration purposes. Workspace documents are saved +// via workspace/persistence.ts export function saveDocument(document: ConstellationDocument): boolean { try { const json = JSON.stringify(document); @@ -67,7 +92,6 @@ export function saveDocument(document: ConstellationDocument): boolean { } catch (error) { if (error instanceof Error && error.name === 'QuotaExceededError') { console.error('Storage quota exceeded'); - // TODO: Handle quota exceeded error in Phase 2 } else { console.error('Failed to save document:', error); } @@ -75,69 +99,7 @@ export function saveDocument(document: ConstellationDocument): boolean { } } -// Save current graph state -export function saveGraphState( - nodes: Actor[], - edges: Relation[], - nodeTypes: NodeTypeConfig[], - edgeTypes: EdgeTypeConfig[] -): boolean { - try { - // Try to load existing document to preserve createdAt timestamp - const existingJson = localStorage.getItem(STORAGE_KEYS.GRAPH_STATE); - let existingDocument: ConstellationDocument | undefined; - - if (existingJson) { - try { - existingDocument = JSON.parse(existingJson); - } catch { - // Ignore parse errors, we'll create a new document - } - } - - const document = createDocument(nodes, edges, nodeTypes, edgeTypes, existingDocument); - return saveDocument(document); - } catch (error) { - console.error('Failed to save graph state:', error); - return false; - } -} - -// Create a debounced save function -let saveTimeout: ReturnType | null = null; -let lastSaveTime = 0; - -export function debouncedSave( - nodes: Actor[], - edges: Relation[], - nodeTypes: NodeTypeConfig[], - edgeTypes: EdgeTypeConfig[], - delay: number = 1000, - maxWait: number = 5000 -): void { - const now = Date.now(); - - // Clear existing timeout - if (saveTimeout) { - clearTimeout(saveTimeout); - } - - // Force save if max wait time exceeded - if (now - lastSaveTime >= maxWait) { - saveGraphState(nodes, edges, nodeTypes, edgeTypes); - lastSaveTime = now; - return; - } - - // Schedule debounced save - saveTimeout = setTimeout(() => { - saveGraphState(nodes, edges, nodeTypes, edgeTypes); - lastSaveTime = now; - saveTimeout = null; - }, delay); -} - -// Clear saved state +// Clear saved state (legacy function) export function clearSavedState(): void { localStorage.removeItem(STORAGE_KEYS.GRAPH_STATE); localStorage.removeItem(STORAGE_KEYS.LAST_SAVED); diff --git a/src/stores/persistence/types.ts b/src/stores/persistence/types.ts index 2f33049..5061f89 100644 --- a/src/stores/persistence/types.ts +++ b/src/stores/persistence/types.ts @@ -1,4 +1,5 @@ import type { ActorData, RelationData, NodeTypeConfig, EdgeTypeConfig } from '../../types'; +import type { ConstellationState } from '../../types/timeline'; /** * Persistence Types @@ -26,6 +27,8 @@ export interface SerializedRelation { } // Complete document structure for storage +// Every document has a timeline with states. The current graph is always +// derived from the current state in the timeline. export interface ConstellationDocument { metadata: { version: string; // Schema version (e.g., "1.0.0") @@ -33,14 +36,18 @@ export interface ConstellationDocument { createdAt: string; // ISO timestamp updatedAt: string; // ISO timestamp lastSavedBy: string; // Browser fingerprint or "unknown" - documentId?: string; // NEW: Unique document ID (for workspace) - title?: string; // NEW: Document title (for workspace) + documentId?: string; // Unique document ID (for workspace) + title?: string; // Document title (for workspace) }; - graph: { - nodes: SerializedActor[]; - edges: SerializedRelation[]; - nodeTypes: NodeTypeConfig[]; - edgeTypes: EdgeTypeConfig[]; + // Global node and edge types for the entire document + nodeTypes: NodeTypeConfig[]; + edgeTypes: EdgeTypeConfig[]; + // Timeline with multiple states - every document has this + // The graph is stored within each state (nodes and edges only, not types) + timeline: { + states: Record; // Map serialized as object + currentStateId: string; + rootStateId: string; }; } diff --git a/src/stores/timelineStore.ts b/src/stores/timelineStore.ts new file mode 100644 index 0000000..783c8ff --- /dev/null +++ b/src/stores/timelineStore.ts @@ -0,0 +1,568 @@ +import { create } from "zustand"; +import type { + Timeline, + ConstellationState, + StateId, + TimelineActions, +} from "../types/timeline"; +import type { Actor, Relation } from "../types"; +import type { SerializedActor, SerializedRelation } from "./persistence/types"; +import { useGraphStore } from "./graphStore"; +import { useWorkspaceStore } from "./workspaceStore"; +import { useToastStore } from "./toastStore"; + +/** + * Timeline Store + * + * Manages multiple constellation states within a document. + * Each document can have its own timeline with branching states. + */ + +interface TimelineStore { + // Map of documentId -> Timeline + timelines: Map; + + // Currently active document's timeline + activeDocumentId: string | null; +} + +// Generate unique state ID +function generateStateId(): StateId { + return `state_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; +} + +export const useTimelineStore = create( + (set, get) => ({ + timelines: new Map(), + activeDocumentId: null, + + initializeTimeline: ( + documentId: string, + initialGraph: ConstellationState["graph"], + ) => { + const state = get(); + + // Don't re-initialize if already exists + if (state.timelines.has(documentId)) { + console.warn(`Timeline already initialized for document ${documentId}`); + return; + } + + const rootStateId = generateStateId(); + const now = new Date().toISOString(); + + const rootState: ConstellationState = { + id: rootStateId, + label: "Initial State", + parentStateId: undefined, + graph: JSON.parse(JSON.stringify(initialGraph)), // Deep copy + createdAt: now, + updatedAt: now, + }; + + const timeline: Timeline = { + states: new Map([[rootStateId, rootState]]), + currentStateId: rootStateId, + rootStateId: rootStateId, + }; + + set((state) => { + const newTimelines = new Map(state.timelines); + newTimelines.set(documentId, timeline); + return { + timelines: newTimelines, + activeDocumentId: documentId, + }; + }); + + console.log(`Timeline initialized for document ${documentId}`); + }, + + loadTimeline: (documentId: string, timeline: Timeline) => { + set((state) => { + const newTimelines = new Map(state.timelines); + + // Convert plain objects back to Maps if needed + const statesMap = + timeline.states instanceof Map + ? timeline.states + : new Map( + Object.entries(timeline.states) as [ + string, + ConstellationState, + ][], + ); + + newTimelines.set(documentId, { + ...timeline, + states: statesMap, + }); + + return { + timelines: newTimelines, + activeDocumentId: documentId, + }; + }); + }, + + createState: ( + label: string, + description?: string, + cloneFromCurrent: boolean = true, + ) => { + const state = get(); + const { activeDocumentId } = state; + + if (!activeDocumentId) { + console.error("No active document"); + useToastStore.getState().showToast("No active document", "error"); + return ""; + } + + const timeline = state.timelines.get(activeDocumentId); + if (!timeline) { + console.error("No timeline for active document"); + useToastStore.getState().showToast("Timeline not initialized", "error"); + return ""; + } + + const newStateId = generateStateId(); + const now = new Date().toISOString(); + + // Get graph to clone (nodes and edges only, types are global) + let graphToClone: ConstellationState["graph"]; + if (cloneFromCurrent) { + // Clone from current graph state (nodes and edges only) + const graphStore = useGraphStore.getState(); + graphToClone = { + nodes: graphStore.nodes as unknown as SerializedActor[], + edges: graphStore.edges as unknown as SerializedRelation[], + }; + } else { + // Empty graph + graphToClone = { + nodes: [], + edges: [], + }; + } + + const newState: ConstellationState = { + id: newStateId, + label, + description, + parentStateId: timeline.currentStateId, // Branch from current + graph: JSON.parse(JSON.stringify(graphToClone)), // Deep copy + createdAt: now, + updatedAt: now, + }; + + set((state) => { + const newTimelines = new Map(state.timelines); + const timeline = newTimelines.get(activeDocumentId)!; + + const newStates = new Map(timeline.states); + newStates.set(newStateId, newState); + + newTimelines.set(activeDocumentId, { + ...timeline, + states: newStates, + currentStateId: newStateId, // Switch to new state + }); + + return { timelines: newTimelines }; + }); + + // Load new state's graph into graph store + // Types come from the document and are already in the graph store + useGraphStore.setState({ + nodes: newState.graph.nodes, + edges: newState.graph.edges, + // nodeTypes and edgeTypes remain unchanged (they're global per document) + }); + + // Mark document as dirty + useWorkspaceStore.getState().markDocumentDirty(activeDocumentId); + + useToastStore.getState().showToast(`State "${label}" created`, "success"); + + return newStateId; + }, + + switchToState: (stateId: StateId) => { + const state = get(); + const { activeDocumentId } = state; + + if (!activeDocumentId) { + console.error("No active document"); + return; + } + + const timeline = state.timelines.get(activeDocumentId); + if (!timeline) { + console.error("No timeline for active document"); + return; + } + + const targetState = timeline.states.get(stateId); + if (!targetState) { + console.error(`State ${stateId} not found`); + useToastStore.getState().showToast("State not found", "error"); + return; + } + + // Save current graph state to current state before switching (nodes and edges only) + const currentState = timeline.states.get(timeline.currentStateId); + if (currentState) { + const graphStore = useGraphStore.getState(); + currentState.graph = { + nodes: graphStore.nodes as unknown as SerializedActor[], + edges: graphStore.edges as unknown as SerializedRelation[], + }; + currentState.updatedAt = new Date().toISOString(); + } + + // Switch to target state + set((state) => { + const newTimelines = new Map(state.timelines); + const timeline = newTimelines.get(activeDocumentId)!; + + newTimelines.set(activeDocumentId, { + ...timeline, + currentStateId: stateId, + }); + + return { timelines: newTimelines }; + }); + + // Load target state's graph (nodes and edges only, types are global) + useGraphStore.setState({ + nodes: targetState.graph.nodes as unknown as Actor[], + edges: targetState.graph.edges as unknown as Relation[], + // nodeTypes and edgeTypes remain unchanged (they're global per document) + }); + }, + + updateState: ( + stateId: StateId, + updates: Partial< + Pick + >, + ) => { + const state = get(); + const { activeDocumentId } = state; + + if (!activeDocumentId) return; + + const timeline = state.timelines.get(activeDocumentId); + if (!timeline) return; + + const stateToUpdate = timeline.states.get(stateId); + if (!stateToUpdate) { + console.error(`State ${stateId} not found`); + return; + } + + set((state) => { + const newTimelines = new Map(state.timelines); + const timeline = newTimelines.get(activeDocumentId)!; + const newStates = new Map(timeline.states); + + const updatedState = { + ...stateToUpdate, + ...updates, + metadata: updates.metadata + ? { ...stateToUpdate.metadata, ...updates.metadata } + : stateToUpdate.metadata, + updatedAt: new Date().toISOString(), + }; + + newStates.set(stateId, updatedState); + + newTimelines.set(activeDocumentId, { + ...timeline, + states: newStates, + }); + + return { timelines: newTimelines }; + }); + + // Mark document as dirty + useWorkspaceStore.getState().markDocumentDirty(activeDocumentId); + }, + + deleteState: (stateId: StateId) => { + const state = get(); + const { activeDocumentId } = state; + + if (!activeDocumentId) return false; + + const timeline = state.timelines.get(activeDocumentId); + if (!timeline) return false; + + // Can't delete root state + if (stateId === timeline.rootStateId) { + useToastStore.getState().showToast("Cannot delete root state", "error"); + return false; + } + + // Can't delete current state + if (stateId === timeline.currentStateId) { + useToastStore + .getState() + .showToast( + "Cannot delete current state. Switch to another state first.", + "error", + ); + return false; + } + + // Check if state has children + const children = get().getChildStates(stateId); + if (children.length > 0) { + const confirmed = window.confirm( + `This state has ${children.length} child state(s). Delete anyway? Children will be orphaned.`, + ); + if (!confirmed) return false; + } + + const stateToDelete = timeline.states.get(stateId); + const stateName = stateToDelete?.label || "Unknown"; + + set((state) => { + const newTimelines = new Map(state.timelines); + const timeline = newTimelines.get(activeDocumentId)!; + const newStates = new Map(timeline.states); + + newStates.delete(stateId); + + newTimelines.set(activeDocumentId, { + ...timeline, + states: newStates, + }); + + return { timelines: newTimelines }; + }); + + // Mark document as dirty + useWorkspaceStore.getState().markDocumentDirty(activeDocumentId); + + useToastStore + .getState() + .showToast(`State "${stateName}" deleted`, "info"); + + return true; + }, + + duplicateState: (stateId: StateId, newLabel?: string) => { + const state = get(); + const { activeDocumentId } = state; + + if (!activeDocumentId) { + console.error("No active document"); + return ""; + } + + const timeline = state.timelines.get(activeDocumentId); + if (!timeline) { + console.error("No timeline for active document"); + return ""; + } + + const stateToDuplicate = timeline.states.get(stateId); + if (!stateToDuplicate) { + console.error(`State ${stateId} not found`); + useToastStore.getState().showToast("State not found", "error"); + return ""; + } + + const newStateId = generateStateId(); + const now = new Date().toISOString(); + const label = newLabel || `${stateToDuplicate.label} (Copy)`; + + const duplicatedState: ConstellationState = { + ...stateToDuplicate, + id: newStateId, + label, + parentStateId: stateToDuplicate.parentStateId, // Same parent as original (parallel) + graph: JSON.parse(JSON.stringify(stateToDuplicate.graph)), // Deep copy + createdAt: now, + updatedAt: now, + }; + + set((state) => { + const newTimelines = new Map(state.timelines); + const timeline = newTimelines.get(activeDocumentId)!; + + const newStates = new Map(timeline.states); + newStates.set(newStateId, duplicatedState); + + newTimelines.set(activeDocumentId, { + ...timeline, + states: newStates, + }); + + return { timelines: newTimelines }; + }); + + // Mark document as dirty + useWorkspaceStore.getState().markDocumentDirty(activeDocumentId); + + useToastStore + .getState() + .showToast(`State "${label}" created`, "success"); + + return newStateId; + }, + + duplicateStateAsChild: (stateId: StateId, newLabel?: string) => { + const state = get(); + const { activeDocumentId } = state; + + if (!activeDocumentId) { + console.error("No active document"); + return ""; + } + + const timeline = state.timelines.get(activeDocumentId); + if (!timeline) { + console.error("No timeline for active document"); + return ""; + } + + const stateToDuplicate = timeline.states.get(stateId); + if (!stateToDuplicate) { + console.error(`State ${stateId} not found`); + useToastStore.getState().showToast("State not found", "error"); + return ""; + } + + const newStateId = generateStateId(); + const now = new Date().toISOString(); + const label = newLabel || `${stateToDuplicate.label} (Copy)`; + + const duplicatedState: ConstellationState = { + ...stateToDuplicate, + id: newStateId, + label, + parentStateId: stateId, // Original state becomes parent (series) + graph: JSON.parse(JSON.stringify(stateToDuplicate.graph)), // Deep copy + createdAt: now, + updatedAt: now, + }; + + set((state) => { + const newTimelines = new Map(state.timelines); + const timeline = newTimelines.get(activeDocumentId)!; + + const newStates = new Map(timeline.states); + newStates.set(newStateId, duplicatedState); + + newTimelines.set(activeDocumentId, { + ...timeline, + states: newStates, + }); + + return { timelines: newTimelines }; + }); + + // Mark document as dirty + useWorkspaceStore.getState().markDocumentDirty(activeDocumentId); + + useToastStore + .getState() + .showToast(`State "${label}" created`, "success"); + + return newStateId; + }, + + getState: (stateId: StateId) => { + const state = get(); + const { activeDocumentId } = state; + + if (!activeDocumentId) return null; + + const timeline = state.timelines.get(activeDocumentId); + if (!timeline) return null; + + return timeline.states.get(stateId) || null; + }, + + getChildStates: (stateId: StateId) => { + const state = get(); + const { activeDocumentId } = state; + + if (!activeDocumentId) return []; + + const timeline = state.timelines.get(activeDocumentId); + if (!timeline) return []; + + const children: ConstellationState[] = []; + timeline.states.forEach((state) => { + if (state.parentStateId === stateId) { + children.push(state); + } + }); + + return children; + }, + + getAllStates: () => { + const state = get(); + const { activeDocumentId } = state; + + if (!activeDocumentId) return []; + + const timeline = state.timelines.get(activeDocumentId); + if (!timeline) return []; + + return Array.from(timeline.states.values()); + }, + + saveCurrentGraph: (graph: ConstellationState["graph"]) => { + const state = get(); + const { activeDocumentId } = state; + + if (!activeDocumentId) return; + + const timeline = state.timelines.get(activeDocumentId); + if (!timeline) return; + + const currentState = timeline.states.get(timeline.currentStateId); + if (!currentState) return; + + set((state) => { + const newTimelines = new Map(state.timelines); + const timeline = newTimelines.get(activeDocumentId)!; + const newStates = new Map(timeline.states); + + const updatedState = { + ...currentState, + graph: JSON.parse(JSON.stringify(graph)), // Deep copy + updatedAt: new Date().toISOString(), + }; + + newStates.set(timeline.currentStateId, updatedState); + + newTimelines.set(activeDocumentId, { + ...timeline, + states: newStates, + }); + + return { timelines: newTimelines }; + }); + }, + + clearTimeline: () => { + const state = get(); + const { activeDocumentId } = state; + + if (!activeDocumentId) return; + + set((state) => { + const newTimelines = new Map(state.timelines); + newTimelines.delete(activeDocumentId); + return { timelines: newTimelines }; + }); + }, + }), +); diff --git a/src/stores/workspace/migration.ts b/src/stores/workspace/migration.ts index 85b13ac..1c434d5 100644 --- a/src/stores/workspace/migration.ts +++ b/src/stores/workspace/migration.ts @@ -52,11 +52,12 @@ export function migrateToWorkspace(): WorkspaceState | null { }; // Create workspace settings from legacy document + // Node and edge types are now global per document const settings: WorkspaceSettings = { maxOpenDocuments: 10, autoSaveEnabled: true, - defaultNodeTypes: legacyDoc.graph.nodeTypes, - defaultEdgeTypes: legacyDoc.graph.edgeTypes, + defaultNodeTypes: legacyDoc.nodeTypes || [], + defaultEdgeTypes: legacyDoc.edgeTypes || [], recentFiles: [], }; diff --git a/src/stores/workspace/useActiveDocument.ts b/src/stores/workspace/useActiveDocument.ts index 307c31c..8a21d8d 100644 --- a/src/stores/workspace/useActiveDocument.ts +++ b/src/stores/workspace/useActiveDocument.ts @@ -1,7 +1,9 @@ import { useEffect, useRef } from 'react'; import { useWorkspaceStore } from '../workspaceStore'; import { useGraphStore } from '../graphStore'; +import { useTimelineStore } from '../timelineStore'; import type { Actor, Relation, NodeTypeConfig, EdgeTypeConfig } from '../../types'; +import { getCurrentGraphFromDocument } from '../persistence/loader'; /** * useActiveDocument Hook @@ -59,22 +61,29 @@ export function useActiveDocument() { if (activeDocument && activeDocumentId) { console.log(`Loading document into graph editor: ${activeDocumentId}`, activeDocument.metadata.title); + // Get the current graph from the document's timeline + const currentGraph = getCurrentGraphFromDocument(activeDocument); + if (!currentGraph) { + console.error('Failed to get current graph from document'); + return; + } + // Set loading flag before updating graph state isLoadingRef.current = true; lastLoadedDocIdRef.current = activeDocumentId; - setNodes(activeDocument.graph.nodes as never[]); - setEdges(activeDocument.graph.edges as never[]); - setNodeTypes(activeDocument.graph.nodeTypes as never[]); - setEdgeTypes(activeDocument.graph.edgeTypes as never[]); + setNodes(currentGraph.nodes as never[]); + setEdges(currentGraph.edges as never[]); + setNodeTypes(currentGraph.nodeTypes as never[]); + setEdgeTypes(currentGraph.edgeTypes as never[]); // Update the last synced state to match what we just loaded lastSyncedStateRef.current = { documentId: activeDocumentId, - nodes: activeDocument.graph.nodes as Actor[], - edges: activeDocument.graph.edges as Relation[], - nodeTypes: activeDocument.graph.nodeTypes as NodeTypeConfig[], - edgeTypes: activeDocument.graph.edgeTypes as EdgeTypeConfig[], + nodes: currentGraph.nodes as Actor[], + edges: currentGraph.edges as Relation[], + nodeTypes: currentGraph.nodeTypes as NodeTypeConfig[], + edgeTypes: currentGraph.edgeTypes as EdgeTypeConfig[], }; // Clear loading flag after a brief delay to allow state to settle @@ -153,14 +162,11 @@ export function useActiveDocument() { edgeTypes: graphEdgeTypes as EdgeTypeConfig[], }; - // Update the document in the workspace store - const updatedDoc = documents.get(activeDocumentId); - if (updatedDoc) { - updatedDoc.graph.nodes = graphNodes as never[]; - updatedDoc.graph.edges = graphEdges as never[]; - updatedDoc.graph.nodeTypes = graphNodeTypes as never[]; - updatedDoc.graph.edgeTypes = graphEdgeTypes as never[]; - } + // Update the timeline's current state with the new graph data (nodes and edges only) + useTimelineStore.getState().saveCurrentGraph({ + nodes: graphNodes as never[], + edges: graphEdges as never[], + }); // Debounced save const timeoutId = setTimeout(() => { diff --git a/src/stores/workspaceStore.ts b/src/stores/workspaceStore.ts index a55ef21..af5aad3 100644 --- a/src/stores/workspaceStore.ts +++ b/src/stores/workspaceStore.ts @@ -1,8 +1,8 @@ import { create } from 'zustand'; import type { ConstellationDocument } from './persistence/types'; import type { Workspace, WorkspaceActions, DocumentMetadata, WorkspaceSettings } from './workspace/types'; -import { createDocument as createDocumentHelper } from './persistence/saver'; -import { selectFileForImport, exportGraphToFile } from './persistence/fileIO'; +import { createDocument as createDocumentHelper, serializeActors, serializeRelations } from './persistence/saver'; +import { selectFileForImport, exportDocumentToFile } from './persistence/fileIO'; import { generateWorkspaceId, generateDocumentId, @@ -23,6 +23,10 @@ import { selectWorkspaceZipForImport, } from './workspace/workspaceIO'; import { useToastStore } from './toastStore'; +import { useTimelineStore } from './timelineStore'; +import { useGraphStore } from './graphStore'; +import type { ConstellationState, Timeline } from '../types/timeline'; +import { getCurrentGraphFromDocument } from './persistence/loader'; /** * Workspace Store @@ -81,6 +85,11 @@ function initializeWorkspace(): Workspace { const doc = loadDocumentFromStorage(savedState.activeDocumentId); if (doc) { documents.set(savedState.activeDocumentId, doc); + + // Load timeline if it exists + if (doc.timeline) { + useTimelineStore.getState().loadTimeline(savedState.activeDocumentId, doc.timeline as unknown as Timeline); + } } } @@ -145,6 +154,9 @@ export const useWorkspaceStore = create((set, get) saveDocumentToStorage(documentId, newDoc); saveDocumentMetadata(documentId, metadata); + // Load the timeline from the newly created document into timelineStore + useTimelineStore.getState().loadTimeline(documentId, newDoc.timeline as unknown as Timeline); + // Update workspace set((state) => { const newDocuments = new Map(state.documents); @@ -192,12 +204,19 @@ export const useWorkspaceStore = create((set, get) const documentId = generateDocumentId(); const now = new Date().toISOString(); + // Get node and edge types from source document's current graph + const sourceGraph = getCurrentGraphFromDocument(sourceDoc); + if (!sourceGraph) { + console.error('Failed to get graph from source document'); + return ''; + } + // Create new document with the same node and edge types, but no actors/relations const newDoc = createDocumentHelper( [], [], - sourceDoc.graph.nodeTypes, - sourceDoc.graph.edgeTypes + sourceGraph.nodeTypes, + sourceGraph.edgeTypes ); newDoc.metadata.documentId = documentId; newDoc.metadata.title = title; @@ -213,6 +232,9 @@ export const useWorkspaceStore = create((set, get) saveDocumentToStorage(documentId, newDoc); saveDocumentMetadata(documentId, metadata); + // Load the timeline from the newly created document into timelineStore + useTimelineStore.getState().loadTimeline(documentId, newDoc.timeline as unknown as Timeline); + // Update workspace set((state) => { const newDocuments = new Map(state.documents); @@ -261,6 +283,11 @@ export const useWorkspaceStore = create((set, get) return; } + // Load timeline if it exists + if (doc.timeline) { + useTimelineStore.getState().loadTimeline(documentId, doc.timeline as unknown as Timeline); + } + set((state) => { const newDocuments = new Map(state.documents); newDocuments.set(documentId, doc); @@ -472,6 +499,10 @@ export const useWorkspaceStore = create((set, get) }; }); + // Initialize timeline for duplicated document - always copy the timeline + // since all documents now have timelines + useTimelineStore.getState().loadTimeline(newDocumentId, duplicatedDoc.timeline as unknown as Timeline); + useToastStore.getState().showToast(`Document duplicated as "${newTitle}"`, 'success'); return newDocumentId; @@ -525,9 +556,13 @@ export const useWorkspaceStore = create((set, get) const documentId = generateDocumentId(); const now = new Date().toISOString(); + // Serialize actors and relations for storage + const serializedNodes = serializeActors(data.nodes); + const serializedEdges = serializeRelations(data.edges); + const importedDoc = createDocumentHelper( - data.nodes, - data.edges, + serializedNodes, + serializedEdges, data.nodeTypes, data.edgeTypes ); @@ -544,6 +579,9 @@ export const useWorkspaceStore = create((set, get) saveDocumentToStorage(documentId, importedDoc); saveDocumentMetadata(documentId, metadata); + // Load the timeline from the imported document into timelineStore + useTimelineStore.getState().loadTimeline(documentId, importedDoc.timeline as unknown as Timeline); + set((state) => { const newDocuments = new Map(state.documents); newDocuments.set(documentId, importedDoc); @@ -593,12 +631,26 @@ export const useWorkspaceStore = create((set, get) } try { - exportGraphToFile( - doc.graph.nodes, - doc.graph.edges, - doc.graph.nodeTypes, - doc.graph.edgeTypes - ); + // Ensure timeline is up-to-date before exporting (similar to saveDocument) + const timelineState = useTimelineStore.getState(); + const timeline = timelineState.timelines.get(documentId); + + if (timeline) { + // Serialize timeline (convert Map to object) + const serializedStates: Record = {}; + timeline.states.forEach((state: ConstellationState, id: string) => { + serializedStates[id] = state; + }); + + doc.timeline = { + states: serializedStates, + currentStateId: timeline.currentStateId, + rootStateId: timeline.rootStateId, + }; + } + + // Export the complete document with all timeline states + exportDocumentToFile(doc); useToastStore.getState().showToast('Document exported successfully', 'success'); } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error'; @@ -673,6 +725,30 @@ export const useWorkspaceStore = create((set, get) const doc = state.documents.get(documentId); if (doc) { doc.metadata.updatedAt = new Date().toISOString(); + + // Save global node and edge types from graph store + const graphStore = useGraphStore.getState(); + doc.nodeTypes = graphStore.nodeTypes; + doc.edgeTypes = graphStore.edgeTypes; + + // Save timeline data if exists + const timelineState = useTimelineStore.getState(); + const timeline = timelineState.timelines.get(documentId); + + if (timeline) { + // Serialize timeline (convert Map to object) + const serializedStates: Record = {}; + timeline.states.forEach((state: ConstellationState, id: string) => { + serializedStates[id] = state; + }); + + doc.timeline = { + states: serializedStates, + currentStateId: timeline.currentStateId, + rootStateId: timeline.rootStateId, + }; + } + saveDocumentToStorage(documentId, doc); const metadata = state.documentMetadata.get(documentId); diff --git a/src/types/timeline.ts b/src/types/timeline.ts new file mode 100644 index 0000000..ba395de --- /dev/null +++ b/src/types/timeline.ts @@ -0,0 +1,100 @@ +import type { SerializedActor, SerializedRelation } from '../stores/persistence/types'; + +/** + * Timeline Types + * + * Support for multiple constellation states within a single document. + * States can represent time-based evolution or alternative scenarios. + */ + +export type StateId = string; + +/** + * A single constellation state - a snapshot of the graph at a point in time or scenario + */ +export interface ConstellationState { + id: StateId; + label: string; // User-defined label (e.g., "Jan 2024", "Strategy A") + description?: string; // Optional detailed description + parentStateId?: string; // Parent state (null/undefined = root state) + + // Graph snapshot (nodes and edges only, types are global per document) + graph: { + nodes: SerializedActor[]; + edges: SerializedRelation[]; + }; + + // Optional metadata - users can use these or ignore them + metadata?: { + date?: string; // Optional ISO date string + tags?: string[]; // Optional categorization tags + color?: string; // Optional color for visualization + notes?: string; // Optional presenter notes + }; + + // Timestamps + createdAt: string; // ISO timestamp + updatedAt: string; // ISO timestamp +} + +/** + * Timeline - collection of constellation states with branching structure + */ +export interface Timeline { + states: Map; // All states by ID + currentStateId: StateId; // Currently active state + rootStateId: StateId; // Initial/root state +} + +/** + * Edge in the state graph (for visualization) + */ +export interface StateEdge { + id: string; + source: StateId; // Parent state + target: StateId; // Child state +} + +/** + * Timeline actions + */ +export interface TimelineActions { + // Initialize timeline for a document + initializeTimeline: (documentId: string, initialGraph: ConstellationState['graph']) => void; + + // Load timeline from document + loadTimeline: (documentId: string, timeline: Timeline) => void; + + // Create new state + createState: (label: string, description?: string, cloneFromCurrent?: boolean) => StateId; + + // Switch to different state + switchToState: (stateId: StateId) => void; + + // Update state metadata + updateState: (stateId: StateId, updates: Partial>) => void; + + // Delete state + deleteState: (stateId: StateId) => boolean; + + // Duplicate state (parallel - same parent as original) + duplicateState: (stateId: StateId, newLabel?: string) => StateId; + + // Duplicate state as child (series - original becomes parent) + duplicateStateAsChild: (stateId: StateId, newLabel?: string) => StateId; + + // Get state by ID + getState: (stateId: StateId) => ConstellationState | null; + + // Get child states + getChildStates: (stateId: StateId) => ConstellationState[]; + + // Get all states + getAllStates: () => ConstellationState[]; + + // Save current graph to current state + saveCurrentGraph: (graph: ConstellationState['graph']) => void; + + // Clear timeline + clearTimeline: () => void; +}