@myrialabs/clopen 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +6 -0
- package/.github/workflows/release.yml +60 -0
- package/.github/workflows/test.yml +40 -0
- package/CONTRIBUTING.md +499 -0
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/backend/index.ts +156 -0
- package/backend/lib/chat/helpers.ts +42 -0
- package/backend/lib/chat/index.ts +2 -0
- package/backend/lib/chat/stream-manager.ts +1126 -0
- package/backend/lib/database/README.md +77 -0
- package/backend/lib/database/index.ts +119 -0
- package/backend/lib/database/migrations/001_create_projects_table.ts +31 -0
- package/backend/lib/database/migrations/002_create_chat_sessions_table.ts +33 -0
- package/backend/lib/database/migrations/003_create_messages_table.ts +32 -0
- package/backend/lib/database/migrations/004_create_prompt_templates_table.ts +34 -0
- package/backend/lib/database/migrations/005_create_settings_table.ts +24 -0
- package/backend/lib/database/migrations/006_add_user_to_messages.ts +58 -0
- package/backend/lib/database/migrations/007_create_stream_states_table.ts +41 -0
- package/backend/lib/database/migrations/008_create_message_snapshots_table.ts +62 -0
- package/backend/lib/database/migrations/009_add_delta_snapshot_fields.ts +41 -0
- package/backend/lib/database/migrations/010_add_soft_delete_and_branch_support.ts +70 -0
- package/backend/lib/database/migrations/011_git_like_commit_graph.ts +156 -0
- package/backend/lib/database/migrations/012_add_file_change_statistics.ts +41 -0
- package/backend/lib/database/migrations/013_checkpoint_tree_state.ts +118 -0
- package/backend/lib/database/migrations/014_add_engine_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/015_add_model_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/016_create_user_projects_table.ts +34 -0
- package/backend/lib/database/migrations/017_add_current_session_to_user_projects.ts +32 -0
- package/backend/lib/database/migrations/018_create_claude_accounts_table.ts +24 -0
- package/backend/lib/database/migrations/019_add_claude_account_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/020_add_snapshot_tree_hash.ts +32 -0
- package/backend/lib/database/migrations/021_drop_prompt_templates_table.ts +33 -0
- package/backend/lib/database/migrations/index.ts +154 -0
- package/backend/lib/database/queries/checkpoint-queries.ts +87 -0
- package/backend/lib/database/queries/engine-queries.ts +75 -0
- package/backend/lib/database/queries/index.ts +9 -0
- package/backend/lib/database/queries/message-queries.ts +472 -0
- package/backend/lib/database/queries/project-queries.ts +117 -0
- package/backend/lib/database/queries/session-queries.ts +271 -0
- package/backend/lib/database/queries/settings-queries.ts +34 -0
- package/backend/lib/database/queries/snapshot-queries.ts +326 -0
- package/backend/lib/database/queries/utils-queries.ts +59 -0
- package/backend/lib/database/seeders/index.ts +13 -0
- package/backend/lib/database/seeders/settings_seeder.ts +84 -0
- package/backend/lib/database/utils/connection.ts +174 -0
- package/backend/lib/database/utils/index.ts +4 -0
- package/backend/lib/database/utils/migration-runner.ts +118 -0
- package/backend/lib/database/utils/seeder-runner.ts +121 -0
- package/backend/lib/engine/adapters/claude/environment.ts +164 -0
- package/backend/lib/engine/adapters/claude/error-handler.ts +60 -0
- package/backend/lib/engine/adapters/claude/index.ts +1 -0
- package/backend/lib/engine/adapters/claude/path-utils.ts +38 -0
- package/backend/lib/engine/adapters/claude/stream.ts +177 -0
- package/backend/lib/engine/adapters/opencode/index.ts +2 -0
- package/backend/lib/engine/adapters/opencode/message-converter.ts +862 -0
- package/backend/lib/engine/adapters/opencode/server.ts +104 -0
- package/backend/lib/engine/adapters/opencode/stream.ts +755 -0
- package/backend/lib/engine/index.ts +196 -0
- package/backend/lib/engine/types.ts +58 -0
- package/backend/lib/files/file-operations.ts +478 -0
- package/backend/lib/files/file-reading.ts +308 -0
- package/backend/lib/files/file-watcher.ts +383 -0
- package/backend/lib/files/path-browsing.ts +382 -0
- package/backend/lib/git/git-executor.ts +88 -0
- package/backend/lib/git/git-parser.ts +411 -0
- package/backend/lib/git/git-service.ts +505 -0
- package/backend/lib/mcp/README.md +1144 -0
- package/backend/lib/mcp/config.ts +316 -0
- package/backend/lib/mcp/index.ts +35 -0
- package/backend/lib/mcp/project-context.ts +236 -0
- package/backend/lib/mcp/servers/browser-automation/actions.ts +156 -0
- package/backend/lib/mcp/servers/browser-automation/browser.ts +419 -0
- package/backend/lib/mcp/servers/browser-automation/index.ts +791 -0
- package/backend/lib/mcp/servers/browser-automation/inspection.ts +501 -0
- package/backend/lib/mcp/servers/helper.ts +143 -0
- package/backend/lib/mcp/servers/index.ts +45 -0
- package/backend/lib/mcp/servers/weather/get-temperature.ts +56 -0
- package/backend/lib/mcp/servers/weather/index.ts +31 -0
- package/backend/lib/mcp/stdio-server.ts +103 -0
- package/backend/lib/mcp/types.ts +65 -0
- package/backend/lib/preview/browser/browser-audio-capture.ts +86 -0
- package/backend/lib/preview/browser/browser-console-manager.ts +263 -0
- package/backend/lib/preview/browser/browser-dialog-handler.ts +222 -0
- package/backend/lib/preview/browser/browser-interaction-handler.ts +421 -0
- package/backend/lib/preview/browser/browser-mcp-control.ts +415 -0
- package/backend/lib/preview/browser/browser-native-ui-handler.ts +512 -0
- package/backend/lib/preview/browser/browser-navigation-tracker.ts +104 -0
- package/backend/lib/preview/browser/browser-pool.ts +357 -0
- package/backend/lib/preview/browser/browser-preview-service.ts +882 -0
- package/backend/lib/preview/browser/browser-tab-manager.ts +935 -0
- package/backend/lib/preview/browser/browser-video-capture.ts +695 -0
- package/backend/lib/preview/browser/scripts/audio-stream.ts +292 -0
- package/backend/lib/preview/browser/scripts/cursor-tracking.ts +85 -0
- package/backend/lib/preview/browser/scripts/video-stream.ts +438 -0
- package/backend/lib/preview/browser/types.ts +359 -0
- package/backend/lib/preview/index.ts +24 -0
- package/backend/lib/project/index.ts +2 -0
- package/backend/lib/project/status-manager.ts +182 -0
- package/backend/lib/shared/index.ts +2 -0
- package/backend/lib/shared/port-utils.ts +25 -0
- package/backend/lib/shared/process-manager.ts +281 -0
- package/backend/lib/snapshot/blob-store.ts +227 -0
- package/backend/lib/snapshot/gitignore.ts +307 -0
- package/backend/lib/snapshot/helpers.ts +397 -0
- package/backend/lib/snapshot/snapshot-service.ts +483 -0
- package/backend/lib/terminal/helpers.ts +14 -0
- package/backend/lib/terminal/index.ts +8 -0
- package/backend/lib/terminal/pty-manager.ts +4 -0
- package/backend/lib/terminal/pty-session-manager.ts +387 -0
- package/backend/lib/terminal/shell-utils.ts +313 -0
- package/backend/lib/terminal/stream-manager.ts +293 -0
- package/backend/lib/tunnel/global-tunnel-manager.ts +243 -0
- package/backend/lib/tunnel/project-tunnel-manager.ts +311 -0
- package/backend/lib/user/helpers.ts +87 -0
- package/backend/lib/utils/ws.ts +944 -0
- package/backend/lib/vite-dev.ts +353 -0
- package/backend/middleware/cors.ts +15 -0
- package/backend/middleware/error-handler.ts +49 -0
- package/backend/middleware/logger.ts +9 -0
- package/backend/types/api.ts +24 -0
- package/backend/ws/README.md +1505 -0
- package/backend/ws/chat/background.ts +198 -0
- package/backend/ws/chat/index.ts +21 -0
- package/backend/ws/chat/stream.ts +707 -0
- package/backend/ws/engine/claude/accounts.ts +401 -0
- package/backend/ws/engine/claude/index.ts +13 -0
- package/backend/ws/engine/claude/status.ts +43 -0
- package/backend/ws/engine/index.ts +14 -0
- package/backend/ws/engine/opencode/index.ts +11 -0
- package/backend/ws/engine/opencode/status.ts +30 -0
- package/backend/ws/engine/utils.ts +36 -0
- package/backend/ws/files/index.ts +30 -0
- package/backend/ws/files/read.ts +189 -0
- package/backend/ws/files/search.ts +453 -0
- package/backend/ws/files/watch.ts +124 -0
- package/backend/ws/files/write.ts +143 -0
- package/backend/ws/git/branch.ts +106 -0
- package/backend/ws/git/commit.ts +39 -0
- package/backend/ws/git/conflict.ts +68 -0
- package/backend/ws/git/diff.ts +69 -0
- package/backend/ws/git/index.ts +24 -0
- package/backend/ws/git/log.ts +41 -0
- package/backend/ws/git/remote.ts +214 -0
- package/backend/ws/git/staging.ts +84 -0
- package/backend/ws/git/status.ts +90 -0
- package/backend/ws/index.ts +69 -0
- package/backend/ws/mcp/index.ts +61 -0
- package/backend/ws/messages/crud.ts +74 -0
- package/backend/ws/messages/index.ts +14 -0
- package/backend/ws/preview/browser/cleanup.ts +129 -0
- package/backend/ws/preview/browser/console.ts +114 -0
- package/backend/ws/preview/browser/interact.ts +513 -0
- package/backend/ws/preview/browser/mcp.ts +129 -0
- package/backend/ws/preview/browser/native-ui.ts +235 -0
- package/backend/ws/preview/browser/stats.ts +55 -0
- package/backend/ws/preview/browser/tab-info.ts +126 -0
- package/backend/ws/preview/browser/tab.ts +166 -0
- package/backend/ws/preview/browser/webcodecs.ts +293 -0
- package/backend/ws/preview/index.ts +146 -0
- package/backend/ws/projects/crud.ts +113 -0
- package/backend/ws/projects/index.ts +25 -0
- package/backend/ws/projects/presence.ts +46 -0
- package/backend/ws/projects/status.ts +116 -0
- package/backend/ws/sessions/crud.ts +327 -0
- package/backend/ws/sessions/index.ts +33 -0
- package/backend/ws/settings/crud.ts +112 -0
- package/backend/ws/settings/index.ts +14 -0
- package/backend/ws/snapshot/index.ts +17 -0
- package/backend/ws/snapshot/restore.ts +173 -0
- package/backend/ws/snapshot/timeline.ts +141 -0
- package/backend/ws/system/index.ts +14 -0
- package/backend/ws/system/operations.ts +49 -0
- package/backend/ws/terminal/index.ts +40 -0
- package/backend/ws/terminal/persistence.ts +153 -0
- package/backend/ws/terminal/session.ts +382 -0
- package/backend/ws/terminal/stream.ts +79 -0
- package/backend/ws/tunnel/index.ts +14 -0
- package/backend/ws/tunnel/operations.ts +91 -0
- package/backend/ws/types.ts +20 -0
- package/backend/ws/user/crud.ts +156 -0
- package/backend/ws/user/index.ts +14 -0
- package/bin/clopen.ts +307 -0
- package/bun.lock +1352 -0
- package/frontend/App.svelte +34 -0
- package/frontend/app.css +313 -0
- package/frontend/lib/app-environment.ts +10 -0
- package/frontend/lib/components/chat/ChatInterface.svelte +407 -0
- package/frontend/lib/components/chat/formatters/ErrorMessage.svelte +57 -0
- package/frontend/lib/components/chat/formatters/MessageFormatter.svelte +224 -0
- package/frontend/lib/components/chat/formatters/TextMessage.svelte +395 -0
- package/frontend/lib/components/chat/formatters/Tools.svelte +70 -0
- package/frontend/lib/components/chat/formatters/index.ts +3 -0
- package/frontend/lib/components/chat/input/ChatInput.svelte +421 -0
- package/frontend/lib/components/chat/input/components/ChatInputActions.svelte +78 -0
- package/frontend/lib/components/chat/input/components/DragDropOverlay.svelte +30 -0
- package/frontend/lib/components/chat/input/components/EditModeIndicator.svelte +33 -0
- package/frontend/lib/components/chat/input/components/EngineModelPicker.svelte +619 -0
- package/frontend/lib/components/chat/input/components/FileAttachmentPreview.svelte +48 -0
- package/frontend/lib/components/chat/input/components/LoadingIndicator.svelte +31 -0
- package/frontend/lib/components/chat/input/composables/use-animations.svelte.ts +201 -0
- package/frontend/lib/components/chat/input/composables/use-chat-actions.svelte.ts +148 -0
- package/frontend/lib/components/chat/input/composables/use-file-handling.svelte.ts +216 -0
- package/frontend/lib/components/chat/input/composables/use-input-state.svelte.ts +357 -0
- package/frontend/lib/components/chat/input/composables/use-textarea-resize.svelte.ts +57 -0
- package/frontend/lib/components/chat/message/ChatMessage.svelte +478 -0
- package/frontend/lib/components/chat/message/ChatMessages.svelte +541 -0
- package/frontend/lib/components/chat/message/DateSeparator.svelte +86 -0
- package/frontend/lib/components/chat/message/MessageBubble.svelte +86 -0
- package/frontend/lib/components/chat/message/MessageHeader.svelte +157 -0
- package/frontend/lib/components/chat/modal/DebugModal.svelte +59 -0
- package/frontend/lib/components/chat/modal/TokenUsageModal.svelte +124 -0
- package/frontend/lib/components/chat/shared/index.ts +2 -0
- package/frontend/lib/components/chat/shared/utils.ts +116 -0
- package/frontend/lib/components/chat/tools/BashOutputTool.svelte +35 -0
- package/frontend/lib/components/chat/tools/BashTool.svelte +46 -0
- package/frontend/lib/components/chat/tools/CustomMcpTool.svelte +139 -0
- package/frontend/lib/components/chat/tools/EditTool.svelte +48 -0
- package/frontend/lib/components/chat/tools/ExitPlanModeTool.svelte +32 -0
- package/frontend/lib/components/chat/tools/GlobTool.svelte +51 -0
- package/frontend/lib/components/chat/tools/GrepTool.svelte +90 -0
- package/frontend/lib/components/chat/tools/KillShellTool.svelte +26 -0
- package/frontend/lib/components/chat/tools/ListMcpResourcesTool.svelte +31 -0
- package/frontend/lib/components/chat/tools/NotebookEditTool.svelte +38 -0
- package/frontend/lib/components/chat/tools/ReadMcpResourceTool.svelte +34 -0
- package/frontend/lib/components/chat/tools/ReadTool.svelte +41 -0
- package/frontend/lib/components/chat/tools/TaskTool.svelte +64 -0
- package/frontend/lib/components/chat/tools/TodoWriteTool.svelte +75 -0
- package/frontend/lib/components/chat/tools/WebFetchTool.svelte +35 -0
- package/frontend/lib/components/chat/tools/WebSearchTool.svelte +84 -0
- package/frontend/lib/components/chat/tools/WriteTool.svelte +33 -0
- package/frontend/lib/components/chat/tools/components/CodeBlock.svelte +79 -0
- package/frontend/lib/components/chat/tools/components/DiffBlock.svelte +408 -0
- package/frontend/lib/components/chat/tools/components/FileHeader.svelte +45 -0
- package/frontend/lib/components/chat/tools/components/InfoLine.svelte +19 -0
- package/frontend/lib/components/chat/tools/components/StatsBadges.svelte +27 -0
- package/frontend/lib/components/chat/tools/components/TerminalCommand.svelte +54 -0
- package/frontend/lib/components/chat/tools/components/index.ts +7 -0
- package/frontend/lib/components/chat/tools/index.ts +26 -0
- package/frontend/lib/components/chat/widgets/FloatingTodoList.svelte +249 -0
- package/frontend/lib/components/chat/widgets/TokenUsage.svelte +78 -0
- package/frontend/lib/components/checkpoint/TimelineModal.svelte +391 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineEdge.svelte +26 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineGraph.svelte +87 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineNode.svelte +108 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineVersionGroup.svelte +59 -0
- package/frontend/lib/components/checkpoint/timeline/animation.ts +168 -0
- package/frontend/lib/components/checkpoint/timeline/config.ts +44 -0
- package/frontend/lib/components/checkpoint/timeline/graph-builder.ts +304 -0
- package/frontend/lib/components/checkpoint/timeline/types.ts +65 -0
- package/frontend/lib/components/checkpoint/timeline/utils.ts +53 -0
- package/frontend/lib/components/common/Alert.svelte +139 -0
- package/frontend/lib/components/common/AvatarBubble.svelte +56 -0
- package/frontend/lib/components/common/Button.svelte +71 -0
- package/frontend/lib/components/common/Card.svelte +102 -0
- package/frontend/lib/components/common/Checkbox.svelte +48 -0
- package/frontend/lib/components/common/Dialog.svelte +249 -0
- package/frontend/lib/components/common/FolderBrowser.svelte +843 -0
- package/frontend/lib/components/common/Icon.svelte +58 -0
- package/frontend/lib/components/common/Input.svelte +72 -0
- package/frontend/lib/components/common/Lightbox.svelte +233 -0
- package/frontend/lib/components/common/LoadingScreen.svelte +52 -0
- package/frontend/lib/components/common/LoadingSpinner.svelte +48 -0
- package/frontend/lib/components/common/Modal.svelte +177 -0
- package/frontend/lib/components/common/ModalProvider.svelte +28 -0
- package/frontend/lib/components/common/ModelSelector.svelte +110 -0
- package/frontend/lib/components/common/MonacoEditor.svelte +569 -0
- package/frontend/lib/components/common/NotificationToast.svelte +113 -0
- package/frontend/lib/components/common/PageTemplate.svelte +76 -0
- package/frontend/lib/components/common/ProjectUserAvatars.svelte +79 -0
- package/frontend/lib/components/common/Select.svelte +98 -0
- package/frontend/lib/components/common/Textarea.svelte +80 -0
- package/frontend/lib/components/common/ThemeToggle.svelte +44 -0
- package/frontend/lib/components/common/lucide-icons.ts +1642 -0
- package/frontend/lib/components/common/material-icons.ts +1082 -0
- package/frontend/lib/components/common/xterm/XTerm.svelte +796 -0
- package/frontend/lib/components/common/xterm/index.ts +16 -0
- package/frontend/lib/components/common/xterm/terminal-config.ts +68 -0
- package/frontend/lib/components/common/xterm/types.ts +31 -0
- package/frontend/lib/components/common/xterm/xterm-service.ts +353 -0
- package/frontend/lib/components/files/FileNode.svelte +384 -0
- package/frontend/lib/components/files/FileTree.svelte +681 -0
- package/frontend/lib/components/files/FileViewer.svelte +728 -0
- package/frontend/lib/components/files/SearchResults.svelte +303 -0
- package/frontend/lib/components/git/BranchManager.svelte +458 -0
- package/frontend/lib/components/git/ChangesSection.svelte +107 -0
- package/frontend/lib/components/git/CommitForm.svelte +76 -0
- package/frontend/lib/components/git/ConflictResolver.svelte +158 -0
- package/frontend/lib/components/git/DiffViewer.svelte +364 -0
- package/frontend/lib/components/git/FileChangeItem.svelte +97 -0
- package/frontend/lib/components/git/GitButton.svelte +33 -0
- package/frontend/lib/components/git/GitLog.svelte +361 -0
- package/frontend/lib/components/git/GitModal.svelte +80 -0
- package/frontend/lib/components/history/HistoryModal.svelte +563 -0
- package/frontend/lib/components/history/HistoryView.svelte +615 -0
- package/frontend/lib/components/index.ts +34 -0
- package/frontend/lib/components/preview/browser/BrowserPreview.svelte +549 -0
- package/frontend/lib/components/preview/browser/components/Canvas.svelte +1058 -0
- package/frontend/lib/components/preview/browser/components/ConsolePanel.svelte +757 -0
- package/frontend/lib/components/preview/browser/components/Container.svelte +450 -0
- package/frontend/lib/components/preview/browser/components/ContextMenu.svelte +236 -0
- package/frontend/lib/components/preview/browser/components/SelectDropdown.svelte +224 -0
- package/frontend/lib/components/preview/browser/components/Toolbar.svelte +339 -0
- package/frontend/lib/components/preview/browser/components/VirtualCursor.svelte +36 -0
- package/frontend/lib/components/preview/browser/core/cleanup.svelte.ts +155 -0
- package/frontend/lib/components/preview/browser/core/coordinator.svelte.ts +837 -0
- package/frontend/lib/components/preview/browser/core/interactions.svelte.ts +113 -0
- package/frontend/lib/components/preview/browser/core/mcp-handlers.svelte.ts +296 -0
- package/frontend/lib/components/preview/browser/core/native-ui-handlers.svelte.ts +391 -0
- package/frontend/lib/components/preview/browser/core/stream-handler.svelte.ts +231 -0
- package/frontend/lib/components/preview/browser/core/tab-manager.svelte.ts +210 -0
- package/frontend/lib/components/preview/browser/core/tab-operations.svelte.ts +239 -0
- package/frontend/lib/components/preview/index.ts +2 -0
- package/frontend/lib/components/settings/SettingsModal.svelte +235 -0
- package/frontend/lib/components/settings/SettingsView.svelte +36 -0
- package/frontend/lib/components/settings/appearance/AppearanceSettings.svelte +51 -0
- package/frontend/lib/components/settings/appearance/LayoutPresetSettings.svelte +160 -0
- package/frontend/lib/components/settings/appearance/LayoutPreview.svelte +76 -0
- package/frontend/lib/components/settings/engines/AIEnginesSettings.svelte +917 -0
- package/frontend/lib/components/settings/general/AdvancedSettings.svelte +187 -0
- package/frontend/lib/components/settings/general/DataManagementSettings.svelte +203 -0
- package/frontend/lib/components/settings/general/GeneralSettings.svelte +10 -0
- package/frontend/lib/components/settings/model/ModelSettings.svelte +357 -0
- package/frontend/lib/components/settings/notifications/NotificationSettings.svelte +205 -0
- package/frontend/lib/components/settings/user/UserSettings.svelte +197 -0
- package/frontend/lib/components/terminal/Terminal.svelte +368 -0
- package/frontend/lib/components/terminal/TerminalTabs.svelte +87 -0
- package/frontend/lib/components/terminal/TerminalView.svelte +55 -0
- package/frontend/lib/components/tunnel/TunnelActive.svelte +142 -0
- package/frontend/lib/components/tunnel/TunnelButton.svelte +54 -0
- package/frontend/lib/components/tunnel/TunnelInactive.svelte +284 -0
- package/frontend/lib/components/tunnel/TunnelModal.svelte +47 -0
- package/frontend/lib/components/tunnel/TunnelQRCode.svelte +49 -0
- package/frontend/lib/components/workspace/DesktopNavigator.svelte +382 -0
- package/frontend/lib/components/workspace/MobileNavigator.svelte +403 -0
- package/frontend/lib/components/workspace/PanelContainer.svelte +100 -0
- package/frontend/lib/components/workspace/PanelHeader.svelte +505 -0
- package/frontend/lib/components/workspace/ViewMenu.svelte +162 -0
- package/frontend/lib/components/workspace/WorkspaceLayout.svelte +169 -0
- package/frontend/lib/components/workspace/layout/DesktopLayout.svelte +15 -0
- package/frontend/lib/components/workspace/layout/MobileLayout.svelte +17 -0
- package/frontend/lib/components/workspace/layout/split-pane/Container.svelte +42 -0
- package/frontend/lib/components/workspace/layout/split-pane/Handle.svelte +85 -0
- package/frontend/lib/components/workspace/layout/split-pane/Layout.svelte +37 -0
- package/frontend/lib/components/workspace/panels/ChatPanel.svelte +274 -0
- package/frontend/lib/components/workspace/panels/FilesPanel.svelte +1261 -0
- package/frontend/lib/components/workspace/panels/GitPanel.svelte +1560 -0
- package/frontend/lib/components/workspace/panels/PreviewPanel.svelte +150 -0
- package/frontend/lib/components/workspace/panels/TerminalPanel.svelte +73 -0
- package/frontend/lib/constants/preview.ts +45 -0
- package/frontend/lib/services/chat/chat.service.ts +704 -0
- package/frontend/lib/services/chat/index.ts +7 -0
- package/frontend/lib/services/notification/global-stream-monitor.ts +86 -0
- package/frontend/lib/services/notification/index.ts +8 -0
- package/frontend/lib/services/notification/push.service.ts +144 -0
- package/frontend/lib/services/notification/sound.service.ts +127 -0
- package/frontend/lib/services/preview/browser/browser-console.service.ts +61 -0
- package/frontend/lib/services/preview/browser/browser-webcodecs.service.ts +1499 -0
- package/frontend/lib/services/preview/browser/mcp-integration.svelte.ts +67 -0
- package/frontend/lib/services/preview/index.ts +23 -0
- package/frontend/lib/services/project/index.ts +8 -0
- package/frontend/lib/services/project/status.service.ts +159 -0
- package/frontend/lib/services/snapshot/snapshot.service.ts +47 -0
- package/frontend/lib/services/terminal/background/index.ts +130 -0
- package/frontend/lib/services/terminal/background/session-restore.ts +274 -0
- package/frontend/lib/services/terminal/background/stream-manager.ts +286 -0
- package/frontend/lib/services/terminal/index.ts +14 -0
- package/frontend/lib/services/terminal/persistence.service.ts +260 -0
- package/frontend/lib/services/terminal/project.service.ts +953 -0
- package/frontend/lib/services/terminal/session.service.ts +364 -0
- package/frontend/lib/services/terminal/terminal.service.ts +369 -0
- package/frontend/lib/stores/core/app.svelte.ts +117 -0
- package/frontend/lib/stores/core/files.svelte.ts +73 -0
- package/frontend/lib/stores/core/presence.svelte.ts +48 -0
- package/frontend/lib/stores/core/projects.svelte.ts +317 -0
- package/frontend/lib/stores/core/sessions.svelte.ts +383 -0
- package/frontend/lib/stores/features/claude-accounts.svelte.ts +58 -0
- package/frontend/lib/stores/features/models.svelte.ts +89 -0
- package/frontend/lib/stores/features/settings.svelte.ts +87 -0
- package/frontend/lib/stores/features/terminal.svelte.ts +701 -0
- package/frontend/lib/stores/features/tunnel.svelte.ts +161 -0
- package/frontend/lib/stores/features/user.svelte.ts +96 -0
- package/frontend/lib/stores/ui/chat-input.svelte.ts +57 -0
- package/frontend/lib/stores/ui/chat-model.svelte.ts +61 -0
- package/frontend/lib/stores/ui/dialog.svelte.ts +59 -0
- package/frontend/lib/stores/ui/edit-mode.svelte.ts +214 -0
- package/frontend/lib/stores/ui/notification.svelte.ts +166 -0
- package/frontend/lib/stores/ui/settings-modal.svelte.ts +88 -0
- package/frontend/lib/stores/ui/theme.svelte.ts +179 -0
- package/frontend/lib/stores/ui/workspace.svelte.ts +754 -0
- package/frontend/lib/types/native-ui.ts +73 -0
- package/frontend/lib/utils/chat/date-separator.ts +39 -0
- package/frontend/lib/utils/chat/message-grouper.ts +219 -0
- package/frontend/lib/utils/chat/message-processor.ts +135 -0
- package/frontend/lib/utils/chat/tool-handler.ts +161 -0
- package/frontend/lib/utils/chat/virtual-scroll.svelte.ts +142 -0
- package/frontend/lib/utils/click-outside.ts +20 -0
- package/frontend/lib/utils/context-manager.ts +257 -0
- package/frontend/lib/utils/file-icon-mappings.ts +769 -0
- package/frontend/lib/utils/folder-icon-mappings.ts +1030 -0
- package/frontend/lib/utils/git-status.ts +68 -0
- package/frontend/lib/utils/platform.ts +113 -0
- package/frontend/lib/utils/port-check.ts +65 -0
- package/frontend/lib/utils/terminalFormatter.ts +207 -0
- package/frontend/lib/utils/theme.ts +6 -0
- package/frontend/lib/utils/tree-visualizer.ts +320 -0
- package/frontend/lib/utils/ws.ts +44 -0
- package/frontend/main.ts +13 -0
- package/index.html +70 -0
- package/package.json +111 -0
- package/scripts/generate-icons.ts +87 -0
- package/scripts/pre-publish-check.sh +142 -0
- package/scripts/setup-hooks.sh +134 -0
- package/scripts/validate-branch-name.sh +47 -0
- package/scripts/validate-commit-msg.sh +42 -0
- package/shared/constants/engines.ts +134 -0
- package/shared/types/database/connection.ts +16 -0
- package/shared/types/database/index.ts +6 -0
- package/shared/types/database/schema.ts +141 -0
- package/shared/types/engine/index.ts +45 -0
- package/shared/types/filesystem/index.ts +22 -0
- package/shared/types/git.ts +171 -0
- package/shared/types/messaging/index.ts +239 -0
- package/shared/types/messaging/tool.ts +526 -0
- package/shared/types/network/api.ts +18 -0
- package/shared/types/network/index.ts +5 -0
- package/shared/types/stores/app.ts +23 -0
- package/shared/types/stores/dialog.ts +21 -0
- package/shared/types/stores/index.ts +3 -0
- package/shared/types/stores/settings.ts +15 -0
- package/shared/types/terminal/index.ts +44 -0
- package/shared/types/ui/components.ts +61 -0
- package/shared/types/ui/icons.ts +23 -0
- package/shared/types/ui/index.ts +22 -0
- package/shared/types/ui/notifications.ts +14 -0
- package/shared/types/ui/theme.ts +12 -0
- package/shared/types/websocket/index.ts +43 -0
- package/shared/types/window.d.ts +13 -0
- package/shared/utils/anonymous-user.ts +168 -0
- package/shared/utils/async.ts +10 -0
- package/shared/utils/diff-calculator.ts +184 -0
- package/shared/utils/file-type-detection.ts +166 -0
- package/shared/utils/logger.ts +158 -0
- package/shared/utils/message-formatter.ts +79 -0
- package/shared/utils/path.ts +47 -0
- package/shared/utils/ws-client.ts +768 -0
- package/shared/utils/ws-server.ts +660 -0
- package/static/audio/notification.ogg +0 -0
- package/static/favicon.svg +8 -0
- package/static/fonts/dm-sans/dm-sans-italic-latin-ext.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-italic-latin.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-normal-latin-ext.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-normal-latin.woff2 +0 -0
- package/static/fonts/dm-sans.css +96 -0
- package/svelte.config.js +20 -0
- package/vite.config.ts +33 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeline animation utilities
|
|
3
|
+
* Handles FLIP animation technique and interpolation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ANIMATION, SIZE } from './config';
|
|
7
|
+
import type { GraphNode, GraphEdge, VersionGroup, AnimationState } from './types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Cubic bezier easing function (ease-in-out)
|
|
11
|
+
*/
|
|
12
|
+
export function cubicBezier(t: number): number {
|
|
13
|
+
// ease-in-out: cubic-bezier(0.42, 0, 0.58, 1)
|
|
14
|
+
const x1 = 0.42, x2 = 0.58;
|
|
15
|
+
|
|
16
|
+
const c = 3 * x1;
|
|
17
|
+
const b = 3 * (x2 - x1) - c;
|
|
18
|
+
const a = 1 - c - b;
|
|
19
|
+
|
|
20
|
+
const t2 = t * t;
|
|
21
|
+
const t3 = t2 * t;
|
|
22
|
+
|
|
23
|
+
return a * t3 + b * t2 + c * t;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get interpolated position for a node
|
|
28
|
+
*/
|
|
29
|
+
export function getInterpolatedPosition(
|
|
30
|
+
node: GraphNode,
|
|
31
|
+
animationState: AnimationState
|
|
32
|
+
): { x: number, y: number } {
|
|
33
|
+
if (!animationState.isAnimating) {
|
|
34
|
+
return { x: node.x, y: node.y };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const oldPos = animationState.oldNodePositions.get(node.id);
|
|
38
|
+
if (!oldPos) {
|
|
39
|
+
// Node is new, just use current position
|
|
40
|
+
return { x: node.x, y: node.y };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Interpolate between old and new position
|
|
44
|
+
const x = oldPos.x + (node.x - oldPos.x) * animationState.progress;
|
|
45
|
+
const y = oldPos.y + (node.y - oldPos.y) * animationState.progress;
|
|
46
|
+
|
|
47
|
+
return { x, y };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get interpolated color for a node
|
|
52
|
+
*/
|
|
53
|
+
export function getInterpolatedNodeClass(node: GraphNode): string {
|
|
54
|
+
if (node.isCurrent) {
|
|
55
|
+
return 'fill-green-500 stroke-green-300';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Orphaned nodes (descendants of current active checkpoint) are always gray
|
|
59
|
+
if (node.isOrphaned) {
|
|
60
|
+
return 'fill-slate-300 stroke-slate-200 dark:fill-slate-600 dark:stroke-slate-500';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// All non-orphaned nodes use blue (Active Path)
|
|
64
|
+
return 'fill-blue-500 stroke-blue-300';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get interpolated path for an edge
|
|
69
|
+
* Recalculates path using interpolated node positions for smooth transitions
|
|
70
|
+
*/
|
|
71
|
+
export function getInterpolatedEdgePath(
|
|
72
|
+
edge: GraphEdge,
|
|
73
|
+
graphNodes: GraphNode[],
|
|
74
|
+
animationState: AnimationState
|
|
75
|
+
): string {
|
|
76
|
+
if (!animationState.isAnimating) {
|
|
77
|
+
return edge.path;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Find the from and to nodes
|
|
81
|
+
const fromNode = graphNodes.find(n => n.id === edge.from);
|
|
82
|
+
const toNode = graphNodes.find(n => n.id === edge.to);
|
|
83
|
+
|
|
84
|
+
if (!fromNode || !toNode) {
|
|
85
|
+
return edge.path;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Get interpolated positions
|
|
89
|
+
const fromPos = getInterpolatedPosition(fromNode, animationState);
|
|
90
|
+
const toPos = getInterpolatedPosition(toNode, animationState);
|
|
91
|
+
|
|
92
|
+
// Recalculate path with interpolated positions
|
|
93
|
+
if (edge.type === 'straight') {
|
|
94
|
+
// Straight vertical line
|
|
95
|
+
return `M ${fromPos.x} ${fromPos.y + SIZE.node} L ${toPos.x} ${toPos.y - SIZE.node}`;
|
|
96
|
+
} else if (edge.type === 'branch') {
|
|
97
|
+
// Horizontal line from main vertical line to branch
|
|
98
|
+
const startX = fromPos.x + 1;
|
|
99
|
+
const endX = toPos.x - SIZE.node;
|
|
100
|
+
const lineY = toPos.y;
|
|
101
|
+
return `M ${startX} ${lineY} L ${endX} ${lineY}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return edge.path;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get interpolated bracket position for version group
|
|
109
|
+
*/
|
|
110
|
+
export function getInterpolatedBracket(
|
|
111
|
+
group: VersionGroup,
|
|
112
|
+
animationState: AnimationState
|
|
113
|
+
): { minY: number, maxY: number, height: number } {
|
|
114
|
+
if (!animationState.isAnimating) {
|
|
115
|
+
return {
|
|
116
|
+
minY: group.minY,
|
|
117
|
+
maxY: group.maxY,
|
|
118
|
+
height: group.height
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Find old group with same branch name
|
|
123
|
+
const oldGroup = animationState.oldVersionGroups.find(g => g.branchName === group.branchName);
|
|
124
|
+
if (!oldGroup) {
|
|
125
|
+
// New group, just use current values
|
|
126
|
+
return {
|
|
127
|
+
minY: group.minY,
|
|
128
|
+
maxY: group.maxY,
|
|
129
|
+
height: group.height
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Interpolate bracket position
|
|
134
|
+
const minY = oldGroup.minY + (group.minY - oldGroup.minY) * animationState.progress;
|
|
135
|
+
const maxY = oldGroup.maxY + (group.maxY - oldGroup.maxY) * animationState.progress;
|
|
136
|
+
const height = oldGroup.height + (group.height - oldGroup.height) * animationState.progress;
|
|
137
|
+
|
|
138
|
+
return { minY, maxY, height };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Start animation loop
|
|
143
|
+
*/
|
|
144
|
+
export function startAnimation(
|
|
145
|
+
targetNodeId: string,
|
|
146
|
+
onProgressUpdate: (progress: number) => void,
|
|
147
|
+
onComplete: () => void
|
|
148
|
+
): void {
|
|
149
|
+
const startTime = performance.now();
|
|
150
|
+
|
|
151
|
+
function animate(currentTime: number) {
|
|
152
|
+
const elapsed = currentTime - startTime;
|
|
153
|
+
const rawProgress = Math.min(elapsed / ANIMATION.duration, 1);
|
|
154
|
+
|
|
155
|
+
// Apply easing
|
|
156
|
+
const easedProgress = cubicBezier(rawProgress);
|
|
157
|
+
onProgressUpdate(easedProgress);
|
|
158
|
+
|
|
159
|
+
if (rawProgress < 1) {
|
|
160
|
+
requestAnimationFrame(animate);
|
|
161
|
+
} else {
|
|
162
|
+
// Animation complete
|
|
163
|
+
onComplete();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
requestAnimationFrame(animate);
|
|
168
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeline visualization configuration
|
|
3
|
+
* Contains all constants for spacing, sizing, layout, style, and animation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Spacing (distances between elements)
|
|
7
|
+
export const SPACING = {
|
|
8
|
+
nodeGap: 52, // Vertical gap between nodes in same timeline
|
|
9
|
+
branchGap: 0, // Gap between different branches from same point
|
|
10
|
+
checkpointToBranch: 52, // Gap from checkpoint to first branch
|
|
11
|
+
branchToCheckpoint: 52, // Gap from last branch to next checkpoint
|
|
12
|
+
branchIndent: 32, // Horizontal indent for branches
|
|
13
|
+
labelGap: 12, // Gap between node and label
|
|
14
|
+
bracketGap: 10, // Gap from label to bracket
|
|
15
|
+
bracketLength: 15 // Length of bracket lines
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Size (element dimensions)
|
|
19
|
+
export const SIZE = {
|
|
20
|
+
node: 6, // Node radius
|
|
21
|
+
nodeDot: 2, // Inner dot radius
|
|
22
|
+
nodeRing: 3, // Current node ring radius
|
|
23
|
+
line: 2, // Line thickness
|
|
24
|
+
labelWidth: 280, // Label width
|
|
25
|
+
labelHeight: 44 // Label height
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Layout (starting positions)
|
|
29
|
+
export const LAYOUT = {
|
|
30
|
+
startX: 10, // Starting X position
|
|
31
|
+
startY: 30 // Starting Y position
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Style (colors & opacity)
|
|
35
|
+
export const STYLE = {
|
|
36
|
+
lineColor: '#94a3b8',
|
|
37
|
+
lineOpacity: 0.4,
|
|
38
|
+
lineDash: '0' // Dash pattern for branch lines
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Animation configuration
|
|
42
|
+
export const ANIMATION = {
|
|
43
|
+
duration: 300 // ms
|
|
44
|
+
};
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeline Graph Builder (Rewritten)
|
|
3
|
+
*
|
|
4
|
+
* Builds visual tree from checkpoint nodes with parent-child relationships.
|
|
5
|
+
* Uses activeChildId to determine which child continues straight (main line)
|
|
6
|
+
* and which children become branches (indented).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { SPACING, SIZE, LAYOUT } from './config';
|
|
10
|
+
import type { TimelineResponse, GraphNode, GraphEdge, VersionGroup, CheckpointNode } from './types';
|
|
11
|
+
import { debug } from '$shared/utils/logger';
|
|
12
|
+
|
|
13
|
+
export interface GraphData {
|
|
14
|
+
nodes: GraphNode[];
|
|
15
|
+
edges: GraphEdge[];
|
|
16
|
+
versionGroups: VersionGroup[];
|
|
17
|
+
svgWidth: number;
|
|
18
|
+
svgHeight: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface TreeNode {
|
|
22
|
+
checkpoint: CheckpointNode;
|
|
23
|
+
children: TreeNode[];
|
|
24
|
+
activeChild: TreeNode | null; // the child that continues straight
|
|
25
|
+
branchChildren: TreeNode[]; // children that are branches (indented)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build a tree structure from flat checkpoint nodes
|
|
30
|
+
*/
|
|
31
|
+
function buildTree(nodes: CheckpointNode[]): TreeNode[] {
|
|
32
|
+
const nodeMap = new Map<string, TreeNode>();
|
|
33
|
+
|
|
34
|
+
// Create TreeNode for each checkpoint
|
|
35
|
+
for (const cp of nodes) {
|
|
36
|
+
nodeMap.set(cp.id, {
|
|
37
|
+
checkpoint: cp,
|
|
38
|
+
children: [],
|
|
39
|
+
activeChild: null,
|
|
40
|
+
branchChildren: []
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Build parent-child relationships
|
|
45
|
+
const roots: TreeNode[] = [];
|
|
46
|
+
for (const cp of nodes) {
|
|
47
|
+
const treeNode = nodeMap.get(cp.id)!;
|
|
48
|
+
|
|
49
|
+
if (cp.parentId && nodeMap.has(cp.parentId)) {
|
|
50
|
+
const parent = nodeMap.get(cp.parentId)!;
|
|
51
|
+
parent.children.push(treeNode);
|
|
52
|
+
} else {
|
|
53
|
+
roots.push(treeNode);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Determine active child vs branch children for each node
|
|
58
|
+
for (const cp of nodes) {
|
|
59
|
+
const treeNode = nodeMap.get(cp.id)!;
|
|
60
|
+
if (treeNode.children.length === 0) continue;
|
|
61
|
+
|
|
62
|
+
if (treeNode.children.length === 1) {
|
|
63
|
+
// Only one child - it's the active/straight continuation
|
|
64
|
+
treeNode.activeChild = treeNode.children[0];
|
|
65
|
+
treeNode.branchChildren = [];
|
|
66
|
+
} else {
|
|
67
|
+
// Multiple children - use activeChildId to determine which goes straight
|
|
68
|
+
const activeChildNode = cp.activeChildId
|
|
69
|
+
? treeNode.children.find(c => c.checkpoint.id === cp.activeChildId)
|
|
70
|
+
: null;
|
|
71
|
+
|
|
72
|
+
if (activeChildNode) {
|
|
73
|
+
treeNode.activeChild = activeChildNode;
|
|
74
|
+
treeNode.branchChildren = treeNode.children.filter(c => c !== activeChildNode);
|
|
75
|
+
} else {
|
|
76
|
+
// No activeChildId set or not found.
|
|
77
|
+
// Use the child on the active path, or fallback to the first child
|
|
78
|
+
const activePathChild = treeNode.children.find(c => c.checkpoint.isOnActivePath);
|
|
79
|
+
if (activePathChild) {
|
|
80
|
+
treeNode.activeChild = activePathChild;
|
|
81
|
+
treeNode.branchChildren = treeNode.children.filter(c => c !== activePathChild);
|
|
82
|
+
} else {
|
|
83
|
+
// Fallback: first child by timestamp goes straight
|
|
84
|
+
const sorted = [...treeNode.children].sort(
|
|
85
|
+
(a, b) => a.checkpoint.timestamp.localeCompare(b.checkpoint.timestamp)
|
|
86
|
+
);
|
|
87
|
+
treeNode.activeChild = sorted[sorted.length - 1]; // last created goes straight
|
|
88
|
+
treeNode.branchChildren = sorted.slice(0, sorted.length - 1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Sort branch children: reverse creation order (newest first)
|
|
93
|
+
treeNode.branchChildren.sort(
|
|
94
|
+
(a, b) => b.checkpoint.timestamp.localeCompare(a.checkpoint.timestamp)
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return roots;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Calculate the total height of a subtree (for spacing)
|
|
104
|
+
*/
|
|
105
|
+
function calculateSubtreeHeight(node: TreeNode, depth: number): number {
|
|
106
|
+
let height = SPACING.nodeGap;
|
|
107
|
+
|
|
108
|
+
// Add height for branch children
|
|
109
|
+
for (const branch of node.branchChildren) {
|
|
110
|
+
height += calculateBranchHeight(branch, depth + 1);
|
|
111
|
+
height += SPACING.branchGap;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Add height for active child's subtree
|
|
115
|
+
if (node.activeChild) {
|
|
116
|
+
height += calculateSubtreeHeight(node.activeChild, depth);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return height;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Calculate the height of a branch (including its own subtree)
|
|
124
|
+
*/
|
|
125
|
+
function calculateBranchHeight(node: TreeNode, depth: number): number {
|
|
126
|
+
let height = SPACING.nodeGap; // This node itself
|
|
127
|
+
|
|
128
|
+
// Add height for this branch's sub-branches
|
|
129
|
+
for (const branch of node.branchChildren) {
|
|
130
|
+
height += calculateBranchHeight(branch, depth + 1);
|
|
131
|
+
height += SPACING.branchGap;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Add height for active child continuation
|
|
135
|
+
if (node.activeChild) {
|
|
136
|
+
height += calculateBranchHeight(node.activeChild, depth);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return height;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Build graph visualization from timeline data
|
|
144
|
+
*/
|
|
145
|
+
export function buildGraph(timelineData: TimelineResponse | null): GraphData {
|
|
146
|
+
if (!timelineData || timelineData.nodes.length === 0) {
|
|
147
|
+
return {
|
|
148
|
+
nodes: [],
|
|
149
|
+
edges: [],
|
|
150
|
+
versionGroups: [],
|
|
151
|
+
svgWidth: 800,
|
|
152
|
+
svgHeight: 100
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
debug.log('snapshot', `Graph builder: ${timelineData.nodes.length} nodes`);
|
|
157
|
+
|
|
158
|
+
// Build tree structure
|
|
159
|
+
const roots = buildTree(timelineData.nodes);
|
|
160
|
+
|
|
161
|
+
if (roots.length === 0) {
|
|
162
|
+
return { nodes: [], edges: [], versionGroups: [], svgWidth: 800, svgHeight: 100 };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const graphNodes: GraphNode[] = [];
|
|
166
|
+
const graphEdges: GraphEdge[] = [];
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Recursively layout the tree.
|
|
170
|
+
* Returns the Y position after this subtree.
|
|
171
|
+
*/
|
|
172
|
+
function layoutNode(
|
|
173
|
+
treeNode: TreeNode,
|
|
174
|
+
x: number,
|
|
175
|
+
startY: number,
|
|
176
|
+
parentId: string | null,
|
|
177
|
+
isMainLine: boolean,
|
|
178
|
+
isBranchEdge: boolean
|
|
179
|
+
): number {
|
|
180
|
+
const nodeY = startY;
|
|
181
|
+
|
|
182
|
+
// Create graph node
|
|
183
|
+
const graphNode: GraphNode = {
|
|
184
|
+
id: treeNode.checkpoint.id,
|
|
185
|
+
checkpoint: treeNode.checkpoint,
|
|
186
|
+
x,
|
|
187
|
+
y: nodeY,
|
|
188
|
+
type: isMainLine ? 'main' : 'branch',
|
|
189
|
+
isCurrent: treeNode.checkpoint.isCurrent,
|
|
190
|
+
isOrphaned: treeNode.checkpoint.isOrphaned
|
|
191
|
+
};
|
|
192
|
+
graphNodes.push(graphNode);
|
|
193
|
+
|
|
194
|
+
// Create edge from parent
|
|
195
|
+
if (parentId) {
|
|
196
|
+
graphEdges.push({
|
|
197
|
+
from: parentId,
|
|
198
|
+
to: treeNode.checkpoint.id,
|
|
199
|
+
type: isBranchEdge ? 'branch' : 'straight',
|
|
200
|
+
path: '' // Will be calculated later
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let currentY = nodeY;
|
|
205
|
+
|
|
206
|
+
// Layout branch children first (they go between this node and the active child)
|
|
207
|
+
for (let i = 0; i < treeNode.branchChildren.length; i++) {
|
|
208
|
+
const branch = treeNode.branchChildren[i];
|
|
209
|
+
const branchX = x + SPACING.branchIndent;
|
|
210
|
+
const branchStartY = currentY + SPACING.checkpointToBranch;
|
|
211
|
+
|
|
212
|
+
const branchEndY = layoutNode(
|
|
213
|
+
branch,
|
|
214
|
+
branchX,
|
|
215
|
+
branchStartY,
|
|
216
|
+
treeNode.checkpoint.id,
|
|
217
|
+
false, // not main line
|
|
218
|
+
true // is a branch edge
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
currentY = branchEndY;
|
|
222
|
+
|
|
223
|
+
// Add gap after branch
|
|
224
|
+
if (i < treeNode.branchChildren.length - 1) {
|
|
225
|
+
currentY += SPACING.branchGap;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Layout active child (straight continuation)
|
|
230
|
+
if (treeNode.activeChild) {
|
|
231
|
+
// If we had branches, add transition spacing
|
|
232
|
+
if (treeNode.branchChildren.length > 0) {
|
|
233
|
+
currentY += SPACING.branchToCheckpoint;
|
|
234
|
+
} else {
|
|
235
|
+
currentY += SPACING.nodeGap;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
currentY = layoutNode(
|
|
239
|
+
treeNode.activeChild,
|
|
240
|
+
x, // same X (straight continuation)
|
|
241
|
+
currentY,
|
|
242
|
+
treeNode.checkpoint.id,
|
|
243
|
+
isMainLine, // inherit main line status
|
|
244
|
+
false // straight edge
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return currentY;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Layout from root
|
|
252
|
+
let currentY = LAYOUT.startY;
|
|
253
|
+
for (let i = 0; i < roots.length; i++) {
|
|
254
|
+
currentY = layoutNode(
|
|
255
|
+
roots[i],
|
|
256
|
+
LAYOUT.startX,
|
|
257
|
+
currentY,
|
|
258
|
+
null,
|
|
259
|
+
true, // root is main line
|
|
260
|
+
false
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
if (i < roots.length - 1) {
|
|
264
|
+
currentY += SPACING.nodeGap;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Calculate SVG paths for edges
|
|
269
|
+
for (const edge of graphEdges) {
|
|
270
|
+
const fromNode = graphNodes.find(n => n.id === edge.from);
|
|
271
|
+
const toNode = graphNodes.find(n => n.id === edge.to);
|
|
272
|
+
|
|
273
|
+
if (fromNode && toNode) {
|
|
274
|
+
if (edge.type === 'straight') {
|
|
275
|
+
// Vertical line
|
|
276
|
+
edge.path = `M ${fromNode.x} ${fromNode.y + SIZE.node} L ${toNode.x} ${toNode.y - SIZE.node}`;
|
|
277
|
+
} else if (edge.type === 'branch') {
|
|
278
|
+
// Horizontal line from main line to branch node
|
|
279
|
+
const startX = fromNode.x + 1;
|
|
280
|
+
const endX = toNode.x - SIZE.node;
|
|
281
|
+
const lineY = toNode.y;
|
|
282
|
+
edge.path = `M ${startX} ${lineY} L ${endX} ${lineY}`;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Calculate SVG dimensions
|
|
288
|
+
const maxX = graphNodes.length > 0
|
|
289
|
+
? Math.max(...graphNodes.map(n => n.x)) + SIZE.labelWidth + 30
|
|
290
|
+
: 800;
|
|
291
|
+
const maxY = graphNodes.length > 0
|
|
292
|
+
? Math.max(...graphNodes.map(n => n.y)) + 50
|
|
293
|
+
: 100;
|
|
294
|
+
|
|
295
|
+
debug.log('snapshot', `Graph: ${graphNodes.length} nodes, ${graphEdges.length} edges, ${maxX}x${maxY}`);
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
nodes: graphNodes,
|
|
299
|
+
edges: graphEdges,
|
|
300
|
+
versionGroups: [], // No longer using version groups
|
|
301
|
+
svgWidth: maxX,
|
|
302
|
+
svgHeight: maxY
|
|
303
|
+
};
|
|
304
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeline data structures and type definitions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface CheckpointNode {
|
|
6
|
+
id: string;
|
|
7
|
+
messageId: string;
|
|
8
|
+
parentId: string | null; // parent checkpoint ID in the tree
|
|
9
|
+
activeChildId: string | null; // which child continues straight
|
|
10
|
+
timestamp: string;
|
|
11
|
+
messageText: string;
|
|
12
|
+
isOnActivePath: boolean;
|
|
13
|
+
isOrphaned: boolean;
|
|
14
|
+
isCurrent: boolean;
|
|
15
|
+
hasSnapshot: boolean;
|
|
16
|
+
senderName?: string | null;
|
|
17
|
+
// File change statistics (git-like)
|
|
18
|
+
filesChanged?: number;
|
|
19
|
+
insertions?: number;
|
|
20
|
+
deletions?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TimelineResponse {
|
|
24
|
+
nodes: CheckpointNode[];
|
|
25
|
+
currentHeadId: string | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Graph visualization interfaces
|
|
29
|
+
export interface GraphNode {
|
|
30
|
+
id: string;
|
|
31
|
+
checkpoint: CheckpointNode;
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
type: 'main' | 'branch';
|
|
35
|
+
isCurrent: boolean;
|
|
36
|
+
isOrphaned: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface GraphEdge {
|
|
40
|
+
from: string;
|
|
41
|
+
to: string;
|
|
42
|
+
type: 'straight' | 'branch';
|
|
43
|
+
path: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface VersionGroup {
|
|
47
|
+
branchName: string;
|
|
48
|
+
versionNumber: number;
|
|
49
|
+
nodes: GraphNode[];
|
|
50
|
+
x: number;
|
|
51
|
+
minY: number;
|
|
52
|
+
maxY: number;
|
|
53
|
+
width: number;
|
|
54
|
+
height: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Animation state
|
|
58
|
+
export interface AnimationState {
|
|
59
|
+
isAnimating: boolean;
|
|
60
|
+
restoringNodeId: string | null;
|
|
61
|
+
progress: number;
|
|
62
|
+
oldNodePositions: Map<string, {x: number, y: number}>;
|
|
63
|
+
oldNodeStyles: Map<string, {type: string}>;
|
|
64
|
+
oldVersionGroups: VersionGroup[];
|
|
65
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeline utility functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { TimelineResponse, GraphNode } from './types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Format timestamp to time string (HH:MM)
|
|
9
|
+
*/
|
|
10
|
+
export function formatTime(timestamp: string): string {
|
|
11
|
+
const date = new Date(timestamp);
|
|
12
|
+
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Format timestamp to date string (MMM DD, HH:MM)
|
|
17
|
+
*/
|
|
18
|
+
export function formatDate(timestamp: string): string {
|
|
19
|
+
const date = new Date(timestamp);
|
|
20
|
+
return date.toLocaleDateString('en-US', {
|
|
21
|
+
month: 'short',
|
|
22
|
+
day: 'numeric',
|
|
23
|
+
hour: '2-digit',
|
|
24
|
+
minute: '2-digit'
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if checkpoint is current HEAD
|
|
30
|
+
*/
|
|
31
|
+
export function isCurrentHead(
|
|
32
|
+
timelineData: TimelineResponse | null,
|
|
33
|
+
checkpointId: string
|
|
34
|
+
): boolean {
|
|
35
|
+
return timelineData?.currentHeadId === checkpointId;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get sorted nodes for Z-index (restoring node rendered last, on top)
|
|
40
|
+
*/
|
|
41
|
+
export function getSortedNodes(
|
|
42
|
+
graphNodes: GraphNode[],
|
|
43
|
+
isAnimating: boolean,
|
|
44
|
+
restoringNodeId: string | null
|
|
45
|
+
): GraphNode[] {
|
|
46
|
+
if (isAnimating && restoringNodeId) {
|
|
47
|
+
return [
|
|
48
|
+
...graphNodes.filter(n => n.id !== restoringNodeId),
|
|
49
|
+
...graphNodes.filter(n => n.id === restoringNodeId)
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
return graphNodes;
|
|
53
|
+
}
|