mirror of
https://github.com/OFFIS-ESC/constellation-analyzer
synced 2026-01-27 07:43:41 +00:00
Implements a comprehensive timeline system that enables documents to contain multiple constellation states with branching timelines. This allows users to create different versions of their analysis for temporal evolution, alternative scenarios, or what-if analysis. Core Features: - Timeline store managing multiple states per document with branching structure - Visual timeline panel with React Flow-based state graph visualization - State management: create, switch, rename, duplicate (parallel/series), delete - Per-state undo/redo history (max 50 actions per state) - Context menu for timeline node operations - Collapsible timeline panel (always visible, moved toolbar to panel header) Architecture Changes: - Document structure: removed top-level graph field, states now only in timeline - Global types: nodeTypes and edgeTypes are now global per document, not per state - State graphs: only contain nodes and edges, types inherited from document - Persistence: full timeline serialization/deserialization with all states - History system: converted from document-level to per-state independent stacks Timeline Components: - TimelineView: main timeline visualization with state nodes and edges - BottomPanel: collapsible container with timeline controls in header - StateNode: custom node component showing state info and active indicator - CreateStateDialog: dialog for creating new timeline states - RenameStateDialog: dialog for renaming existing states - Context menu: right-click operations (rename, duplicate parallel/series, delete) Document Management: - Documents always have timeline (initialized with root state on creation) - Timeline persisted with document in localStorage - Export/import includes complete timeline with all states - Migration support for legacy single-state documents Store Updates: - timelineStore: manages timelines, states, and timeline operations - historyStore: per-state history with independent undo/redo stacks - workspaceStore: saves/loads timeline data, handles global types - panelStore: added timeline panel visibility state - useActiveDocument: syncs timeline state with graph editor Context Menu Improvements: - Smart viewport edge detection to prevent overflow - Click-outside detection for React Flow panes - Consistent styling across application Files Added: - src/types/timeline.ts - Timeline type definitions - src/stores/timelineStore.ts - Timeline state management - src/components/Timeline/TimelineView.tsx - Main timeline component - src/components/Timeline/BottomPanel.tsx - Timeline panel container - src/components/Timeline/StateNode.tsx - State node visualization - src/components/Timeline/CreateStateDialog.tsx - State creation dialog - src/components/Timeline/RenameStateDialog.tsx - State rename dialog Files Removed: - src/stores/persistence/middleware.ts - Obsolete persistence middleware Documentation: - Added comprehensive timeline feature documentation - Implementation checklists and quick reference guides - Temporal analysis concepts and UX guidelines 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
96 lines
2.7 KiB
TypeScript
96 lines
2.7 KiB
TypeScript
import type { ConstellationDocument } from '../persistence/types';
|
|
import type { WorkspaceState, WorkspaceSettings, DocumentMetadata } from './types';
|
|
import { loadDocument } from '../persistence/loader';
|
|
import {
|
|
WORKSPACE_STORAGE_KEYS,
|
|
generateWorkspaceId,
|
|
generateDocumentId,
|
|
saveWorkspaceState,
|
|
saveDocumentToStorage,
|
|
saveDocumentMetadata,
|
|
} from './persistence';
|
|
|
|
/**
|
|
* Migration from Single-Document to Multi-Document Workspace
|
|
*
|
|
* Converts legacy single-document format to new workspace format
|
|
*/
|
|
|
|
export function migrateToWorkspace(): WorkspaceState | null {
|
|
console.log('Checking for legacy data to migrate...');
|
|
|
|
// Check for legacy data
|
|
const legacyDoc = loadDocument();
|
|
if (!legacyDoc) {
|
|
console.log('No legacy data found');
|
|
return null;
|
|
}
|
|
|
|
console.log('Legacy data found, migrating to workspace format...');
|
|
|
|
try {
|
|
// Generate IDs
|
|
const workspaceId = generateWorkspaceId();
|
|
const documentId = generateDocumentId();
|
|
|
|
// Create document with new metadata
|
|
const migratedDoc: ConstellationDocument = {
|
|
...legacyDoc,
|
|
metadata: {
|
|
...legacyDoc.metadata,
|
|
documentId,
|
|
title: 'Imported Analysis',
|
|
},
|
|
};
|
|
|
|
// Create document metadata
|
|
const metadata: DocumentMetadata = {
|
|
id: documentId,
|
|
title: 'Imported Analysis',
|
|
isDirty: false,
|
|
lastModified: new Date().toISOString(),
|
|
};
|
|
|
|
// Create workspace settings from legacy document
|
|
// Node and edge types are now global per document
|
|
const settings: WorkspaceSettings = {
|
|
maxOpenDocuments: 10,
|
|
autoSaveEnabled: true,
|
|
defaultNodeTypes: legacyDoc.nodeTypes || [],
|
|
defaultEdgeTypes: legacyDoc.edgeTypes || [],
|
|
recentFiles: [],
|
|
};
|
|
|
|
// Create workspace state
|
|
const workspace: WorkspaceState = {
|
|
workspaceId,
|
|
workspaceName: 'My Workspace',
|
|
documentOrder: [documentId],
|
|
activeDocumentId: documentId,
|
|
settings,
|
|
};
|
|
|
|
// Save to new format
|
|
saveWorkspaceState(workspace);
|
|
saveDocumentToStorage(documentId, migratedDoc);
|
|
saveDocumentMetadata(documentId, metadata);
|
|
|
|
// Remove legacy data
|
|
localStorage.removeItem(WORKSPACE_STORAGE_KEYS.LEGACY_GRAPH_STATE);
|
|
localStorage.removeItem('constellation:lastSaved'); // Old timestamp key
|
|
|
|
console.log('Migration completed successfully');
|
|
return workspace;
|
|
} catch (error) {
|
|
console.error('Migration failed:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Check if migration is needed
|
|
export function needsMigration(): boolean {
|
|
const hasWorkspace = localStorage.getItem(WORKSPACE_STORAGE_KEYS.WORKSPACE_STATE) !== null;
|
|
const hasLegacyData = localStorage.getItem(WORKSPACE_STORAGE_KEYS.LEGACY_GRAPH_STATE) !== null;
|
|
|
|
return !hasWorkspace && hasLegacyData;
|
|
}
|