@parhelia/core 0.1.12880 → 0.1.12881

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 (291) hide show
  1. package/dist/components/ui/card.d.ts +3 -1
  2. package/dist/components/ui/card.js +2 -2
  3. package/dist/components/ui/card.js.map +1 -1
  4. package/dist/components/ui/context-menu.js +2 -2
  5. package/dist/config/config.js +6 -5
  6. package/dist/config/config.js.map +1 -1
  7. package/dist/config/types.d.ts +0 -7
  8. package/dist/config/types.js.map +1 -1
  9. package/dist/editor/FieldActionsOverlay.d.ts +0 -1
  10. package/dist/editor/FieldActionsOverlay.js +1 -45
  11. package/dist/editor/FieldActionsOverlay.js.map +1 -1
  12. package/dist/editor/FieldListField.d.ts +1 -1
  13. package/dist/editor/FieldListField.js +20 -18
  14. package/dist/editor/FieldListField.js.map +1 -1
  15. package/dist/editor/ImageEditor.d.ts +6 -1
  16. package/dist/editor/ImageEditor.js +19 -3
  17. package/dist/editor/ImageEditor.js.map +1 -1
  18. package/dist/editor/PictureEditor.d.ts +2 -1
  19. package/dist/editor/PictureEditor.js +5 -14
  20. package/dist/editor/PictureEditor.js.map +1 -1
  21. package/dist/editor/ai/Agents.js +2 -2
  22. package/dist/editor/ai/Agents.js.map +1 -1
  23. package/dist/editor/ai/GuidanceOverlay.js +1 -11
  24. package/dist/editor/ai/GuidanceOverlay.js.map +1 -1
  25. package/dist/editor/ai/InlineAiDialog.js +22 -11
  26. package/dist/editor/ai/InlineAiDialog.js.map +1 -1
  27. package/dist/editor/ai/InlineAiTrigger.js +17 -57
  28. package/dist/editor/ai/InlineAiTrigger.js.map +1 -1
  29. package/dist/editor/ai/dialogs/capturePageDom.js +66 -36
  30. package/dist/editor/ai/dialogs/capturePageDom.js.map +1 -1
  31. package/dist/editor/ai/dialogs/capturePageScreenshot.js +281 -162
  32. package/dist/editor/ai/dialogs/capturePageScreenshot.js.map +1 -1
  33. package/dist/editor/ai/terminal/agentSessionState.d.ts +3 -0
  34. package/dist/editor/ai/terminal/agentSessionState.js +3 -1
  35. package/dist/editor/ai/terminal/agentSessionState.js.map +1 -1
  36. package/dist/editor/ai/terminal/agentStartRequest.d.ts +2 -1
  37. package/dist/editor/ai/terminal/agentStartRequest.js +2 -1
  38. package/dist/editor/ai/terminal/agentStartRequest.js.map +1 -1
  39. package/dist/editor/ai/terminal/components/AgentCostDisplay.js +1 -1
  40. package/dist/editor/ai/terminal/components/AgentCostDisplay.js.map +1 -1
  41. package/dist/editor/ai/terminal/components/AgentDocumentList.d.ts +7 -0
  42. package/dist/editor/ai/terminal/components/AgentDocumentList.js +55 -13
  43. package/dist/editor/ai/terminal/components/AgentDocumentList.js.map +1 -1
  44. package/dist/editor/ai/terminal/components/AgentEditHistoryButton.d.ts +5 -0
  45. package/dist/editor/ai/terminal/components/AgentEditHistoryButton.js +12 -0
  46. package/dist/editor/ai/terminal/components/AgentEditHistoryButton.js.map +1 -0
  47. package/dist/editor/ai/terminal/components/AgentFullPromptControls.d.ts +3 -1
  48. package/dist/editor/ai/terminal/components/AgentFullPromptControls.js +22 -14
  49. package/dist/editor/ai/terminal/components/AgentFullPromptControls.js.map +1 -1
  50. package/dist/editor/ai/terminal/components/AgentModeSelector.js +4 -4
  51. package/dist/editor/ai/terminal/components/AgentModeSelector.js.map +1 -1
  52. package/dist/editor/ai/terminal/components/AgentPromptActionButtons.js +4 -4
  53. package/dist/editor/ai/terminal/components/AgentPromptActionButtons.js.map +1 -1
  54. package/dist/editor/ai/terminal/components/AgentPromptComposer.js +1 -1
  55. package/dist/editor/ai/terminal/components/AgentPromptComposer.js.map +1 -1
  56. package/dist/editor/ai/terminal/components/AgentPromptInputArea.d.ts +2 -1
  57. package/dist/editor/ai/terminal/components/AgentPromptInputArea.js +8 -11
  58. package/dist/editor/ai/terminal/components/AgentPromptInputArea.js.map +1 -1
  59. package/dist/editor/ai/terminal/components/AgentPromptTrayPopovers.d.ts +1 -4
  60. package/dist/editor/ai/terminal/components/AgentPromptTrayPopovers.js +31 -14
  61. package/dist/editor/ai/terminal/components/AgentPromptTrayPopovers.js.map +1 -1
  62. package/dist/editor/ai/terminal/components/AgentSettingsPopover.js +1 -1
  63. package/dist/editor/ai/terminal/components/AgentSettingsPopover.js.map +1 -1
  64. package/dist/editor/ai/terminal/components/AgentTerminalFullLayout.d.ts +2 -1
  65. package/dist/editor/ai/terminal/components/AgentTerminalFullLayout.js +2 -4
  66. package/dist/editor/ai/terminal/components/AgentTerminalFullLayout.js.map +1 -1
  67. package/dist/editor/ai/terminal/components/AgentTerminalMessageGroups.js +1 -1
  68. package/dist/editor/ai/terminal/components/AgentTerminalMessageGroups.js.map +1 -1
  69. package/dist/editor/ai/terminal/components/AgentTerminalView.js +13 -2
  70. package/dist/editor/ai/terminal/components/AgentTerminalView.js.map +1 -1
  71. package/dist/editor/ai/terminal/components/AiResponseMessage.js +16 -14
  72. package/dist/editor/ai/terminal/components/AiResponseMessage.js.map +1 -1
  73. package/dist/editor/ai/terminal/components/ContextInfoBar.js +22 -2
  74. package/dist/editor/ai/terminal/components/ContextInfoBar.js.map +1 -1
  75. package/dist/editor/ai/terminal/components/QueuedPromptsPanel.js +37 -26
  76. package/dist/editor/ai/terminal/components/QueuedPromptsPanel.js.map +1 -1
  77. package/dist/editor/ai/terminal/components/UserMessage.d.ts +2 -1
  78. package/dist/editor/ai/terminal/components/UserMessage.js +144 -8
  79. package/dist/editor/ai/terminal/components/UserMessage.js.map +1 -1
  80. package/dist/editor/ai/terminal/useAgentPromptComposerHandlers.js +1 -1
  81. package/dist/editor/ai/terminal/useAgentPromptComposerHandlers.js.map +1 -1
  82. package/dist/editor/ai/terminal/useAgentSessionSync.d.ts +1 -0
  83. package/dist/editor/ai/terminal/useAgentSubmitHandlers.d.ts +3 -1
  84. package/dist/editor/ai/terminal/useAgentSubmitHandlers.js +9 -3
  85. package/dist/editor/ai/terminal/useAgentSubmitHandlers.js.map +1 -1
  86. package/dist/editor/ai/terminal/useAgentTerminalController.js +7 -0
  87. package/dist/editor/ai/terminal/useAgentTerminalController.js.map +1 -1
  88. package/dist/editor/ai/terminal/useAgentTerminalUiState.js +1 -1
  89. package/dist/editor/ai/terminal/useAgentTerminalUiState.js.map +1 -1
  90. package/dist/editor/ai/terminal/useAgentUserMessageSocketHandler.js +3 -1
  91. package/dist/editor/ai/terminal/useAgentUserMessageSocketHandler.js.map +1 -1
  92. package/dist/editor/ai/useInlineAiPosition.d.ts +1 -1
  93. package/dist/editor/ai/useInlineAiPosition.js +22 -52
  94. package/dist/editor/ai/useInlineAiPosition.js.map +1 -1
  95. package/dist/editor/ai-image-editor/AiImageResultOverlay.js +30 -62
  96. package/dist/editor/ai-image-editor/AiImageResultOverlay.js.map +1 -1
  97. package/dist/editor/bridge/BridgeClient.d.ts +80 -0
  98. package/dist/editor/bridge/BridgeClient.js +417 -0
  99. package/dist/editor/bridge/BridgeClient.js.map +1 -0
  100. package/dist/editor/client/EditorShell.d.ts +5 -1
  101. package/dist/editor/client/EditorShell.js +282 -126
  102. package/dist/editor/client/EditorShell.js.map +1 -1
  103. package/dist/editor/client/editContext.d.ts +33 -5
  104. package/dist/editor/client/editContext.js.map +1 -1
  105. package/dist/editor/client/hooks/useSocketMessageHandler.js +14 -17
  106. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  107. package/dist/editor/client/itemsRepository.d.ts +2 -0
  108. package/dist/editor/client/itemsRepository.js +15 -8
  109. package/dist/editor/client/itemsRepository.js.map +1 -1
  110. package/dist/editor/client/operations.d.ts +1 -1
  111. package/dist/editor/client/operations.js +41 -17
  112. package/dist/editor/client/operations.js.map +1 -1
  113. package/dist/editor/client/pageModelBuilder.js +24 -7
  114. package/dist/editor/client/pageModelBuilder.js.map +1 -1
  115. package/dist/editor/field-types/MultiLineText.js +10 -3
  116. package/dist/editor/field-types/MultiLineText.js.map +1 -1
  117. package/dist/editor/field-types/RawEditor.js +8 -1
  118. package/dist/editor/field-types/RawEditor.js.map +1 -1
  119. package/dist/editor/field-types/RichTextEditorComponent.js +156 -45
  120. package/dist/editor/field-types/RichTextEditorComponent.js.map +1 -1
  121. package/dist/editor/field-types/SingleLineText.js +10 -3
  122. package/dist/editor/field-types/SingleLineText.js.map +1 -1
  123. package/dist/editor/field-types/richtext/components/ReactSlate.js +8 -2
  124. package/dist/editor/field-types/richtext/components/ReactSlate.js.map +1 -1
  125. package/dist/editor/field-types/richtext/contextMenuFactory.d.ts +1 -2
  126. package/dist/editor/field-types/richtext/contextMenuFactory.js +100 -303
  127. package/dist/editor/field-types/richtext/contextMenuFactory.js.map +1 -1
  128. package/dist/editor/field-types/richtext/types.d.ts +2 -0
  129. package/dist/editor/field-types/richtext/types.js.map +1 -1
  130. package/dist/editor/field-types/useFormFieldCaretPresence.d.ts +13 -0
  131. package/dist/editor/field-types/useFormFieldCaretPresence.js +92 -0
  132. package/dist/editor/field-types/useFormFieldCaretPresence.js.map +1 -0
  133. package/dist/editor/media-selector/TreeSelector.js +15 -15
  134. package/dist/editor/media-selector/TreeSelector.js.map +1 -1
  135. package/dist/editor/menubar/PageSelector.js +8 -2
  136. package/dist/editor/menubar/PageSelector.js.map +1 -1
  137. package/dist/editor/menubar/VersionPreviewCard.js +4 -249
  138. package/dist/editor/menubar/VersionPreviewCard.js.map +1 -1
  139. package/dist/editor/menubar/toolbar-sections/EditControls.js +2 -2
  140. package/dist/editor/menubar/toolbar-sections/EditControls.js.map +1 -1
  141. package/dist/editor/menubar/toolbar-sections/ManualBrowser.d.ts +0 -10
  142. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js +63 -462
  143. package/dist/editor/menubar/toolbar-sections/ManualBrowser.js.map +1 -1
  144. package/dist/editor/menubar/toolbar-sections/ViewportControls.js +1 -1
  145. package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.d.ts +6 -0
  146. package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js +123 -0
  147. package/dist/editor/page-editor-chrome/BridgeInlineFormatOverlay.js.map +1 -0
  148. package/dist/editor/page-editor-chrome/CommentHighlightings.d.ts +5 -2
  149. package/dist/editor/page-editor-chrome/CommentHighlightings.js +340 -215
  150. package/dist/editor/page-editor-chrome/CommentHighlightings.js.map +1 -1
  151. package/dist/editor/page-editor-chrome/FeedbackHighlightBadge.d.ts +5 -1
  152. package/dist/editor/page-editor-chrome/FeedbackHighlightBadge.js +11 -4
  153. package/dist/editor/page-editor-chrome/FeedbackHighlightBadge.js.map +1 -1
  154. package/dist/editor/page-editor-chrome/FieldActionIndicator.js +21 -13
  155. package/dist/editor/page-editor-chrome/FieldActionIndicator.js.map +1 -1
  156. package/dist/editor/page-editor-chrome/FieldEditedIndicator.js +23 -29
  157. package/dist/editor/page-editor-chrome/FieldEditedIndicator.js.map +1 -1
  158. package/dist/editor/page-editor-chrome/FrameMenu.js +110 -19
  159. package/dist/editor/page-editor-chrome/FrameMenu.js.map +1 -1
  160. package/dist/editor/page-editor-chrome/LockedFieldIndicator.d.ts +3 -2
  161. package/dist/editor/page-editor-chrome/LockedFieldIndicator.js +148 -45
  162. package/dist/editor/page-editor-chrome/LockedFieldIndicator.js.map +1 -1
  163. package/dist/editor/page-editor-chrome/PageEditorChrome.d.ts +2 -0
  164. package/dist/editor/page-editor-chrome/PageEditorChrome.js +25 -21
  165. package/dist/editor/page-editor-chrome/PageEditorChrome.js.map +1 -1
  166. package/dist/editor/page-editor-chrome/PictureEditorOverlay.js +163 -128
  167. package/dist/editor/page-editor-chrome/PictureEditorOverlay.js.map +1 -1
  168. package/dist/editor/page-editor-chrome/PlaceholderDropZone.d.ts +1 -1
  169. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +6 -3
  170. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
  171. package/dist/editor/page-editor-chrome/PlaceholderDropZones.d.ts +1 -2
  172. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js +83 -146
  173. package/dist/editor/page-editor-chrome/PlaceholderDropZones.js.map +1 -1
  174. package/dist/editor/page-editor-chrome/SuggestionHighlightings.d.ts +5 -2
  175. package/dist/editor/page-editor-chrome/SuggestionHighlightings.js +144 -63
  176. package/dist/editor/page-editor-chrome/SuggestionHighlightings.js.map +1 -1
  177. package/dist/editor/page-editor-chrome/VersionDiffHighlightings.d.ts +1 -2
  178. package/dist/editor/page-editor-chrome/VersionDiffHighlightings.js +101 -30
  179. package/dist/editor/page-editor-chrome/VersionDiffHighlightings.js.map +1 -1
  180. package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.d.ts +10 -1
  181. package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.js +105 -122
  182. package/dist/editor/page-editor-chrome/overlay/IframeOverlayProvider.js.map +1 -1
  183. package/dist/editor/page-editor-chrome/overlay/geometry.d.ts +11 -4
  184. package/dist/editor/page-editor-chrome/overlay/geometry.js +139 -36
  185. package/dist/editor/page-editor-chrome/overlay/geometry.js.map +1 -1
  186. package/dist/editor/page-editor-chrome/useBridgeInlineEditing.d.ts +26 -0
  187. package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js +222 -0
  188. package/dist/editor/page-editor-chrome/useBridgeInlineEditing.js.map +1 -0
  189. package/dist/editor/page-viewer/EditorForm.js +17 -1
  190. package/dist/editor/page-viewer/EditorForm.js.map +1 -1
  191. package/dist/editor/page-viewer/MiniMap.d.ts +2 -2
  192. package/dist/editor/page-viewer/MiniMap.js +176 -364
  193. package/dist/editor/page-viewer/MiniMap.js.map +1 -1
  194. package/dist/editor/page-viewer/PageViewer.js +40 -13
  195. package/dist/editor/page-viewer/PageViewer.js.map +1 -1
  196. package/dist/editor/page-viewer/PageViewerFrame.d.ts +0 -5
  197. package/dist/editor/page-viewer/PageViewerFrame.js +1529 -1511
  198. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  199. package/dist/editor/page-viewer/bridgeFieldPatch.d.ts +20 -0
  200. package/dist/editor/page-viewer/bridgeFieldPatch.js +33 -0
  201. package/dist/editor/page-viewer/bridgeFieldPatch.js.map +1 -0
  202. package/dist/editor/page-viewer/pageViewContext.d.ts +32 -0
  203. package/dist/editor/page-viewer/pageViewContext.js +37 -6
  204. package/dist/editor/page-viewer/pageViewContext.js.map +1 -1
  205. package/dist/editor/reviews/Comment.d.ts +2 -1
  206. package/dist/editor/reviews/Comment.js +9 -4
  207. package/dist/editor/reviews/Comment.js.map +1 -1
  208. package/dist/editor/reviews/CommentEditor.js +1 -1
  209. package/dist/editor/reviews/CommentEditor.js.map +1 -1
  210. package/dist/editor/reviews/CommentPopover.js +68 -9
  211. package/dist/editor/reviews/CommentPopover.js.map +1 -1
  212. package/dist/editor/reviews/CommentView.js +24 -4
  213. package/dist/editor/reviews/CommentView.js.map +1 -1
  214. package/dist/editor/reviews/Comments.d.ts +0 -2
  215. package/dist/editor/reviews/Comments.js +29 -30
  216. package/dist/editor/reviews/Comments.js.map +1 -1
  217. package/dist/editor/reviews/FeedbackCard.d.ts +4 -2
  218. package/dist/editor/reviews/FeedbackCard.js +5 -5
  219. package/dist/editor/reviews/FeedbackCard.js.map +1 -1
  220. package/dist/editor/reviews/SuggestedEdit.js +4 -6
  221. package/dist/editor/reviews/SuggestedEdit.js.map +1 -1
  222. package/dist/editor/reviews/SuggestionDisplayPopover.js +3 -2
  223. package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
  224. package/dist/editor/reviews/commentAi.js +96 -27
  225. package/dist/editor/reviews/commentAi.js.map +1 -1
  226. package/dist/editor/reviews/commentTransientSelection.d.ts +23 -0
  227. package/dist/editor/reviews/commentTransientSelection.js +7 -0
  228. package/dist/editor/reviews/commentTransientSelection.js.map +1 -0
  229. package/dist/editor/reviews/feedbackOrdering.d.ts +5 -0
  230. package/dist/editor/reviews/feedbackOrdering.js +27 -0
  231. package/dist/editor/reviews/feedbackOrdering.js.map +1 -0
  232. package/dist/editor/reviews/feedbackSelection.js +32 -4
  233. package/dist/editor/reviews/feedbackSelection.js.map +1 -1
  234. package/dist/editor/reviews/suggestedEditState.d.ts +12 -0
  235. package/dist/editor/reviews/suggestedEditState.js +90 -0
  236. package/dist/editor/reviews/suggestedEditState.js.map +1 -0
  237. package/dist/editor/reviews/suggestionDisplayValue.d.ts +43 -0
  238. package/dist/editor/reviews/suggestionDisplayValue.js +93 -0
  239. package/dist/editor/reviews/suggestionDisplayValue.js.map +1 -0
  240. package/dist/editor/services/agentService.d.ts +15 -0
  241. package/dist/editor/services/agentService.js +11 -1
  242. package/dist/editor/services/agentService.js.map +1 -1
  243. package/dist/editor/services/reviewsService.d.ts +2 -2
  244. package/dist/editor/services/reviewsService.js.map +1 -1
  245. package/dist/editor/settings/SettingsView.js +2 -2
  246. package/dist/editor/settings/SettingsView.js.map +1 -1
  247. package/dist/editor/settings/panels/ProjectTemplatesPanel.js +1 -1
  248. package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
  249. package/dist/editor/settings/panels/ProvidersPanel.js +2 -3
  250. package/dist/editor/settings/panels/ProvidersPanel.js.map +1 -1
  251. package/dist/editor/sidebar/MorePanelsButton.js +1 -1
  252. package/dist/editor/sidebar/MorePanelsButton.js.map +1 -1
  253. package/dist/editor/sidebar/Workbox.js +1 -1
  254. package/dist/editor/sidebar/Workbox.js.map +1 -1
  255. package/dist/editor/ui/IconSelectorDialog.js +1 -1
  256. package/dist/editor/ui/IconSelectorDialog.js.map +1 -1
  257. package/dist/editor/ui/SimpleIconButton.d.ts +2 -2
  258. package/dist/editor/ui/SimpleIconButton.js +1 -1
  259. package/dist/editor/ui/SimpleIconButton.js.map +1 -1
  260. package/dist/editor/utils.d.ts +1 -17
  261. package/dist/editor/utils.js +0 -143
  262. package/dist/editor/utils.js.map +1 -1
  263. package/dist/editor/version-diff/versionDiffTargets.d.ts +3 -8
  264. package/dist/editor/version-diff/versionDiffTargets.js +37 -94
  265. package/dist/editor/version-diff/versionDiffTargets.js.map +1 -1
  266. package/dist/revision.d.ts +2 -2
  267. package/dist/revision.js +2 -2
  268. package/dist/splash-screen/DialogWrappers.js +2 -2
  269. package/dist/splash-screen/DialogWrappers.js.map +1 -1
  270. package/dist/splash-screen/ModernSplashScreen.js +11 -3
  271. package/dist/splash-screen/ModernSplashScreen.js.map +1 -1
  272. package/dist/splash-screen/NewPage.js +7 -5
  273. package/dist/splash-screen/NewPage.js.map +1 -1
  274. package/dist/splash-screen/OpenPage.js +5 -3
  275. package/dist/splash-screen/OpenPage.js.map +1 -1
  276. package/dist/splash-screen/RecentPages.js +3 -3
  277. package/dist/splash-screen/RecentPages.js.map +1 -1
  278. package/package.json +2 -1
  279. package/styles.css +49 -0
  280. package/dist/editor/page-editor-chrome/InlineEditor.d.ts +0 -7
  281. package/dist/editor/page-editor-chrome/InlineEditor.js +0 -1719
  282. package/dist/editor/page-editor-chrome/InlineEditor.js.map +0 -1
  283. package/dist/editor/page-editor-chrome/overlay/iframeAccess.d.ts +0 -2
  284. package/dist/editor/page-editor-chrome/overlay/iframeAccess.js +0 -21
  285. package/dist/editor/page-editor-chrome/overlay/iframeAccess.js.map +0 -1
  286. package/dist/editor/page-editor-chrome/useInlineAICompletion.d.ts +0 -7
  287. package/dist/editor/page-editor-chrome/useInlineAICompletion.js +0 -758
  288. package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +0 -1
  289. package/dist/editor/page-viewer/pageModelSkeletonBuilder.d.ts +0 -3
  290. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js +0 -796
  291. package/dist/editor/page-viewer/pageModelSkeletonBuilder.js.map +0 -1
@@ -46,6 +46,7 @@ import { QuickItemSwitcher } from "../QuickItemSwitcher";
46
46
  import { useEditorSlotContext, } from "../views/editorSlotContext";
47
47
  import { getComments, getAvailableCommentTags, } from "../services/reviewsService";
48
48
  import { useReviews } from "../reviews/useReviews";
49
+ import { COMMENT_TRANSIENT_ELEMENT_KEY, COMMENT_TRANSIENT_SELECTION_RECT, } from "../reviews/commentTransientSelection";
49
50
  import uuid from "react-uuid";
50
51
  import { flushSync } from "react-dom";
51
52
  import { getSuggestedEdits } from "../services/suggestedEditsService";
@@ -67,6 +68,24 @@ import { FeatureGate, LicenseFeatures, LicenseProvider, LicenseOverlay, } from "
67
68
  // nothing open" (param present with this value) so reload can honor the user's intent.
68
69
  const SIDEBAR_NONE_SENTINEL = "none";
69
70
  const sidebarUrlValue = (ids) => ids.length ? ids.join(",") : SIDEBAR_NONE_SENTINEL;
71
+ // moved to hooks/useQuota
72
+ function isUsableTextSelectionRange(range) {
73
+ return !!(range &&
74
+ range.text?.trim() &&
75
+ typeof range.startOffset === "number" &&
76
+ typeof range.endOffset === "number" &&
77
+ range.startOffset !== range.endOffset &&
78
+ range.fieldId &&
79
+ range.itemId);
80
+ }
81
+ function selectionRangeMatchesField(range, field) {
82
+ if (!range || !field)
83
+ return false;
84
+ return (cleanId(range.fieldId) === cleanId(field.fieldId) &&
85
+ cleanId(range.itemId) === cleanId(field.item.id) &&
86
+ (!range.language || range.language === field.item.language) &&
87
+ (typeof range.version !== "number" || range.version === field.item.version));
88
+ }
70
89
  function reconcileSlotSettingsState(previous, slots, defaults) {
71
90
  const next = {};
72
91
  let changed = false;
@@ -134,8 +153,10 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
134
153
  const [focusedField, setFocusedField] = useState();
135
154
  const focusedFieldRef = useRef(undefined);
136
155
  const [selectedRange, setSelectedRange] = useState();
156
+ const lastTextSelectionRangeRef = useRef(undefined);
137
157
  const [validating, setValidating] = useState(false);
138
158
  const [inserting, setInserting] = useState();
159
+ const insertingSequenceRef = useRef(0);
139
160
  const [showFullscreenHint, setShowFullscreenHint] = useState(false);
140
161
  // Fullscreen is a shell-level UI mode. Keep it in EditorShell state so the shell can rerender
141
162
  // immediately when toggled, while still syncing the active slot's PageViewContext.fullscreen.
@@ -465,7 +486,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
465
486
  // The very first URL sync after initial load should replaceState (not pushState)
466
487
  // so the initial bare URL isn't left as a dead-end history entry.
467
488
  const isFirstUrlSyncRef = useRef(true);
468
- const [inlineEditingFieldElement, setInlineEditingFieldElement] = useState();
469
489
  const [lockedField, setLockedField] = useState();
470
490
  const [itemLanguages, setItemLanguages] = useState([]);
471
491
  const [itemVersions, setItemVersions] = useState([]);
@@ -555,9 +575,67 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
555
575
  const [isTourActive, setIsTourActive] = useState(tourRestoredFromUrl);
556
576
  // Track if tour was restored from URL to skip prepareAction on initial render
557
577
  const tourRestoredRef = useRef(tourRestoredFromUrl);
558
- const [mode, setMode] = useState("edit");
578
+ const [mode, setModeState] = useState("edit");
579
+ const modeRef = useRef("edit");
580
+ modeRef.current = mode;
581
+ const modeChangeParticipantsRef = useRef(new Set());
582
+ const loadSuggestedEditsRef = useRef(async () => { });
583
+ const [modeTransitionRevision, setModeTransitionRevision] = useState(0);
559
584
  const [previewDate, setPreviewDate] = useState();
560
585
  const [layoutMode, setLayoutMode] = useState("final");
586
+ const registerModeChangeParticipant = useCallback((participant) => {
587
+ modeChangeParticipantsRef.current.add(participant);
588
+ return () => {
589
+ modeChangeParticipantsRef.current.delete(participant);
590
+ };
591
+ }, []);
592
+ const changeMode = useCallback(async (nextMode, options) => {
593
+ const previousMode = modeRef.current;
594
+ if (nextMode === previousMode)
595
+ return;
596
+ const participants = Array.from(modeChangeParticipantsRef.current);
597
+ for (const participant of participants) {
598
+ await participant.beforeModeChange?.(nextMode, previousMode, options);
599
+ }
600
+ for (const participant of participants) {
601
+ participant.clearInlineDedupe?.(nextMode, previousMode, options);
602
+ }
603
+ flushSync(() => {
604
+ modeRef.current = nextMode;
605
+ setModeState(nextMode);
606
+ setModeTransitionRevision((revision) => revision + 1);
607
+ if (nextMode !== "preview") {
608
+ setPreviewDate(undefined);
609
+ }
610
+ if (nextMode === "suggestions") {
611
+ setShowSuggestedEdits(true);
612
+ setWorkspaceId("editor");
613
+ setOpenSidebars((prev) => {
614
+ if (prev.includes("feedback"))
615
+ return prev;
616
+ const next = [...prev, "feedback"];
617
+ openSidebarsRef.current = next;
618
+ return next;
619
+ });
620
+ }
621
+ else {
622
+ setShowSuggestedEdits(false);
623
+ }
624
+ });
625
+ if (nextMode === "suggestions") {
626
+ void loadSuggestedEditsRef.current();
627
+ }
628
+ }, []);
629
+ const setMode = useCallback((nextMode) => {
630
+ const resolvedMode = typeof nextMode === "function" ? nextMode(modeRef.current) : nextMode;
631
+ void changeMode(resolvedMode);
632
+ }, [changeMode]);
633
+ const workspaceShowsModeControls = !!configuration.editor.workspaces?.find((workspace) => workspace.id === workspaceId)?.toolbarFactory;
634
+ useEffect(() => {
635
+ if (workspaceShowsModeControls || mode !== "suggestions")
636
+ return;
637
+ void changeMode("edit", { reason: "mode-controls-hidden" });
638
+ }, [changeMode, mode, workspaceId, workspaceShowsModeControls]);
561
639
  useEffect(() => {
562
640
  if (mode !== "preview") {
563
641
  setPreviewDate(undefined);
@@ -663,19 +741,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
663
741
  setSelectedHelpSectionId(helpParam === "contents" || helpParam === "true" ? null : helpParam);
664
742
  }
665
743
  }, [searchParams, isInitialLoad]);
666
- useEffect(() => {
667
- if (mode === "suggestions") {
668
- // Ensure we're in the editor workspace and open the feedback sidebar
669
- setWorkspaceId("editor");
670
- // Open the feedback sidebar if not already open
671
- setOpenSidebars((prev) => {
672
- if (!prev.includes("comments")) {
673
- return [...prev, "comments"];
674
- }
675
- return prev;
676
- });
677
- }
678
- }, [mode]);
679
744
  useEffect(() => {
680
745
  if (focusedField &&
681
746
  selection.length > 0 &&
@@ -685,7 +750,19 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
685
750
  }, [selection]);
686
751
  useEffect(() => {
687
752
  focusedFieldRef.current = focusedField;
753
+ const lastTextSelection = lastTextSelectionRangeRef.current;
754
+ if (focusedField &&
755
+ lastTextSelection &&
756
+ (focusedField.fieldId !== lastTextSelection.fieldId ||
757
+ focusedField.item.id !== lastTextSelection.itemId)) {
758
+ lastTextSelectionRangeRef.current = undefined;
759
+ }
688
760
  }, [focusedField]);
761
+ useEffect(() => {
762
+ if (isUsableTextSelectionRange(selectedRange)) {
763
+ lastTextSelectionRangeRef.current = selectedRange;
764
+ }
765
+ }, [selectedRange]);
689
766
  // Track the current page item descriptor for itemsRepository
690
767
  const [currentPageItem, setCurrentPageItem] = useState();
691
768
  // Create itemsRepository with current page item (will be updated when page loads)
@@ -1216,7 +1293,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
1216
1293
  const toggleHelpTerminal = useCallback((options) => {
1217
1294
  const isCurrentlyOpen = showHelpTerminal;
1218
1295
  const requestedTab = options?.activeTab;
1219
- if (isCurrentlyOpen && (!requestedTab || requestedTab === helpTerminalActiveTab)) {
1296
+ if (isCurrentlyOpen &&
1297
+ (!requestedTab || requestedTab === helpTerminalActiveTab)) {
1220
1298
  // Close when toggling the currently active help view.
1221
1299
  handleSetShowHelpTerminal(false);
1222
1300
  }
@@ -1419,9 +1497,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
1419
1497
  }
1420
1498
  // Handle mode changes — always set to avoid stale-closure mismatch
1421
1499
  const urlMode = urlParams.get("mode");
1422
- if (urlMode) {
1423
- setMode(urlMode);
1424
- }
1500
+ setMode(urlMode ?? "edit");
1425
1501
  // Handle fullscreen changes (shell-level state)
1426
1502
  const isFullscreen = urlParams.get("fullscreen") === "true";
1427
1503
  if (configuration.forceFullscreen) {
@@ -1698,6 +1774,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
1698
1774
  const edits = result.data || [];
1699
1775
  setSuggestedEdits(edits);
1700
1776
  }, [item, searchParams]);
1777
+ useEffect(() => {
1778
+ loadSuggestedEditsRef.current = loadSuggestedEdits;
1779
+ }, [loadSuggestedEdits]);
1701
1780
  const loadFavorites = useCallback(async () => {
1702
1781
  try {
1703
1782
  const result = await getAllFavorites();
@@ -1710,6 +1789,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
1710
1789
  setFavorites([]);
1711
1790
  }
1712
1791
  }, []);
1792
+ const refreshFavorites = useCallback(async (opts) => {
1793
+ await loadFavorites();
1794
+ }, [loadFavorites]);
1713
1795
  const page = activePageViewContext.page;
1714
1796
  const currentHistoryScope = useMemo(() => {
1715
1797
  const currentItem = item?.descriptor;
@@ -2106,6 +2188,9 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
2106
2188
  globalThis.editorRefreshTimer = undefined;
2107
2189
  };
2108
2190
  if (mode === "immediate") {
2191
+ if (refreshTimer) {
2192
+ clearTimeout(refreshTimer);
2193
+ }
2109
2194
  doRefresh();
2110
2195
  return;
2111
2196
  }
@@ -2299,7 +2384,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
2299
2384
  else {
2300
2385
  current.set("compare", "true");
2301
2386
  }
2302
- current.set("mode", mode);
2387
+ current.set("mode", workspaceShowsModeControls ? mode : "edit");
2303
2388
  // Handle fullscreen parameter
2304
2389
  if (fullscreen) {
2305
2390
  current.set("fullscreen", "true");
@@ -3004,9 +3089,35 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
3004
3089
  };
3005
3090
  }, [promptSessionReconnect]);
3006
3091
  const sendSocketMessage = useCallback((message) => {
3092
+ if (message.type === "caret-position" &&
3093
+ message.payload?.source === "form-field") {
3094
+ const payload = message.payload;
3095
+ setRemoteCarets((current) => {
3096
+ if (payload.offset == null) {
3097
+ if (!current[sessionId])
3098
+ return current;
3099
+ const next = { ...current };
3100
+ delete next[sessionId];
3101
+ return next;
3102
+ }
3103
+ if (!payload.fieldId || !payload.item) {
3104
+ return current;
3105
+ }
3106
+ return {
3107
+ ...current,
3108
+ [sessionId]: {
3109
+ sessionId,
3110
+ fieldId: payload.fieldId,
3111
+ item: payload.item,
3112
+ offset: payload.offset,
3113
+ value: payload.value,
3114
+ },
3115
+ };
3116
+ });
3117
+ }
3007
3118
  // Routes over whichever transport is active (WebSocket preferred, SSE fallback).
3008
3119
  getEditorRealtime().send(message);
3009
- }, []);
3120
+ }, [sessionId]);
3010
3121
  // URL update helper - defined early so it can be used by workspace/sidebar functions
3011
3122
  const updateUrl = useCallback((params) => {
3012
3123
  // Use the actual browser pathname (including any basePath like /parhelia)
@@ -3484,34 +3595,51 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
3484
3595
  operationsContext.context.markOperationComplete;
3485
3596
  }, [operationsContext.context.markOperationComplete]);
3486
3597
  // Replace selected text with new content (for inline AI editing)
3487
- const replaceSelectedText = useCallback(async (newText) => {
3488
- if (!focusedField)
3489
- return;
3598
+ const replaceSelectedText = useCallback(async (newText, selectionOverride) => {
3599
+ const rangeToReplace = selectionOverride ?? selectedRange ?? lastTextSelectionRangeRef.current;
3490
3600
  try {
3491
3601
  // If we have selected text and a replaceText callback, use it
3492
- if (selectedRange?.text && selectedRange.replaceText) {
3493
- selectedRange.replaceText(newText);
3602
+ if (rangeToReplace?.text && rangeToReplace.replaceText) {
3603
+ rangeToReplace.replaceText(newText);
3494
3604
  setSelectedRange(undefined);
3605
+ lastTextSelectionRangeRef.current = undefined;
3495
3606
  return;
3496
3607
  }
3497
3608
  // If no selection but we have a replaceText callback, try to use it for insertion
3498
3609
  // (some editors might support insertion via the same callback)
3499
- if (!selectedRange?.text && selectedRange?.replaceText) {
3500
- selectedRange.replaceText(newText);
3610
+ if (!rangeToReplace?.text && rangeToReplace?.replaceText) {
3611
+ rangeToReplace.replaceText(newText);
3501
3612
  setSelectedRange(undefined);
3613
+ lastTextSelectionRangeRef.current = undefined;
3502
3614
  return;
3503
3615
  }
3616
+ const fieldToReplace = focusedField ??
3617
+ (rangeToReplace?.fieldId &&
3618
+ rangeToReplace.itemId &&
3619
+ rangeToReplace.language &&
3620
+ typeof rangeToReplace.version === "number"
3621
+ ? {
3622
+ fieldId: rangeToReplace.fieldId,
3623
+ item: {
3624
+ id: rangeToReplace.itemId,
3625
+ language: rangeToReplace.language,
3626
+ version: rangeToReplace.version,
3627
+ },
3628
+ }
3629
+ : undefined);
3630
+ if (!fieldToReplace)
3631
+ return;
3504
3632
  // Fallback: indexOf-based replacement/insertion for plain text fields
3505
- const { fieldId, item } = focusedField;
3633
+ const { fieldId, item } = fieldToReplace;
3506
3634
  const modifiedField = getActiveSlotContext()?.fieldModificationStore.getField(fieldId, item.id, item.language, item.version);
3507
3635
  const currentValue = modifiedField
3508
3636
  ? modifiedField.value || ""
3509
- : (await itemsRepository.getField(focusedField))?.value ||
3510
- "";
3637
+ : (await itemsRepository.getField(fieldToReplace))
3638
+ ?.value || "";
3511
3639
  let newValue;
3512
- if (selectedRange?.text) {
3640
+ if (rangeToReplace?.text) {
3513
3641
  // Replace selected text
3514
- const { text: originalSelectedText } = selectedRange;
3642
+ const { text: originalSelectedText } = rangeToReplace;
3515
3643
  const actualPosition = currentValue.indexOf(originalSelectedText);
3516
3644
  if (actualPosition === -1) {
3517
3645
  console.error("Failed to find selected text in field value");
@@ -3524,7 +3652,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
3524
3652
  }
3525
3653
  else {
3526
3654
  // Insert at cursor position (use startOffset if available, otherwise append)
3527
- const insertPosition = selectedRange?.startOffset ?? currentValue.length;
3655
+ const insertPosition = rangeToReplace?.startOffset ?? currentValue.length;
3528
3656
  newValue =
3529
3657
  currentValue.slice(0, insertPosition) +
3530
3658
  newText +
@@ -3532,12 +3660,13 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
3532
3660
  }
3533
3661
  // Update the field
3534
3662
  await operations.editField({
3535
- field: focusedField,
3663
+ field: fieldToReplace,
3536
3664
  value: newValue,
3537
3665
  refresh: "immediate",
3538
3666
  });
3539
3667
  // Clear the selection after replacement/insertion
3540
3668
  setSelectedRange(undefined);
3669
+ lastTextSelectionRangeRef.current = undefined;
3541
3670
  }
3542
3671
  catch (error) {
3543
3672
  console.error("Failed to replace/insert text:", error);
@@ -4014,18 +4143,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4014
4143
  };
4015
4144
  }
4016
4145
  }, [operations.onFieldBlur]);
4017
- useEffect(() => {
4018
- if (mode === "suggestions") {
4019
- setShowSuggestedEdits(true);
4020
- // Refresh from the server to recover from any missed
4021
- // `suggested-edit-updated` WebSocket events (e.g. created by an
4022
- // agent tool while another tab was focused).
4023
- loadSuggestedEdits();
4024
- }
4025
- else {
4026
- setShowSuggestedEdits(false);
4027
- }
4028
- }, [mode, loadSuggestedEdits]);
4029
4146
  const isReadOnly = !item ||
4030
4147
  !item.canWriteItem ||
4031
4148
  !item.canWriteLanguage ||
@@ -4044,15 +4161,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4044
4161
  // Otherwise, only show workspaces that are in the settings
4045
4162
  return workspaceIdsFromSettings.includes(w.id) && !w.visible;
4046
4163
  });
4047
- // Handle initial mode setup from URL (only on initial load)
4048
- useEffect(() => {
4049
- if (!isInitialLoad)
4050
- return;
4051
- const queryMode = searchParams.get("mode");
4052
- if (queryMode) {
4053
- setMode(queryMode);
4054
- }
4055
- }, [searchParams, isInitialLoad]);
4056
4164
  // Add this new effect for initial fullscreen setup
4057
4165
  useEffect(() => {
4058
4166
  if (!isInitialLoad)
@@ -4180,16 +4288,11 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4180
4288
  dragEnd: () => {
4181
4289
  setDragObject(undefined);
4182
4290
  },
4183
- droppedInPlaceholder: async (placeholderKey, index, spotPositionElement, spotPositionAnchor, insertOption) => {
4184
- if ((!dragObject && !insertOption) || !page)
4291
+ droppedInPlaceholder: async (placeholderKey, index, spotPositionElement, spotPositionAnchor, insertOption, spotPosition) => {
4292
+ const droppedDragObject = dragObject;
4293
+ if ((!droppedDragObject && !insertOption) || !page)
4185
4294
  return;
4186
4295
  setDragObject(undefined);
4187
- if (spotPositionElement && spotPositionAnchor) {
4188
- setInserting({
4189
- positionElement: spotPositionElement,
4190
- positionAnchor: spotPositionAnchor,
4191
- });
4192
- }
4193
4296
  const placeholderKeyComponents = placeholderKey.split("_");
4194
4297
  const parentId = placeholderKeyComponents.length === 1
4195
4298
  ? undefined
@@ -4199,39 +4302,31 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4199
4302
  let op;
4200
4303
  if (!currentItemDescriptorRef.current)
4201
4304
  return;
4305
+ let insertionSequence;
4306
+ if ((spotPosition || spotPositionElement) && spotPositionAnchor) {
4307
+ insertionSequence = ++insertingSequenceRef.current;
4308
+ setInserting({
4309
+ position: spotPosition,
4310
+ positionElement: spotPositionElement,
4311
+ positionAnchor: spotPositionAnchor,
4312
+ });
4313
+ }
4314
+ let operationPromise;
4202
4315
  if (insertOption) {
4203
4316
  // Clear any selected component type for insertion once the component
4204
4317
  // has been dropped into a placeholder. This ensures that insert mode
4205
4318
  // can exit cleanly and the editor header updates from "Add Component"
4206
4319
  // to the actual component name after the operation completes.
4207
4320
  setSelectedForInsertion("");
4208
- operations
4209
- .addComponent(insertOption.typeId, placeholderKey, index, currentItemDescriptorRef.current)
4210
- .then((result) => {
4211
- setInserting(undefined);
4212
- })
4213
- .catch((err) => {
4214
- setInserting(undefined);
4215
- });
4216
- return;
4321
+ operationPromise = operations.addComponent(insertOption.typeId, placeholderKey, index, currentItemDescriptorRef.current);
4217
4322
  }
4218
- if (!dragObject)
4219
- return;
4220
- if (dragObject && dragObject.type == "template") {
4221
- operations
4222
- .addComponent(dragObject.typeId, placeholderKey, index, currentItemDescriptorRef.current)
4223
- .then((result) => {
4224
- setInserting(undefined);
4225
- })
4226
- .catch((err) => {
4227
- setInserting(undefined);
4228
- });
4229
- return;
4323
+ else if (droppedDragObject?.type == "template") {
4324
+ operationPromise = operations.addComponent(droppedDragObject.typeId, placeholderKey, index, currentItemDescriptorRef.current);
4230
4325
  }
4231
- if (dragObject.type == "component" ||
4232
- (dragObject.type == "items" && dragObject.items)) {
4326
+ else if (droppedDragObject?.type == "component" ||
4327
+ (droppedDragObject?.type == "items" && droppedDragObject.items)) {
4233
4328
  const parentComponent = parentId && page ? getComponentById(parentId, page) : null;
4234
- if (dragObject.type == "items" && dragObject.items) {
4329
+ if (droppedDragObject.type == "items" && droppedDragObject.items) {
4235
4330
  op = {
4236
4331
  type: "link-component",
4237
4332
  mainItem: page.item.descriptor,
@@ -4245,33 +4340,52 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4245
4340
  placeholderIndex: index,
4246
4341
  date: new Date().toISOString(),
4247
4342
  id: uuid(),
4248
- linkedComponentItem: dragObject.items[0],
4343
+ linkedComponentItem: droppedDragObject.items[0],
4249
4344
  title: "Link component",
4250
4345
  };
4251
4346
  }
4252
4347
  else {
4253
- const componentIds = dragObject.components?.map((c) => c.id) ?? [];
4254
- if (componentIds.length === 0)
4255
- return;
4256
- op = {
4257
- type: "move-component",
4258
- mainItem: page.item.descriptor,
4259
- parent: parentId && {
4260
- id: parentId,
4261
- language: page.item.descriptor.language,
4262
- version: page.item.descriptor.version,
4263
- },
4264
- placeholderKey,
4265
- placeholderIndex: index,
4266
- componentIds,
4267
- date: new Date().toISOString(),
4268
- id: uuid(),
4269
- title: "Move component",
4270
- };
4348
+ const componentIds = droppedDragObject.components?.map((c) => c.id) ?? [];
4349
+ if (componentIds.length > 0) {
4350
+ op = {
4351
+ type: "move-component",
4352
+ mainItem: page.item.descriptor,
4353
+ parent: parentId && {
4354
+ id: parentId,
4355
+ language: page.item.descriptor.language,
4356
+ version: page.item.descriptor.version,
4357
+ },
4358
+ placeholderKey,
4359
+ placeholderIndex: index,
4360
+ componentIds,
4361
+ date: new Date().toISOString(),
4362
+ id: uuid(),
4363
+ title: "Move component",
4364
+ };
4365
+ }
4271
4366
  }
4272
4367
  }
4273
4368
  if (op)
4274
- operations.executeEditOperation(op);
4369
+ operationPromise = operations.executeEditOperation(op);
4370
+ if (!operationPromise) {
4371
+ if (insertionSequence !== undefined &&
4372
+ insertingSequenceRef.current === insertionSequence) {
4373
+ setInserting(undefined);
4374
+ }
4375
+ return;
4376
+ }
4377
+ try {
4378
+ await operationPromise;
4379
+ }
4380
+ catch (err) {
4381
+ console.error("Failed to drop component in placeholder", err);
4382
+ }
4383
+ finally {
4384
+ if (insertionSequence !== undefined &&
4385
+ insertingSequenceRef.current === insertionSequence) {
4386
+ setInserting(undefined);
4387
+ }
4388
+ }
4275
4389
  },
4276
4390
  page,
4277
4391
  triggerFieldAction: async (fieldDescriptor, actionButton, parameters, event) => {
@@ -4550,27 +4664,50 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4550
4664
  setShowComments,
4551
4665
  showResolvedComments,
4552
4666
  setShowResolvedComments,
4553
- addComment: async () => {
4667
+ addComment: async (options) => {
4554
4668
  const descriptor = item?.descriptor;
4555
4669
  if (!descriptor)
4556
4670
  return;
4557
4671
  // Snapshot selection context up-front so switching views doesn't change what we comment on.
4672
+ const hintedFieldSnapshot = options?.field;
4558
4673
  const focusedFieldSnapshot = focusedFieldRef.current;
4559
- const selectionSnapshot = [...selection];
4560
- const selectedRangeSnapshot = selectedRange;
4561
- const itemId = focusedFieldSnapshot?.item.id ||
4562
- (selectionSnapshot.length > 0 ? selectionSnapshot[0] : undefined) ||
4674
+ const selectedRangeCandidate = isUsableTextSelectionRange(selectedRange)
4675
+ ? selectedRange
4676
+ : lastTextSelectionRangeRef.current;
4677
+ const selectedRangeSnapshot = !hintedFieldSnapshot ||
4678
+ selectionRangeMatchesField(selectedRangeCandidate, hintedFieldSnapshot)
4679
+ ? selectedRangeCandidate
4680
+ : undefined;
4681
+ const selectedRangeFieldSnapshot = isUsableTextSelectionRange(selectedRangeSnapshot)
4682
+ ? {
4683
+ fieldId: selectedRangeSnapshot.fieldId,
4684
+ item: {
4685
+ id: selectedRangeSnapshot.itemId,
4686
+ language: selectedRangeSnapshot.language || descriptor.language,
4687
+ version: typeof selectedRangeSnapshot.version === "number"
4688
+ ? selectedRangeSnapshot.version
4689
+ : descriptor.version,
4690
+ },
4691
+ }
4692
+ : undefined;
4693
+ const commentFieldSnapshot = selectedRangeFieldSnapshot ??
4694
+ hintedFieldSnapshot ??
4695
+ focusedFieldSnapshot;
4696
+ const itemId = commentFieldSnapshot?.item.id ||
4697
+ (selection.length > 0 ? selection[0] : undefined) ||
4563
4698
  descriptor.id;
4564
4699
  if (!itemId)
4565
4700
  return;
4566
4701
  // Ensure the feedback sidebar is open when adding a comment (e.g. command palette / toolbar button).
4567
4702
  openSidebar("feedback");
4568
- const language = descriptor.language;
4569
- const version = descriptor.version;
4703
+ const language = commentFieldSnapshot?.item.language || descriptor.language;
4704
+ const version = typeof commentFieldSnapshot?.item.version === "number"
4705
+ ? commentFieldSnapshot.item.version
4706
+ : descriptor.version;
4570
4707
  const getFieldName = async () => {
4571
- if (!focusedFieldSnapshot)
4708
+ if (!commentFieldSnapshot)
4572
4709
  return "";
4573
- const field = await itemsRepository.getField(focusedFieldSnapshot);
4710
+ const field = await itemsRepository.getField(commentFieldSnapshot);
4574
4711
  return field?.name;
4575
4712
  };
4576
4713
  const getItemName = async () => {
@@ -4586,24 +4723,40 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4586
4723
  isNew: true,
4587
4724
  itemId,
4588
4725
  itemName: await getItemName(),
4589
- fieldId: focusedFieldSnapshot?.fieldId,
4726
+ fieldId: commentFieldSnapshot?.fieldId,
4590
4727
  fieldName: await getFieldName(),
4591
4728
  mainItemId: descriptor.id,
4592
4729
  language,
4593
4730
  version,
4594
4731
  position: 0,
4595
- rangeStart: selectedRangeSnapshot?.startOffset || 0,
4596
- rangeEnd: selectedRangeSnapshot?.endOffset || 0,
4732
+ rangeStart: isUsableTextSelectionRange(selectedRangeSnapshot)
4733
+ ? selectedRangeSnapshot.startOffset
4734
+ : 0,
4735
+ rangeEnd: isUsableTextSelectionRange(selectedRangeSnapshot)
4736
+ ? selectedRangeSnapshot.endOffset
4737
+ : 0,
4597
4738
  author: user?.name,
4598
4739
  authorDisplayName: user?.fullName,
4599
- date: new Date().toISOString(),
4740
+ created: new Date().toISOString(),
4600
4741
  };
4742
+ if (selectedRangeSnapshot?.clientRect) {
4743
+ newComment[COMMENT_TRANSIENT_SELECTION_RECT] =
4744
+ selectedRangeSnapshot.clientRect;
4745
+ }
4746
+ if (selectedRangeSnapshot?.elementKey) {
4747
+ newComment[COMMENT_TRANSIENT_ELEMENT_KEY] =
4748
+ selectedRangeSnapshot.elementKey;
4749
+ }
4601
4750
  setComments([newComment, ...comments]);
4602
4751
  setSelectedComment(newComment);
4603
4752
  setShowComments(true);
4753
+ lastTextSelectionRangeRef.current = undefined;
4604
4754
  },
4605
4755
  mode,
4756
+ changeMode,
4606
4757
  setMode,
4758
+ registerModeChangeParticipant,
4759
+ modeTransitionRevision,
4607
4760
  previewDate,
4608
4761
  setPreviewDate,
4609
4762
  layoutMode,
@@ -4671,6 +4824,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4671
4824
  setCurrentWizardId,
4672
4825
  favorites,
4673
4826
  loadFavorites,
4827
+ refreshFavorites,
4674
4828
  isQuickSwitcherVisible: quickSwitcherVisible,
4675
4829
  // Context factory registry methods
4676
4830
  registerContextFactory: (name, factory) => {
@@ -4779,6 +4933,7 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4779
4933
  isRefreshing,
4780
4934
  favorites,
4781
4935
  loadFavorites,
4936
+ refreshFavorites,
4782
4937
  currentWizardId,
4783
4938
  quickSwitcherVisible,
4784
4939
  quickSwitcherSelectedIndex,
@@ -4789,6 +4944,10 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4789
4944
  factoriesRef,
4790
4945
  user,
4791
4946
  statusMessage,
4947
+ changeMode,
4948
+ setMode,
4949
+ registerModeChangeParticipant,
4950
+ modeTransitionRevision,
4792
4951
  previewDate,
4793
4952
  layoutMode,
4794
4953
  enableCompletions,
@@ -4854,8 +5013,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4854
5013
  setLockedField(undefined); // Clear client-side lock state
4855
5014
  await releaseFieldLocks(sessionId);
4856
5015
  },
4857
- inlineEditingFieldElement,
4858
- setInlineEditingFieldElement,
4859
5016
  };
4860
5017
  // Debug: Track editContext changes
4861
5018
  // const prevDependencies = useRef<any[]>([]);
@@ -4894,7 +5051,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4894
5051
  // componentDesignerRendering,
4895
5052
  // insertMode,
4896
5053
  // currentOverlay,
4897
- // inlineEditingFieldElement,
4898
5054
  // lockedField,
4899
5055
  // selectedRange,
4900
5056
  // pageViewContext,
@@ -4982,7 +5138,6 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
4982
5138
  // 'componentDesignerRendering',
4983
5139
  // 'insertMode',
4984
5140
  // 'currentOverlay',
4985
- // 'inlineEditingFieldElement',
4986
5141
  // 'lockedField',
4987
5142
  // 'selectedRange',
4988
5143
  // 'pageViewContext',
@@ -5110,7 +5265,8 @@ export function EditorShell({ configuration, className, item: loadItemDescriptor
5110
5265
  : window.location.pathname;
5111
5266
  window.history.replaceState(getCurrentHistoryState(), "", newUrl);
5112
5267
  }, configuration: configuration, restoredFromUrl: tourRestoredRef.current })), _jsx(FeatureGate, { feature: LicenseFeatures.AI, children: _jsx(GuidanceOverlay, {}) }), _jsx(FeatureGate, { feature: LicenseFeatures.AI, children: _jsx(AgentDialogHandler, {}) })] }));
5113
- 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: _jsx(EditContextProvider, { value: editContext, children: _jsxs(SplashScreenAgentProvider, { children: [_jsx(DevModeIndicator, {}), startupChecks.state === "loading" && _jsx(EditorLoadingOverlay, {}), editContext.isRefreshing && (_jsx("div", { className: "text-neutral-grey-50 pointer-events-none fixed right-0 bottom-0 flex h-24 w-24 items-center justify-center opacity-50 select-none", children: _jsx(Spinner, {}) })), (currentWorkspace.id === "agents" ||
5268
+ const showRefreshSpinner = editContext.isRefreshing && !activePageViewContext.iframeSupportsRefresh;
5269
+ 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: _jsx(EditContextProvider, { value: editContext, children: _jsxs(SplashScreenAgentProvider, { children: [_jsx(DevModeIndicator, {}), startupChecks.state === "loading" && _jsx(EditorLoadingOverlay, {}), showRefreshSpinner && (_jsx("div", { className: "text-neutral-grey-50 pointer-events-none fixed right-0 bottom-0 flex h-24 w-24 items-center justify-center opacity-50 select-none", children: _jsx(Spinner, {}) })), (currentWorkspace.id === "agents" ||
5114
5270
  currentWorkspace.id === "taskboard" ||
5115
5271
  currentWorkspace.id === "translation-management") &&
5116
5272
  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) => {