@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.
Files changed (579) hide show
  1. package/dist/agents-view/AgentCard.d.ts +6 -4
  2. package/dist/agents-view/AgentCard.js +143 -24
  3. package/dist/agents-view/AgentCard.js.map +1 -1
  4. package/dist/agents-view/AgentsInbox.d.ts +1 -1
  5. package/dist/agents-view/AgentsInbox.js +7 -92
  6. package/dist/agents-view/AgentsInbox.js.map +1 -1
  7. package/dist/agents-view/AgentsTitlebar.js +3 -2
  8. package/dist/agents-view/AgentsTitlebar.js.map +1 -1
  9. package/dist/agents-view/AgentsView.d.ts +6 -7
  10. package/dist/agents-view/AgentsView.js +187 -98
  11. package/dist/agents-view/AgentsView.js.map +1 -1
  12. package/dist/agents-view/AgentsWorkspaceView.d.ts +2 -6
  13. package/dist/agents-view/AgentsWorkspaceView.js +266 -113
  14. package/dist/agents-view/AgentsWorkspaceView.js.map +1 -1
  15. package/dist/agents-view/ProfileAgentsGroup.d.ts +2 -1
  16. package/dist/agents-view/ProfileAgentsGroup.js +4 -3
  17. package/dist/agents-view/ProfileAgentsGroup.js.map +1 -1
  18. package/dist/components/ActionButton.d.ts +1 -1
  19. package/dist/components/ActionButton.js.map +1 -1
  20. package/dist/components/FilterInput.d.ts +1 -1
  21. package/dist/components/FilterInput.js +1 -1
  22. package/dist/components/FilterInput.js.map +1 -1
  23. package/dist/components/ui/LanguageSelector.js +2 -4
  24. package/dist/components/ui/LanguageSelector.js.map +1 -1
  25. package/dist/components/ui/PlaceholderInput.js +3 -3
  26. package/dist/components/ui/PlaceholderInput.js.map +1 -1
  27. package/dist/components/ui/PlaceholderInputTypes.js +1 -1
  28. package/dist/components/ui/PlaceholderInputTypes.js.map +1 -1
  29. package/dist/components/ui/alert-dialog.d.ts +1 -1
  30. package/dist/components/ui/alert-dialog.js +6 -10
  31. package/dist/components/ui/alert-dialog.js.map +1 -1
  32. package/dist/components/ui/button.d.ts +4 -4
  33. package/dist/components/ui/button.js +4 -1
  34. package/dist/components/ui/button.js.map +1 -1
  35. package/dist/components/ui/context-menu.d.ts +1 -1
  36. package/dist/components/ui/context-menu.js +12 -4
  37. package/dist/components/ui/context-menu.js.map +1 -1
  38. package/dist/components/ui/copy-button.d.ts +2 -1
  39. package/dist/components/ui/copy-button.js +2 -2
  40. package/dist/components/ui/copy-button.js.map +1 -1
  41. package/dist/components/ui/dialog.d.ts +1 -1
  42. package/dist/components/ui/dialog.js +21 -126
  43. package/dist/components/ui/dialog.js.map +1 -1
  44. package/dist/components/ui/input.d.ts +1 -1
  45. package/dist/components/ui/input.js +5 -3
  46. package/dist/components/ui/input.js.map +1 -1
  47. package/dist/components/ui/paste-button.d.ts +2 -1
  48. package/dist/components/ui/paste-button.js +2 -2
  49. package/dist/components/ui/paste-button.js.map +1 -1
  50. package/dist/components/ui/popover.js +1 -9
  51. package/dist/components/ui/popover.js.map +1 -1
  52. package/dist/components/ui/select.js +1 -1
  53. package/dist/components/ui/select.js.map +1 -1
  54. package/dist/components/ui/styled-dialog-title.js +1 -1
  55. package/dist/components/ui/styled-dialog-title.js.map +1 -1
  56. package/dist/components/ui/tabs.d.ts +1 -1
  57. package/dist/components/ui/tabs.js +4 -11
  58. package/dist/components/ui/tabs.js.map +1 -1
  59. package/dist/config/config.d.ts +4 -2
  60. package/dist/config/config.js +250 -70
  61. package/dist/config/config.js.map +1 -1
  62. package/dist/config/notificationRoutes.js +14 -0
  63. package/dist/config/notificationRoutes.js.map +1 -1
  64. package/dist/config/types/workspace.d.ts +6 -0
  65. package/dist/config/types.d.ts +63 -12
  66. package/dist/config/types.js.map +1 -1
  67. package/dist/editor/ConfirmationDialog.js +20 -4
  68. package/dist/editor/ConfirmationDialog.js.map +1 -1
  69. package/dist/editor/ContentTree.d.ts +2 -1
  70. package/dist/editor/ContentTree.js +93 -32
  71. package/dist/editor/ContentTree.js.map +1 -1
  72. package/dist/editor/Editor.js +87 -22
  73. package/dist/editor/Editor.js.map +1 -1
  74. package/dist/editor/FieldHistory.js +84 -36
  75. package/dist/editor/FieldHistory.js.map +1 -1
  76. package/dist/editor/FieldListField.js +21 -9
  77. package/dist/editor/FieldListField.js.map +1 -1
  78. package/dist/editor/FieldListFieldWithFallbacks.js +23 -2
  79. package/dist/editor/FieldListFieldWithFallbacks.js.map +1 -1
  80. package/dist/editor/GlobalMenuBar.js +29 -2
  81. package/dist/editor/GlobalMenuBar.js.map +1 -1
  82. package/dist/editor/ImageEditor.js +5 -2
  83. package/dist/editor/ImageEditor.js.map +1 -1
  84. package/dist/editor/ItemInfo.js +36 -1
  85. package/dist/editor/ItemInfo.js.map +1 -1
  86. package/dist/editor/LinkEditorDialog.js +3 -0
  87. package/dist/editor/LinkEditorDialog.js.map +1 -1
  88. package/dist/editor/MainLayout.d.ts +0 -2
  89. package/dist/editor/MainLayout.js +65 -8
  90. package/dist/editor/MainLayout.js.map +1 -1
  91. package/dist/editor/MigrationsView.js +29 -5
  92. package/dist/editor/MigrationsView.js.map +1 -1
  93. package/dist/editor/MobileLayout.js +37 -12
  94. package/dist/editor/MobileLayout.js.map +1 -1
  95. package/dist/editor/PictureCropper.js +54 -45
  96. package/dist/editor/PictureCropper.js.map +1 -1
  97. package/dist/editor/PictureEditor.js +17 -15
  98. package/dist/editor/PictureEditor.js.map +1 -1
  99. package/dist/editor/QuickItemSwitcher.js +21 -21
  100. package/dist/editor/QuickItemSwitcher.js.map +1 -1
  101. package/dist/editor/SetupWizard.js +52 -12
  102. package/dist/editor/SetupWizard.js.map +1 -1
  103. package/dist/editor/Titlebar.js +7 -2
  104. package/dist/editor/Titlebar.js.map +1 -1
  105. package/dist/editor/ai/AgentCostDisplay.d.ts +1 -0
  106. package/dist/editor/ai/AgentCostDisplay.js +1 -1
  107. package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
  108. package/dist/editor/ai/AgentDocumentList.js +32 -14
  109. package/dist/editor/ai/AgentDocumentList.js.map +1 -1
  110. package/dist/editor/ai/AgentGreeting.js +3 -2
  111. package/dist/editor/ai/AgentGreeting.js.map +1 -1
  112. package/dist/editor/ai/AgentProfileSelector.js +2 -1
  113. package/dist/editor/ai/AgentProfileSelector.js.map +1 -1
  114. package/dist/editor/ai/AgentStatusBadge.d.ts +0 -5
  115. package/dist/editor/ai/AgentStatusBadge.js +67 -65
  116. package/dist/editor/ai/AgentStatusBadge.js.map +1 -1
  117. package/dist/editor/ai/AgentTerminal.d.ts +14 -2
  118. package/dist/editor/ai/AgentTerminal.js +2377 -483
  119. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  120. package/dist/editor/ai/AgentTerminalStatusBar.d.ts +8 -3
  121. package/dist/editor/ai/AgentTerminalStatusBar.js +460 -56
  122. package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
  123. package/dist/editor/ai/Agents.js +150 -113
  124. package/dist/editor/ai/Agents.js.map +1 -1
  125. package/dist/editor/ai/AiResponseMessage.d.ts +10 -1
  126. package/dist/editor/ai/AiResponseMessage.js +238 -23
  127. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  128. package/dist/editor/ai/ContextInfoBar.d.ts +2 -3
  129. package/dist/editor/ai/ContextInfoBar.js +64 -7
  130. package/dist/editor/ai/ContextInfoBar.js.map +1 -1
  131. package/dist/editor/ai/GuidanceOverlay.js +17 -11
  132. package/dist/editor/ai/GuidanceOverlay.js.map +1 -1
  133. package/dist/editor/ai/InlineAiDialog.d.ts +1 -1
  134. package/dist/editor/ai/InlineAiDialog.js +514 -192
  135. package/dist/editor/ai/InlineAiDialog.js.map +1 -1
  136. package/dist/editor/ai/InlineAiTrigger.js +115 -12
  137. package/dist/editor/ai/InlineAiTrigger.js.map +1 -1
  138. package/dist/editor/ai/MediaImage.js +40 -8
  139. package/dist/editor/ai/MediaImage.js.map +1 -1
  140. package/dist/editor/ai/SpawnedAgentsPanel.js +10 -12
  141. package/dist/editor/ai/SpawnedAgentsPanel.js.map +1 -1
  142. package/dist/editor/ai/ToolCallDisplay.d.ts +22 -2
  143. package/dist/editor/ai/ToolCallDisplay.js +518 -147
  144. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  145. package/dist/editor/ai/dialogs/AgentDialogHandler.d.ts +1 -8
  146. package/dist/editor/ai/dialogs/AgentDialogHandler.js +379 -42
  147. package/dist/editor/ai/dialogs/AgentDialogHandler.js.map +1 -1
  148. package/dist/editor/ai/dialogs/QuestionnaireInline.d.ts +5 -1
  149. package/dist/editor/ai/dialogs/QuestionnaireInline.js +628 -60
  150. package/dist/editor/ai/dialogs/QuestionnaireInline.js.map +1 -1
  151. package/dist/editor/ai/dialogs/agentDialogTypes.d.ts +115 -0
  152. package/dist/editor/ai/dialogs/agentDialogTypes.js +2 -0
  153. package/dist/editor/ai/dialogs/agentDialogTypes.js.map +1 -1
  154. package/dist/editor/ai/types.d.ts +3 -1
  155. package/dist/editor/ai/useAgentStatus.d.ts +2 -1
  156. package/dist/editor/ai/useAgentStatus.js +86 -99
  157. package/dist/editor/ai/useAgentStatus.js.map +1 -1
  158. package/dist/editor/ai/useInlineAiPosition.js +45 -5
  159. package/dist/editor/ai/useInlineAiPosition.js.map +1 -1
  160. package/dist/editor/client/AboutDialog.js +4 -2
  161. package/dist/editor/client/AboutDialog.js.map +1 -1
  162. package/dist/editor/client/EditorShell.d.ts +4 -1
  163. package/dist/editor/client/EditorShell.js +770 -237
  164. package/dist/editor/client/EditorShell.js.map +1 -1
  165. package/dist/editor/client/editContext.d.ts +33 -19
  166. package/dist/editor/client/editContext.js.map +1 -1
  167. package/dist/editor/client/helpers.js +6 -0
  168. package/dist/editor/client/helpers.js.map +1 -1
  169. package/dist/editor/client/hooks/useEditorUrlSync.js +1 -2
  170. package/dist/editor/client/hooks/useEditorUrlSync.js.map +1 -1
  171. package/dist/editor/client/hooks/useEditorWebSocket.d.ts +10 -0
  172. package/dist/editor/client/hooks/useEditorWebSocket.js +209 -14
  173. package/dist/editor/client/hooks/useEditorWebSocket.js.map +1 -1
  174. package/dist/editor/client/hooks/useQuota.d.ts +8 -0
  175. package/dist/editor/client/hooks/useQuota.js.map +1 -1
  176. package/dist/editor/client/hooks/useSocketMessageHandler.js +68 -7
  177. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  178. package/dist/editor/client/itemsRepository.js +10 -6
  179. package/dist/editor/client/itemsRepository.js.map +1 -1
  180. package/dist/editor/client/navigation.js +35 -3
  181. package/dist/editor/client/navigation.js.map +1 -1
  182. package/dist/editor/client/operations.d.ts +6 -3
  183. package/dist/editor/client/operations.js +208 -30
  184. package/dist/editor/client/operations.js.map +1 -1
  185. package/dist/editor/client/pageModelBuilder.js +4 -31
  186. package/dist/editor/client/pageModelBuilder.js.map +1 -1
  187. package/dist/editor/client/ui/DevModeIndicator.js +2 -2
  188. package/dist/editor/client/ui/DevModeIndicator.js.map +1 -1
  189. package/dist/editor/client/ui/EditorChrome.d.ts +0 -6
  190. package/dist/editor/client/ui/EditorChrome.js +55 -72
  191. package/dist/editor/client/ui/EditorChrome.js.map +1 -1
  192. package/dist/editor/client/ui/FullscreenControls.js +5 -3
  193. package/dist/editor/client/ui/FullscreenControls.js.map +1 -1
  194. package/dist/editor/commands/commands.d.ts +11 -1
  195. package/dist/editor/commands/commands.js +12 -1
  196. package/dist/editor/commands/commands.js.map +1 -1
  197. package/dist/editor/commands/componentCommands.js +109 -55
  198. package/dist/editor/commands/componentCommands.js.map +1 -1
  199. package/dist/editor/commands/customCommandConverter.d.ts +8 -1
  200. package/dist/editor/commands/customCommandConverter.js +35 -5
  201. package/dist/editor/commands/customCommandConverter.js.map +1 -1
  202. package/dist/editor/commands/handlers/agentHandler.js +2 -1
  203. package/dist/editor/commands/handlers/agentHandler.js.map +1 -1
  204. package/dist/editor/commands/itemCommands.d.ts +3 -0
  205. package/dist/editor/commands/itemCommands.js +93 -10
  206. package/dist/editor/commands/itemCommands.js.map +1 -1
  207. package/dist/editor/commands/undo.d.ts +9 -15
  208. package/dist/editor/commands/undo.js +24 -0
  209. package/dist/editor/commands/undo.js.map +1 -1
  210. package/dist/editor/context-menu/InsertMenu.js +83 -39
  211. package/dist/editor/context-menu/InsertMenu.js.map +1 -1
  212. package/dist/editor/field-types/MultiLineText.js +1 -1
  213. package/dist/editor/field-types/MultiLineText.js.map +1 -1
  214. package/dist/editor/field-types/RawEditor.js +1 -1
  215. package/dist/editor/field-types/RichTextEditor.js +13 -5
  216. package/dist/editor/field-types/RichTextEditor.js.map +1 -1
  217. package/dist/editor/field-types/RichTextEditorComponent.js +37 -3
  218. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  219. package/dist/editor/field-types/SingleLineText.js +1 -1
  220. package/dist/editor/field-types/TreeListEditor.js +3 -2
  221. package/dist/editor/field-types/TreeListEditor.js.map +1 -1
  222. package/dist/editor/field-types/richtext/components/ReactSlate.css +23 -5
  223. package/dist/editor/field-types/richtext/components/ReactSlate.d.ts +2 -0
  224. package/dist/editor/field-types/richtext/components/ReactSlate.js +28 -4
  225. package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
  226. package/dist/editor/field-types/richtext/components/ToolbarButton.js +4 -2
  227. package/dist/editor/field-types/richtext/components/ToolbarButton.js.map +1 -1
  228. package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +13 -0
  229. package/dist/editor/field-types/richtext/contextMenuFactory.js +181 -24
  230. package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -1
  231. package/dist/editor/field-types/richtext/types.d.ts +2 -0
  232. package/dist/editor/field-types/richtext/types.js.map +1 -1
  233. package/dist/editor/field-types/richtext/utils/plugins.js +4 -0
  234. package/dist/editor/field-types/richtext/utils/plugins.js.map +1 -1
  235. package/dist/editor/field-types/textContextMenuFactory.js +3 -2
  236. package/dist/editor/field-types/textContextMenuFactory.js.map +1 -1
  237. package/dist/editor/media-selector/AiImageSearchPrompt.js +4 -2
  238. package/dist/editor/media-selector/AiImageSearchPrompt.js.map +1 -1
  239. package/dist/editor/media-selector/MediaFolderBrowser.js +1 -1
  240. package/dist/editor/media-selector/MediaFolderBrowser.js.map +1 -1
  241. package/dist/editor/media-selector/MediaSelector.js +7 -1
  242. package/dist/editor/media-selector/MediaSelector.js.map +1 -1
  243. package/dist/editor/media-selector/TreeSelector.js +40 -35
  244. package/dist/editor/media-selector/TreeSelector.js.map +1 -1
  245. package/dist/editor/menubar/ActiveUsers.js +1 -1
  246. package/dist/editor/menubar/ActiveUsers.js.map +1 -1
  247. package/dist/editor/menubar/GenericToolbar.js +4 -2
  248. package/dist/editor/menubar/GenericToolbar.js.map +1 -1
  249. package/dist/editor/menubar/ItemLanguageVersion.js +2 -2
  250. package/dist/editor/menubar/ItemLanguageVersion.js.map +1 -1
  251. package/dist/editor/menubar/PageSelector.js +26 -147
  252. package/dist/editor/menubar/PageSelector.js.map +1 -1
  253. package/dist/editor/menubar/Separator.js +1 -1
  254. package/dist/editor/menubar/VersionSelector.js +2 -4
  255. package/dist/editor/menubar/VersionSelector.js.map +1 -1
  256. package/dist/editor/menubar/WorkflowButton.js +39 -12
  257. package/dist/editor/menubar/WorkflowButton.js.map +1 -1
  258. package/dist/editor/menubar/toolbar-sections/CustomCommandsToolbar.js +16 -38
  259. package/dist/editor/menubar/toolbar-sections/CustomCommandsToolbar.js.map +1 -1
  260. package/dist/editor/menubar/toolbar-sections/EditControls.js +3 -3
  261. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  262. package/dist/editor/menubar/toolbar-sections/HelpButton.js +1 -0
  263. package/dist/editor/menubar/toolbar-sections/HelpButton.js.map +1 -1
  264. package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +6 -10
  265. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +597 -220
  266. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
  267. package/dist/editor/menubar/toolbar-sections/UtilityControls.js +13 -2
  268. package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
  269. package/dist/editor/page-editor-chrome/CommentHighlighting.js +42 -1
  270. package/dist/editor/page-editor-chrome/CommentHighlighting.js.map +1 -1
  271. package/dist/editor/page-editor-chrome/FrameMenu.js +1 -1
  272. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  273. package/dist/editor/page-editor-chrome/InlineEditor.js +97 -48
  274. package/dist/editor/page-editor-chrome/InlineEditor.js.map +1 -1
  275. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +38 -17
  276. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
  277. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +17 -11
  278. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
  279. package/dist/editor/page-editor-chrome/useInlineAICompletion.js +301 -301
  280. package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
  281. package/dist/editor/page-viewer/DeviceToolbar.js +1 -1
  282. package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -1
  283. package/dist/editor/page-viewer/EditorForm.js +69 -11
  284. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  285. package/dist/editor/page-viewer/MiniMap.d.ts +2 -4
  286. package/dist/editor/page-viewer/MiniMap.js +91 -28
  287. package/dist/editor/page-viewer/MiniMap.js.map +1 -1
  288. package/dist/editor/page-viewer/PageViewer.d.ts +3 -1
  289. package/dist/editor/page-viewer/PageViewer.js +92 -19
  290. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  291. package/dist/editor/page-viewer/PageViewerFrame.d.ts +2 -1
  292. package/dist/editor/page-viewer/PageViewerFrame.js +348 -115
  293. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  294. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +114 -49
  295. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +1 -1
  296. package/dist/editor/page-viewer/pageViewContext.d.ts +1 -0
  297. package/dist/editor/page-viewer/pageViewContext.js +51 -14
  298. package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
  299. package/dist/editor/pageModel.d.ts +14 -1
  300. package/dist/editor/reviews/Comment.js +26 -12
  301. package/dist/editor/reviews/Comment.js.map +1 -1
  302. package/dist/editor/reviews/CommentDisplayPopover.js +7 -5
  303. package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
  304. package/dist/editor/reviews/CommentView.js +19 -4
  305. package/dist/editor/reviews/CommentView.js.map +1 -1
  306. package/dist/editor/reviews/Comments.js +89 -72
  307. package/dist/editor/reviews/Comments.js.map +1 -1
  308. package/dist/editor/reviews/CreateReviewDialog.js +281 -177
  309. package/dist/editor/reviews/CreateReviewDialog.js.map +1 -1
  310. package/dist/editor/reviews/DecisionsMatrix.js +96 -25
  311. package/dist/editor/reviews/DecisionsMatrix.js.map +1 -1
  312. package/dist/editor/reviews/DiffView.js +7 -14
  313. package/dist/editor/reviews/DiffView.js.map +1 -1
  314. package/dist/editor/reviews/EditReviewSettingsDialog.js +6 -4
  315. package/dist/editor/reviews/EditReviewSettingsDialog.js.map +1 -1
  316. package/dist/editor/reviews/MultiReviewManager.js +25 -3
  317. package/dist/editor/reviews/MultiReviewManager.js.map +1 -1
  318. package/dist/editor/reviews/PagesPanel.js +31 -15
  319. package/dist/editor/reviews/PagesPanel.js.map +1 -1
  320. package/dist/editor/reviews/PreviewInfo.js +1 -4
  321. package/dist/editor/reviews/PreviewInfo.js.map +1 -1
  322. package/dist/editor/reviews/ReviewCard.js +13 -7
  323. package/dist/editor/reviews/ReviewCard.js.map +1 -1
  324. package/dist/editor/reviews/ReviewDetail.js +3 -2
  325. package/dist/editor/reviews/ReviewDetail.js.map +1 -1
  326. package/dist/editor/reviews/ReviewsList.js +7 -3
  327. package/dist/editor/reviews/ReviewsList.js.map +1 -1
  328. package/dist/editor/reviews/SuggestedEdit.js +34 -3
  329. package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
  330. package/dist/editor/reviews/SuggestionDisplayPopover.js +31 -5
  331. package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
  332. package/dist/editor/reviews/commentAi.js +25 -6
  333. package/dist/editor/reviews/commentAi.js.map +1 -1
  334. package/dist/editor/reviews/reviewCommands.js +4 -1
  335. package/dist/editor/reviews/reviewCommands.js.map +1 -1
  336. package/dist/editor/reviews/useMultiReview.js +2 -2
  337. package/dist/editor/reviews/useMultiReview.js.map +1 -1
  338. package/dist/editor/reviews/useReviews.d.ts +2 -2
  339. package/dist/editor/reviews/useReviews.js +12 -30
  340. package/dist/editor/reviews/useReviews.js.map +1 -1
  341. package/dist/editor/services/agentService.d.ts +229 -5
  342. package/dist/editor/services/agentService.js +292 -39
  343. package/dist/editor/services/agentService.js.map +1 -1
  344. package/dist/editor/services/aiService.d.ts +57 -1
  345. package/dist/editor/services/aiService.js +79 -6
  346. package/dist/editor/services/aiService.js.map +1 -1
  347. package/dist/editor/services/contentService.d.ts +6 -3
  348. package/dist/editor/services/contentService.js +13 -12
  349. package/dist/editor/services/contentService.js.map +1 -1
  350. package/dist/editor/services/editService.d.ts +52 -1
  351. package/dist/editor/services/editService.js +94 -2
  352. package/dist/editor/services/editService.js.map +1 -1
  353. package/dist/editor/services/indexService.js +1 -1
  354. package/dist/editor/services/indexService.js.map +1 -1
  355. package/dist/editor/services/reviewsService.d.ts +3 -6
  356. package/dist/editor/services/reviewsService.js +2 -11
  357. package/dist/editor/services/reviewsService.js.map +1 -1
  358. package/dist/editor/services/serviceHelper.d.ts +2 -1
  359. package/dist/editor/services/serviceHelper.js +112 -20
  360. package/dist/editor/services/serviceHelper.js.map +1 -1
  361. package/dist/editor/services/systemService.d.ts +2 -1
  362. package/dist/editor/services/systemService.js +3 -0
  363. package/dist/editor/services/systemService.js.map +1 -1
  364. package/dist/editor/services-server/api.d.ts +1 -2
  365. package/dist/editor/services-server/api.js +11 -6
  366. package/dist/editor/services-server/api.js.map +1 -1
  367. package/dist/editor/settings/About.js +317 -3
  368. package/dist/editor/settings/About.js.map +1 -1
  369. package/dist/editor/settings/QuotaInfo.js +210 -4
  370. package/dist/editor/settings/QuotaInfo.js.map +1 -1
  371. package/dist/editor/settings/SettingsView.js +25 -23
  372. package/dist/editor/settings/SettingsView.js.map +1 -1
  373. package/dist/editor/settings/Status.js +7 -6
  374. package/dist/editor/settings/Status.js.map +1 -1
  375. package/dist/editor/settings/index/useIndexStatus.js +20 -22
  376. package/dist/editor/settings/index/useIndexStatus.js.map +1 -1
  377. package/dist/editor/settings/panels/AgentsPanel.d.ts +0 -4
  378. package/dist/editor/settings/panels/AgentsPanel.js +95 -121
  379. package/dist/editor/settings/panels/AgentsPanel.js.map +1 -1
  380. package/dist/editor/settings/panels/ModelsPanel.js +329 -108
  381. package/dist/editor/settings/panels/ModelsPanel.js.map +1 -1
  382. package/dist/editor/settings/panels/ProvidersPanel.d.ts +1 -1
  383. package/dist/editor/settings/panels/ProvidersPanel.js +86 -59
  384. package/dist/editor/settings/panels/ProvidersPanel.js.map +1 -1
  385. package/dist/editor/settings/panels/SearchConfigPanel.js +4 -4
  386. package/dist/editor/settings/panels/SearchConfigPanel.js.map +1 -1
  387. package/dist/editor/settings/panels/index.d.ts +3 -2
  388. package/dist/editor/settings/panels/index.js +3 -2
  389. package/dist/editor/settings/panels/index.js.map +1 -1
  390. package/dist/editor/settings/status/coreStatusChecks.js +124 -19
  391. package/dist/editor/settings/status/coreStatusChecks.js.map +1 -1
  392. package/dist/editor/settings/status/useStartupChecks.d.ts +3 -1
  393. package/dist/editor/settings/status/useStartupChecks.js +9 -5
  394. package/dist/editor/settings/status/useStartupChecks.js.map +1 -1
  395. package/dist/editor/setup-wizard/steps/CompleteStep.d.ts +2 -1
  396. package/dist/editor/setup-wizard/steps/CompleteStep.js +2 -1
  397. package/dist/editor/setup-wizard/steps/CompleteStep.js.map +1 -1
  398. package/dist/editor/sidebar/ComponentPalette.js +2 -1
  399. package/dist/editor/sidebar/ComponentPalette.js.map +1 -1
  400. package/dist/editor/sidebar/ComponentTree.d.ts +8 -1
  401. package/dist/editor/sidebar/ComponentTree.js +216 -69
  402. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  403. package/dist/editor/sidebar/EditHistory.js +22 -46
  404. package/dist/editor/sidebar/EditHistory.js.map +1 -1
  405. package/dist/editor/sidebar/Favorites.js +4 -8
  406. package/dist/editor/sidebar/Favorites.js.map +1 -1
  407. package/dist/editor/sidebar/MainContentTree.js +4 -3
  408. package/dist/editor/sidebar/MainContentTree.js.map +1 -1
  409. package/dist/editor/sidebar/OperationItem.js +21 -7
  410. package/dist/editor/sidebar/OperationItem.js.map +1 -1
  411. package/dist/editor/sidebar/SidebarPanel.d.ts +3 -1
  412. package/dist/editor/sidebar/SidebarPanel.js +44 -12
  413. package/dist/editor/sidebar/SidebarPanel.js.map +1 -1
  414. package/dist/editor/sidebar/SidebarStack.d.ts +2 -1
  415. package/dist/editor/sidebar/SidebarStack.js +4 -3
  416. package/dist/editor/sidebar/SidebarStack.js.map +1 -1
  417. package/dist/editor/sidebar/Validation.js +22 -12
  418. package/dist/editor/sidebar/Validation.js.map +1 -1
  419. package/dist/editor/sidebar/Workbox.js +53 -3
  420. package/dist/editor/sidebar/Workbox.js.map +1 -1
  421. package/dist/editor/sidebar/WorkspaceRail.d.ts +0 -1
  422. package/dist/editor/sidebar/WorkspaceRail.js +56 -167
  423. package/dist/editor/sidebar/WorkspaceRail.js.map +1 -1
  424. package/dist/editor/tree-indicators/GutterColumns.d.ts +3 -1
  425. package/dist/editor/tree-indicators/GutterColumns.js +26 -5
  426. package/dist/editor/tree-indicators/GutterColumns.js.map +1 -1
  427. package/dist/editor/tree-indicators/GutterContext.d.ts +4 -0
  428. package/dist/editor/tree-indicators/GutterContext.js +23 -0
  429. package/dist/editor/tree-indicators/GutterContext.js.map +1 -1
  430. package/dist/editor/tree-indicators/index.d.ts +0 -1
  431. package/dist/editor/tree-indicators/index.js +0 -1
  432. package/dist/editor/tree-indicators/index.js.map +1 -1
  433. package/dist/editor/tree-indicators/types.d.ts +12 -1
  434. package/dist/editor/ui/CopyMoveTargetSelectorDialog.js +1 -1
  435. package/dist/editor/ui/CopyMoveTargetSelectorDialog.js.map +1 -1
  436. package/dist/editor/ui/Icons.js +1 -1
  437. package/dist/editor/ui/Icons.js.map +1 -1
  438. package/dist/editor/ui/ItemNameDialogNew.d.ts +2 -0
  439. package/dist/editor/ui/ItemNameDialogNew.js +33 -17
  440. package/dist/editor/ui/ItemNameDialogNew.js.map +1 -1
  441. package/dist/editor/ui/ItemSearch.js +7 -11
  442. package/dist/editor/ui/ItemSearch.js.map +1 -1
  443. package/dist/editor/ui/SimpleIconButton.js +1 -1
  444. package/dist/editor/ui/SimpleIconButton.js.map +1 -1
  445. package/dist/editor/ui/SimpleTabs.d.ts +1 -0
  446. package/dist/editor/ui/SimpleTabs.js +45 -25
  447. package/dist/editor/ui/SimpleTabs.js.map +1 -1
  448. package/dist/editor/ui/Splitter.d.ts +1 -0
  449. package/dist/editor/ui/Splitter.js +102 -86
  450. package/dist/editor/ui/Splitter.js.map +1 -1
  451. package/dist/editor/ui/TemplateSelectorDialog.js +4 -4
  452. package/dist/editor/ui/TemplateSelectorDialog.js.map +1 -1
  453. package/dist/editor/ui/TreeListSelector.d.ts +6 -1
  454. package/dist/editor/ui/TreeListSelector.js +2 -2
  455. package/dist/editor/ui/TreeListSelector.js.map +1 -1
  456. package/dist/editor/utils/keyboardNavigation.d.ts +6 -20
  457. package/dist/editor/utils/keyboardNavigation.js +48 -140
  458. package/dist/editor/utils/keyboardNavigation.js.map +1 -1
  459. package/dist/editor/utils.js +19 -9
  460. package/dist/editor/utils.js.map +1 -1
  461. package/dist/editor/views/CompareView.d.ts +3 -1
  462. package/dist/editor/views/CompareView.js +7 -5
  463. package/dist/editor/views/CompareView.js.map +1 -1
  464. package/dist/editor/views/EditView.js +1 -1
  465. package/dist/editor/views/EditView.js.map +1 -1
  466. package/dist/editor/views/EditorSlot.js +27 -34
  467. package/dist/editor/views/EditorSlot.js.map +1 -1
  468. package/dist/editor/views/ItemEditor.js +7 -3
  469. package/dist/editor/views/ItemEditor.js.map +1 -1
  470. package/dist/editor/views/MediaFolderEditView.js +1 -1
  471. package/dist/editor/views/MediaFolderEditView.js.map +1 -1
  472. package/dist/editor/views/ParheliaView.js +5 -6
  473. package/dist/editor/views/ParheliaView.js.map +1 -1
  474. package/dist/editor/views/SingleEditView.d.ts +2 -1
  475. package/dist/editor/views/SingleEditView.js +10 -8
  476. package/dist/editor/views/SingleEditView.js.map +1 -1
  477. package/dist/editor/views/editorSlotContext.js +35 -6
  478. package/dist/editor/views/editorSlotContext.js.map +1 -1
  479. package/dist/index.d.ts +16 -2
  480. package/dist/index.js +11 -0
  481. package/dist/index.js.map +1 -1
  482. package/dist/revision.d.ts +2 -2
  483. package/dist/revision.js +2 -2
  484. package/dist/setup/services/setupWizardService.d.ts +40 -13
  485. package/dist/setup/services/setupWizardService.js +32 -17
  486. package/dist/setup/services/setupWizardService.js.map +1 -1
  487. package/dist/setup/wizard/steps/AddModelDialog.js +12 -3
  488. package/dist/setup/wizard/steps/AddModelDialog.js.map +1 -1
  489. package/dist/setup/wizard/steps/ImportModelDialog.js +39 -22
  490. package/dist/setup/wizard/steps/ImportModelDialog.js.map +1 -1
  491. package/dist/splash-screen/ModernSplashScreen.js +112 -32
  492. package/dist/splash-screen/ModernSplashScreen.js.map +1 -1
  493. package/dist/splash-screen/NewPage.js +33 -50
  494. package/dist/splash-screen/NewPage.js.map +1 -1
  495. package/dist/splash-screen/OpenPage.js +2 -6
  496. package/dist/splash-screen/OpenPage.js.map +1 -1
  497. package/dist/splash-screen/ParheliaAssistantChat.js +12 -29
  498. package/dist/splash-screen/ParheliaAssistantChat.js.map +1 -1
  499. package/dist/splash-screen/ParheliaLogo.js +87 -37
  500. package/dist/splash-screen/ParheliaLogo.js.map +1 -1
  501. package/dist/splash-screen/RecentPages.js +3 -3
  502. package/dist/splash-screen/RecentPages.js.map +1 -1
  503. package/dist/tour/Tour.d.ts +2 -1
  504. package/dist/tour/Tour.js +256 -75
  505. package/dist/tour/Tour.js.map +1 -1
  506. package/dist/tour/default-tour.js +222 -96
  507. package/dist/tour/default-tour.js.map +1 -1
  508. package/dist/types.d.ts +63 -29
  509. package/package.json +19 -15
  510. package/styles.css +14 -10
  511. package/dist/editor/ComponentInfo.d.ts +0 -4
  512. package/dist/editor/ComponentInfo.js +0 -41
  513. package/dist/editor/ComponentInfo.js.map +0 -1
  514. package/dist/editor/ai/HelpTerminal.d.ts +0 -5
  515. package/dist/editor/ai/HelpTerminal.js +0 -166
  516. package/dist/editor/ai/HelpTerminal.js.map +0 -1
  517. package/dist/editor/field-types/ReactQuill.d.ts +0 -125
  518. package/dist/editor/field-types/ReactQuill.js +0 -385
  519. package/dist/editor/field-types/ReactQuill.js.map +0 -1
  520. package/dist/editor/services-server/graphQL.d.ts +0 -29
  521. package/dist/editor/services-server/graphQL.js +0 -53
  522. package/dist/editor/services-server/graphQL.js.map +0 -1
  523. package/dist/editor/settings/AllAgentsPanel.d.ts +0 -5
  524. package/dist/editor/settings/AllAgentsPanel.js +0 -139
  525. package/dist/editor/settings/AllAgentsPanel.js.map +0 -1
  526. package/dist/editor/settings/LatestFeedback.d.ts +0 -1
  527. package/dist/editor/settings/LatestFeedback.js +0 -136
  528. package/dist/editor/settings/LatestFeedback.js.map +0 -1
  529. package/dist/editor/settings/Setup.d.ts +0 -1
  530. package/dist/editor/settings/Setup.js +0 -211
  531. package/dist/editor/settings/Setup.js.map +0 -1
  532. package/dist/editor/settings/panels/DatabasePanel.d.ts +0 -6
  533. package/dist/editor/settings/panels/DatabasePanel.js +0 -50
  534. package/dist/editor/settings/panels/DatabasePanel.js.map +0 -1
  535. package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.d.ts +0 -2
  536. package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.js +0 -195
  537. package/dist/editor/settings/setup-steps/AiSetupStep/EmbeddingsModelSection.js.map +0 -1
  538. package/dist/editor/settings/setup-steps/AiSetupStep/index.d.ts +0 -2
  539. package/dist/editor/settings/setup-steps/AiSetupStep/index.js +0 -21
  540. package/dist/editor/settings/setup-steps/AiSetupStep/index.js.map +0 -1
  541. package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.d.ts +0 -1
  542. package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.js +0 -233
  543. package/dist/editor/settings/setup-steps/AiSetupStep/provider/ProviderSection.js.map +0 -1
  544. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.d.ts +0 -15
  545. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js +0 -14
  546. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersList.js.map +0 -1
  547. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.d.ts +0 -1
  548. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js +0 -94
  549. package/dist/editor/settings/setup-steps/AiSetupStep/required-containers/RequiredContainersSection.js.map +0 -1
  550. package/dist/editor/settings/setup-steps/AiSetupStep/types.d.ts +0 -1
  551. package/dist/editor/settings/setup-steps/AiSetupStep/types.js +0 -2
  552. package/dist/editor/settings/setup-steps/AiSetupStep/types.js.map +0 -1
  553. package/dist/editor/settings/setup-steps/AiSetupStep/utils.d.ts +0 -5
  554. package/dist/editor/settings/setup-steps/AiSetupStep/utils.js +0 -44
  555. package/dist/editor/settings/setup-steps/AiSetupStep/utils.js.map +0 -1
  556. package/dist/editor/settings/setup-steps/IndexSetupStep.d.ts +0 -2
  557. package/dist/editor/settings/setup-steps/IndexSetupStep.js +0 -36
  558. package/dist/editor/settings/setup-steps/IndexSetupStep.js.map +0 -1
  559. package/dist/editor/settings/setup-steps/SettingsSetupStep.d.ts +0 -2
  560. package/dist/editor/settings/setup-steps/SettingsSetupStep.js +0 -111
  561. package/dist/editor/settings/setup-steps/SettingsSetupStep.js.map +0 -1
  562. package/dist/editor/settings/setup-steps/SetupOverview.d.ts +0 -14
  563. package/dist/editor/settings/setup-steps/SetupOverview.js +0 -38
  564. package/dist/editor/settings/setup-steps/SetupOverview.js.map +0 -1
  565. package/dist/editor/sidebar/Debug.d.ts +0 -1
  566. package/dist/editor/sidebar/Debug.js +0 -70
  567. package/dist/editor/sidebar/Debug.js.map +0 -1
  568. package/dist/editor/sidebar/GraphQL.d.ts +0 -2
  569. package/dist/editor/sidebar/GraphQL.js +0 -234
  570. package/dist/editor/sidebar/GraphQL.js.map +0 -1
  571. package/dist/editor/sidebar/LeftToolbar.d.ts +0 -1
  572. package/dist/editor/sidebar/LeftToolbar.js +0 -12
  573. package/dist/editor/sidebar/LeftToolbar.js.map +0 -1
  574. package/dist/editor/sidebar/NavigationSidebar.d.ts +0 -4
  575. package/dist/editor/sidebar/NavigationSidebar.js +0 -254
  576. package/dist/editor/sidebar/NavigationSidebar.js.map +0 -1
  577. package/dist/editor/tree-indicators/GutterSelector.d.ts +0 -5
  578. package/dist/editor/tree-indicators/GutterSelector.js +0 -91
  579. package/dist/editor/tree-indicators/GutterSelector.js.map +0 -1
@@ -1,15 +1,17 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
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 "next/navigation";
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
- export function EditorShell({ configuration, className, item: loadItemDescriptor, sessionId, userInfo, userPreferences, parheliaSettings, setUserPreferences, children, }) {
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 view/workspace=reviews, we should
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") ?? searchParams.get("view"), [searchParams]);
162
+ const rawWorkspace = useMemo(() => searchParams.get("workspace"), [searchParams]);
132
163
  const isReviewsSidebarRequest = rawWorkspace === "reviews";
133
164
  const [workspaceId, setWorkspaceId] = useState(
134
- // If view=reviews, use "editor" workspace (reviews is a sidebar, not a workspace)
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 view/workspace=reviews was requested, ensure reviews sidebar is open
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 stored = window.localStorage.getItem("editor.lockedSidebars");
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 stored = window.localStorage.getItem("editor.sidebarStacks");
256
- if (!stored) {
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
- window.localStorage.setItem("editor.sidebarStacks", JSON.stringify(sidebarStacks));
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
- window.localStorage.setItem("editor.lockedSidebars", JSON.stringify(lockedSidebars));
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
- const savedShowComments = typeof window !== "undefined"
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
- if (typeof window !== "undefined") {
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
- const saved = typeof window !== "undefined"
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
- if (typeof window !== "undefined") {
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 (view + item combinations)
443
+ // Navigation history for browser history (workspace + item combinations)
412
444
  const [navigationHistory, setNavigationHistory] = useState(() => {
413
- // Initialize from browse history with current view
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 [showComponentNavigator, setShowComponentNavigator] = useState(userPreferences.showComponentNavigator ?? false);
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 [showAgentsWorkspaceEditor, setShowAgentsWorkspaceEditor] = useState(true);
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") ?? searchParams.get("view");
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
- // Legacy alias for backwards compatibility
713
- const currentView = currentWorkspace;
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(null, "", newUrl);
838
+ window.history.replaceState(getCurrentHistoryState(), "", newUrl);
749
839
  }, [setIsTourActive]);
750
- const isMobile = useMediaQuery("(max-width: 768px)");
751
- const handleSetShowComponentNavigator = useCallback((value) => {
752
- const newValue = typeof value === "function" ? value(showComponentNavigator) : value;
753
- setShowComponentNavigator(newValue);
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
- showComponentNavigator,
762
- setShowComponentNavigator,
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
- setShowComponentNavigator(false);
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
- setShowComponentNavigator,
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 interval = setInterval(() => {
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
- }, 5 * 60 * 1000);
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
- // Handle workspace changes (with legacy view fallback)
919
- const urlWorkspace = urlParams.get("workspace") ?? urlParams.get("view");
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 = localStorage.getItem(key);
1220
+ const tourShown = localStorageService.getString(key);
1007
1221
  if (!tourShown) {
1008
1222
  startTour();
1009
- localStorage.setItem(key, "true");
1223
+ localStorageService.setString(key, "true");
1010
1224
  }
1011
- }, [user]);
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 result = await getComments(currentItemDescriptor.id, currentItemDescriptor.language, currentItemDescriptor.version);
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
- setSuggestedEdits(result.data || []);
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
- for (const [id, priorOp] of prevById) {
1384
- if (!nextById.has(id) && matchesHistoryScope(priorOp, scope)) {
1385
- merged.push(priorOp);
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
- for (const [id, priorOp] of prevById) {
1474
- if (!nextById.has(id) && matchesHistoryScope(priorOp, scope)) {
1475
- merged.push(priorOp);
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
- // Only preserve ccpanel parameter when on settings view, remove it otherwise
1707
- if (viewName === "settings") {
1708
- const ccpanel = current.get("ccpanel");
1709
- if (ccpanel) {
1710
- current.set("ccpanel", ccpanel);
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
- window.history.pushState(null, "", newUrl);
1734
- // Update lastUrlRef so the popstate handler knows the current URL
1735
- // This fixes the issue where goBack() wouldn't work because lastUrl was stale
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. When an undo
2144
- // completes, the server sends canUndo: false and canRedo: true (or undone: true).
2145
- // We must respect that transition so the UI reflects the undo.
2146
- const isUndoUpdate = op.undone === true || op.canRedo === true;
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(null, "", newUrl);
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
- // Update URL
2338
- updateUrl({ workspace: targetWorkspaceId });
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
- }, [updateUrl, getPreservedSidebarIds, normalizeSidebarStacks]);
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
- // Keep sidebars that are locked OR in a stack with a locked sidebar
2430
- const preservedSet = getPreservedSidebarIds();
2431
- const newSidebars = [
2432
- ...currentOpenSidebars.filter((id) => preservedSet.has(id)), // Keep locked stacks
2433
- sidebarId, // Add the new one
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
- }, [updateUrl, getPreservedSidebarIds, normalizeSidebarStacks]);
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 = sidebars.find((s) => s.id === sidebarId);
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
- }, [sidebars]);
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 && navigationHistory.length > 1) {
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
- }, [navigationHistory]);
3267
+ }, [quickSwitcherEntries]);
2779
3268
  const cycleQuickSwitcher = useCallback((direction) => {
2780
3269
  if (!quickSwitcherVisible)
2781
3270
  return;
2782
- const maxItems = Math.min(5, navigationHistory.length);
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, navigationHistory]);
3316
+ }, [quickSwitcherVisible, quickSwitcherEntries]);
2828
3317
  const handleQuickSwitcherSelect = useCallback((index) => {
2829
- const selectedEntry = navigationHistory[index];
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
- navigationHistory,
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
- operations,
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 (or Cmd on Mac) is pressed
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
- // Switch to the editor view
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, switchView, showInfoToast, showErrorToast]);
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
- switchView("home", {
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 - filter by workspace settings if available
3529
- availableSidebars: (configuration.editor.sidebars ?? []).filter((s) => {
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 (!currentView)
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, showComponentNavigator: showComponentNavigator, handleSetShowComponentNavigator: handleSetShowComponentNavigator, showAgentsPanel: showAgentsPanel, handleSetShowAgentsPanel: handleSetShowAgentsPanel, workspaceId: workspaceId,
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(null, "", newUrl);
4123
- }, configuration: configuration, restoredFromUrl: tourRestoredRef.current })), _jsx(GuidanceOverlay, {}), _jsx(AgentDialogHandler, { sendWebSocketMessage: (type, payload) => sendSocketMessage({ type, payload }) })] }));
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, {}) })), children || editorUi, dialog, _jsx(Toaster, { position: "top-center" }), " ", _jsx(ConfirmationDialog, { ref: confirmationDialogRef }), _jsx(QuickItemSwitcher, { visible: quickSwitcherVisible, entries: navigationHistory.slice(0, 5), selectedIndex: quickSwitcherSelectedIndex, onSelect: handleQuickSwitcherSelect, onClose: () => setQuickSwitcherVisible(false) }), _jsx(EditContextMenu, { ref: contextMenuRef }), _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 })] }) }) }) }));
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