@parhelia/core 0.1.12556 → 0.1.12560
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/dist/agents-view/AgentCard.d.ts +6 -4
- package/dist/agents-view/AgentCard.js +143 -24
- package/dist/agents-view/AgentCard.js.map +1 -1
- package/dist/agents-view/AgentsInbox.d.ts +1 -1
- package/dist/agents-view/AgentsInbox.js +7 -92
- package/dist/agents-view/AgentsInbox.js.map +1 -1
- package/dist/agents-view/AgentsTitlebar.js +3 -2
- package/dist/agents-view/AgentsTitlebar.js.map +1 -1
- package/dist/agents-view/AgentsView.d.ts +6 -7
- package/dist/agents-view/AgentsView.js +187 -98
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/agents-view/AgentsWorkspaceView.d.ts +2 -6
- package/dist/agents-view/AgentsWorkspaceView.js +266 -113
- package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
- package/dist/agents-view/ProfileAgentsGroup.d.ts +2 -1
- package/dist/agents-view/ProfileAgentsGroup.js +4 -3
- package/dist/agents-view/ProfileAgentsGroup.js.map +1 -1
- package/dist/components/ActionButton.d.ts +1 -1
- package/dist/components/ActionButton.js.map +1 -1
- package/dist/components/FilterInput.d.ts +1 -1
- package/dist/components/FilterInput.js +1 -1
- package/dist/components/FilterInput.js.map +1 -1
- package/dist/components/ui/LanguageSelector.js +2 -4
- package/dist/components/ui/LanguageSelector.js.map +1 -1
- package/dist/components/ui/PlaceholderInput.js +3 -3
- package/dist/components/ui/PlaceholderInput.js.map +1 -1
- package/dist/components/ui/PlaceholderInputTypes.js +1 -1
- package/dist/components/ui/PlaceholderInputTypes.js.map +1 -1
- package/dist/components/ui/alert-dialog.d.ts +1 -1
- package/dist/components/ui/alert-dialog.js +6 -10
- package/dist/components/ui/alert-dialog.js.map +1 -1
- package/dist/components/ui/button.d.ts +4 -4
- package/dist/components/ui/button.js +4 -1
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/context-menu.d.ts +1 -1
- package/dist/components/ui/context-menu.js +12 -4
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/components/ui/copy-button.d.ts +2 -1
- package/dist/components/ui/copy-button.js +2 -2
- package/dist/components/ui/copy-button.js.map +1 -1
- package/dist/components/ui/dialog.d.ts +1 -1
- package/dist/components/ui/dialog.js +21 -126
- package/dist/components/ui/dialog.js.map +1 -1
- package/dist/components/ui/input.d.ts +1 -1
- package/dist/components/ui/input.js +5 -3
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/paste-button.d.ts +2 -1
- package/dist/components/ui/paste-button.js +2 -2
- package/dist/components/ui/paste-button.js.map +1 -1
- package/dist/components/ui/popover.js +1 -9
- package/dist/components/ui/popover.js.map +1 -1
- package/dist/components/ui/select.js +1 -1
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/styled-dialog-title.js +1 -1
- package/dist/components/ui/styled-dialog-title.js.map +1 -1
- package/dist/components/ui/tabs.d.ts +1 -1
- package/dist/components/ui/tabs.js +4 -11
- package/dist/components/ui/tabs.js.map +1 -1
- package/dist/config/config.d.ts +4 -2
- package/dist/config/config.js +250 -70
- package/dist/config/config.js.map +1 -1
- package/dist/config/notificationRoutes.js +14 -0
- package/dist/config/notificationRoutes.js.map +1 -1
- package/dist/config/types/workspace.d.ts +6 -0
- package/dist/config/types.d.ts +63 -12
- package/dist/config/types.js.map +1 -1
- package/dist/editor/ConfirmationDialog.js +20 -4
- package/dist/editor/ConfirmationDialog.js.map +1 -1
- package/dist/editor/ContentTree.d.ts +2 -1
- package/dist/editor/ContentTree.js +93 -32
- package/dist/editor/ContentTree.js.map +1 -1
- package/dist/editor/Editor.js +87 -22
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/FieldHistory.js +84 -36
- package/dist/editor/FieldHistory.js.map +1 -1
- package/dist/editor/FieldListField.js +21 -9
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/FieldListFieldWithFallbacks.js +23 -2
- package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -1
- package/dist/editor/GlobalMenuBar.js +29 -2
- package/dist/editor/GlobalMenuBar.js.map +1 -1
- package/dist/editor/ImageEditor.js +5 -2
- package/dist/editor/ImageEditor.js.map +1 -1
- package/dist/editor/ItemInfo.js +36 -1
- package/dist/editor/ItemInfo.js.map +1 -1
- package/dist/editor/LinkEditorDialog.js +3 -0
- package/dist/editor/LinkEditorDialog.js.map +1 -1
- package/dist/editor/MainLayout.d.ts +0 -2
- package/dist/editor/MainLayout.js +65 -8
- package/dist/editor/MainLayout.js.map +1 -1
- package/dist/editor/MigrationsView.js +29 -5
- package/dist/editor/MigrationsView.js.map +1 -1
- package/dist/editor/MobileLayout.js +37 -12
- package/dist/editor/MobileLayout.js.map +1 -1
- package/dist/editor/PictureCropper.js +54 -45
- package/dist/editor/PictureCropper.js.map +1 -1
- package/dist/editor/PictureEditor.js +17 -15
- package/dist/editor/PictureEditor.js.map +1 -1
- package/dist/editor/QuickItemSwitcher.js +21 -21
- package/dist/editor/QuickItemSwitcher.js.map +1 -1
- package/dist/editor/SetupWizard.js +52 -12
- package/dist/editor/SetupWizard.js.map +1 -1
- package/dist/editor/Titlebar.js +7 -2
- package/dist/editor/Titlebar.js.map +1 -1
- package/dist/editor/ai/AgentCostDisplay.d.ts +1 -0
- package/dist/editor/ai/AgentCostDisplay.js +1 -1
- package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
- package/dist/editor/ai/AgentDocumentList.js +32 -14
- package/dist/editor/ai/AgentDocumentList.js.map +1 -1
- package/dist/editor/ai/AgentGreeting.js +3 -2
- package/dist/editor/ai/AgentGreeting.js.map +1 -1
- package/dist/editor/ai/AgentProfileSelector.js +2 -1
- package/dist/editor/ai/AgentProfileSelector.js.map +1 -1
- package/dist/editor/ai/AgentStatusBadge.d.ts +0 -5
- package/dist/editor/ai/AgentStatusBadge.js +67 -65
- package/dist/editor/ai/AgentStatusBadge.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.d.ts +14 -2
- package/dist/editor/ai/AgentTerminal.js +2377 -483
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/AgentTerminalStatusBar.d.ts +8 -3
- package/dist/editor/ai/AgentTerminalStatusBar.js +460 -56
- package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
- package/dist/editor/ai/Agents.js +150 -113
- package/dist/editor/ai/Agents.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.d.ts +10 -1
- package/dist/editor/ai/AiResponseMessage.js +238 -23
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/ContextInfoBar.d.ts +2 -3
- package/dist/editor/ai/ContextInfoBar.js +64 -7
- package/dist/editor/ai/ContextInfoBar.js.map +1 -1
- package/dist/editor/ai/GuidanceOverlay.js +17 -11
- package/dist/editor/ai/GuidanceOverlay.js.map +1 -1
- package/dist/editor/ai/InlineAiDialog.d.ts +1 -1
- package/dist/editor/ai/InlineAiDialog.js +514 -192
- package/dist/editor/ai/InlineAiDialog.js.map +1 -1
- package/dist/editor/ai/InlineAiTrigger.js +115 -12
- package/dist/editor/ai/InlineAiTrigger.js.map +1 -1
- package/dist/editor/ai/MediaImage.js +40 -8
- package/dist/editor/ai/MediaImage.js.map +1 -1
- package/dist/editor/ai/SpawnedAgentsPanel.js +10 -12
- package/dist/editor/ai/SpawnedAgentsPanel.js.map +1 -1
- package/dist/editor/ai/ToolCallDisplay.d.ts +22 -2
- package/dist/editor/ai/ToolCallDisplay.js +518 -147
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/dialogs/AgentDialogHandler.d.ts +1 -8
- package/dist/editor/ai/dialogs/AgentDialogHandler.js +379 -42
- package/dist/editor/ai/dialogs/AgentDialogHandler.js.map +1 -1
- package/dist/editor/ai/dialogs/QuestionnaireInline.d.ts +5 -1
- package/dist/editor/ai/dialogs/QuestionnaireInline.js +628 -60
- package/dist/editor/ai/dialogs/QuestionnaireInline.js.map +1 -1
- package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +115 -0
- package/dist/editor/ai/dialogs/agentDialogTypes.js +2 -0
- package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
- package/dist/editor/ai/types.d.ts +3 -1
- package/dist/editor/ai/useAgentStatus.d.ts +2 -1
- package/dist/editor/ai/useAgentStatus.js +86 -99
- package/dist/editor/ai/useAgentStatus.js.map +1 -1
- package/dist/editor/ai/useInlineAiPosition.js +45 -5
- package/dist/editor/ai/useInlineAiPosition.js.map +1 -1
- package/dist/editor/client/AboutDialog.js +4 -2
- package/dist/editor/client/AboutDialog.js.map +1 -1
- package/dist/editor/client/EditorShell.d.ts +4 -1
- package/dist/editor/client/EditorShell.js +770 -237
- package/dist/editor/client/EditorShell.js.map +1 -1
- package/dist/editor/client/editContext.d.ts +33 -19
- package/dist/editor/client/editContext.js.map +1 -1
- package/dist/editor/client/helpers.js +6 -0
- package/dist/editor/client/helpers.js.map +1 -1
- package/dist/editor/client/hooks/useEditorUrlSync.js +1 -2
- package/dist/editor/client/hooks/useEditorUrlSync.js.map +1 -1
- package/dist/editor/client/hooks/useEditorWebSocket.d.ts +10 -0
- package/dist/editor/client/hooks/useEditorWebSocket.js +209 -14
- package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -1
- package/dist/editor/client/hooks/useQuota.d.ts +8 -0
- package/dist/editor/client/hooks/useQuota.js.map +1 -1
- package/dist/editor/client/hooks/useSocketMessageHandler.js +68 -7
- package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
- package/dist/editor/client/itemsRepository.js +10 -6
- package/dist/editor/client/itemsRepository.js.map +1 -1
- package/dist/editor/client/navigation.js +35 -3
- package/dist/editor/client/navigation.js.map +1 -1
- package/dist/editor/client/operations.d.ts +6 -3
- package/dist/editor/client/operations.js +208 -30
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/client/pageModelBuilder.js +4 -31
- package/dist/editor/client/pageModelBuilder.js.map +1 -1
- package/dist/editor/client/ui/DevModeIndicator.js +2 -2
- package/dist/editor/client/ui/DevModeIndicator.js.map +1 -1
- package/dist/editor/client/ui/EditorChrome.d.ts +0 -6
- package/dist/editor/client/ui/EditorChrome.js +55 -72
- package/dist/editor/client/ui/EditorChrome.js.map +1 -1
- package/dist/editor/client/ui/FullscreenControls.js +5 -3
- package/dist/editor/client/ui/FullscreenControls.js.map +1 -1
- package/dist/editor/commands/commands.d.ts +11 -1
- package/dist/editor/commands/commands.js +12 -1
- package/dist/editor/commands/commands.js.map +1 -1
- package/dist/editor/commands/componentCommands.js +109 -55
- package/dist/editor/commands/componentCommands.js.map +1 -1
- package/dist/editor/commands/customCommandConverter.d.ts +8 -1
- package/dist/editor/commands/customCommandConverter.js +35 -5
- package/dist/editor/commands/customCommandConverter.js.map +1 -1
- package/dist/editor/commands/handlers/agentHandler.js +2 -1
- package/dist/editor/commands/handlers/agentHandler.js.map +1 -1
- package/dist/editor/commands/itemCommands.d.ts +3 -0
- package/dist/editor/commands/itemCommands.js +93 -10
- package/dist/editor/commands/itemCommands.js.map +1 -1
- package/dist/editor/commands/undo.d.ts +9 -15
- package/dist/editor/commands/undo.js +24 -0
- package/dist/editor/commands/undo.js.map +1 -1
- package/dist/editor/context-menu/InsertMenu.js +83 -39
- package/dist/editor/context-menu/InsertMenu.js.map +1 -1
- package/dist/editor/field-types/MultiLineText.js +1 -1
- package/dist/editor/field-types/MultiLineText.js.map +1 -1
- package/dist/editor/field-types/RawEditor.js +1 -1
- package/dist/editor/field-types/RichTextEditor.js +13 -5
- package/dist/editor/field-types/RichTextEditor.js.map +1 -1
- package/dist/editor/field-types/RichTextEditorComponent.js +37 -3
- package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
- package/dist/editor/field-types/SingleLineText.js +1 -1
- package/dist/editor/field-types/TreeListEditor.js +3 -2
- package/dist/editor/field-types/TreeListEditor.js.map +1 -1
- package/dist/editor/field-types/richtext/components/ReactSlate.css +23 -5
- package/dist/editor/field-types/richtext/components/ReactSlate.d.ts +2 -0
- package/dist/editor/field-types/richtext/components/ReactSlate.js +28 -4
- package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
- package/dist/editor/field-types/richtext/components/ToolbarButton.js +4 -2
- package/dist/editor/field-types/richtext/components/ToolbarButton.js.map +1 -1
- package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +13 -0
- package/dist/editor/field-types/richtext/contextMenuFactory.js +181 -24
- package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -1
- package/dist/editor/field-types/richtext/types.d.ts +2 -0
- package/dist/editor/field-types/richtext/types.js.map +1 -1
- package/dist/editor/field-types/richtext/utils/plugins.js +4 -0
- package/dist/editor/field-types/richtext/utils/plugins.js.map +1 -1
- package/dist/editor/field-types/textContextMenuFactory.js +3 -2
- package/dist/editor/field-types/textContextMenuFactory.js.map +1 -1
- package/dist/editor/media-selector/AiImageSearchPrompt.js +4 -2
- package/dist/editor/media-selector/AiImageSearchPrompt.js.map +1 -1
- package/dist/editor/media-selector/MediaFolderBrowser.js +1 -1
- package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
- package/dist/editor/media-selector/MediaSelector.js +7 -1
- package/dist/editor/media-selector/MediaSelector.js.map +1 -1
- package/dist/editor/media-selector/TreeSelector.js +40 -35
- package/dist/editor/media-selector/TreeSelector.js.map +1 -1
- package/dist/editor/menubar/ActiveUsers.js +1 -1
- package/dist/editor/menubar/ActiveUsers.js.map +1 -1
- package/dist/editor/menubar/GenericToolbar.js +4 -2
- package/dist/editor/menubar/GenericToolbar.js.map +1 -1
- package/dist/editor/menubar/ItemLanguageVersion.js +2 -2
- package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +26 -147
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/menubar/Separator.js +1 -1
- package/dist/editor/menubar/VersionSelector.js +2 -4
- package/dist/editor/menubar/VersionSelector.js.map +1 -1
- package/dist/editor/menubar/WorkflowButton.js +39 -12
- package/dist/editor/menubar/WorkflowButton.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/CustomCommandsToolbar.js +16 -38
- package/dist/editor/menubar/toolbar-sections/CustomCommandsToolbar.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/EditControls.js +3 -3
- package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/HelpButton.js +1 -0
- package/dist/editor/menubar/toolbar-sections/HelpButton.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +6 -10
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +597 -220
- package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js +13 -2
- package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
- package/dist/editor/page-editor-chrome/CommentHighlighting.js +42 -1
- package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js +1 -1
- package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
- package/dist/editor/page-editor-chrome/InlineEditor.js +97 -48
- package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +38 -17
- package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +17 -11
- package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +301 -301
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/page-viewer/DeviceToolbar.js +1 -1
- package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -1
- package/dist/editor/page-viewer/EditorForm.js +69 -11
- package/dist/editor/page-viewer/EditorForm.js.map +1 -1
- package/dist/editor/page-viewer/MiniMap.d.ts +2 -4
- package/dist/editor/page-viewer/MiniMap.js +91 -28
- package/dist/editor/page-viewer/MiniMap.js.map +1 -1
- package/dist/editor/page-viewer/PageViewer.d.ts +3 -1
- package/dist/editor/page-viewer/PageViewer.js +92 -19
- package/dist/editor/page-viewer/PageViewer.js.map +1 -1
- package/dist/editor/page-viewer/PageViewerFrame.d.ts +2 -1
- package/dist/editor/page-viewer/PageViewerFrame.js +348 -115
- package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +114 -49
- package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
- package/dist/editor/page-viewer/pageViewContext.d.ts +1 -0
- package/dist/editor/page-viewer/pageViewContext.js +51 -14
- package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
- package/dist/editor/pageModel.d.ts +14 -1
- package/dist/editor/reviews/Comment.js +26 -12
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/reviews/CommentDisplayPopover.js +7 -5
- package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/CommentView.js +19 -4
- package/dist/editor/reviews/CommentView.js.map +1 -1
- package/dist/editor/reviews/Comments.js +89 -72
- package/dist/editor/reviews/Comments.js.map +1 -1
- package/dist/editor/reviews/CreateReviewDialog.js +281 -177
- package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
- package/dist/editor/reviews/DecisionsMatrix.js +96 -25
- package/dist/editor/reviews/DecisionsMatrix.js.map +1 -1
- package/dist/editor/reviews/DiffView.js +7 -14
- package/dist/editor/reviews/DiffView.js.map +1 -1
- package/dist/editor/reviews/EditReviewSettingsDialog.js +6 -4
- package/dist/editor/reviews/EditReviewSettingsDialog.js.map +1 -1
- package/dist/editor/reviews/MultiReviewManager.js +25 -3
- package/dist/editor/reviews/MultiReviewManager.js.map +1 -1
- package/dist/editor/reviews/PagesPanel.js +31 -15
- package/dist/editor/reviews/PagesPanel.js.map +1 -1
- package/dist/editor/reviews/PreviewInfo.js +1 -4
- package/dist/editor/reviews/PreviewInfo.js.map +1 -1
- package/dist/editor/reviews/ReviewCard.js +13 -7
- package/dist/editor/reviews/ReviewCard.js.map +1 -1
- package/dist/editor/reviews/ReviewDetail.js +3 -2
- package/dist/editor/reviews/ReviewDetail.js.map +1 -1
- package/dist/editor/reviews/ReviewsList.js +7 -3
- package/dist/editor/reviews/ReviewsList.js.map +1 -1
- package/dist/editor/reviews/SuggestedEdit.js +34 -3
- package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
- package/dist/editor/reviews/SuggestionDisplayPopover.js +31 -5
- package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/commentAi.js +25 -6
- package/dist/editor/reviews/commentAi.js.map +1 -1
- package/dist/editor/reviews/reviewCommands.js +4 -1
- package/dist/editor/reviews/reviewCommands.js.map +1 -1
- package/dist/editor/reviews/useMultiReview.js +2 -2
- package/dist/editor/reviews/useMultiReview.js.map +1 -1
- package/dist/editor/reviews/useReviews.d.ts +2 -2
- package/dist/editor/reviews/useReviews.js +12 -30
- package/dist/editor/reviews/useReviews.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +229 -5
- package/dist/editor/services/agentService.js +292 -39
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +57 -1
- package/dist/editor/services/aiService.js +79 -6
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/contentService.d.ts +6 -3
- package/dist/editor/services/contentService.js +13 -12
- package/dist/editor/services/contentService.js.map +1 -1
- package/dist/editor/services/editService.d.ts +52 -1
- package/dist/editor/services/editService.js +94 -2
- package/dist/editor/services/editService.js.map +1 -1
- package/dist/editor/services/indexService.js +1 -1
- package/dist/editor/services/indexService.js.map +1 -1
- package/dist/editor/services/reviewsService.d.ts +3 -6
- package/dist/editor/services/reviewsService.js +2 -11
- package/dist/editor/services/reviewsService.js.map +1 -1
- package/dist/editor/services/serviceHelper.d.ts +2 -1
- package/dist/editor/services/serviceHelper.js +112 -20
- package/dist/editor/services/serviceHelper.js.map +1 -1
- package/dist/editor/services/systemService.d.ts +2 -1
- package/dist/editor/services/systemService.js +3 -0
- package/dist/editor/services/systemService.js.map +1 -1
- package/dist/editor/services-server/api.d.ts +1 -2
- package/dist/editor/services-server/api.js +11 -6
- package/dist/editor/services-server/api.js.map +1 -1
- package/dist/editor/settings/About.js +317 -3
- package/dist/editor/settings/About.js.map +1 -1
- package/dist/editor/settings/QuotaInfo.js +210 -4
- package/dist/editor/settings/QuotaInfo.js.map +1 -1
- package/dist/editor/settings/SettingsView.js +25 -23
- package/dist/editor/settings/SettingsView.js.map +1 -1
- package/dist/editor/settings/Status.js +7 -6
- package/dist/editor/settings/Status.js.map +1 -1
- package/dist/editor/settings/index/useIndexStatus.js +20 -22
- package/dist/editor/settings/index/useIndexStatus.js.map +1 -1
- package/dist/editor/settings/panels/AgentsPanel.d.ts +0 -4
- package/dist/editor/settings/panels/AgentsPanel.js +95 -121
- package/dist/editor/settings/panels/AgentsPanel.js.map +1 -1
- package/dist/editor/settings/panels/ModelsPanel.js +329 -108
- package/dist/editor/settings/panels/ModelsPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProvidersPanel.d.ts +1 -1
- package/dist/editor/settings/panels/ProvidersPanel.js +86 -59
- package/dist/editor/settings/panels/ProvidersPanel.js.map +1 -1
- package/dist/editor/settings/panels/SearchConfigPanel.js +4 -4
- package/dist/editor/settings/panels/SearchConfigPanel.js.map +1 -1
- package/dist/editor/settings/panels/index.d.ts +3 -2
- package/dist/editor/settings/panels/index.js +3 -2
- package/dist/editor/settings/panels/index.js.map +1 -1
- package/dist/editor/settings/status/coreStatusChecks.js +124 -19
- package/dist/editor/settings/status/coreStatusChecks.js.map +1 -1
- package/dist/editor/settings/status/useStartupChecks.d.ts +3 -1
- package/dist/editor/settings/status/useStartupChecks.js +9 -5
- package/dist/editor/settings/status/useStartupChecks.js.map +1 -1
- package/dist/editor/setup-wizard/steps/CompleteStep.d.ts +2 -1
- package/dist/editor/setup-wizard/steps/CompleteStep.js +2 -1
- package/dist/editor/setup-wizard/steps/CompleteStep.js.map +1 -1
- package/dist/editor/sidebar/ComponentPalette.js +2 -1
- package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
- package/dist/editor/sidebar/ComponentTree.d.ts +8 -1
- package/dist/editor/sidebar/ComponentTree.js +216 -69
- package/dist/editor/sidebar/ComponentTree.js.map +1 -1
- package/dist/editor/sidebar/EditHistory.js +22 -46
- package/dist/editor/sidebar/EditHistory.js.map +1 -1
- package/dist/editor/sidebar/Favorites.js +4 -8
- package/dist/editor/sidebar/Favorites.js.map +1 -1
- package/dist/editor/sidebar/MainContentTree.js +4 -3
- package/dist/editor/sidebar/MainContentTree.js.map +1 -1
- package/dist/editor/sidebar/OperationItem.js +21 -7
- package/dist/editor/sidebar/OperationItem.js.map +1 -1
- package/dist/editor/sidebar/SidebarPanel.d.ts +3 -1
- package/dist/editor/sidebar/SidebarPanel.js +44 -12
- package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
- package/dist/editor/sidebar/SidebarStack.d.ts +2 -1
- package/dist/editor/sidebar/SidebarStack.js +4 -3
- package/dist/editor/sidebar/SidebarStack.js.map +1 -1
- package/dist/editor/sidebar/Validation.js +22 -12
- package/dist/editor/sidebar/Validation.js.map +1 -1
- package/dist/editor/sidebar/Workbox.js +53 -3
- package/dist/editor/sidebar/Workbox.js.map +1 -1
- package/dist/editor/sidebar/WorkspaceRail.d.ts +0 -1
- package/dist/editor/sidebar/WorkspaceRail.js +56 -167
- package/dist/editor/sidebar/WorkspaceRail.js.map +1 -1
- package/dist/editor/tree-indicators/GutterColumns.d.ts +3 -1
- package/dist/editor/tree-indicators/GutterColumns.js +26 -5
- package/dist/editor/tree-indicators/GutterColumns.js.map +1 -1
- package/dist/editor/tree-indicators/GutterContext.d.ts +4 -0
- package/dist/editor/tree-indicators/GutterContext.js +23 -0
- package/dist/editor/tree-indicators/GutterContext.js.map +1 -1
- package/dist/editor/tree-indicators/index.d.ts +0 -1
- package/dist/editor/tree-indicators/index.js +0 -1
- package/dist/editor/tree-indicators/index.js.map +1 -1
- package/dist/editor/tree-indicators/types.d.ts +12 -1
- package/dist/editor/ui/CopyMoveTargetSelectorDialog.js +1 -1
- package/dist/editor/ui/CopyMoveTargetSelectorDialog.js.map +1 -1
- package/dist/editor/ui/Icons.js +1 -1
- package/dist/editor/ui/Icons.js.map +1 -1
- package/dist/editor/ui/ItemNameDialogNew.d.ts +2 -0
- package/dist/editor/ui/ItemNameDialogNew.js +33 -17
- package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
- package/dist/editor/ui/ItemSearch.js +7 -11
- package/dist/editor/ui/ItemSearch.js.map +1 -1
- package/dist/editor/ui/SimpleIconButton.js +1 -1
- package/dist/editor/ui/SimpleIconButton.js.map +1 -1
- package/dist/editor/ui/SimpleTabs.d.ts +1 -0
- package/dist/editor/ui/SimpleTabs.js +45 -25
- package/dist/editor/ui/SimpleTabs.js.map +1 -1
- package/dist/editor/ui/Splitter.d.ts +1 -0
- package/dist/editor/ui/Splitter.js +102 -86
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/editor/ui/TemplateSelectorDialog.js +4 -4
- package/dist/editor/ui/TemplateSelectorDialog.js.map +1 -1
- package/dist/editor/ui/TreeListSelector.d.ts +6 -1
- package/dist/editor/ui/TreeListSelector.js +2 -2
- package/dist/editor/ui/TreeListSelector.js.map +1 -1
- package/dist/editor/utils/keyboardNavigation.d.ts +6 -20
- package/dist/editor/utils/keyboardNavigation.js +48 -140
- package/dist/editor/utils/keyboardNavigation.js.map +1 -1
- package/dist/editor/utils.js +19 -9
- package/dist/editor/utils.js.map +1 -1
- package/dist/editor/views/CompareView.d.ts +3 -1
- package/dist/editor/views/CompareView.js +7 -5
- package/dist/editor/views/CompareView.js.map +1 -1
- package/dist/editor/views/EditView.js +1 -1
- package/dist/editor/views/EditView.js.map +1 -1
- package/dist/editor/views/EditorSlot.js +27 -34
- package/dist/editor/views/EditorSlot.js.map +1 -1
- package/dist/editor/views/ItemEditor.js +7 -3
- package/dist/editor/views/ItemEditor.js.map +1 -1
- package/dist/editor/views/MediaFolderEditView.js +1 -1
- package/dist/editor/views/MediaFolderEditView.js.map +1 -1
- package/dist/editor/views/ParheliaView.js +5 -6
- package/dist/editor/views/ParheliaView.js.map +1 -1
- package/dist/editor/views/SingleEditView.d.ts +2 -1
- package/dist/editor/views/SingleEditView.js +10 -8
- package/dist/editor/views/SingleEditView.js.map +1 -1
- package/dist/editor/views/editorSlotContext.js +35 -6
- package/dist/editor/views/editorSlotContext.js.map +1 -1
- package/dist/index.d.ts +16 -2
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/setup/services/setupWizardService.d.ts +40 -13
- package/dist/setup/services/setupWizardService.js +32 -17
- package/dist/setup/services/setupWizardService.js.map +1 -1
- package/dist/setup/wizard/steps/AddModelDialog.js +12 -3
- package/dist/setup/wizard/steps/AddModelDialog.js.map +1 -1
- package/dist/setup/wizard/steps/ImportModelDialog.js +39 -22
- package/dist/setup/wizard/steps/ImportModelDialog.js.map +1 -1
- package/dist/splash-screen/ModernSplashScreen.js +112 -32
- package/dist/splash-screen/ModernSplashScreen.js.map +1 -1
- package/dist/splash-screen/NewPage.js +33 -50
- package/dist/splash-screen/NewPage.js.map +1 -1
- package/dist/splash-screen/OpenPage.js +2 -6
- package/dist/splash-screen/OpenPage.js.map +1 -1
- package/dist/splash-screen/ParheliaAssistantChat.js +12 -29
- package/dist/splash-screen/ParheliaAssistantChat.js.map +1 -1
- package/dist/splash-screen/ParheliaLogo.js +87 -37
- package/dist/splash-screen/ParheliaLogo.js.map +1 -1
- package/dist/splash-screen/RecentPages.js +3 -3
- package/dist/splash-screen/RecentPages.js.map +1 -1
- package/dist/tour/Tour.d.ts +2 -1
- package/dist/tour/Tour.js +256 -75
- package/dist/tour/Tour.js.map +1 -1
- package/dist/tour/default-tour.js +222 -96
- package/dist/tour/default-tour.js.map +1 -1
- package/dist/types.d.ts +63 -29
- package/package.json +19 -15
- package/styles.css +14 -10
- package/dist/editor/ComponentInfo.d.ts +0 -4
- package/dist/editor/ComponentInfo.js +0 -41
- package/dist/editor/ComponentInfo.js.map +0 -1
- package/dist/editor/ai/HelpTerminal.d.ts +0 -5
- package/dist/editor/ai/HelpTerminal.js +0 -166
- package/dist/editor/ai/HelpTerminal.js.map +0 -1
- package/dist/editor/field-types/ReactQuill.d.ts +0 -125
- package/dist/editor/field-types/ReactQuill.js +0 -385
- package/dist/editor/field-types/ReactQuill.js.map +0 -1
- package/dist/editor/services-server/graphQL.d.ts +0 -29
- package/dist/editor/services-server/graphQL.js +0 -53
- package/dist/editor/services-server/graphQL.js.map +0 -1
- package/dist/editor/settings/AllAgentsPanel.d.ts +0 -5
- package/dist/editor/settings/AllAgentsPanel.js +0 -139
- package/dist/editor/settings/AllAgentsPanel.js.map +0 -1
- package/dist/editor/settings/LatestFeedback.d.ts +0 -1
- package/dist/editor/settings/LatestFeedback.js +0 -136
- package/dist/editor/settings/LatestFeedback.js.map +0 -1
- package/dist/editor/settings/Setup.d.ts +0 -1
- package/dist/editor/settings/Setup.js +0 -211
- package/dist/editor/settings/Setup.js.map +0 -1
- package/dist/editor/settings/panels/DatabasePanel.d.ts +0 -6
- package/dist/editor/settings/panels/DatabasePanel.js +0 -50
- package/dist/editor/settings/panels/DatabasePanel.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.d.ts +0 -2
- package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.js +0 -195
- package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/index.d.ts +0 -2
- package/dist/editor/settings/setup-steps/AiSetupStep/index.js +0 -21
- package/dist/editor/settings/setup-steps/AiSetupStep/index.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.d.ts +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.js +0 -233
- package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.d.ts +0 -15
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js +0 -14
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.d.ts +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js +0 -94
- package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/types.d.ts +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/types.js +0 -2
- package/dist/editor/settings/setup-steps/AiSetupStep/types.js.map +0 -1
- package/dist/editor/settings/setup-steps/AiSetupStep/utils.d.ts +0 -5
- package/dist/editor/settings/setup-steps/AiSetupStep/utils.js +0 -44
- package/dist/editor/settings/setup-steps/AiSetupStep/utils.js.map +0 -1
- package/dist/editor/settings/setup-steps/IndexSetupStep.d.ts +0 -2
- package/dist/editor/settings/setup-steps/IndexSetupStep.js +0 -36
- package/dist/editor/settings/setup-steps/IndexSetupStep.js.map +0 -1
- package/dist/editor/settings/setup-steps/SettingsSetupStep.d.ts +0 -2
- package/dist/editor/settings/setup-steps/SettingsSetupStep.js +0 -111
- package/dist/editor/settings/setup-steps/SettingsSetupStep.js.map +0 -1
- package/dist/editor/settings/setup-steps/SetupOverview.d.ts +0 -14
- package/dist/editor/settings/setup-steps/SetupOverview.js +0 -38
- package/dist/editor/settings/setup-steps/SetupOverview.js.map +0 -1
- package/dist/editor/sidebar/Debug.d.ts +0 -1
- package/dist/editor/sidebar/Debug.js +0 -70
- package/dist/editor/sidebar/Debug.js.map +0 -1
- package/dist/editor/sidebar/GraphQL.d.ts +0 -2
- package/dist/editor/sidebar/GraphQL.js +0 -234
- package/dist/editor/sidebar/GraphQL.js.map +0 -1
- package/dist/editor/sidebar/LeftToolbar.d.ts +0 -1
- package/dist/editor/sidebar/LeftToolbar.js +0 -12
- package/dist/editor/sidebar/LeftToolbar.js.map +0 -1
- package/dist/editor/sidebar/NavigationSidebar.d.ts +0 -4
- package/dist/editor/sidebar/NavigationSidebar.js +0 -254
- package/dist/editor/sidebar/NavigationSidebar.js.map +0 -1
- package/dist/editor/tree-indicators/GutterSelector.d.ts +0 -5
- package/dist/editor/tree-indicators/GutterSelector.js +0 -91
- package/dist/editor/tree-indicators/GutterSelector.js.map +0 -1
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useEffect, useRef, useCallback, useSyncExternalStore, useMemo, startTransition, } from "react";
|
|
4
4
|
import { toast } from "sonner";
|
|
5
|
-
import { EditContextProvider, FieldsEditContextProvider, OperationsContextProvider, } from "./editContext";
|
|
5
|
+
import { EditContextProvider, FieldsEditContextProvider, OperationsContextProvider, useEditContext, } from "./editContext";
|
|
6
6
|
import { fieldModificationStore } from "./fieldModificationStore";
|
|
7
|
-
import { useRouter, useSearchParams, usePathname } from "
|
|
7
|
+
import { useRouter, useSearchParams, usePathname } from "./navigation";
|
|
8
8
|
import { findComponent, getComponentById } from "../componentTreeHelper";
|
|
9
9
|
import { getOperationsContext } from "./operations";
|
|
10
10
|
import { handleErrorResult } from "./helpers";
|
|
11
|
-
import { executeFieldAction as executeFieldServerAction, connectSocket, getEditHistory, getRunningOperations, releaseFieldLocks, validateItems, } from "../services/editService";
|
|
11
|
+
import { executeFieldAction as executeFieldServerAction, connectSocket, getEditHistory, getRunningOperations, reconnectSession, releaseFieldLocks, validateItems, } from "../services/editService";
|
|
12
12
|
import { useEditorWebSocket } from "./hooks/useEditorWebSocket";
|
|
13
|
+
import { createEditorSocketDiagnostics, } from "./socketDiagnostics";
|
|
14
|
+
import { localStorageService } from "../services/localStorageService";
|
|
13
15
|
import { useSocketMessageHandler } from "./hooks/useSocketMessageHandler";
|
|
14
16
|
import "react-json-view-lite/dist/index.css";
|
|
15
17
|
import { MediaSelector, } from "../media-selector/MediaSelector";
|
|
@@ -20,6 +22,7 @@ import { getItemDescriptor } from "../utils";
|
|
|
20
22
|
import { EditContextMenu } from "../ContextMenu";
|
|
21
23
|
import { InlineAiTrigger } from "../ai/InlineAiTrigger";
|
|
22
24
|
import { FieldEditorPopup } from "../FieldEditorPopup";
|
|
25
|
+
import { ConcurrentUserLimitDialog } from "../ConcurrentUserLimitDialog";
|
|
23
26
|
import { post } from "../services/serviceHelper";
|
|
24
27
|
import { PageViewerFrame } from "../page-viewer/PageViewerFrame";
|
|
25
28
|
import { useItemsRepository } from "./itemsRepository";
|
|
@@ -33,6 +36,7 @@ import { GuidanceOverlay } from "../ai/GuidanceOverlay";
|
|
|
33
36
|
import { AgentDialogHandler } from "../ai/dialogs";
|
|
34
37
|
import { usePageViewContext, } from "../page-viewer/pageViewContext";
|
|
35
38
|
import { QuickItemSwitcher } from "../QuickItemSwitcher";
|
|
39
|
+
import { useEditorSlotContext, } from "../views/editorSlotContext";
|
|
36
40
|
import { getComments, getAvailableCommentTags, } from "../services/reviewsService";
|
|
37
41
|
import { useReviews } from "../reviews/useReviews";
|
|
38
42
|
import uuid from "react-uuid";
|
|
@@ -49,7 +53,33 @@ import { useWorkbox } from "./hooks/useWorkbox";
|
|
|
49
53
|
import { useMediaSelector } from "./hooks/useMediaSelector";
|
|
50
54
|
import { useGlobalEditorKeyDown } from "./hooks/useGlobalEditorKeyDown";
|
|
51
55
|
import { useStartupChecks } from "../settings/status/index";
|
|
52
|
-
|
|
56
|
+
import { FeatureGate, LicenseFeatures, LicenseProvider, LicenseOverlay, } from "../../licensing";
|
|
57
|
+
function AgentsSlotContextBridge({ slot }) {
|
|
58
|
+
const editContext = useEditContext();
|
|
59
|
+
const slotContext = useEditorSlotContext({
|
|
60
|
+
slotId: slot.slotId,
|
|
61
|
+
itemDescriptor: slot.itemDescriptor,
|
|
62
|
+
refreshToken: slot.refreshToken,
|
|
63
|
+
});
|
|
64
|
+
if (!editContext || !slotContext)
|
|
65
|
+
return null;
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
editContext.registerSlotContext(slot.slotId, slotContext);
|
|
68
|
+
return () => {
|
|
69
|
+
editContext.unregisterSlotContext(slot.slotId);
|
|
70
|
+
};
|
|
71
|
+
}, [
|
|
72
|
+
slot.slotId,
|
|
73
|
+
slotContext,
|
|
74
|
+
editContext.registerSlotContext,
|
|
75
|
+
editContext.unregisterSlotContext,
|
|
76
|
+
]);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function AgentsSlotContextBridgeHost({ slots }) {
|
|
80
|
+
return (_jsx(_Fragment, { children: slots.map((slot) => (_jsx(AgentsSlotContextBridge, { slot: slot }, `agents-slot-bridge-${slot.slotId}`))) }));
|
|
81
|
+
}
|
|
82
|
+
export function EditorShell({ configuration, className, item: loadItemDescriptor, sessionId, userInfo, userPreferences, initialLicenseStatus, initialLicenseStatusLoaded, parheliaSettings, setUserPreferences, children, }) {
|
|
53
83
|
const router = useRouter();
|
|
54
84
|
const pathname = usePathname();
|
|
55
85
|
const searchParams = useSearchParams();
|
|
@@ -97,6 +127,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
97
127
|
const [historyMode, setHistoryMode] = useState("global");
|
|
98
128
|
const [showOnlyMyChanges, setShowOnlyMyChanges] = useState(true);
|
|
99
129
|
const [filterByCurrentLanguage, setFilterByCurrentLanguage] = useState(true);
|
|
130
|
+
const [historySearchQuery, setHistorySearchQuery] = useState("");
|
|
100
131
|
const [recentEdits, setRecentEdits] = useState([]);
|
|
101
132
|
const addRecentEdit = useCallback((edit) => {
|
|
102
133
|
setRecentEdits((prevEditedFields) => {
|
|
@@ -124,14 +155,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
124
155
|
if (typeof window !== "undefined")
|
|
125
156
|
sessionStorage?.setItem("sessionId", sessionId);
|
|
126
157
|
// Workspace state
|
|
127
|
-
// Note: "reviews" is a sidebar, not a workspace. If
|
|
158
|
+
// Note: "reviews" is a sidebar, not a workspace. If workspace=reviews, we should
|
|
128
159
|
// set workspace to "editor" and open the reviews sidebar instead.
|
|
129
160
|
// Memoize searchParams reads to avoid triggering Router state updates during render
|
|
130
161
|
// (Next.js App Router uses startTransition internally for URL changes)
|
|
131
|
-
const rawWorkspace = useMemo(() => searchParams.get("workspace")
|
|
162
|
+
const rawWorkspace = useMemo(() => searchParams.get("workspace"), [searchParams]);
|
|
132
163
|
const isReviewsSidebarRequest = rawWorkspace === "reviews";
|
|
133
164
|
const [workspaceId, setWorkspaceId] = useState(
|
|
134
|
-
// If
|
|
165
|
+
// If workspace=reviews, use "editor" workspace (reviews is a sidebar, not a workspace)
|
|
135
166
|
isReviewsSidebarRequest
|
|
136
167
|
? "editor"
|
|
137
168
|
: (rawWorkspace ??
|
|
@@ -153,7 +184,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
153
184
|
else {
|
|
154
185
|
sidebars = [...(configuration.editor.defaultOpenSidebars ?? [])];
|
|
155
186
|
}
|
|
156
|
-
// If
|
|
187
|
+
// If workspace=reviews was requested, ensure reviews sidebar is open
|
|
157
188
|
if (isReviewsSidebarRequest && !sidebars.includes("reviews")) {
|
|
158
189
|
sidebars.push("reviews");
|
|
159
190
|
}
|
|
@@ -161,17 +192,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
161
192
|
});
|
|
162
193
|
// Toolbar pinned sidebars - icons always visible in toolbar (defined early so callbacks can access)
|
|
163
194
|
const [pinnedSidebars, setPinnedSidebars] = useState(userInfo.preferences?.pinnedSidebars || []);
|
|
195
|
+
const pinnedSidebarsRef = useRef(pinnedSidebars);
|
|
164
196
|
// Locked sidebars - open panels that stay visible when selecting another sidebar
|
|
165
197
|
const [lockedSidebars, setLockedSidebars] = useState(() => {
|
|
166
|
-
if (typeof window === "undefined") {
|
|
167
|
-
return [];
|
|
168
|
-
}
|
|
169
198
|
try {
|
|
170
|
-
const
|
|
171
|
-
if (!stored) {
|
|
172
|
-
return [];
|
|
173
|
-
}
|
|
174
|
-
const parsed = JSON.parse(stored);
|
|
199
|
+
const parsed = localStorageService.getOrSetItem("editor.lockedSidebars", []);
|
|
175
200
|
if (!Array.isArray(parsed) ||
|
|
176
201
|
!parsed.every((id) => typeof id === "string")) {
|
|
177
202
|
return [];
|
|
@@ -187,6 +212,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
187
212
|
useEffect(() => {
|
|
188
213
|
lockedSidebarsRef.current = lockedSidebars;
|
|
189
214
|
}, [lockedSidebars]);
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
pinnedSidebarsRef.current = pinnedSidebars;
|
|
217
|
+
}, [pinnedSidebars]);
|
|
190
218
|
// Filter locked sidebars to only include currently open sidebars
|
|
191
219
|
useEffect(() => {
|
|
192
220
|
const openSet = new Set(openSidebars);
|
|
@@ -248,15 +276,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
248
276
|
// Group open sidebars into vertical stacks (each stack shares one left column).
|
|
249
277
|
const [sidebarStacks, setSidebarStacks] = useState(() => {
|
|
250
278
|
const defaultStacks = openSidebars.map((id) => [id]);
|
|
251
|
-
if (typeof window === "undefined") {
|
|
252
|
-
return normalizeSidebarStacks(openSidebars, defaultStacks);
|
|
253
|
-
}
|
|
254
279
|
try {
|
|
255
|
-
const
|
|
256
|
-
if (!
|
|
280
|
+
const parsed = localStorageService.getItem("editor.sidebarStacks");
|
|
281
|
+
if (!parsed) {
|
|
257
282
|
return normalizeSidebarStacks(openSidebars, defaultStacks);
|
|
258
283
|
}
|
|
259
|
-
const parsed = JSON.parse(stored);
|
|
260
284
|
if (!Array.isArray(parsed) ||
|
|
261
285
|
!parsed.every((x) => Array.isArray(x) && x.every((id) => typeof id === "string"))) {
|
|
262
286
|
return normalizeSidebarStacks(openSidebars, defaultStacks);
|
|
@@ -278,28 +302,19 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
278
302
|
}, [openSidebars, normalizeSidebarStacks]);
|
|
279
303
|
// Persist stacks independently of the URL (user layout preference).
|
|
280
304
|
useEffect(() => {
|
|
281
|
-
if (typeof window === "undefined")
|
|
282
|
-
return;
|
|
283
305
|
try {
|
|
284
|
-
|
|
306
|
+
localStorageService.setItem("editor.sidebarStacks", sidebarStacks);
|
|
285
307
|
}
|
|
286
308
|
catch { }
|
|
287
309
|
}, [sidebarStacks]);
|
|
288
310
|
// Persist locked sidebars to localStorage.
|
|
289
311
|
useEffect(() => {
|
|
290
|
-
if (typeof window === "undefined")
|
|
291
|
-
return;
|
|
292
312
|
try {
|
|
293
|
-
|
|
313
|
+
localStorageService.setItem("editor.lockedSidebars", lockedSidebars);
|
|
294
314
|
}
|
|
295
315
|
catch { }
|
|
296
316
|
}, [lockedSidebars]);
|
|
297
|
-
// Legacy aliases for backwards compatibility
|
|
298
317
|
const viewName = workspaceId;
|
|
299
|
-
const setViewName = setWorkspaceId;
|
|
300
|
-
const viewNameRef = workspaceIdRef;
|
|
301
|
-
const previousViewName = previousWorkspaceId;
|
|
302
|
-
const setPreviousViewName = setPreviousWorkspaceId;
|
|
303
318
|
const [compareMode, setCompareModeState] = useState(false);
|
|
304
319
|
const [componentDesignerComponent, setComponentDesignerComponent] = useState();
|
|
305
320
|
const [componentDesignerRendering, setComponentDesignerRendering] = useState();
|
|
@@ -307,6 +322,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
307
322
|
const [ignoreBlur, setIgnoreBlur] = useState(false);
|
|
308
323
|
const [currentItemDescriptor, setCurrentItemDescriptor] = useState();
|
|
309
324
|
const currentItemDescriptorRef = useRef(undefined);
|
|
325
|
+
const latestGlobalLoadKeyRef = useRef(null);
|
|
310
326
|
const [editorSlots, setEditorSlots] = useState(() => {
|
|
311
327
|
// Always start with empty slots - don't persist the number of slots
|
|
312
328
|
return [];
|
|
@@ -326,6 +342,29 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
326
342
|
activeSlotIdRef.current = activeSlotId;
|
|
327
343
|
}, [activeSlotId]);
|
|
328
344
|
const [slotContexts, setSlotContexts] = useState(() => new Map());
|
|
345
|
+
const promptSessionReconnect = useCallback((reason) => {
|
|
346
|
+
const description = reason && reason !== "session-revoked"
|
|
347
|
+
? `${reason}. Click reconnect to continue in this browser.`
|
|
348
|
+
: "You were disconnected because this account is active in another browser.";
|
|
349
|
+
toast.error("Session disconnected", {
|
|
350
|
+
id: "session-revoked",
|
|
351
|
+
description,
|
|
352
|
+
action: {
|
|
353
|
+
label: "Reconnect",
|
|
354
|
+
onClick: async () => {
|
|
355
|
+
const result = await reconnectSession(sessionId);
|
|
356
|
+
if (result.type === "success") {
|
|
357
|
+
window.location.reload();
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
toast.error("Reconnect failed", {
|
|
361
|
+
description: "Could not claim this browser session. Try again.",
|
|
362
|
+
});
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
duration: Infinity,
|
|
366
|
+
});
|
|
367
|
+
}, [sessionId]);
|
|
329
368
|
// Track previous item ID to detect actual navigation between pages
|
|
330
369
|
const previousItemIdRef = useRef(undefined);
|
|
331
370
|
useEffect(() => {
|
|
@@ -354,6 +393,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
354
393
|
// Ref to track when we're handling a popstate event (browser back/forward)
|
|
355
394
|
// This prevents the URL sync effect from pushing new history entries during back navigation
|
|
356
395
|
const isHandlingPopStateRef = useRef(false);
|
|
396
|
+
// When switchWorkspace already pushed a URL entry, the follow-up URL sync
|
|
397
|
+
// effect should only *replace* that entry (not push a second one).
|
|
398
|
+
const switchWorkspacePushedRef = useRef(false);
|
|
357
399
|
// Ref to track the last known URL for the popstate handler
|
|
358
400
|
// This is updated both when the popstate handler runs AND when the URL sync effect pushes a new URL
|
|
359
401
|
// Without this, the popstate handler would have a stale lastUrl value after pushState calls
|
|
@@ -370,26 +412,16 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
370
412
|
const [showSuggestedEditsDiff, setShowSuggestedEditsDiff] = useState(false);
|
|
371
413
|
const [availableCommentTags, setAvailableCommentTags] = useState([]);
|
|
372
414
|
const [showComments, setShowComments] = useState(() => {
|
|
373
|
-
|
|
374
|
-
? localStorage.getItem("editor.showComments")
|
|
375
|
-
: null;
|
|
376
|
-
return savedShowComments ? JSON.parse(savedShowComments) : true;
|
|
415
|
+
return localStorageService.getOrSetItem("editor.showComments", true);
|
|
377
416
|
});
|
|
378
417
|
useEffect(() => {
|
|
379
|
-
|
|
380
|
-
localStorage.setItem("editor.showComments", JSON.stringify(showComments));
|
|
381
|
-
}
|
|
418
|
+
localStorageService.setItem("editor.showComments", showComments);
|
|
382
419
|
}, [showComments]);
|
|
383
420
|
const [showResolvedComments, setShowResolvedComments] = useState(() => {
|
|
384
|
-
|
|
385
|
-
? localStorage.getItem("editor.showResolvedComments")
|
|
386
|
-
: null;
|
|
387
|
-
return saved ? JSON.parse(saved) : false;
|
|
421
|
+
return localStorageService.getOrSetItem("editor.showResolvedComments", false);
|
|
388
422
|
});
|
|
389
423
|
useEffect(() => {
|
|
390
|
-
|
|
391
|
-
localStorage.setItem("editor.showResolvedComments", JSON.stringify(showResolvedComments));
|
|
392
|
-
}
|
|
424
|
+
localStorageService.setItem("editor.showResolvedComments", showResolvedComments);
|
|
393
425
|
}, [showResolvedComments]);
|
|
394
426
|
const [selectedComment, setSelectedComment] = useState();
|
|
395
427
|
const [browseHistory, setBrowseHistory] = useState(() => {
|
|
@@ -408,13 +440,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
408
440
|
visitedAt: entry.visitedAt,
|
|
409
441
|
}));
|
|
410
442
|
});
|
|
411
|
-
// Navigation history for browser history (
|
|
443
|
+
// Navigation history for browser history (workspace + item combinations)
|
|
412
444
|
const [navigationHistory, setNavigationHistory] = useState(() => {
|
|
413
|
-
// Initialize from browse history with current
|
|
445
|
+
// Initialize from browse history with the current workspace
|
|
414
446
|
if (!userInfo.browseHistory)
|
|
415
447
|
return [];
|
|
416
448
|
const defaultWorkspaceId = searchParams.get("workspace") ??
|
|
417
|
-
searchParams.get("view") ??
|
|
418
449
|
configuration.editor.defaultWorkspace ??
|
|
419
450
|
configuration.editor.workspaces?.[0]?.id ??
|
|
420
451
|
"editor";
|
|
@@ -446,20 +477,43 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
446
477
|
const [statusMessage, setStatusMessage] = useState("");
|
|
447
478
|
const [focusFieldComponentId, setFocusFieldComponentId] = useState();
|
|
448
479
|
const [enableCompletions, setEnableCompletions] = useState(false);
|
|
449
|
-
const [
|
|
480
|
+
const [showComponentNavigatorDefault, setShowComponentNavigatorDefault] = useState(userPreferences.showComponentNavigator ?? false);
|
|
481
|
+
const [slotComponentNavigatorVisibility, setSlotComponentNavigatorVisibility] = useState({});
|
|
482
|
+
useEffect(() => {
|
|
483
|
+
setSlotComponentNavigatorVisibility((prev) => {
|
|
484
|
+
const next = {};
|
|
485
|
+
let changed = false;
|
|
486
|
+
for (const slot of editorSlots) {
|
|
487
|
+
if (Object.prototype.hasOwnProperty.call(prev, slot.slotId)) {
|
|
488
|
+
next[slot.slotId] = prev[slot.slotId];
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
next[slot.slotId] = showComponentNavigatorDefault;
|
|
492
|
+
changed = true;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (Object.keys(prev).length !== editorSlots.length) {
|
|
496
|
+
changed = true;
|
|
497
|
+
}
|
|
498
|
+
return changed ? next : prev;
|
|
499
|
+
});
|
|
500
|
+
}, [editorSlots, showComponentNavigatorDefault]);
|
|
450
501
|
const [showAgentsPanel, setShowAgentsPanel] = useState(userPreferences.showAgentsPanel ?? false);
|
|
451
502
|
const [showMinimap, setShowMinimap] = useState(userPreferences.showMinimap ?? true);
|
|
452
503
|
const [showHelpTerminal, setShowHelpTerminal] = useState(false);
|
|
453
504
|
const [helpTerminalInitialPrompt, setHelpTerminalInitialPrompt] = useState(undefined);
|
|
454
505
|
const [helpTerminalProfileName, setHelpTerminalProfileName] = useState(undefined);
|
|
455
506
|
const [helpTerminalActiveTab, setHelpTerminalActiveTab] = useState(undefined);
|
|
456
|
-
const [
|
|
507
|
+
const [selectedHelpSectionId, setSelectedHelpSectionId] = useState(null);
|
|
508
|
+
const [showAgentsWorkspaceEditor, setShowAgentsWorkspaceEditor] = useState(false);
|
|
509
|
+
const [selectedAgentsWorkspaceAgentId, setSelectedAgentsWorkspaceAgentId] = useState(null);
|
|
457
510
|
const [activeEditorTab, setActiveEditorTab] = useState(null);
|
|
458
511
|
const [showLayoutComponents, setShowLayoutComponents] = useState(userPreferences.showLayoutComponents ?? false);
|
|
459
512
|
const { quotaInfo, setQuotaInfo, isQuotaExceeded, getQuotaWarningMessage } = useQuota({
|
|
460
513
|
showError: ({ summary, details }) => showErrorToast({ summary, details }),
|
|
461
514
|
});
|
|
462
515
|
const [webSocketMessages, setWebSocketMessages] = useState([]);
|
|
516
|
+
const [socketDiagnostics, setSocketDiagnostics] = useState(() => createEditorSocketDiagnostics(sessionId));
|
|
463
517
|
const [favorites, setFavorites] = useState([]);
|
|
464
518
|
// Quick item switcher state
|
|
465
519
|
const [quickSwitcherVisible, setQuickSwitcherVisible] = useState(false);
|
|
@@ -477,13 +531,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
477
531
|
if (!startupChecks.hasBlockingIssues)
|
|
478
532
|
return;
|
|
479
533
|
// Don't redirect if already in settings workspace - let user navigate freely within settings
|
|
480
|
-
const currentWorkspace = searchParams.get("workspace")
|
|
534
|
+
const currentWorkspace = searchParams.get("workspace");
|
|
481
535
|
if (currentWorkspace === "settings")
|
|
482
536
|
return;
|
|
483
537
|
// Redirect to the status panel (where user can see all issues and navigate to fixes)
|
|
484
538
|
const url = new URL(window.location.href);
|
|
485
539
|
url.searchParams.set("workspace", "settings");
|
|
486
|
-
url.searchParams.delete("view"); // Remove legacy param
|
|
487
540
|
url.searchParams.set("ccpanel", "status");
|
|
488
541
|
router.push(url.toString(), { scroll: false });
|
|
489
542
|
}, [
|
|
@@ -501,6 +554,16 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
501
554
|
setMode(queryMode);
|
|
502
555
|
}
|
|
503
556
|
}, [searchParams, isInitialLoad]);
|
|
557
|
+
useEffect(() => {
|
|
558
|
+
if (!isInitialLoad)
|
|
559
|
+
return;
|
|
560
|
+
const helpParam = searchParams.get("help");
|
|
561
|
+
if (helpParam) {
|
|
562
|
+
setHelpTerminalActiveTab("manual");
|
|
563
|
+
setShowHelpTerminal(true);
|
|
564
|
+
setSelectedHelpSectionId(helpParam === "contents" || helpParam === "true" ? null : helpParam);
|
|
565
|
+
}
|
|
566
|
+
}, [searchParams, isInitialLoad]);
|
|
504
567
|
useEffect(() => {
|
|
505
568
|
if (mode === "suggestions") {
|
|
506
569
|
// Ensure we're in the editor workspace and open the feedback sidebar
|
|
@@ -547,12 +610,19 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
547
610
|
setSlotContexts((prev) => {
|
|
548
611
|
if (!prev.has(slotId))
|
|
549
612
|
return prev;
|
|
613
|
+
if (slotId === activeSlotIdRef.current) {
|
|
614
|
+
const activeCtx = prev.get(slotId);
|
|
615
|
+
if (activeCtx) {
|
|
616
|
+
lastActiveSlotContextRef.current = activeCtx;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
550
619
|
const next = new Map(prev);
|
|
551
620
|
next.delete(slotId);
|
|
552
621
|
return next;
|
|
553
622
|
});
|
|
554
623
|
}, []);
|
|
555
624
|
const slotContextsRef = useRef(slotContexts);
|
|
625
|
+
const lastActiveSlotContextRef = useRef(null);
|
|
556
626
|
useEffect(() => {
|
|
557
627
|
slotContextsRef.current = slotContexts;
|
|
558
628
|
}, [slotContexts]);
|
|
@@ -578,6 +648,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
578
648
|
const activeSlotContext = activeSlotId
|
|
579
649
|
? slotContexts.get(activeSlotId)
|
|
580
650
|
: undefined;
|
|
651
|
+
const isComponentNavigatorOpenForSlot = useCallback((slotId) => {
|
|
652
|
+
if (!slotId)
|
|
653
|
+
return showComponentNavigatorDefault;
|
|
654
|
+
return (slotComponentNavigatorVisibility[slotId] ?? showComponentNavigatorDefault);
|
|
655
|
+
}, [slotComponentNavigatorVisibility, showComponentNavigatorDefault]);
|
|
656
|
+
const showComponentNavigator = isComponentNavigatorOpenForSlot(activeSlotId);
|
|
581
657
|
// Sync global compareMode from the active slot's compareMode for URL syncing
|
|
582
658
|
useEffect(() => {
|
|
583
659
|
if (activeSlotContext && activeSlotContext.compareMode !== compareMode) {
|
|
@@ -625,6 +701,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
625
701
|
const isItemUsedInCurrentPage = useCallback((item) => pageItemsSetRef.current.has(makeItemKey(item)), [makeItemKey]);
|
|
626
702
|
const socketMessageListeners = useRef(new Set());
|
|
627
703
|
const [socketConnectionVersion, setSocketConnectionVersion] = useState(0);
|
|
704
|
+
useEffect(() => {
|
|
705
|
+
setSocketDiagnostics(createEditorSocketDiagnostics(sessionId));
|
|
706
|
+
}, [sessionId]);
|
|
628
707
|
const addSocketMessageListener = useCallback((callback) => {
|
|
629
708
|
socketMessageListeners.current.add(callback);
|
|
630
709
|
return () => socketMessageListeners.current.delete(callback);
|
|
@@ -709,8 +788,13 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
709
788
|
console.error(`No workspace found for id: ${workspaceId}`);
|
|
710
789
|
return null;
|
|
711
790
|
}
|
|
712
|
-
|
|
713
|
-
|
|
791
|
+
const activeKeyboardCommands = useMemo(() => {
|
|
792
|
+
const activeCommandIds = new Set(currentWorkspace.keyboardCommandIds ?? []);
|
|
793
|
+
return (configuration.commands.keyboardCommands ?? []).filter((command) => activeCommandIds.has(command.id));
|
|
794
|
+
}, [
|
|
795
|
+
currentWorkspace.keyboardCommandIds,
|
|
796
|
+
configuration.commands.keyboardCommands,
|
|
797
|
+
]);
|
|
714
798
|
useEffect(() => {
|
|
715
799
|
if (currentWorkspace?.component) {
|
|
716
800
|
setCenterPanelView(currentWorkspace.component);
|
|
@@ -738,6 +822,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
738
822
|
const sendClientInfo = useCallback(() => {
|
|
739
823
|
debouncedSendClientInfo();
|
|
740
824
|
}, [debouncedSendClientInfo]);
|
|
825
|
+
const getCurrentHistoryState = useCallback(() => {
|
|
826
|
+
if (typeof window === "undefined") {
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
829
|
+
return window.history.state;
|
|
830
|
+
}, []);
|
|
741
831
|
const startTour = useCallback(() => {
|
|
742
832
|
setIsTourActive(true);
|
|
743
833
|
// Persist tour state to URL so it survives navigation/remounts
|
|
@@ -745,32 +835,61 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
745
835
|
const params = new URLSearchParams(window.location.search);
|
|
746
836
|
params.set("tour", "active");
|
|
747
837
|
const newUrl = `${window.location.pathname}?${params.toString()}`;
|
|
748
|
-
window.history.replaceState(
|
|
838
|
+
window.history.replaceState(getCurrentHistoryState(), "", newUrl);
|
|
749
839
|
}, [setIsTourActive]);
|
|
750
|
-
const isMobile = useMediaQuery("(max-width:
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
840
|
+
const isMobile = useMediaQuery("(max-width: 767px)");
|
|
841
|
+
// On mobile, clear all locked sidebars and keep only the last-opened panel.
|
|
842
|
+
// Handles viewport resize: desktop -> mobile unlocks everything and trims to one panel.
|
|
843
|
+
useEffect(() => {
|
|
844
|
+
if (isMobile) {
|
|
845
|
+
setLockedSidebars([]);
|
|
846
|
+
const current = openSidebarsRef.current;
|
|
847
|
+
const lastSidebar = current[current.length - 1];
|
|
848
|
+
if (current.length > 1 && lastSidebar) {
|
|
849
|
+
const trimmed = [lastSidebar];
|
|
850
|
+
openSidebarsRef.current = trimmed;
|
|
851
|
+
setOpenSidebars(trimmed);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}, [isMobile]);
|
|
855
|
+
const setComponentNavigatorOpenForSlot = useCallback((slotId, value) => {
|
|
856
|
+
const previousValue = isComponentNavigatorOpenForSlot(slotId);
|
|
857
|
+
const newValue = typeof value === "function" ? value(previousValue) : value;
|
|
858
|
+
if (slotId) {
|
|
859
|
+
setSlotComponentNavigatorVisibility((prev) => {
|
|
860
|
+
const currentValue = prev[slotId] ?? showComponentNavigatorDefault;
|
|
861
|
+
if (currentValue === newValue && prev[slotId] !== undefined) {
|
|
862
|
+
return prev;
|
|
863
|
+
}
|
|
864
|
+
return {
|
|
865
|
+
...prev,
|
|
866
|
+
[slotId]: newValue,
|
|
867
|
+
};
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
setShowComponentNavigatorDefault(newValue);
|
|
754
871
|
setUserPreferences({ showComponentNavigator: newValue });
|
|
755
|
-
// On mobile, close Agents Panel when opening Component Navigator
|
|
756
872
|
if (isMobile && newValue) {
|
|
757
873
|
setShowAgentsPanel(false);
|
|
758
874
|
setUserPreferences({ showAgentsPanel: false });
|
|
759
875
|
}
|
|
760
876
|
}, [
|
|
761
|
-
|
|
762
|
-
|
|
877
|
+
isComponentNavigatorOpenForSlot,
|
|
878
|
+
showComponentNavigatorDefault,
|
|
763
879
|
setUserPreferences,
|
|
764
880
|
isMobile,
|
|
765
881
|
setShowAgentsPanel,
|
|
766
882
|
]);
|
|
883
|
+
const handleSetShowComponentNavigator = useCallback((value) => {
|
|
884
|
+
setComponentNavigatorOpenForSlot(activeSlotIdRef.current, value);
|
|
885
|
+
}, [setComponentNavigatorOpenForSlot]);
|
|
767
886
|
const handleSetShowAgentsPanel = useCallback((value) => {
|
|
768
887
|
const newValue = typeof value === "function" ? value(showAgentsPanel) : value;
|
|
769
888
|
setShowAgentsPanel(newValue);
|
|
770
889
|
setUserPreferences({ showAgentsPanel: newValue });
|
|
771
890
|
// On mobile, close Component Navigator when opening Agents Panel
|
|
772
891
|
if (isMobile && newValue) {
|
|
773
|
-
|
|
892
|
+
setComponentNavigatorOpenForSlot(activeSlotIdRef.current, false);
|
|
774
893
|
setUserPreferences({ showComponentNavigator: false });
|
|
775
894
|
}
|
|
776
895
|
}, [
|
|
@@ -778,8 +897,40 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
778
897
|
setShowAgentsPanel,
|
|
779
898
|
setUserPreferences,
|
|
780
899
|
isMobile,
|
|
781
|
-
|
|
900
|
+
setComponentNavigatorOpenForSlot,
|
|
782
901
|
]);
|
|
902
|
+
// Mobile editor panel state (EditorForm shown in bottom panel on mobile)
|
|
903
|
+
const [mobileEditorPanelOpen, setMobileEditorPanelOpenRaw] = useState(false);
|
|
904
|
+
const [dismissedMobilePanelToken, setDismissedMobilePanelToken] = useState(null);
|
|
905
|
+
const previousActiveSlotIdRef = useRef(null);
|
|
906
|
+
const mobilePanelDismissToken = useMemo(() => {
|
|
907
|
+
const selectionKey = selection.join(",");
|
|
908
|
+
return [
|
|
909
|
+
activeSlotId || "no-slot",
|
|
910
|
+
selectionKey,
|
|
911
|
+
insertMode ? "insert" : "browse",
|
|
912
|
+
].join("|");
|
|
913
|
+
}, [activeSlotId, insertMode, selection]);
|
|
914
|
+
useEffect(() => {
|
|
915
|
+
const previousActiveSlotId = previousActiveSlotIdRef.current;
|
|
916
|
+
if (previousActiveSlotId !== activeSlotId) {
|
|
917
|
+
setDismissedMobilePanelToken(null);
|
|
918
|
+
}
|
|
919
|
+
previousActiveSlotIdRef.current = activeSlotId;
|
|
920
|
+
}, [activeSlotId]);
|
|
921
|
+
const handleSetMobileEditorPanelOpen = useCallback((open) => {
|
|
922
|
+
setMobileEditorPanelOpenRaw(open);
|
|
923
|
+
if (!open) {
|
|
924
|
+
setDismissedMobilePanelToken(mobilePanelDismissToken);
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
setDismissedMobilePanelToken(null);
|
|
928
|
+
if (open && isMobile) {
|
|
929
|
+
// Close all sidebars when opening the editor panel on mobile
|
|
930
|
+
openSidebarsRef.current = [];
|
|
931
|
+
setOpenSidebars([]);
|
|
932
|
+
}
|
|
933
|
+
}, [isMobile, mobilePanelDismissToken]);
|
|
783
934
|
const handleSetShowMinimap = useCallback((value) => {
|
|
784
935
|
const newValue = typeof value === "function" ? value(showMinimap) : value;
|
|
785
936
|
setShowMinimap(newValue);
|
|
@@ -793,6 +944,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
793
944
|
setHelpTerminalInitialPrompt(undefined);
|
|
794
945
|
setHelpTerminalProfileName(undefined);
|
|
795
946
|
setHelpTerminalActiveTab(undefined);
|
|
947
|
+
setSelectedHelpSectionId(null);
|
|
796
948
|
}
|
|
797
949
|
}, [showHelpTerminal]);
|
|
798
950
|
const toggleHelpTerminal = useCallback((options) => {
|
|
@@ -835,13 +987,27 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
835
987
|
setOpenSidebars(newOrder);
|
|
836
988
|
setSidebarStacks((prev) => normalizeSidebarStacks(newOrder, prev));
|
|
837
989
|
}, []);
|
|
990
|
+
const ensureSidebarPinned = useCallback((sidebarId) => {
|
|
991
|
+
const currentPinnedSidebars = pinnedSidebarsRef.current;
|
|
992
|
+
if (currentPinnedSidebars.includes(sidebarId)) {
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
const newPinnedSidebars = [...currentPinnedSidebars, sidebarId];
|
|
996
|
+
pinnedSidebarsRef.current = newPinnedSidebars;
|
|
997
|
+
setPinnedSidebars(newPinnedSidebars);
|
|
998
|
+
setUserPreferences({ pinnedSidebars: newPinnedSidebars });
|
|
999
|
+
}, [setUserPreferences]);
|
|
838
1000
|
// messageHandler is defined after loadItem/loadHistory declarations to avoid temporal dead zones
|
|
839
1001
|
const user = activeSessions.find((x) => x.sessionId === sessionId)?.user ||
|
|
840
1002
|
userInfo.user;
|
|
841
|
-
// Self-heal if our session disappears (e.g., after HMR)
|
|
1003
|
+
// Self-heal if our session disappears (e.g., after HMR). Skip when concurrent user limit
|
|
1004
|
+
// is showing so we don't spam recovery attempts when the connection was rejected.
|
|
842
1005
|
const missingSessionRecoveryTimerRef = useRef(null);
|
|
843
1006
|
const missingSessionRecoveryAttemptsRef = useRef(0);
|
|
1007
|
+
const concurrentUserLimitErrorRef = useRef(null);
|
|
844
1008
|
useEffect(() => {
|
|
1009
|
+
if (concurrentUserLimitErrorRef.current !== null)
|
|
1010
|
+
return;
|
|
845
1011
|
const hasMySession = activeSessions.some((s) => s.sessionId === sessionId);
|
|
846
1012
|
if (hasMySession) {
|
|
847
1013
|
// Reset recovery state when we see ourselves again
|
|
@@ -857,7 +1023,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
857
1023
|
return;
|
|
858
1024
|
const attempt = missingSessionRecoveryAttemptsRef.current + 1;
|
|
859
1025
|
const delay = Math.min(250 * Math.pow(2, attempt - 1), 2000);
|
|
860
|
-
console.warn(`⚠️ Current session not present in active sessions. Recovery attempt ${attempt} in ${delay}ms...`);
|
|
861
1026
|
missingSessionRecoveryTimerRef.current = setTimeout(() => {
|
|
862
1027
|
missingSessionRecoveryTimerRef.current = null;
|
|
863
1028
|
missingSessionRecoveryAttemptsRef.current = attempt;
|
|
@@ -866,6 +1031,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
866
1031
|
}
|
|
867
1032
|
else {
|
|
868
1033
|
// Force a reconnect to refresh presence after several failed nudges
|
|
1034
|
+
console.warn("Session presence did not recover after retries. Forcing reconnect.");
|
|
869
1035
|
try {
|
|
870
1036
|
globalThis.editorSocket?.close(4000, "recover-presence");
|
|
871
1037
|
}
|
|
@@ -885,9 +1051,15 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
885
1051
|
// Initialize lastUrlRef to current URL on mount
|
|
886
1052
|
lastUrlRef.current = window.location.href;
|
|
887
1053
|
const keepAliveUrl = "/parhelia/keepalive";
|
|
888
|
-
const
|
|
1054
|
+
const runSessionCheck = () => {
|
|
889
1055
|
fetch(keepAliveUrl + "?ts=" + Date.now())
|
|
890
1056
|
.then((response) => {
|
|
1057
|
+
if (response.headers.get("X-Parhelia-Session-Revoked") === "true") {
|
|
1058
|
+
window.dispatchEvent(new CustomEvent("parhelia:session-revoked", {
|
|
1059
|
+
detail: { reason: "session-revoked" },
|
|
1060
|
+
}));
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
891
1063
|
if (response.status === 401 || response.status === 403) {
|
|
892
1064
|
toast.error("Your session has expired", {
|
|
893
1065
|
description: "Please login again to continue editing.",
|
|
@@ -900,7 +1072,24 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
900
1072
|
}
|
|
901
1073
|
})
|
|
902
1074
|
.catch((error) => console.error("Keep Alive error:", error));
|
|
903
|
-
}
|
|
1075
|
+
};
|
|
1076
|
+
const keepaliveIntervalMs = (() => {
|
|
1077
|
+
const param = new URLSearchParams(window.location.search).get("keepaliveIntervalMs");
|
|
1078
|
+
if (!param)
|
|
1079
|
+
return 5 * 60 * 1000;
|
|
1080
|
+
const ms = parseInt(param, 10);
|
|
1081
|
+
return Number.isFinite(ms) && ms >= 2000 && ms <= 60000
|
|
1082
|
+
? ms
|
|
1083
|
+
: 5 * 60 * 1000;
|
|
1084
|
+
})();
|
|
1085
|
+
const interval = setInterval(() => {
|
|
1086
|
+
runSessionCheck();
|
|
1087
|
+
}, keepaliveIntervalMs);
|
|
1088
|
+
const handleVisibilityChange = () => {
|
|
1089
|
+
if (document.visibilityState === "visible") {
|
|
1090
|
+
runSessionCheck();
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
904
1093
|
const handleMessage = (event) => {
|
|
905
1094
|
if (event.data.type === "componentsSelected") {
|
|
906
1095
|
setSelection(event.data.componentIds);
|
|
@@ -915,9 +1104,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
915
1104
|
isHandlingPopStateRef.current = true;
|
|
916
1105
|
// Sync URL parameters back to component state for browser navigation
|
|
917
1106
|
const urlParams = new URLSearchParams(window.location.search);
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
if (urlWorkspace && urlWorkspace !== viewNameRef.current) {
|
|
1107
|
+
const urlWorkspace = urlParams.get("workspace");
|
|
1108
|
+
if (urlWorkspace && urlWorkspace !== workspaceIdRef.current) {
|
|
921
1109
|
setWorkspaceId(urlWorkspace);
|
|
922
1110
|
}
|
|
923
1111
|
// Handle sidebar changes
|
|
@@ -934,6 +1122,18 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
934
1122
|
if (compareValue !== compareMode) {
|
|
935
1123
|
setCompareMode(compareValue);
|
|
936
1124
|
}
|
|
1125
|
+
// Handle help panel changes
|
|
1126
|
+
const helpParam = urlParams.get("help");
|
|
1127
|
+
if (helpParam) {
|
|
1128
|
+
setHelpTerminalActiveTab("manual");
|
|
1129
|
+
setShowHelpTerminal(true);
|
|
1130
|
+
setSelectedHelpSectionId(helpParam === "contents" || helpParam === "true"
|
|
1131
|
+
? null
|
|
1132
|
+
: helpParam);
|
|
1133
|
+
}
|
|
1134
|
+
else {
|
|
1135
|
+
handleSetShowHelpTerminal(false);
|
|
1136
|
+
}
|
|
937
1137
|
// Handle mode changes
|
|
938
1138
|
const urlMode = urlParams.get("mode");
|
|
939
1139
|
if (urlMode && urlMode !== mode) {
|
|
@@ -989,9 +1189,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
989
1189
|
};
|
|
990
1190
|
window.addEventListener("message", handleMessage);
|
|
991
1191
|
window.addEventListener("popstate", handlePopState);
|
|
1192
|
+
window.addEventListener("visibilitychange", handleVisibilityChange);
|
|
992
1193
|
return () => {
|
|
993
1194
|
window.removeEventListener("message", handleMessage);
|
|
994
1195
|
window.removeEventListener("popstate", handlePopState);
|
|
1196
|
+
window.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
995
1197
|
clearInterval(interval);
|
|
996
1198
|
};
|
|
997
1199
|
}, []);
|
|
@@ -1001,14 +1203,34 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1001
1203
|
if (searchParams.get("noTour") !== null) {
|
|
1002
1204
|
return;
|
|
1003
1205
|
}
|
|
1206
|
+
// Don't start tour when there are setup errors (user is or will be on system status page)
|
|
1207
|
+
if (startupChecks.state === "complete" && startupChecks.hasBlockingIssues) {
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
// Don't start tour when already on settings system status page
|
|
1211
|
+
if (viewName === "settings" && searchParams.get("ccpanel") === "status") {
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
// Wait for startup checks so we know whether we'll redirect to status
|
|
1215
|
+
if (startupChecks.state !== "complete") {
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1004
1218
|
const tour = configuration.activeTour;
|
|
1005
1219
|
const key = tour === "default" ? "editor.tourShown" : "editor.tourShown." + tour;
|
|
1006
|
-
const tourShown =
|
|
1220
|
+
const tourShown = localStorageService.getString(key);
|
|
1007
1221
|
if (!tourShown) {
|
|
1008
1222
|
startTour();
|
|
1009
|
-
|
|
1223
|
+
localStorageService.setString(key, "true");
|
|
1010
1224
|
}
|
|
1011
|
-
}, [
|
|
1225
|
+
}, [
|
|
1226
|
+
user,
|
|
1227
|
+
startupChecks.state,
|
|
1228
|
+
startupChecks.hasBlockingIssues,
|
|
1229
|
+
viewName,
|
|
1230
|
+
searchParams,
|
|
1231
|
+
configuration.activeTour,
|
|
1232
|
+
startTour,
|
|
1233
|
+
]);
|
|
1012
1234
|
// WebSocket initialization is performed after messageHandler is defined
|
|
1013
1235
|
// Defer URL sync until loadItem is defined below
|
|
1014
1236
|
// Mark end of initial load phase
|
|
@@ -1140,7 +1362,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1140
1362
|
const loadComments = useCallback(async () => {
|
|
1141
1363
|
if (!currentItemDescriptor)
|
|
1142
1364
|
return;
|
|
1143
|
-
const
|
|
1365
|
+
const reviewId = searchParams.get("reviewId");
|
|
1366
|
+
const result = await getComments(currentItemDescriptor.id, currentItemDescriptor.language, currentItemDescriptor.version, reviewId ?? undefined);
|
|
1144
1367
|
if (handleErrorResult(result, ui, state))
|
|
1145
1368
|
return;
|
|
1146
1369
|
setComments((x) => {
|
|
@@ -1153,7 +1376,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1153
1376
|
allComments.sort((a, b) => a.position - b.position);
|
|
1154
1377
|
return allComments;
|
|
1155
1378
|
});
|
|
1156
|
-
}, [currentItemDescriptor]);
|
|
1379
|
+
}, [currentItemDescriptor, searchParams]);
|
|
1157
1380
|
// Assuming currentItemDescriptor, ui, state, handleErrorResult, and setSuggestedEdits
|
|
1158
1381
|
// are available in your component context.
|
|
1159
1382
|
const loadSuggestedEdits = useCallback(async () => {
|
|
@@ -1162,7 +1385,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1162
1385
|
const result = await getSuggestedEdits(item.descriptor.id, item.descriptor.language, item.descriptor.version);
|
|
1163
1386
|
if (handleErrorResult(result, ui, state))
|
|
1164
1387
|
return;
|
|
1165
|
-
|
|
1388
|
+
const edits = result.data || [];
|
|
1389
|
+
setSuggestedEdits(edits);
|
|
1166
1390
|
}, [item]);
|
|
1167
1391
|
const loadFavorites = useCallback(async () => {
|
|
1168
1392
|
try {
|
|
@@ -1236,6 +1460,25 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1236
1460
|
return idB.localeCompare(idA);
|
|
1237
1461
|
});
|
|
1238
1462
|
}, []);
|
|
1463
|
+
const normalizeEditHistoryPayload = useCallback((value) => {
|
|
1464
|
+
if (Array.isArray(value)) {
|
|
1465
|
+
return value;
|
|
1466
|
+
}
|
|
1467
|
+
if (value && typeof value === "object") {
|
|
1468
|
+
const payload = value;
|
|
1469
|
+
const candidates = [
|
|
1470
|
+
payload.operations,
|
|
1471
|
+
payload.history,
|
|
1472
|
+
payload.items,
|
|
1473
|
+
payload.data,
|
|
1474
|
+
];
|
|
1475
|
+
const firstArray = candidates.find((candidate) => Array.isArray(candidate));
|
|
1476
|
+
if (Array.isArray(firstArray)) {
|
|
1477
|
+
return firstArray;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
return [];
|
|
1481
|
+
}, []);
|
|
1239
1482
|
useEffect(() => {
|
|
1240
1483
|
// Read fresh page from the mutable slot context ref chain.
|
|
1241
1484
|
// The slot context uses a stable ref that is mutated in-place (see editorSlotContext.ts),
|
|
@@ -1258,7 +1501,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1258
1501
|
if (handleErrorResult(result, ui, state))
|
|
1259
1502
|
return;
|
|
1260
1503
|
setEditHistory((prev) => {
|
|
1261
|
-
const next = result.data
|
|
1504
|
+
const next = normalizeEditHistoryPayload(result.data);
|
|
1262
1505
|
if (!prev.length)
|
|
1263
1506
|
return sortEditHistoryByDateDesc(next);
|
|
1264
1507
|
if (!next.length)
|
|
@@ -1317,6 +1560,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1317
1560
|
const shouldFilterByLanguage = filterByLanguage !== undefined
|
|
1318
1561
|
? filterByLanguage
|
|
1319
1562
|
: filterByCurrentLanguage;
|
|
1563
|
+
const trimmedHistoryQuery = historySearchQuery.trim();
|
|
1564
|
+
const historyQuery = trimmedHistoryQuery.length > 0 ? trimmedHistoryQuery : undefined;
|
|
1320
1565
|
if (currentMode === "global") {
|
|
1321
1566
|
// Global mode: optionally filter by session and/or language
|
|
1322
1567
|
const currentLanguage = item?.descriptor?.language || currentItemDescriptor?.language;
|
|
@@ -1326,13 +1571,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1326
1571
|
language: shouldFilterByLanguage && currentLanguage
|
|
1327
1572
|
? currentLanguage
|
|
1328
1573
|
: undefined,
|
|
1574
|
+
query: historyQuery,
|
|
1329
1575
|
});
|
|
1330
1576
|
if (handleErrorResult(result, ui, state)) {
|
|
1331
1577
|
console.error("[EditorShell] Failed to load history:", result);
|
|
1332
1578
|
return;
|
|
1333
1579
|
}
|
|
1334
1580
|
setEditHistory((prev) => {
|
|
1335
|
-
const next = result.data
|
|
1581
|
+
const next = normalizeEditHistoryPayload(result.data);
|
|
1336
1582
|
const scope = {
|
|
1337
1583
|
mode: currentMode,
|
|
1338
1584
|
filterBySession: shouldFilterBySession,
|
|
@@ -1343,8 +1589,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1343
1589
|
};
|
|
1344
1590
|
if (!prev.length)
|
|
1345
1591
|
return sortEditHistoryByDateDesc(next.filter((op) => matchesHistoryScope(op, scope)));
|
|
1346
|
-
if (!next.length)
|
|
1592
|
+
if (!next.length) {
|
|
1593
|
+
// When searching, respect the empty result — don't fall back to previous items
|
|
1594
|
+
if (historyQuery)
|
|
1595
|
+
return [];
|
|
1347
1596
|
return sortEditHistoryByDateDesc(prev.filter((op) => matchesHistoryScope(op, scope)));
|
|
1597
|
+
}
|
|
1348
1598
|
const prevById = new Map(prev.map((x) => [x.id, x]));
|
|
1349
1599
|
const nextById = new Set(next.map((x) => x.id));
|
|
1350
1600
|
const merged = next
|
|
@@ -1380,9 +1630,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1380
1630
|
return mergedOp;
|
|
1381
1631
|
});
|
|
1382
1632
|
// Preserve operations that arrived via WebSocket during the fetch window
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1633
|
+
// but not when filtering by search query — search results are authoritative
|
|
1634
|
+
if (!historyQuery) {
|
|
1635
|
+
for (const [id, priorOp] of prevById) {
|
|
1636
|
+
if (!nextById.has(id) && matchesHistoryScope(priorOp, scope)) {
|
|
1637
|
+
merged.push(priorOp);
|
|
1638
|
+
}
|
|
1386
1639
|
}
|
|
1387
1640
|
}
|
|
1388
1641
|
return sortEditHistoryByDateDesc(merged);
|
|
@@ -1405,12 +1658,13 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1405
1658
|
const result = await getEditHistory({
|
|
1406
1659
|
item: itemFilter,
|
|
1407
1660
|
sessionId: shouldFilterBySession ? sessionId : undefined,
|
|
1661
|
+
query: historyQuery,
|
|
1408
1662
|
});
|
|
1409
1663
|
if (handleErrorResult(result, ui, state)) {
|
|
1410
1664
|
console.error("[EditorShell] Failed to load item history:", result);
|
|
1411
1665
|
return;
|
|
1412
1666
|
}
|
|
1413
|
-
let operations = result.data
|
|
1667
|
+
let operations = normalizeEditHistoryPayload(result.data);
|
|
1414
1668
|
// Client-side version filtering for current-version mode
|
|
1415
1669
|
if (currentMode === "current-version") {
|
|
1416
1670
|
// Defensive filter: only include operations for the current version
|
|
@@ -1433,8 +1687,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1433
1687
|
};
|
|
1434
1688
|
if (!prev.length)
|
|
1435
1689
|
return sortEditHistoryByDateDesc(operations.filter((op) => matchesHistoryScope(op, scope)));
|
|
1436
|
-
if (!operations.length)
|
|
1690
|
+
if (!operations.length) {
|
|
1691
|
+
if (historyQuery)
|
|
1692
|
+
return [];
|
|
1437
1693
|
return sortEditHistoryByDateDesc(prev.filter((op) => matchesHistoryScope(op, scope)));
|
|
1694
|
+
}
|
|
1438
1695
|
const prevById = new Map(prev.map((x) => [x.id, x]));
|
|
1439
1696
|
const nextById = new Set(operations.map((x) => x.id));
|
|
1440
1697
|
const merged = operations
|
|
@@ -1470,9 +1727,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1470
1727
|
return mergedOp;
|
|
1471
1728
|
});
|
|
1472
1729
|
// Preserve operations that arrived via WebSocket during the fetch window
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1730
|
+
// but not when filtering by search query — search results are authoritative
|
|
1731
|
+
if (!historyQuery) {
|
|
1732
|
+
for (const [id, priorOp] of prevById) {
|
|
1733
|
+
if (!nextById.has(id) && matchesHistoryScope(priorOp, scope)) {
|
|
1734
|
+
merged.push(priorOp);
|
|
1735
|
+
}
|
|
1476
1736
|
}
|
|
1477
1737
|
}
|
|
1478
1738
|
return sortEditHistoryByDateDesc(merged);
|
|
@@ -1483,9 +1743,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1483
1743
|
historyMode,
|
|
1484
1744
|
showOnlyMyChanges,
|
|
1485
1745
|
filterByCurrentLanguage,
|
|
1746
|
+
historySearchQuery,
|
|
1486
1747
|
item,
|
|
1487
1748
|
currentItemDescriptor,
|
|
1488
1749
|
matchesHistoryScope,
|
|
1750
|
+
normalizeEditHistoryPayload,
|
|
1489
1751
|
sortEditHistoryByDateDesc,
|
|
1490
1752
|
]);
|
|
1491
1753
|
// Debounced history refresh to avoid hammering `/parhelia/editHistory` on rapid UI changes.
|
|
@@ -1551,13 +1813,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1551
1813
|
}
|
|
1552
1814
|
}
|
|
1553
1815
|
else if (historyMode !== "global" && currentItemDescriptor) {
|
|
1816
|
+
// Always load immediately for page-centric/current-version/timeline so we don't show
|
|
1817
|
+
// an empty history list for the debounce window (e.g. 600ms). The effect only runs on
|
|
1818
|
+
// mode or item id/lang/version change, not on every keystroke, so this avoids flaky
|
|
1819
|
+
// undo/redo tests and improves UX when switching to page-centric.
|
|
1554
1820
|
if (!historyInitialLoadDoneRef.current) {
|
|
1555
1821
|
historyInitialLoadDoneRef.current = true;
|
|
1556
|
-
refreshHistoryRef.current(historyMode);
|
|
1557
|
-
}
|
|
1558
|
-
else {
|
|
1559
|
-
debouncedRefreshHistoryRef.current(historyMode);
|
|
1560
1822
|
}
|
|
1823
|
+
refreshHistoryRef.current(historyMode);
|
|
1561
1824
|
}
|
|
1562
1825
|
else if (historyMode !== "global" && !currentItemDescriptor) {
|
|
1563
1826
|
// Clear history if no item loaded in page-centric modes
|
|
@@ -1570,6 +1833,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1570
1833
|
historyMode,
|
|
1571
1834
|
showOnlyMyChanges,
|
|
1572
1835
|
filterByCurrentLanguage,
|
|
1836
|
+
historySearchQuery,
|
|
1573
1837
|
currentItemDescriptor?.id,
|
|
1574
1838
|
currentItemDescriptor?.language,
|
|
1575
1839
|
currentItemDescriptor?.version,
|
|
@@ -1634,6 +1898,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1634
1898
|
if (isInitialLoad)
|
|
1635
1899
|
return;
|
|
1636
1900
|
const current = new URLSearchParams(window.location.search);
|
|
1901
|
+
const urlWorkspace = current.get("workspace");
|
|
1902
|
+
const urlView = current.get("view");
|
|
1903
|
+
const isWorkspaceTransitioning = !!urlWorkspace && urlWorkspace !== viewName;
|
|
1637
1904
|
// Sync item-related parameters only when an item is selected
|
|
1638
1905
|
if (currentItemDescriptor) {
|
|
1639
1906
|
if (current.get("itemid") !== currentItemDescriptor.id) {
|
|
@@ -1664,10 +1931,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1664
1931
|
// If reviewId or urlItemId exists, preserve itemid/lang/version from URL
|
|
1665
1932
|
}
|
|
1666
1933
|
// Always sync workspace-related parameters regardless of item selection
|
|
1667
|
-
if (current.get("workspace") !== viewName) {
|
|
1934
|
+
if (!isWorkspaceTransitioning && current.get("workspace") !== viewName) {
|
|
1668
1935
|
current.set("workspace", viewName);
|
|
1669
1936
|
}
|
|
1670
|
-
current.delete("view"); // Remove legacy view param
|
|
1671
1937
|
// Sync sidebar state
|
|
1672
1938
|
const currentSidebars = current.get("sidebar") ?? "";
|
|
1673
1939
|
const newSidebars = openSidebars.join(",");
|
|
@@ -1679,6 +1945,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1679
1945
|
current.delete("sidebar");
|
|
1680
1946
|
}
|
|
1681
1947
|
}
|
|
1948
|
+
if (showHelpTerminal) {
|
|
1949
|
+
current.set("help", selectedHelpSectionId ?? "contents");
|
|
1950
|
+
}
|
|
1951
|
+
else {
|
|
1952
|
+
current.delete("help");
|
|
1953
|
+
}
|
|
1682
1954
|
if (!compareMode) {
|
|
1683
1955
|
current.delete("compare");
|
|
1684
1956
|
current.delete("compareLanguage");
|
|
@@ -1703,15 +1975,15 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1703
1975
|
else {
|
|
1704
1976
|
current.delete("wizardid");
|
|
1705
1977
|
}
|
|
1706
|
-
//
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
}
|
|
1713
|
-
else {
|
|
1978
|
+
// Preserve settings-specific parameters while transitioning into Settings too.
|
|
1979
|
+
// Some callers update the URL first and switch the workspace state a moment later.
|
|
1980
|
+
const isSettingsNavigation = viewName === "settings" ||
|
|
1981
|
+
urlWorkspace === "settings" ||
|
|
1982
|
+
urlView === "settings";
|
|
1983
|
+
if (!isSettingsNavigation) {
|
|
1714
1984
|
current.delete("ccpanel");
|
|
1985
|
+
current.delete("providerId");
|
|
1986
|
+
current.delete("modelId");
|
|
1715
1987
|
}
|
|
1716
1988
|
// Preserve reviewId parameter if it exists (for review links)
|
|
1717
1989
|
// This ensures review links don't lose the reviewId when URL is synced
|
|
@@ -1724,17 +1996,28 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1724
1996
|
const browserPathname = typeof window !== "undefined" ? window.location.pathname : pathname;
|
|
1725
1997
|
const newUrl = `${browserPathname}?${current.toString()}`;
|
|
1726
1998
|
const oldUrl = `${browserPathname}${window.location.search}`;
|
|
1999
|
+
const isRestoringLastSyncedUrl = newUrl === lastUrlRef.current;
|
|
1727
2000
|
// Skip pushing to history if we're handling a popstate event (browser back/forward)
|
|
1728
2001
|
// This prevents the URL sync from overwriting the history during back navigation
|
|
1729
2002
|
if (isHandlingPopStateRef.current) {
|
|
1730
2003
|
return;
|
|
1731
2004
|
}
|
|
1732
2005
|
if (newUrl !== oldUrl) {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
2006
|
+
if (switchWorkspacePushedRef.current || isRestoringLastSyncedUrl) {
|
|
2007
|
+
window.history.replaceState(getCurrentHistoryState(), "", newUrl);
|
|
2008
|
+
switchWorkspacePushedRef.current = false;
|
|
2009
|
+
}
|
|
2010
|
+
else {
|
|
2011
|
+
window.history.pushState(getCurrentHistoryState(), "", newUrl);
|
|
2012
|
+
}
|
|
1736
2013
|
lastUrlRef.current = newUrl;
|
|
1737
2014
|
}
|
|
2015
|
+
else if (switchWorkspacePushedRef.current) {
|
|
2016
|
+
// A workspace change may already have pushed the exact target URL. If we leave
|
|
2017
|
+
// this flag set, the next unrelated item navigation will incorrectly replace
|
|
2018
|
+
// history instead of pushing a new entry, which breaks browser back/forward.
|
|
2019
|
+
switchWorkspacePushedRef.current = false;
|
|
2020
|
+
}
|
|
1738
2021
|
}, [
|
|
1739
2022
|
currentItemDescriptor,
|
|
1740
2023
|
viewName,
|
|
@@ -1746,6 +2029,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1746
2029
|
fullscreen,
|
|
1747
2030
|
currentWizardId,
|
|
1748
2031
|
openSidebars,
|
|
2032
|
+
showHelpTerminal,
|
|
2033
|
+
selectedHelpSectionId,
|
|
1749
2034
|
]);
|
|
1750
2035
|
useEffect(() => {
|
|
1751
2036
|
async function load() {
|
|
@@ -1842,12 +2127,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1842
2127
|
"en",
|
|
1843
2128
|
version: 0,
|
|
1844
2129
|
};
|
|
1845
|
-
const loadedItem = await itemsRepository.getItem(itemToLoad);
|
|
1846
|
-
if (!loadedItem) {
|
|
1847
|
-
return undefined;
|
|
1848
|
-
}
|
|
1849
2130
|
// ensure this is object has no additional properties
|
|
1850
2131
|
itemToLoad = getItemDescriptor(itemToLoad);
|
|
2132
|
+
const requestedItemKey = makeItemKey(itemToLoad);
|
|
1851
2133
|
// Check if item is already open in any slot - if so, reuse that slot instead of creating a new one
|
|
1852
2134
|
const existingSlotForItem = editorSlots.find((s) => s.itemDescriptor.id === itemToLoad.id &&
|
|
1853
2135
|
s.itemDescriptor.language === itemToLoad.language &&
|
|
@@ -1858,6 +2140,17 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1858
2140
|
!options?.openInNewSlot &&
|
|
1859
2141
|
!options?.targetSlotId) {
|
|
1860
2142
|
const isExistingSlotActive = existingSlotForItem.slotId === activeSlotIdRef.current;
|
|
2143
|
+
if (isExistingSlotActive) {
|
|
2144
|
+
latestGlobalLoadKeyRef.current = requestedItemKey;
|
|
2145
|
+
}
|
|
2146
|
+
const loadedItem = await itemsRepository.getItem(itemToLoad);
|
|
2147
|
+
if (!loadedItem) {
|
|
2148
|
+
return undefined;
|
|
2149
|
+
}
|
|
2150
|
+
if (isExistingSlotActive &&
|
|
2151
|
+
latestGlobalLoadKeyRef.current !== requestedItemKey) {
|
|
2152
|
+
return loadedItem;
|
|
2153
|
+
}
|
|
1861
2154
|
// If forceRefresh is true, update the slot with a new refreshToken to trigger a reload
|
|
1862
2155
|
if (options?.forceRefresh) {
|
|
1863
2156
|
setEditorSlots((prev) => {
|
|
@@ -1932,6 +2225,17 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
1932
2225
|
// - If the slot is active (or is becoming active as the first slot), do update.
|
|
1933
2226
|
const isFirstSlot = editorSlots.length === 0;
|
|
1934
2227
|
const shouldUpdateGlobalCurrentItem = isFirstSlot || targetSlotId === activeSlotIdRef.current;
|
|
2228
|
+
if (shouldUpdateGlobalCurrentItem) {
|
|
2229
|
+
latestGlobalLoadKeyRef.current = requestedItemKey;
|
|
2230
|
+
}
|
|
2231
|
+
const loadedItem = await itemsRepository.getItem(itemToLoad);
|
|
2232
|
+
if (!loadedItem) {
|
|
2233
|
+
return undefined;
|
|
2234
|
+
}
|
|
2235
|
+
if (shouldUpdateGlobalCurrentItem &&
|
|
2236
|
+
latestGlobalLoadKeyRef.current !== requestedItemKey) {
|
|
2237
|
+
return loadedItem;
|
|
2238
|
+
}
|
|
1935
2239
|
// Active slot is controlled ONLY by mouse hover - only activate the first slot on initial load
|
|
1936
2240
|
if (isFirstSlot) {
|
|
1937
2241
|
activeSlotIdRef.current = targetSlotId;
|
|
@@ -2039,9 +2343,10 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2039
2343
|
useEffect(() => {
|
|
2040
2344
|
if (fullscreen &&
|
|
2041
2345
|
!searchParams.get("fullscreen") &&
|
|
2042
|
-
!configuration.forceFullscreen
|
|
2346
|
+
!configuration.forceFullscreen &&
|
|
2347
|
+
!isMobile)
|
|
2043
2348
|
setShowFullscreenHint(true);
|
|
2044
|
-
}, [fullscreen, configuration.forceFullscreen, searchParams]);
|
|
2349
|
+
}, [fullscreen, configuration.forceFullscreen, searchParams, isMobile]);
|
|
2045
2350
|
const state = {
|
|
2046
2351
|
page,
|
|
2047
2352
|
configuration,
|
|
@@ -2140,20 +2445,35 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2140
2445
|
? existingOp.progress
|
|
2141
2446
|
: op.progress;
|
|
2142
2447
|
// IMPORTANT: Once canUndo becomes true, never downgrade it to false,
|
|
2143
|
-
// UNLESS this update indicates the operation was undone.
|
|
2144
|
-
//
|
|
2145
|
-
//
|
|
2146
|
-
|
|
2448
|
+
// UNLESS this update indicates the operation was undone.
|
|
2449
|
+
// Runtime logs show long-running undo completion can arrive as:
|
|
2450
|
+
// - existing: canUndo=true, executionStatus=executing
|
|
2451
|
+
// - incoming: canUndo=false, executionStatus=completed
|
|
2452
|
+
// without explicit undone/canRedo flags yet.
|
|
2453
|
+
const isExplicitUndoUpdate = op.undone === true || op.canRedo === true;
|
|
2454
|
+
const isInferredUndoCompletion = existingOp.executionStatus === "executing" &&
|
|
2455
|
+
op.executionStatus === "completed" &&
|
|
2456
|
+
existingOp.canUndo === true &&
|
|
2457
|
+
op.canUndo === false;
|
|
2458
|
+
const isUndoUpdate = isExplicitUndoUpdate || isInferredUndoCompletion;
|
|
2147
2459
|
const mergedCanUndo = isUndoUpdate
|
|
2148
2460
|
? false
|
|
2149
2461
|
: existingOp.canUndo === true
|
|
2150
2462
|
? true
|
|
2151
2463
|
: op.canUndo;
|
|
2464
|
+
const mergedCanRedo = isUndoUpdate
|
|
2465
|
+
? true
|
|
2466
|
+
: op.canRedo ?? existingOp.canRedo;
|
|
2467
|
+
const mergedUndone = isUndoUpdate
|
|
2468
|
+
? true
|
|
2469
|
+
: op.undone ?? existingOp.undone;
|
|
2152
2470
|
const mergedOp = {
|
|
2153
2471
|
...existingOp,
|
|
2154
2472
|
...op,
|
|
2155
2473
|
progress: mergedProgress,
|
|
2156
2474
|
canUndo: mergedCanUndo,
|
|
2475
|
+
canRedo: mergedCanRedo,
|
|
2476
|
+
undone: mergedUndone,
|
|
2157
2477
|
};
|
|
2158
2478
|
// Ensure undone operations always have canRedo: true.
|
|
2159
2479
|
if (mergedOp.undone && !mergedOp.canRedo) {
|
|
@@ -2207,10 +2527,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2207
2527
|
});
|
|
2208
2528
|
// Ref for markOperationComplete callback (needed because operationsContext is created later)
|
|
2209
2529
|
const markOperationCompleteRef = useRef(null);
|
|
2210
|
-
// When the websocket is reconnecting, we can briefly lose the ability to send messages.
|
|
2211
|
-
// Agent dialog responses (e.g. questionnaire cancel/submit) must not be dropped, otherwise
|
|
2212
|
-
// the backend tool call can remain pending and tests/users will hang.
|
|
2213
|
-
const pendingAgentDialogResponsesRef = useRef([]);
|
|
2214
2530
|
// WebSocket message handler and connection
|
|
2215
2531
|
const messageHandler = useSocketMessageHandler({
|
|
2216
2532
|
sessionId,
|
|
@@ -2236,28 +2552,31 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2236
2552
|
markOperationCompleteRef.current?.(operationId);
|
|
2237
2553
|
},
|
|
2238
2554
|
});
|
|
2555
|
+
// Concurrent user limit error state
|
|
2556
|
+
const [concurrentUserLimitError, setConcurrentUserLimitError] = useState(null);
|
|
2557
|
+
concurrentUserLimitErrorRef.current = concurrentUserLimitError;
|
|
2558
|
+
const handleRetryConnection = useCallback(() => {
|
|
2559
|
+
setConcurrentUserLimitError(null);
|
|
2560
|
+
// Force reconnection by triggering a new connection attempt
|
|
2561
|
+
// The useEditorWebSocket hook will check availability again
|
|
2562
|
+
const socket = globalThis.editorSocket;
|
|
2563
|
+
if (socket) {
|
|
2564
|
+
socket.close();
|
|
2565
|
+
delete globalThis.editorSocket;
|
|
2566
|
+
}
|
|
2567
|
+
// The hook will automatically attempt to reconnect
|
|
2568
|
+
}, []);
|
|
2239
2569
|
const { socketRef: socketInstanceRef } = useEditorWebSocket({
|
|
2240
2570
|
sessionId,
|
|
2241
2571
|
onMessage: messageHandler,
|
|
2242
2572
|
onOpen: async () => {
|
|
2573
|
+
// Clear concurrent user limit error on successful connection
|
|
2574
|
+
setConcurrentUserLimitError(null);
|
|
2575
|
+
// Startup WebSocket probe may have failed with a blocking error while seats were full;
|
|
2576
|
+
// re-run status checks quietly so "1 error" does not stick after a successful connect.
|
|
2577
|
+
void startupChecks.recheckQuiet();
|
|
2243
2578
|
// Increment socket connection version to trigger re-subscriptions
|
|
2244
2579
|
setSocketConnectionVersion((v) => v + 1);
|
|
2245
|
-
// Flush any queued agent dialog responses now that the socket is open.
|
|
2246
|
-
// Keep this early so pending tool calls unblock ASAP.
|
|
2247
|
-
try {
|
|
2248
|
-
if (socketInstanceRef.current &&
|
|
2249
|
-
socketInstanceRef.current.readyState === WebSocket.OPEN &&
|
|
2250
|
-
pendingAgentDialogResponsesRef.current.length > 0) {
|
|
2251
|
-
// FIFO flush
|
|
2252
|
-
while (pendingAgentDialogResponsesRef.current.length > 0) {
|
|
2253
|
-
const queued = pendingAgentDialogResponsesRef.current.shift();
|
|
2254
|
-
socketInstanceRef.current.send(JSON.stringify(queued));
|
|
2255
|
-
}
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2258
|
-
catch (e) {
|
|
2259
|
-
console.error("Failed to flush queued agent dialog responses:", e);
|
|
2260
|
-
}
|
|
2261
2580
|
// Fetch any running operations on (re)connect for auto-resume
|
|
2262
2581
|
// This ensures the UI shows operations that are still executing
|
|
2263
2582
|
try {
|
|
@@ -2273,24 +2592,37 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2273
2592
|
}
|
|
2274
2593
|
},
|
|
2275
2594
|
onError: (error) => console.error("WebSocket error:", error),
|
|
2595
|
+
onConcurrentUserLimit: (error) => {
|
|
2596
|
+
setConcurrentUserLimitError(error);
|
|
2597
|
+
},
|
|
2598
|
+
onSessionRevoked: () => promptSessionReconnect("session-revoked"),
|
|
2276
2599
|
connectSocket,
|
|
2277
2600
|
requestQuota,
|
|
2278
2601
|
sendClientInfo,
|
|
2602
|
+
setSocketDiagnostics,
|
|
2279
2603
|
});
|
|
2604
|
+
useEffect(() => {
|
|
2605
|
+
const hasMySession = activeSessions.some((s) => s.sessionId === sessionId);
|
|
2606
|
+
if (hasMySession &&
|
|
2607
|
+
socketInstanceRef.current?.readyState === WebSocket.OPEN) {
|
|
2608
|
+
toast.dismiss("session-revoked");
|
|
2609
|
+
}
|
|
2610
|
+
}, [activeSessions, sessionId, socketConnectionVersion, socketInstanceRef]);
|
|
2611
|
+
useEffect(() => {
|
|
2612
|
+
const handleRevoked = (event) => {
|
|
2613
|
+
const customEvent = event;
|
|
2614
|
+
promptSessionReconnect(customEvent?.detail?.reason);
|
|
2615
|
+
};
|
|
2616
|
+
window.addEventListener("parhelia:session-revoked", handleRevoked);
|
|
2617
|
+
return () => {
|
|
2618
|
+
window.removeEventListener("parhelia:session-revoked", handleRevoked);
|
|
2619
|
+
};
|
|
2620
|
+
}, [promptSessionReconnect]);
|
|
2280
2621
|
const sendSocketMessage = useCallback((message) => {
|
|
2281
2622
|
if (socketInstanceRef.current &&
|
|
2282
2623
|
socketInstanceRef.current.readyState === WebSocket.OPEN) {
|
|
2283
2624
|
socketInstanceRef.current.send(JSON.stringify(message));
|
|
2284
2625
|
}
|
|
2285
|
-
else if (message.type === "agent-dialog-response") {
|
|
2286
|
-
// Queue dialog responses to avoid losing them during reconnects.
|
|
2287
|
-
pendingAgentDialogResponsesRef.current.push(message);
|
|
2288
|
-
// Prevent unbounded growth in pathological scenarios.
|
|
2289
|
-
if (pendingAgentDialogResponsesRef.current.length > 50) {
|
|
2290
|
-
pendingAgentDialogResponsesRef.current =
|
|
2291
|
-
pendingAgentDialogResponsesRef.current.slice(-50);
|
|
2292
|
-
}
|
|
2293
|
-
}
|
|
2294
2626
|
}, [socketInstanceRef]);
|
|
2295
2627
|
// URL update helper - defined early so it can be used by workspace/sidebar functions
|
|
2296
2628
|
const updateUrl = useCallback((params) => {
|
|
@@ -2309,7 +2641,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2309
2641
|
? `${browserPathname}?${queryString}`
|
|
2310
2642
|
: browserPathname;
|
|
2311
2643
|
if (typeof window !== "undefined") {
|
|
2312
|
-
window.history.pushState(
|
|
2644
|
+
window.history.pushState(getCurrentHistoryState(), "", newUrl);
|
|
2313
2645
|
}
|
|
2314
2646
|
else {
|
|
2315
2647
|
router.push(newUrl, { scroll: false });
|
|
@@ -2334,8 +2666,13 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2334
2666
|
if (!options?.skipNavigationHistory) {
|
|
2335
2667
|
addNavigationEntry(targetWorkspaceId, item);
|
|
2336
2668
|
}
|
|
2337
|
-
//
|
|
2338
|
-
|
|
2669
|
+
// Mark that we're pushing from switchWorkspace so the URL sync effect
|
|
2670
|
+
// will replaceState instead of pushing a second history entry.
|
|
2671
|
+
switchWorkspacePushedRef.current = true;
|
|
2672
|
+
updateUrl({
|
|
2673
|
+
workspace: targetWorkspaceId,
|
|
2674
|
+
...options?.urlParams,
|
|
2675
|
+
});
|
|
2339
2676
|
if (typeof document.startViewTransition === "function") {
|
|
2340
2677
|
document.startViewTransition(() => {
|
|
2341
2678
|
flushSync(() => {
|
|
@@ -2355,14 +2692,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2355
2692
|
updateUrl,
|
|
2356
2693
|
handleSetShowAgentsPanel,
|
|
2357
2694
|
]);
|
|
2358
|
-
// Legacy alias for backwards compatibility
|
|
2359
|
-
const switchView = useCallback((viewName, options) => {
|
|
2360
|
-
// Handle ccpanel for settings workspace
|
|
2361
|
-
if (options?.ccpanel) {
|
|
2362
|
-
updateUrl({ ccpanel: options.ccpanel, workspace: viewName });
|
|
2363
|
-
}
|
|
2364
|
-
switchWorkspace(viewName, options);
|
|
2365
|
-
}, [switchWorkspace, updateUrl]);
|
|
2366
2695
|
// Helper: get all sidebar IDs that should be preserved (locked sidebars + their stack mates)
|
|
2367
2696
|
const getPreservedSidebarIds = useCallback(() => {
|
|
2368
2697
|
const lockedSet = new Set(lockedSidebarsRef.current);
|
|
@@ -2400,6 +2729,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2400
2729
|
setOpenSidebars(newSidebars);
|
|
2401
2730
|
setLockedSidebars((locked) => locked.filter((id) => id !== sidebarId));
|
|
2402
2731
|
setSidebarStacks((prevStacks) => normalizeSidebarStacks(newSidebars, prevStacks));
|
|
2732
|
+
if (sidebarId === "agents-panel") {
|
|
2733
|
+
setUserPreferences({ showAgentsPanel: false });
|
|
2734
|
+
}
|
|
2403
2735
|
startTransition(() => {
|
|
2404
2736
|
updateUrl({ sidebar: newSidebars.join(",") || undefined });
|
|
2405
2737
|
});
|
|
@@ -2414,34 +2746,69 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2414
2746
|
];
|
|
2415
2747
|
openSidebarsRef.current = newSidebars;
|
|
2416
2748
|
setOpenSidebars(newSidebars);
|
|
2749
|
+
ensureSidebarPinned(sidebarId);
|
|
2417
2750
|
setSidebarStacks((prevStacks) => normalizeSidebarStacks(newSidebars, prevStacks));
|
|
2751
|
+
if (sidebarId === "agents-panel") {
|
|
2752
|
+
setUserPreferences({ showAgentsPanel: true });
|
|
2753
|
+
}
|
|
2754
|
+
// On mobile, close the editor form panel when opening a sidebar
|
|
2755
|
+
if (isMobile) {
|
|
2756
|
+
setMobileEditorPanelOpenRaw(false);
|
|
2757
|
+
}
|
|
2418
2758
|
startTransition(() => {
|
|
2419
2759
|
updateUrl({ sidebar: newSidebars.join(",") || undefined });
|
|
2420
2760
|
});
|
|
2421
|
-
}, [
|
|
2761
|
+
}, [
|
|
2762
|
+
updateUrl,
|
|
2763
|
+
getPreservedSidebarIds,
|
|
2764
|
+
normalizeSidebarStacks,
|
|
2765
|
+
ensureSidebarPinned,
|
|
2766
|
+
setUserPreferences,
|
|
2767
|
+
isMobile,
|
|
2768
|
+
]);
|
|
2422
2769
|
// Ensure a sidebar is open (without toggling it closed if already open)
|
|
2423
|
-
const openSidebar = useCallback((sidebarId) => {
|
|
2770
|
+
const openSidebar = useCallback((sidebarId, options) => {
|
|
2424
2771
|
const currentOpenSidebars = openSidebarsRef.current;
|
|
2425
2772
|
if (currentOpenSidebars.includes(sidebarId)) {
|
|
2426
2773
|
// Already open, nothing to do
|
|
2427
2774
|
return;
|
|
2428
2775
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2776
|
+
const preservedSet = options?.preserveOpenSidebars
|
|
2777
|
+
? undefined
|
|
2778
|
+
: getPreservedSidebarIds();
|
|
2779
|
+
const newSidebars = options?.preserveOpenSidebars
|
|
2780
|
+
? [...currentOpenSidebars, sidebarId]
|
|
2781
|
+
: [
|
|
2782
|
+
...currentOpenSidebars.filter((id) => preservedSet?.has(id)),
|
|
2783
|
+
sidebarId,
|
|
2784
|
+
];
|
|
2435
2785
|
openSidebarsRef.current = newSidebars;
|
|
2436
2786
|
setOpenSidebars(newSidebars);
|
|
2787
|
+
ensureSidebarPinned(sidebarId);
|
|
2437
2788
|
setSidebarStacks((prevStacks) => normalizeSidebarStacks(newSidebars, prevStacks));
|
|
2789
|
+
if (sidebarId === "agents-panel") {
|
|
2790
|
+
setUserPreferences({ showAgentsPanel: true });
|
|
2791
|
+
}
|
|
2792
|
+
// On mobile, close the editor form panel when opening a sidebar
|
|
2793
|
+
if (isMobile) {
|
|
2794
|
+
setMobileEditorPanelOpenRaw(false);
|
|
2795
|
+
}
|
|
2438
2796
|
startTransition(() => {
|
|
2439
2797
|
updateUrl({ sidebar: newSidebars.join(",") || undefined });
|
|
2440
2798
|
});
|
|
2441
|
-
}, [
|
|
2799
|
+
}, [
|
|
2800
|
+
updateUrl,
|
|
2801
|
+
getPreservedSidebarIds,
|
|
2802
|
+
normalizeSidebarStacks,
|
|
2803
|
+
ensureSidebarPinned,
|
|
2804
|
+
setUserPreferences,
|
|
2805
|
+
isMobile,
|
|
2806
|
+
]);
|
|
2442
2807
|
// Toggle lock state for a sidebar stack (keeps it visible when selecting another)
|
|
2443
2808
|
// When toggling any sidebar, we toggle the entire stack it belongs to
|
|
2444
2809
|
const toggleSidebarLock = useCallback((sidebarId) => {
|
|
2810
|
+
if (isMobile)
|
|
2811
|
+
return;
|
|
2445
2812
|
const currentStacks = sidebarStacksRef.current;
|
|
2446
2813
|
const stackContainingSidebar = currentStacks.find((stack) => stack.includes(sidebarId));
|
|
2447
2814
|
const sidebarIdsToToggle = stackContainingSidebar || [sidebarId];
|
|
@@ -2459,7 +2826,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2459
2826
|
];
|
|
2460
2827
|
}
|
|
2461
2828
|
});
|
|
2462
|
-
}, []);
|
|
2829
|
+
}, [isMobile]);
|
|
2463
2830
|
const stackSidebar = useCallback((sidebarId, targetSidebarId, position = "after") => {
|
|
2464
2831
|
if (!sidebarId || !targetSidebarId || sidebarId === targetSidebarId)
|
|
2465
2832
|
return;
|
|
@@ -2491,6 +2858,38 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2491
2858
|
return normalizeSidebarStacks(openIds, next);
|
|
2492
2859
|
});
|
|
2493
2860
|
}, [normalizeSidebarStacks]);
|
|
2861
|
+
const moveSidebarToColumn = useCallback((sidebarId, targetSidebarId, position = "after") => {
|
|
2862
|
+
if (!sidebarId || !targetSidebarId || sidebarId === targetSidebarId) {
|
|
2863
|
+
return;
|
|
2864
|
+
}
|
|
2865
|
+
const currentOpen = openSidebarsRef.current;
|
|
2866
|
+
const baseOpen = currentOpen.filter((id) => id !== sidebarId);
|
|
2867
|
+
const targetIndex = baseOpen.indexOf(targetSidebarId);
|
|
2868
|
+
if (targetIndex === -1) {
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
const insertIndex = position === "before" ? targetIndex : targetIndex + 1;
|
|
2872
|
+
const nextOpen = [...baseOpen];
|
|
2873
|
+
nextOpen.splice(insertIndex, 0, sidebarId);
|
|
2874
|
+
openSidebarsRef.current = nextOpen;
|
|
2875
|
+
setOpenSidebars(nextOpen);
|
|
2876
|
+
startTransition(() => {
|
|
2877
|
+
updateUrl({ sidebar: nextOpen.join(",") || undefined });
|
|
2878
|
+
});
|
|
2879
|
+
setSidebarStacks((prev) => {
|
|
2880
|
+
const normalized = normalizeSidebarStacks(nextOpen, prev);
|
|
2881
|
+
const next = normalized
|
|
2882
|
+
.map((stack) => stack.filter((id) => id !== sidebarId))
|
|
2883
|
+
.filter((stack) => stack.length > 0);
|
|
2884
|
+
const targetStackIndex = next.findIndex((stack) => stack.includes(targetSidebarId));
|
|
2885
|
+
if (targetStackIndex === -1) {
|
|
2886
|
+
next.push([sidebarId]);
|
|
2887
|
+
return normalizeSidebarStacks(nextOpen, next);
|
|
2888
|
+
}
|
|
2889
|
+
next.splice(position === "before" ? targetStackIndex : targetStackIndex + 1, 0, [sidebarId]);
|
|
2890
|
+
return normalizeSidebarStacks(nextOpen, next);
|
|
2891
|
+
});
|
|
2892
|
+
}, [normalizeSidebarStacks, updateUrl]);
|
|
2494
2893
|
const unstackSidebar = useCallback((sidebarId) => {
|
|
2495
2894
|
setSidebarStacks((prev) => {
|
|
2496
2895
|
const openIds = openSidebarsRef.current;
|
|
@@ -2536,8 +2935,34 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2536
2935
|
// Get resolved sidebar (with panels materialized)
|
|
2537
2936
|
// Note: This is defined as a function that will be called later when editContext is available
|
|
2538
2937
|
const sidebars = configuration.editor.sidebars ?? [];
|
|
2938
|
+
const taskboardSidebarIds = new Set([
|
|
2939
|
+
"taskboard-project-list",
|
|
2940
|
+
"taskboard-my-tasks",
|
|
2941
|
+
]);
|
|
2942
|
+
const getSidebarsForWorkspace = useCallback((targetWorkspaceId) => {
|
|
2943
|
+
const isTaskboardWorkspace = targetWorkspaceId === "taskboard";
|
|
2944
|
+
const workspaceAllowedSidebarIds = userInfo.workspaces?.find((w) => w.id === targetWorkspaceId)
|
|
2945
|
+
?.sidebars ?? [];
|
|
2946
|
+
return sidebars.filter((s) => {
|
|
2947
|
+
const isTaskboardSidebar = taskboardSidebarIds.has(s.id);
|
|
2948
|
+
if (isTaskboardWorkspace && !isTaskboardSidebar)
|
|
2949
|
+
return false;
|
|
2950
|
+
if (!isTaskboardWorkspace && isTaskboardSidebar)
|
|
2951
|
+
return false;
|
|
2952
|
+
// Always show agents-panel regardless of workspace settings.
|
|
2953
|
+
if (s.id === "agents-panel") {
|
|
2954
|
+
return true;
|
|
2955
|
+
}
|
|
2956
|
+
// If no workspace settings or no sidebars defined for current workspace, show all.
|
|
2957
|
+
if (workspaceAllowedSidebarIds.length === 0) {
|
|
2958
|
+
return true;
|
|
2959
|
+
}
|
|
2960
|
+
// Only show sidebars that are in the allowed list for the current workspace.
|
|
2961
|
+
return workspaceAllowedSidebarIds.includes(s.id);
|
|
2962
|
+
});
|
|
2963
|
+
}, [sidebars, userInfo.workspaces]);
|
|
2539
2964
|
const getResolvedSidebar = useCallback((sidebarId) => {
|
|
2540
|
-
const sidebar =
|
|
2965
|
+
const sidebar = getSidebarsForWorkspace(workspaceId).find((s) => s.id === sidebarId);
|
|
2541
2966
|
if (!sidebar)
|
|
2542
2967
|
return undefined;
|
|
2543
2968
|
// Resolve panel factories using editContextRef to avoid circular dependency
|
|
@@ -2551,7 +2976,35 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2551
2976
|
...sidebar,
|
|
2552
2977
|
panels: resolvedPanels,
|
|
2553
2978
|
};
|
|
2554
|
-
}, [
|
|
2979
|
+
}, [getSidebarsForWorkspace, workspaceId]);
|
|
2980
|
+
useEffect(() => {
|
|
2981
|
+
if (!currentWorkspace.supportsSidebars) {
|
|
2982
|
+
return;
|
|
2983
|
+
}
|
|
2984
|
+
const allowedIds = new Set(getSidebarsForWorkspace(workspaceId).map((sidebar) => sidebar.id));
|
|
2985
|
+
const currentOpen = openSidebarsRef.current;
|
|
2986
|
+
let nextOpen = currentOpen.filter((id) => allowedIds.has(id));
|
|
2987
|
+
if (nextOpen.length === 0 && currentWorkspace.defaultSidebars?.length) {
|
|
2988
|
+
nextOpen = currentWorkspace.defaultSidebars.filter((id) => allowedIds.has(id));
|
|
2989
|
+
}
|
|
2990
|
+
const unchanged = nextOpen.length === currentOpen.length &&
|
|
2991
|
+
nextOpen.every((id, index) => id === currentOpen[index]);
|
|
2992
|
+
if (unchanged) {
|
|
2993
|
+
return;
|
|
2994
|
+
}
|
|
2995
|
+
openSidebarsRef.current = nextOpen;
|
|
2996
|
+
setOpenSidebars(nextOpen);
|
|
2997
|
+
setSidebarStacks((prev) => normalizeSidebarStacks(nextOpen, prev));
|
|
2998
|
+
startTransition(() => {
|
|
2999
|
+
updateUrl({ sidebar: nextOpen.join(",") || undefined });
|
|
3000
|
+
});
|
|
3001
|
+
}, [
|
|
3002
|
+
currentWorkspace,
|
|
3003
|
+
getSidebarsForWorkspace,
|
|
3004
|
+
normalizeSidebarStacks,
|
|
3005
|
+
updateUrl,
|
|
3006
|
+
workspaceId,
|
|
3007
|
+
]);
|
|
2555
3008
|
// Listen for switch-workspace and open-sidebar commands from agents via websocket
|
|
2556
3009
|
useEffect(() => {
|
|
2557
3010
|
const handleAgentMessage = (message) => {
|
|
@@ -2765,9 +3218,45 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2765
3218
|
}
|
|
2766
3219
|
return true;
|
|
2767
3220
|
}, [operations, ignoreBlur, sessionId]);
|
|
3221
|
+
const quickSwitcherEntries = useMemo(() => {
|
|
3222
|
+
const entries = [];
|
|
3223
|
+
const seen = new Set();
|
|
3224
|
+
const pushEntry = (entry) => {
|
|
3225
|
+
const key = entry.item
|
|
3226
|
+
? `${entry.workspaceId}:${entry.item.id}:${entry.item.language}:${entry.item.version}`
|
|
3227
|
+
: `${entry.workspaceId}:no-item`;
|
|
3228
|
+
if (seen.has(key))
|
|
3229
|
+
return;
|
|
3230
|
+
seen.add(key);
|
|
3231
|
+
entries.push(entry);
|
|
3232
|
+
};
|
|
3233
|
+
for (const entry of navigationHistory) {
|
|
3234
|
+
pushEntry(entry);
|
|
3235
|
+
}
|
|
3236
|
+
for (const entry of browseHistory) {
|
|
3237
|
+
if (!entry.id || !entry.language)
|
|
3238
|
+
continue;
|
|
3239
|
+
pushEntry({
|
|
3240
|
+
workspaceId,
|
|
3241
|
+
item: {
|
|
3242
|
+
id: entry.id,
|
|
3243
|
+
language: entry.language,
|
|
3244
|
+
version: entry.version ?? 0,
|
|
3245
|
+
},
|
|
3246
|
+
timestamp: entry.visitedAt
|
|
3247
|
+
? new Date(entry.visitedAt).getTime()
|
|
3248
|
+
: Date.now(),
|
|
3249
|
+
displayName: entry.name || workspaceId,
|
|
3250
|
+
itemName: entry.name,
|
|
3251
|
+
itemPath: entry.path,
|
|
3252
|
+
itemIcon: entry.icon,
|
|
3253
|
+
});
|
|
3254
|
+
}
|
|
3255
|
+
return entries.slice(0, 25);
|
|
3256
|
+
}, [navigationHistory, browseHistory, workspaceId]);
|
|
2768
3257
|
// Quick switcher handlers
|
|
2769
3258
|
const showQuickSwitcher = useCallback((show) => {
|
|
2770
|
-
if (show &&
|
|
3259
|
+
if (show && quickSwitcherEntries.length > 1) {
|
|
2771
3260
|
setQuickSwitcherVisible(true);
|
|
2772
3261
|
// Start with index 1 (second entry - previous entry) for quick switching
|
|
2773
3262
|
setQuickSwitcherSelectedIndex(1);
|
|
@@ -2775,11 +3264,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2775
3264
|
else {
|
|
2776
3265
|
setQuickSwitcherVisible(false);
|
|
2777
3266
|
}
|
|
2778
|
-
}, [
|
|
3267
|
+
}, [quickSwitcherEntries]);
|
|
2779
3268
|
const cycleQuickSwitcher = useCallback((direction) => {
|
|
2780
3269
|
if (!quickSwitcherVisible)
|
|
2781
3270
|
return;
|
|
2782
|
-
const maxItems = Math.min(5,
|
|
3271
|
+
const maxItems = Math.min(5, quickSwitcherEntries.length);
|
|
2783
3272
|
setQuickSwitcherSelectedIndex((current) => {
|
|
2784
3273
|
let newIndex = current;
|
|
2785
3274
|
// Determine grid layout (responsive columns)
|
|
@@ -2824,9 +3313,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2824
3313
|
}
|
|
2825
3314
|
return newIndex;
|
|
2826
3315
|
});
|
|
2827
|
-
}, [quickSwitcherVisible,
|
|
3316
|
+
}, [quickSwitcherVisible, quickSwitcherEntries]);
|
|
2828
3317
|
const handleQuickSwitcherSelect = useCallback((index) => {
|
|
2829
|
-
const selectedEntry =
|
|
3318
|
+
const selectedEntry = quickSwitcherEntries[index];
|
|
2830
3319
|
if (selectedEntry) {
|
|
2831
3320
|
// Determine target workspace: entries with items should go to "editor" workspace
|
|
2832
3321
|
// (fixes issue where browse history entries initialized with wrong workspaceId)
|
|
@@ -2878,23 +3367,46 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
2878
3367
|
}
|
|
2879
3368
|
setQuickSwitcherVisible(false);
|
|
2880
3369
|
}, [
|
|
2881
|
-
|
|
3370
|
+
quickSwitcherEntries,
|
|
2882
3371
|
loadItem,
|
|
2883
3372
|
switchWorkspace,
|
|
2884
3373
|
workspaceId,
|
|
2885
3374
|
item,
|
|
2886
3375
|
setNavigationHistory,
|
|
2887
3376
|
]);
|
|
3377
|
+
useEffect(() => {
|
|
3378
|
+
if (typeof window === "undefined" ||
|
|
3379
|
+
process.env.PARHELIA_DEV_MODE !== "true") {
|
|
3380
|
+
return;
|
|
3381
|
+
}
|
|
3382
|
+
window.__parheliaQuickSwitcherTestApi = {
|
|
3383
|
+
open: () => showQuickSwitcher(true),
|
|
3384
|
+
cycle: (direction = "next") => cycleQuickSwitcher(direction),
|
|
3385
|
+
closeWithoutSelection: () => setQuickSwitcherVisible(false),
|
|
3386
|
+
selectCurrent: () => handleQuickSwitcherSelect(quickSwitcherSelectedIndex),
|
|
3387
|
+
getState: () => ({
|
|
3388
|
+
visible: quickSwitcherVisible,
|
|
3389
|
+
selectedIndex: quickSwitcherSelectedIndex,
|
|
3390
|
+
entryCount: quickSwitcherEntries.length,
|
|
3391
|
+
entries: quickSwitcherEntries.map((entry) => ({
|
|
3392
|
+
workspaceId: entry.workspaceId,
|
|
3393
|
+
itemId: entry.item?.id,
|
|
3394
|
+
displayName: entry.displayName,
|
|
3395
|
+
})),
|
|
3396
|
+
}),
|
|
3397
|
+
};
|
|
3398
|
+
return () => {
|
|
3399
|
+
delete window.__parheliaQuickSwitcherTestApi;
|
|
3400
|
+
};
|
|
3401
|
+
}, [
|
|
3402
|
+
showQuickSwitcher,
|
|
3403
|
+
cycleQuickSwitcher,
|
|
3404
|
+
handleQuickSwitcherSelect,
|
|
3405
|
+
quickSwitcherSelectedIndex,
|
|
3406
|
+
]);
|
|
2888
3407
|
const { handleKeyDown } = useKeyboardNavigation({
|
|
2889
3408
|
editContextRef,
|
|
2890
|
-
|
|
2891
|
-
pageViewContext: activePageViewContext,
|
|
2892
|
-
configuration,
|
|
2893
|
-
item,
|
|
2894
|
-
browseHistory,
|
|
2895
|
-
loadItem,
|
|
2896
|
-
showInfoToast,
|
|
2897
|
-
showErrorToast,
|
|
3409
|
+
keyboardCommands: activeKeyboardCommands,
|
|
2898
3410
|
executeCommand,
|
|
2899
3411
|
showQuickSwitcher,
|
|
2900
3412
|
cycleQuickSwitcher,
|
|
@@ -3014,10 +3526,19 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3014
3526
|
}
|
|
3015
3527
|
return null;
|
|
3016
3528
|
};
|
|
3529
|
+
let modifierWasPressedOnMouseDown = false;
|
|
3530
|
+
const handleMouseDown = (event) => {
|
|
3531
|
+
modifierWasPressedOnMouseDown = event.ctrlKey || event.metaKey;
|
|
3532
|
+
};
|
|
3017
3533
|
const handleCtrlClick = async (event) => {
|
|
3018
|
-
// Only proceed if Ctrl
|
|
3534
|
+
// Only proceed if Ctrl/Cmd was already pressed when the mouse interaction started.
|
|
3535
|
+
// This avoids accidental navigation when users press Ctrl after selecting text to copy.
|
|
3536
|
+
if (!modifierWasPressedOnMouseDown)
|
|
3537
|
+
return;
|
|
3538
|
+
// Also require the modifier to still be held for the final click event.
|
|
3019
3539
|
if (!event.ctrlKey && !event.metaKey)
|
|
3020
3540
|
return;
|
|
3541
|
+
modifierWasPressedOnMouseDown = false;
|
|
3021
3542
|
const target = event.target;
|
|
3022
3543
|
const text = getTextFromElement(target);
|
|
3023
3544
|
if (text && isGuid(text)) {
|
|
@@ -3032,8 +3553,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3032
3553
|
skipViewChange: true,
|
|
3033
3554
|
});
|
|
3034
3555
|
if (item) {
|
|
3035
|
-
|
|
3036
|
-
switchView("editor", {
|
|
3556
|
+
switchWorkspace("editor", {
|
|
3037
3557
|
skipNavigationHistory: true,
|
|
3038
3558
|
});
|
|
3039
3559
|
showInfoToast({
|
|
@@ -3059,12 +3579,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3059
3579
|
}
|
|
3060
3580
|
};
|
|
3061
3581
|
if (typeof document !== "undefined") {
|
|
3582
|
+
document.addEventListener("mousedown", handleMouseDown, true);
|
|
3062
3583
|
document.addEventListener("click", handleCtrlClick, true);
|
|
3063
3584
|
return () => {
|
|
3585
|
+
document.removeEventListener("mousedown", handleMouseDown, true);
|
|
3064
3586
|
document.removeEventListener("click", handleCtrlClick, true);
|
|
3065
3587
|
};
|
|
3066
3588
|
}
|
|
3067
|
-
}, [loadItem,
|
|
3589
|
+
}, [loadItem, switchWorkspace, showInfoToast, showErrorToast]);
|
|
3068
3590
|
useEffect(() => {
|
|
3069
3591
|
const handleGlobalBlur = () => {
|
|
3070
3592
|
operations.onFieldBlur?.();
|
|
@@ -3102,21 +3624,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3102
3624
|
// Otherwise, only show workspaces that are in the settings
|
|
3103
3625
|
return workspaceIdsFromSettings.includes(w.id) && !w.visible;
|
|
3104
3626
|
});
|
|
3105
|
-
// Get sidebars allowed for the current workspace from settings
|
|
3106
|
-
const currentWorkspaceSettings = userInfo.workspaces?.find((w) => w.id === workspaceId);
|
|
3107
|
-
const allowedSidebarIds = currentWorkspaceSettings?.sidebars ?? [];
|
|
3108
|
-
// Legacy: Calculate visible views for backwards compatibility
|
|
3109
|
-
const allViews = (configuration.editor.views ?? [])
|
|
3110
|
-
.filter((x) => {
|
|
3111
|
-
return !x.visible && !x.hidden;
|
|
3112
|
-
})
|
|
3113
|
-
.filter((x) => !userInfo.views ||
|
|
3114
|
-
userInfo.views.map((view) => view.name).includes(x.name));
|
|
3115
|
-
const pinnedViews = userInfo.preferences?.pinnedViews ||
|
|
3116
|
-
configuration.editor.defaultPinnedViews ||
|
|
3117
|
-
[];
|
|
3118
|
-
// Legacy visibleViews for backwards compatibility
|
|
3119
|
-
const visibleViews = allViews.filter((view) => view.name === viewName || pinnedViews.includes(view.name));
|
|
3120
3627
|
// Handle initial mode setup from URL (only on initial load)
|
|
3121
3628
|
useEffect(() => {
|
|
3122
3629
|
if (!isInitialLoad)
|
|
@@ -3161,7 +3668,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3161
3668
|
// This is especially important when called from the tour, where the current workspace
|
|
3162
3669
|
// might be the editor and URL-only navigation would get "corrected" back.
|
|
3163
3670
|
try {
|
|
3164
|
-
|
|
3671
|
+
switchWorkspace("home", {
|
|
3165
3672
|
skipConfirmation: true,
|
|
3166
3673
|
skipNavigationHistory: true,
|
|
3167
3674
|
});
|
|
@@ -3208,7 +3715,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3208
3715
|
const current = new URLSearchParams(searchParams.toString());
|
|
3209
3716
|
current.delete("version");
|
|
3210
3717
|
current.delete("itemid");
|
|
3211
|
-
current.delete("view"); // Remove legacy param
|
|
3212
3718
|
current.delete("workspace"); // Clear workspace
|
|
3213
3719
|
current.set("create", "1");
|
|
3214
3720
|
const newUrl = `${pathname}?${current.toString()}`;
|
|
@@ -3488,6 +3994,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3488
3994
|
setShowOnlyMyChanges,
|
|
3489
3995
|
filterByCurrentLanguage,
|
|
3490
3996
|
setFilterByCurrentLanguage,
|
|
3997
|
+
historySearchQuery,
|
|
3998
|
+
setHistorySearchQuery,
|
|
3491
3999
|
refreshHistory,
|
|
3492
4000
|
isRefreshing,
|
|
3493
4001
|
activeSessions,
|
|
@@ -3525,19 +4033,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3525
4033
|
workspaceId,
|
|
3526
4034
|
previousWorkspaceId,
|
|
3527
4035
|
switchWorkspace,
|
|
3528
|
-
// Sidebar state
|
|
3529
|
-
availableSidebars: (
|
|
3530
|
-
// Always show agents-panel regardless of workspace settings
|
|
3531
|
-
if (s.id === "agents-panel") {
|
|
3532
|
-
return true;
|
|
3533
|
-
}
|
|
3534
|
-
// If no workspace settings or no sidebars defined for current workspace, show all
|
|
3535
|
-
if (!allowedSidebarIds || allowedSidebarIds.length === 0) {
|
|
3536
|
-
return true;
|
|
3537
|
-
}
|
|
3538
|
-
// Only show sidebars that are in the allowed list for the current workspace
|
|
3539
|
-
return allowedSidebarIds.includes(s.id);
|
|
3540
|
-
}),
|
|
4036
|
+
// Sidebar state
|
|
4037
|
+
availableSidebars: getSidebarsForWorkspace(workspaceId),
|
|
3541
4038
|
openSidebars,
|
|
3542
4039
|
pinnedSidebars,
|
|
3543
4040
|
lockedSidebars,
|
|
@@ -3547,17 +4044,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3547
4044
|
toggleSidebarPin,
|
|
3548
4045
|
toggleSidebarLock,
|
|
3549
4046
|
stackSidebar,
|
|
4047
|
+
moveSidebarToColumn,
|
|
3550
4048
|
unstackSidebar,
|
|
3551
4049
|
reorderSidebarInStack,
|
|
3552
4050
|
reorderPinnedSidebars,
|
|
3553
4051
|
reorderOpenSidebars,
|
|
3554
4052
|
getResolvedSidebar,
|
|
3555
|
-
// Legacy compatibility (deprecated)
|
|
3556
|
-
viewName,
|
|
3557
|
-
previousViewName,
|
|
3558
|
-
switchView,
|
|
3559
|
-
view: currentView,
|
|
3560
|
-
visibleViews,
|
|
3561
4053
|
compareMode,
|
|
3562
4054
|
setCompareMode,
|
|
3563
4055
|
fullscreen,
|
|
@@ -3597,6 +4089,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3597
4089
|
addSocketMessageListener,
|
|
3598
4090
|
sendSocketMessage,
|
|
3599
4091
|
socketConnectionVersion,
|
|
4092
|
+
socketDiagnostics,
|
|
3600
4093
|
currentItemDescriptor,
|
|
3601
4094
|
editorSlots,
|
|
3602
4095
|
activeSlotId,
|
|
@@ -3609,6 +4102,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3609
4102
|
setActiveSlot,
|
|
3610
4103
|
revision,
|
|
3611
4104
|
notifyPageModelReady,
|
|
4105
|
+
pageModelReadyToken,
|
|
3612
4106
|
selectedComment,
|
|
3613
4107
|
setSelectedComment,
|
|
3614
4108
|
comments,
|
|
@@ -3687,6 +4181,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3687
4181
|
setEnableCompletions,
|
|
3688
4182
|
showComponentNavigator,
|
|
3689
4183
|
setShowComponentNavigator: handleSetShowComponentNavigator,
|
|
4184
|
+
isComponentNavigatorOpenForSlot,
|
|
4185
|
+
setComponentNavigatorOpenForSlot,
|
|
3690
4186
|
showAgentsPanel,
|
|
3691
4187
|
setShowAgentsPanel: handleSetShowAgentsPanel,
|
|
3692
4188
|
showMinimap,
|
|
@@ -3698,8 +4194,12 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3698
4194
|
helpTerminalProfileName,
|
|
3699
4195
|
helpTerminalActiveTab,
|
|
3700
4196
|
setHelpTerminalActiveTab,
|
|
4197
|
+
selectedHelpSectionId,
|
|
4198
|
+
setSelectedHelpSectionId,
|
|
3701
4199
|
showAgentsWorkspaceEditor,
|
|
3702
4200
|
setShowAgentsWorkspaceEditor: handleSetShowAgentsWorkspaceEditor,
|
|
4201
|
+
selectedAgentsWorkspaceAgentId,
|
|
4202
|
+
setSelectedAgentsWorkspaceAgentId,
|
|
3703
4203
|
activeEditorTab,
|
|
3704
4204
|
setActiveEditorTab,
|
|
3705
4205
|
showLayoutComponents,
|
|
@@ -3708,6 +4208,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3708
4208
|
isQuotaExceeded: isQuotaExceeded(),
|
|
3709
4209
|
getQuotaWarningMessage,
|
|
3710
4210
|
isMobile,
|
|
4211
|
+
mobileEditorPanelOpen,
|
|
4212
|
+
setMobileEditorPanelOpen: handleSetMobileEditorPanelOpen,
|
|
3711
4213
|
openDialog,
|
|
3712
4214
|
webSocketMessages,
|
|
3713
4215
|
clearWebSocketMessages: () => setWebSocketMessages([]),
|
|
@@ -3746,7 +4248,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3746
4248
|
configuration,
|
|
3747
4249
|
updateUrl,
|
|
3748
4250
|
workspaceId,
|
|
3749
|
-
switchView,
|
|
3750
4251
|
pathname,
|
|
3751
4252
|
router,
|
|
3752
4253
|
item,
|
|
@@ -3776,7 +4277,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3776
4277
|
currentWorkspace,
|
|
3777
4278
|
previousWorkspaceId,
|
|
3778
4279
|
switchWorkspace,
|
|
3779
|
-
allowedSidebarIds,
|
|
3780
4280
|
openSidebars,
|
|
3781
4281
|
pinnedSidebars,
|
|
3782
4282
|
lockedSidebars,
|
|
@@ -3788,9 +4288,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3788
4288
|
reorderOpenSidebars,
|
|
3789
4289
|
getResolvedSidebar,
|
|
3790
4290
|
viewName,
|
|
3791
|
-
previousViewName,
|
|
3792
|
-
currentView,
|
|
3793
|
-
visibleViews,
|
|
3794
4291
|
compareMode,
|
|
3795
4292
|
// Important: in multi-slot mode the active PageViewContext can change
|
|
3796
4293
|
// without the base `pageViewContext` identity changing (e.g. switching slots).
|
|
@@ -3815,6 +4312,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3815
4312
|
currentItemDescriptor,
|
|
3816
4313
|
revision,
|
|
3817
4314
|
notifyPageModelReady,
|
|
4315
|
+
pageModelReadyToken,
|
|
3818
4316
|
selectedComment,
|
|
3819
4317
|
comments,
|
|
3820
4318
|
availableCommentTags,
|
|
@@ -3833,6 +4331,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3833
4331
|
quickSwitcherSelectedIndex,
|
|
3834
4332
|
handleQuickSwitcherSelect,
|
|
3835
4333
|
webSocketMessages,
|
|
4334
|
+
socketDiagnostics,
|
|
3836
4335
|
factoriesRef,
|
|
3837
4336
|
user,
|
|
3838
4337
|
statusMessage,
|
|
@@ -3841,7 +4340,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3841
4340
|
isQuotaExceeded,
|
|
3842
4341
|
getQuotaWarningMessage,
|
|
3843
4342
|
isMobile,
|
|
4343
|
+
mobileEditorPanelOpen,
|
|
4344
|
+
handleSetMobileEditorPanelOpen,
|
|
3844
4345
|
showComponentNavigator,
|
|
4346
|
+
isComponentNavigatorOpenForSlot,
|
|
4347
|
+
setComponentNavigatorOpenForSlot,
|
|
3845
4348
|
handleSetShowComponentNavigator,
|
|
3846
4349
|
showAgentsPanel,
|
|
3847
4350
|
handleSetShowAgentsPanel,
|
|
@@ -3853,7 +4356,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
3853
4356
|
helpTerminalProfileName,
|
|
3854
4357
|
helpTerminalActiveTab,
|
|
3855
4358
|
setHelpTerminalActiveTab,
|
|
4359
|
+
selectedHelpSectionId,
|
|
3856
4360
|
showAgentsWorkspaceEditor,
|
|
4361
|
+
selectedAgentsWorkspaceAgentId,
|
|
3857
4362
|
activeEditorTab,
|
|
3858
4363
|
showLayoutComponents,
|
|
3859
4364
|
openDialog,
|
|
@@ -4098,18 +4603,40 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
4098
4603
|
// prevDependencies.current = currentDependencies;
|
|
4099
4604
|
// editContextRef.current = editContext;
|
|
4100
4605
|
// }, [editContext]);
|
|
4606
|
+
// Auto-open the mobile editor panel for new selection/intent changes, but
|
|
4607
|
+
// keep a manual close sticky for the exact same context.
|
|
4608
|
+
useEffect(() => {
|
|
4609
|
+
if (!isMobile || workspaceId !== "editor")
|
|
4610
|
+
return;
|
|
4611
|
+
if (activeEditorTab) {
|
|
4612
|
+
handleSetMobileEditorPanelOpen(true);
|
|
4613
|
+
return;
|
|
4614
|
+
}
|
|
4615
|
+
if (!(selection.length > 0 || insertMode))
|
|
4616
|
+
return;
|
|
4617
|
+
if (dismissedMobilePanelToken === mobilePanelDismissToken)
|
|
4618
|
+
return;
|
|
4619
|
+
handleSetMobileEditorPanelOpen(true);
|
|
4620
|
+
}, [
|
|
4621
|
+
activeEditorTab,
|
|
4622
|
+
dismissedMobilePanelToken,
|
|
4623
|
+
handleSetMobileEditorPanelOpen,
|
|
4624
|
+
insertMode,
|
|
4625
|
+
isMobile,
|
|
4626
|
+
mobilePanelDismissToken,
|
|
4627
|
+
selection,
|
|
4628
|
+
workspaceId,
|
|
4629
|
+
]);
|
|
4101
4630
|
useEffect(() => {
|
|
4102
4631
|
fieldsEditContext.clearModifiedFields();
|
|
4103
4632
|
}, [currentItemDescriptor]);
|
|
4104
|
-
if (!
|
|
4633
|
+
if (!currentWorkspace)
|
|
4105
4634
|
return null;
|
|
4106
|
-
const editorUi = fullscreen ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "fixed inset-0 flex", children: [_jsx(PageViewerFrame, { compareView: compareMode, pageViewContext: activePageViewContext }), _jsx(FullscreenControls, { device: activePageViewContext.device, setDevice: (d) => activePageViewContext.setDevice(d), canExit: !configuration.forceFullscreen, onExit: () => setFullscreen(false), firstMobileDeviceName: configuration.devices[0]?.name })] }), showFullscreenHint && !configuration.forceFullscreen && (_jsx("div", { className: "fixed inset-0", onMouseMoveCapture: () => {
|
|
4635
|
+
const editorUi = fullscreen ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "fixed inset-0 flex", children: [_jsx(PageViewerFrame, { compareView: compareMode, pageViewContext: activePageViewContext }), _jsx(FullscreenControls, { device: activePageViewContext.device, setDevice: (d) => activePageViewContext.setDevice(d), canExit: !configuration.forceFullscreen, onExit: () => setFullscreen(false), firstMobileDeviceName: configuration.devices[0]?.name })] }), showFullscreenHint && !configuration.forceFullscreen && !isMobile && (_jsx("div", { className: "fixed inset-0 z-10000", onMouseMoveCapture: () => {
|
|
4107
4636
|
setTimeout(() => {
|
|
4108
4637
|
setShowFullscreenHint(false);
|
|
4109
4638
|
}, 600);
|
|
4110
|
-
}, "data-testid": "fullscreen-hint-overlay", children: _jsxs("div", { className: "fixed top-6 left-1/2 -translate-x-1/2 transform rounded-full bg-black/60 px-6 py-2.5 text-sm font-medium text-white shadow-2xl backdrop-blur-md transition-all duration-500", children: ["Press", " ", _jsx("kbd", { className: "mx-1 rounded bg-white/20 px-1.5 py-0.5 text-xs font-semibold", children: "Ctrl + F11" }), " ", "to exit fullscreen"] }) }))] })) : (_jsxs(_Fragment, { children: [_jsx(EditorChrome, { className: className, currentWorkspace: currentWorkspace, centerPanelView: centerPanelView, editContext: editContext,
|
|
4111
|
-
// Legacy props for backwards compatibility
|
|
4112
|
-
currentView: currentView, viewName: viewName }), isTourActive && (_jsx(Tour, { tourStopCallback: () => {
|
|
4639
|
+
}, "data-testid": "fullscreen-hint-overlay", children: _jsxs("div", { className: "fixed top-6 left-1/2 -translate-x-1/2 transform rounded-full bg-black/60 px-6 py-2.5 text-sm font-medium text-white shadow-2xl backdrop-blur-md transition-all duration-500", children: ["Press", " ", _jsx("kbd", { className: "mx-1 rounded bg-white/20 px-1.5 py-0.5 text-xs font-semibold", children: "Ctrl + F11" }), " ", "to exit fullscreen"] }) }))] })) : (_jsxs(_Fragment, { children: [_jsx(EditorChrome, { className: className, currentWorkspace: currentWorkspace, centerPanelView: centerPanelView, editContext: editContext, showAgentsPanel: showAgentsPanel, handleSetShowAgentsPanel: handleSetShowAgentsPanel, workspaceId: workspaceId }), isTourActive && (_jsx(Tour, { tourStopCallback: () => {
|
|
4113
4640
|
setIsTourActive(false);
|
|
4114
4641
|
// Remove tour state from URL
|
|
4115
4642
|
// Use history.replaceState instead of router.replace to avoid triggering React navigation
|
|
@@ -4119,8 +4646,14 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
|
|
|
4119
4646
|
const newUrl = queryString
|
|
4120
4647
|
? `${window.location.pathname}?${queryString}`
|
|
4121
4648
|
: window.location.pathname;
|
|
4122
|
-
window.history.replaceState(
|
|
4123
|
-
}, configuration: configuration, restoredFromUrl: tourRestoredRef.current })), _jsx(GuidanceOverlay, {}), _jsx(
|
|
4124
|
-
return (_jsx("div", { className: `editor h-full w-full`, children: _jsx(OperationsContextProvider, { value: operationsContext.context, children: _jsx(FieldsEditContextProvider, { value: fieldsEditContext, children: _jsxs(EditContextProvider, { value: editContext, children: [_jsx(DevModeIndicator, {}), startupChecks.state === "loading" && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-white/70 backdrop-blur-[1px]", children: _jsx("div", { className: "flex items-center gap-3 rounded-md border border-gray-200 bg-white px-4 py-3 text-gray-700 shadow-sm", children: _jsx(Spinner, { size: "xl" }) }) })), editContext.isRefreshing && (_jsx("div", { className: "pointer-events-none fixed right-0 bottom-0 flex h-24 w-24 items-center justify-center text-gray-600 opacity-50 select-none", children: _jsx(Spinner, {}) })),
|
|
4649
|
+
window.history.replaceState(getCurrentHistoryState(), "", newUrl);
|
|
4650
|
+
}, configuration: configuration, restoredFromUrl: tourRestoredRef.current })), _jsx(FeatureGate, { feature: LicenseFeatures.AI, children: _jsx(GuidanceOverlay, {}) }), _jsx(FeatureGate, { feature: LicenseFeatures.AI, children: _jsx(AgentDialogHandler, {}) })] }));
|
|
4651
|
+
return (_jsx(LicenseProvider, { initialLicenseStatus: initialLicenseStatus, initialStatusLoaded: initialLicenseStatusLoaded, children: _jsx("div", { className: `editor h-full w-full`, children: _jsx(OperationsContextProvider, { value: operationsContext.context, children: _jsx(FieldsEditContextProvider, { value: fieldsEditContext, children: _jsxs(EditContextProvider, { value: editContext, children: [_jsx(DevModeIndicator, {}), startupChecks.state === "loading" && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-white/70 backdrop-blur-[1px]", children: _jsx("div", { className: "flex items-center gap-3 rounded-md border border-gray-200 bg-white px-4 py-3 text-gray-700 shadow-sm", children: _jsx(Spinner, { size: "xl" }) }) })), editContext.isRefreshing && (_jsx("div", { className: "pointer-events-none fixed right-0 bottom-0 flex h-24 w-24 items-center justify-center text-gray-600 opacity-50 select-none", children: _jsx(Spinner, {}) })), (currentWorkspace.id === "agents" ||
|
|
4652
|
+
currentWorkspace.id === "taskboard") &&
|
|
4653
|
+
showAgentsWorkspaceEditor && (_jsx(AgentsSlotContextBridgeHost, { slots: editorSlots })), startupChecks.state !== "loading" && (children || editorUi), startupChecks.state !== "loading" && dialog, _jsx(Toaster, { position: "top-center" }), " ", _jsx(ConfirmationDialog, { ref: confirmationDialogRef }), _jsx(ConcurrentUserLimitDialog, { open: concurrentUserLimitError !== null, onOpenChange: (open) => {
|
|
4654
|
+
if (!open) {
|
|
4655
|
+
setConcurrentUserLimitError(null);
|
|
4656
|
+
}
|
|
4657
|
+
}, sessionId: sessionId, currentUsers: concurrentUserLimitError?.currentUsers ?? 0, maxUsers: concurrentUserLimitError?.maxUsers ?? 0, message: concurrentUserLimitError?.message ?? "", onRetry: handleRetryConnection, isAdministrator: userInfo.user.isAdministrator === true }), _jsx(QuickItemSwitcher, { visible: quickSwitcherVisible, entries: quickSwitcherEntries.slice(0, 5), selectedIndex: quickSwitcherSelectedIndex, onSelect: handleQuickSwitcherSelect, onClose: () => setQuickSwitcherVisible(false) }), _jsx(EditContextMenu, { ref: contextMenuRef }), _jsx(FeatureGate, { feature: LicenseFeatures.AI, children: _jsx(InlineAiTrigger, {}) }), media.mediaSelectorVisible && (_jsx(MediaSelector, { language: editContext.currentItemDescriptor.language, visible: media.mediaSelectorVisible, onHide: media.handleHide, onMediaSelected: media.onMediaSelect, selectedIdPath: media.selectedMediaIdPath, mode: media.mediaSelectorMode, initialSearchTerm: media.initialSearchTerm })), _jsx(FieldEditorPopup, { ref: fieldEditorPopupRef }), _jsx(LicenseOverlay, {})] }) }) }) }) }));
|
|
4125
4658
|
}
|
|
4126
4659
|
//# sourceMappingURL=EditorShell.js.map
|