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; +}