@puredesktop/puredesktop-ui-bridge 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (412) hide show
  1. package/package.json +96 -0
  2. package/src/agents/runtime/agentLiveReducer.test.ts +72 -0
  3. package/src/agents/runtime/agentLiveReducer.ts +139 -0
  4. package/src/agents/runtime/agentRunEventTypes.ts +115 -0
  5. package/src/agents/runtime/contextWindowUsage.test.ts +38 -0
  6. package/src/agents/runtime/contextWindowUsage.ts +25 -0
  7. package/src/agents/runtime/index.ts +24 -0
  8. package/src/agents/runtime/mapAgentMessages.ts +54 -0
  9. package/src/agents/runtime/mapAgentSnapshot.ts +71 -0
  10. package/src/agents/runtime/mergeUsageTurn.ts +23 -0
  11. package/src/agents/runtime/pendingToolCalls.test.ts +85 -0
  12. package/src/agents/runtime/pendingToolCalls.ts +14 -0
  13. package/src/agents/runtime/toolRequiresApproval.ts +20 -0
  14. package/src/agents/runtime/unresolvedToolCalls.ts +42 -0
  15. package/src/bridge/agentModelTiers.ts +50 -0
  16. package/src/bridge/agentToolHandlers.ts +23 -0
  17. package/src/bridge/agentToolHelpers.test.ts +27 -0
  18. package/src/bridge/agentToolHelpers.ts +20 -0
  19. package/src/bridge/agentTypes.ts +294 -0
  20. package/src/bridge/agents.d.mts +113 -0
  21. package/src/bridge/agents.mjs +233 -0
  22. package/src/bridge/agents.ts +62 -0
  23. package/src/bridge/appSettings.d.mts +9 -0
  24. package/src/bridge/appSettings.mjs +18 -0
  25. package/src/bridge/appSettings.ts +4 -0
  26. package/src/bridge/applyThemeFromHost.d.mts +7 -0
  27. package/src/bridge/applyThemeFromHost.mjs +33 -0
  28. package/src/bridge/assets.d.mts +66 -0
  29. package/src/bridge/assets.mjs +121 -0
  30. package/src/bridge/catalog.d.mts +5 -0
  31. package/src/bridge/catalog.mjs +24 -0
  32. package/src/bridge/catalog.ts +1 -0
  33. package/src/bridge/client.d.mts +19 -0
  34. package/src/bridge/client.mjs +147 -0
  35. package/src/bridge/collectionAssetAdapter.ts +63 -0
  36. package/src/bridge/collectionAssets.d.mts +1 -0
  37. package/src/bridge/collectionAssets.mjs +2 -0
  38. package/src/bridge/collectionAssets.ts +1 -0
  39. package/src/bridge/collectionDocumentHtml.test.ts +71 -0
  40. package/src/bridge/collectionDocumentHtml.ts +118 -0
  41. package/src/bridge/collectionImagePaste.test.ts +32 -0
  42. package/src/bridge/collectionImagePaste.ts +130 -0
  43. package/src/bridge/context.mjs +12 -0
  44. package/src/bridge/credentials.mjs +22 -0
  45. package/src/bridge/dialog.d.mts +5 -0
  46. package/src/bridge/dialog.mjs +13 -0
  47. package/src/bridge/events.d.mts +14 -0
  48. package/src/bridge/events.mjs +12 -0
  49. package/src/bridge/filePreviewKind.test.ts +43 -0
  50. package/src/bridge/filePreviewKind.ts +76 -0
  51. package/src/bridge/fs.d.mts +41 -0
  52. package/src/bridge/fs.mjs +82 -0
  53. package/src/bridge/fs.test.ts +16 -0
  54. package/src/bridge/fs.ts +10 -0
  55. package/src/bridge/index.ts +12 -0
  56. package/src/bridge/inlinePreview.d.mts +35 -0
  57. package/src/bridge/inlinePreview.mjs +99 -0
  58. package/src/bridge/inlinePreview.test.ts +56 -0
  59. package/src/bridge/inlinePreview.ts +13 -0
  60. package/src/bridge/methods.d.mts +115 -0
  61. package/src/bridge/methods.mjs +132 -0
  62. package/src/bridge/os.d.mts +11 -0
  63. package/src/bridge/os.mjs +26 -0
  64. package/src/bridge/plugins.d.mts +9 -0
  65. package/src/bridge/plugins.mjs +31 -0
  66. package/src/bridge/plugins.ts +5 -0
  67. package/src/bridge/preferences.d.mts +7 -0
  68. package/src/bridge/preferences.mjs +17 -0
  69. package/src/bridge/preferences.ts +13 -0
  70. package/src/bridge/printDocument.test.ts +47 -0
  71. package/src/bridge/printDocument.ts +75 -0
  72. package/src/bridge/pureRender/adapters.test.ts +144 -0
  73. package/src/bridge/pureRender/adapters.ts +175 -0
  74. package/src/bridge/pureRender/assets.test.ts +44 -0
  75. package/src/bridge/pureRender/base.css +76 -0
  76. package/src/bridge/pureRender/compiler.test.ts +279 -0
  77. package/src/bridge/pureRender/compiler.ts +660 -0
  78. package/src/bridge/pureRender/document.test.ts +283 -0
  79. package/src/bridge/pureRender/document.ts +35 -0
  80. package/src/bridge/pureRender/extraction.test.ts +432 -0
  81. package/src/bridge/pureRender/extraction.ts +1023 -0
  82. package/src/bridge/pureRender/flow.css +3 -0
  83. package/src/bridge/pureRender/index.ts +66 -0
  84. package/src/bridge/pureRender/normalize.test.ts +442 -0
  85. package/src/bridge/pureRender/normalize.ts +682 -0
  86. package/src/bridge/pureRender/presets.ts +365 -0
  87. package/src/bridge/pureRender/profiles/book.css +19 -0
  88. package/src/bridge/pureRender/profiles/manuscript.css +11 -0
  89. package/src/bridge/pureRender/profiles/writer.css +10 -0
  90. package/src/bridge/pureRender/reset.css +5 -0
  91. package/src/bridge/pureRender/samples.ts +94 -0
  92. package/src/bridge/pureRender/styles.ts +164 -0
  93. package/src/bridge/pureRender/theme.test.ts +53 -0
  94. package/src/bridge/pureRender/theme.ts +258 -0
  95. package/src/bridge/pureRender/tokens/default.css +36 -0
  96. package/src/bridge/pureRender/tokens/default.json +32 -0
  97. package/src/bridge/pureRender/types.ts +264 -0
  98. package/src/bridge/pureRender/visualLayoutInspection.test.ts +57 -0
  99. package/src/bridge/pureRender/visualLayoutInspection.ts +161 -0
  100. package/src/bridge/react/useDebouncedEffect.ts +21 -0
  101. package/src/bridge/react/useIpc.tsx +75 -0
  102. package/src/bridge/react/usePlatformAgentRunLiveState.tsx +78 -0
  103. package/src/bridge/react/usePlatformAgentSessionDrive.tsx +729 -0
  104. package/src/bridge/react/usePlatformAgentSessions.tsx +60 -0
  105. package/src/bridge/react/usePlatformAgentTools.tsx +82 -0
  106. package/src/bridge/react/usePlatformAppManifest.tsx +32 -0
  107. package/src/bridge/react/usePlatformBridge.tsx +56 -0
  108. package/src/bridge/react/usePlatformDeepLink.tsx +57 -0
  109. package/src/bridge/react/usePlatformFileBinaryDataUrl.tsx +54 -0
  110. package/src/bridge/react/usePlatformFilePreview.tsx +92 -0
  111. package/src/bridge/react/usePlatformPreferences.tsx +77 -0
  112. package/src/bridge/react/usePlatformThemeDocumentAttrs.ts +35 -0
  113. package/src/bridge/react/usePlatformThemeFromBridge.tsx +16 -0
  114. package/src/bridge/react/usePlatformViewportResource.test.tsx +106 -0
  115. package/src/bridge/react/usePlatformViewportResource.tsx +72 -0
  116. package/src/bridge/react/useScheduledTask.tsx +59 -0
  117. package/src/bridge/render.d.mts +31 -0
  118. package/src/bridge/render.mjs +62 -0
  119. package/src/bridge/render.ts +8 -0
  120. package/src/bridge/screenAnnotation.test.ts +103 -0
  121. package/src/bridge/screenAnnotation.ts +269 -0
  122. package/src/bridge/theme.d.mts +3 -0
  123. package/src/bridge/theme.mjs +9 -0
  124. package/src/bridge/types.ts +439 -0
  125. package/src/bridge/watch.d.mts +9 -0
  126. package/src/bridge/watch.mjs +34 -0
  127. package/src/bridge/watch.ts +1 -0
  128. package/src/bridge/window.d.mts +44 -0
  129. package/src/bridge/window.mjs +33 -0
  130. package/src/chrome/index.ts +1 -0
  131. package/src/components/accessibility/AccessibilityReviewDialog.test.tsx +104 -0
  132. package/src/components/accessibility/AccessibilityReviewDialog.tsx +350 -0
  133. package/src/components/accessibility/accessibilityReview.test.ts +101 -0
  134. package/src/components/accessibility/accessibilityReview.ts +639 -0
  135. package/src/components/accessibility/index.ts +16 -0
  136. package/src/components/agent-feedback/ScreenAnnotationAgentDialog.tsx +509 -0
  137. package/src/components/agent-feedback/index.ts +5 -0
  138. package/src/components/agents/AgentComposer.tsx +34 -0
  139. package/src/components/agents/AgentContextUsageBar.tsx +127 -0
  140. package/src/components/agents/AgentDrawerPanel.tsx +82 -0
  141. package/src/components/agents/AgentMarkdown.tsx +12 -0
  142. package/src/components/agents/AgentMessageBubble.tsx +82 -0
  143. package/src/components/agents/AgentMessageList.tsx +47 -0
  144. package/src/components/agents/AgentModelMenu.tsx +55 -0
  145. package/src/components/agents/AgentThinkingBlock.tsx +66 -0
  146. package/src/components/agents/AgentToolApprovalActions.tsx +65 -0
  147. package/src/components/agents/AgentToolCallCard.tsx +26 -0
  148. package/src/components/agents/AgentToolPendingList.tsx +39 -0
  149. package/src/components/agents/AgentToolPendingReview.tsx +118 -0
  150. package/src/components/agents/agentPanelStyles.ts +79 -0
  151. package/src/components/agents/agentTypes.ts +96 -0
  152. package/src/components/agents/formatToolCallArguments.test.ts +16 -0
  153. package/src/components/agents/formatToolCallArguments.ts +9 -0
  154. package/src/components/agents/index.ts +40 -0
  155. package/src/components/assets/AssetLibraryDialog.tsx +5 -0
  156. package/src/components/assets/asset-library/AssetLibraryDialog.tsx +43 -0
  157. package/src/components/assets/asset-library/AssetLibraryWorkspace.tsx +54 -0
  158. package/src/components/assets/asset-library/detail/AssetLibraryDetailActions.tsx +68 -0
  159. package/src/components/assets/asset-library/detail/AssetLibraryDetailHeader.tsx +56 -0
  160. package/src/components/assets/asset-library/detail/AssetLibraryDetailPane.tsx +90 -0
  161. package/src/components/assets/asset-library/detail/AssetLibraryDetailPreview.tsx +67 -0
  162. package/src/components/assets/asset-library/detail/AssetLibraryInlinePreview.tsx +87 -0
  163. package/src/components/assets/asset-library/detail/AssetLibraryInlineSourceField.tsx +29 -0
  164. package/src/components/assets/asset-library/detail/AssetLibraryMetadataFields.tsx +34 -0
  165. package/src/components/assets/asset-library/detail/AssetLibraryUsagePanel.tsx +46 -0
  166. package/src/components/assets/asset-library/detail/AssetLibraryVersionsPanel.tsx +145 -0
  167. package/src/components/assets/asset-library/helpers/assetKind.ts +74 -0
  168. package/src/components/assets/asset-library/helpers.ts +41 -0
  169. package/src/components/assets/asset-library/hooks/useAssetDetailDraft.ts +81 -0
  170. package/src/components/assets/asset-library/hooks/useAssetLibraryDialog.ts +233 -0
  171. package/src/components/assets/asset-library/hooks/useAssetLibraryMutations.ts +266 -0
  172. package/src/components/assets/asset-library/hooks/useAssetLibrarySelectionSync.ts +24 -0
  173. package/src/components/assets/asset-library/hooks/useAssetLibraryVersions.ts +145 -0
  174. package/src/components/assets/asset-library/hooks/useInlineAssetPreview.ts +44 -0
  175. package/src/components/assets/asset-library/index.ts +19 -0
  176. package/src/components/assets/asset-library/layout/assetLibraryLayout.ts +201 -0
  177. package/src/components/assets/asset-library/shared/fieldStyles.ts +62 -0
  178. package/src/components/assets/asset-library/sidebar/AssetLibraryFilterPills.tsx +89 -0
  179. package/src/components/assets/asset-library/sidebar/AssetLibraryList.tsx +91 -0
  180. package/src/components/assets/asset-library/sidebar/AssetLibraryListRow.tsx +99 -0
  181. package/src/components/assets/asset-library/sidebar/AssetLibraryListStatus.tsx +24 -0
  182. package/src/components/assets/asset-library/sidebar/AssetLibrarySidebar.tsx +48 -0
  183. package/src/components/assets/asset-library/sidebar/AssetLibrarySidebarActions.tsx +38 -0
  184. package/src/components/assets/asset-library/sidebar/AssetLibrarySidebarToolbar.tsx +78 -0
  185. package/src/components/assets/asset-library/sidebar/AssetListThumbnail.tsx +86 -0
  186. package/src/components/assets/asset-library/types.ts +128 -0
  187. package/src/components/assets/asset-library/utils/assetLibraryUtils.ts +181 -0
  188. package/src/components/assets/asset-library/utils/pathBasename.ts +5 -0
  189. package/src/components/assets/asset-library/utils/sanitizeUploadFileName.ts +7 -0
  190. package/src/components/assets/asset-library/utils/uploadCollectionAsset.ts +52 -0
  191. package/src/components/chrome/OverlayActionCapsule.test.tsx +82 -0
  192. package/src/components/chrome/OverlayActionCapsule.tsx +90 -0
  193. package/src/components/chrome/PanelTabStrip.tsx +113 -0
  194. package/src/components/chrome/PlatformIcon.tsx +27 -0
  195. package/src/components/chrome/PureAppWordmark.tsx +89 -0
  196. package/src/components/chrome/ShellWindowControls.tsx +51 -0
  197. package/src/components/chrome/ThemeModeButton.tsx +16 -0
  198. package/src/components/chrome/Titlebar.tsx +79 -0
  199. package/src/components/chrome/WorkspaceTabStrip.tsx +300 -0
  200. package/src/components/chrome/index.ts +31 -0
  201. package/src/components/chrome/workspaceTabTypes.ts +29 -0
  202. package/src/components/common/buttons/Button.tsx +241 -0
  203. package/src/components/common/buttons/ChoiceChip.tsx +73 -0
  204. package/src/components/common/buttons/CleanButton.tsx +34 -0
  205. package/src/components/common/buttons/IconButton.tsx +89 -0
  206. package/src/components/common/buttons/QuietAction.tsx +29 -0
  207. package/src/components/common/buttons/SegmentedControl.tsx +67 -0
  208. package/src/components/common/buttons/TextActionButton.tsx +55 -0
  209. package/src/components/common/buttons/TextLink.tsx +38 -0
  210. package/src/components/common/canvas/CanvasElementFrame.test.tsx +79 -0
  211. package/src/components/common/canvas/CanvasElementFrame.tsx +182 -0
  212. package/src/components/common/canvas/canvasGeometry.test.ts +66 -0
  213. package/src/components/common/canvas/canvasGeometry.ts +133 -0
  214. package/src/components/common/canvas/index.ts +27 -0
  215. package/src/components/common/canvas/useCanvasDragResize.test.tsx +152 -0
  216. package/src/components/common/canvas/useCanvasDragResize.ts +161 -0
  217. package/src/components/common/chat/ChatAssistantMessage.tsx +81 -0
  218. package/src/components/common/chat/ChatBox.tsx +189 -0
  219. package/src/components/common/chat/ChatMarkdown.tsx +109 -0
  220. package/src/components/common/chat/ChatModelMenu.tsx +258 -0
  221. package/src/components/common/chat/ChatStatusRow.tsx +24 -0
  222. package/src/components/common/chat/ChatThinkingIndicator.tsx +54 -0
  223. package/src/components/common/chat/ChatThread.tsx +59 -0
  224. package/src/components/common/chat/ChatToolRow.tsx +141 -0
  225. package/src/components/common/chat/ChatUserBubble.tsx +45 -0
  226. package/src/components/common/chat/chatUtils.ts +7 -0
  227. package/src/components/common/chat/index.ts +24 -0
  228. package/src/components/common/connections/ConnectionSetup.tsx +529 -0
  229. package/src/components/common/connections/index.ts +26 -0
  230. package/src/components/common/containers/AppFrame.test.tsx +47 -0
  231. package/src/components/common/containers/AppFrame.tsx +65 -0
  232. package/src/components/common/containers/AppHeader.tsx +60 -0
  233. package/src/components/common/containers/AutoFillGrid.tsx +37 -0
  234. package/src/components/common/containers/Collapsible.tsx +43 -0
  235. package/src/components/common/containers/CollapsibleGrid.tsx +174 -0
  236. package/src/components/common/containers/MotionScope.tsx +32 -0
  237. package/src/components/common/containers/PageHeader.tsx +35 -0
  238. package/src/components/common/containers/PanelToolbarHeader.tsx +113 -0
  239. package/src/components/common/containers/QuietDisclosure.tsx +91 -0
  240. package/src/components/common/containers/QuietSurface.tsx +41 -0
  241. package/src/components/common/containers/ScrollPanel.tsx +33 -0
  242. package/src/components/common/containers/StatusBar.tsx +42 -0
  243. package/src/components/common/containers/SurfacePanel.tsx +66 -0
  244. package/src/components/common/containers/ToolbarStrip.tsx +80 -0
  245. package/src/components/common/containers/flex.ts +117 -0
  246. package/src/components/common/containers/useOpenTransition.ts +33 -0
  247. package/src/components/common/context/ContextSelector.tsx +269 -0
  248. package/src/components/common/context/index.ts +7 -0
  249. package/src/components/common/desk/ActionReader.tsx +270 -0
  250. package/src/components/common/desk/ActivityLedger.tsx +143 -0
  251. package/src/components/common/desk/CreationDesk.tsx +139 -0
  252. package/src/components/common/desk/DailyBrief.tsx +160 -0
  253. package/src/components/common/desk/DeskCard.tsx +157 -0
  254. package/src/components/common/desk/index.ts +30 -0
  255. package/src/components/common/dropdown/ContextMenu.tsx +87 -0
  256. package/src/components/common/dropdown/MenuButtonDropdown.tsx +121 -0
  257. package/src/components/common/dropdown/MenuDropdown.tsx +63 -0
  258. package/src/components/common/dropdown/MenuDropdownEntry.tsx +51 -0
  259. package/src/components/common/dropdown/MenuDropdownItem.tsx +82 -0
  260. package/src/components/common/dropdown/MenuDropdownSubmenu.tsx +145 -0
  261. package/src/components/common/dropdown/MenuPanel.tsx +69 -0
  262. package/src/components/common/dropdown/MenuPortal.tsx +162 -0
  263. package/src/components/common/dropdown/MenuPortalLayer.tsx +6 -0
  264. package/src/components/common/dropdown/SelectMenu.tsx +243 -0
  265. package/src/components/common/dropdown/SelectMenuControl.tsx +23 -0
  266. package/src/components/common/dropdown/SelectMenuOptions.tsx +38 -0
  267. package/src/components/common/dropdown/menuPortalConstants.ts +11 -0
  268. package/src/components/common/dropdown/menuTypes.ts +71 -0
  269. package/src/components/common/dropdown/menuViewportPosition.test.ts +139 -0
  270. package/src/components/common/dropdown/menuViewportPosition.ts +122 -0
  271. package/src/components/common/dropdown/selectMenuLayout.test.ts +42 -0
  272. package/src/components/common/dropdown/selectMenuLayout.ts +123 -0
  273. package/src/components/common/dropdown/useMenuAnchorPosition.ts +109 -0
  274. package/src/components/common/dropdown/useSelectMenuAnchor.ts +30 -0
  275. package/src/components/common/evidence/StructuredEvidenceView.test.tsx +77 -0
  276. package/src/components/common/evidence/StructuredEvidenceView.tsx +312 -0
  277. package/src/components/common/evidence/index.ts +13 -0
  278. package/src/components/common/feedback/Badge.tsx +33 -0
  279. package/src/components/common/feedback/CapabilityPills.tsx +36 -0
  280. package/src/components/common/feedback/EmptyState.tsx +132 -0
  281. package/src/components/common/feedback/InfoHint.tsx +67 -0
  282. package/src/components/common/feedback/InlineBanner.tsx +82 -0
  283. package/src/components/common/feedback/PlatformChip.tsx +104 -0
  284. package/src/components/common/feedback/QuietPill.tsx +75 -0
  285. package/src/components/common/feedback/WorkProgressPanel.tsx +156 -0
  286. package/src/components/common/feedback/index.ts +33 -0
  287. package/src/components/common/inputs/BareInput.tsx +31 -0
  288. package/src/components/common/inputs/BareTextarea.tsx +10 -0
  289. package/src/components/common/inputs/Checkbox.tsx +13 -0
  290. package/src/components/common/inputs/FieldControlShell.tsx +49 -0
  291. package/src/components/common/inputs/FormField.tsx +40 -0
  292. package/src/components/common/inputs/HybridSearchField.tsx +280 -0
  293. package/src/components/common/inputs/InlineComposer.tsx +197 -0
  294. package/src/components/common/inputs/PaletteCirclePicker.tsx +26 -0
  295. package/src/components/common/inputs/PaletteDropdown.tsx +413 -0
  296. package/src/components/common/inputs/Radio.tsx +19 -0
  297. package/src/components/common/inputs/SearchField.tsx +128 -0
  298. package/src/components/common/inputs/SelectField.tsx +169 -0
  299. package/src/components/common/inputs/SurfacePalettePicker.tsx +54 -0
  300. package/src/components/common/inputs/TextAreaField.tsx +22 -0
  301. package/src/components/common/inputs/TextField.tsx +51 -0
  302. package/src/components/common/inputs/ToggleSwitch.tsx +110 -0
  303. package/src/components/common/inputs/WorkspacePicker.tsx +349 -0
  304. package/src/components/common/inputs/fieldStyles.ts +137 -0
  305. package/src/components/common/layout/PanelGroup.tsx +479 -0
  306. package/src/components/common/layout/index.ts +31 -0
  307. package/src/components/common/layout/panelLayoutConstants.ts +7 -0
  308. package/src/components/common/layout/panelLayoutPersistence.ts +35 -0
  309. package/src/components/common/layout/panelLayoutState.test.ts +38 -0
  310. package/src/components/common/layout/panelLayoutState.ts +174 -0
  311. package/src/components/common/layout/panelLayoutTypes.ts +19 -0
  312. package/src/components/common/layout/tabs/Tabs.tsx +136 -0
  313. package/src/components/common/lists/listRowStyles.ts +32 -0
  314. package/src/components/common/lists/quiet-ledger/QuietActivityList.tsx +64 -0
  315. package/src/components/common/lists/quiet-ledger/QuietLedgerRow.tsx +119 -0
  316. package/src/components/common/lists/quiet-ledger/index.ts +9 -0
  317. package/src/components/common/lists/reorderable/DragHandle.tsx +65 -0
  318. package/src/components/common/lists/reorderable/ReorderableList.tsx +280 -0
  319. package/src/components/common/lists/reorderable/ReorderableListRow.tsx +102 -0
  320. package/src/components/common/lists/reorderable/ReorderableSections.tsx +344 -0
  321. package/src/components/common/lists/reorderable/ReorderableSectionsDemo.tsx +94 -0
  322. package/src/components/common/lists/reorderable/index.ts +15 -0
  323. package/src/components/common/lists/reorderable/reorderableUtils.test.ts +28 -0
  324. package/src/components/common/lists/reorderable/reorderableUtils.ts +23 -0
  325. package/src/components/common/lists/reorderable/types.ts +40 -0
  326. package/src/components/common/lists/reorderable/useReorderableDragState.ts +49 -0
  327. package/src/components/common/media/ImageReferencePicker.tsx +242 -0
  328. package/src/components/common/menubar/MenuBar.tsx +78 -0
  329. package/src/components/common/menubar/MenuBarTrigger.tsx +70 -0
  330. package/src/components/common/narrative/NarrativeMention.tsx +74 -0
  331. package/src/components/common/navigation/PathBreadcrumbs.tsx +78 -0
  332. package/src/components/common/navigation/UnderlineTabs.tsx +83 -0
  333. package/src/components/common/overlays/InstallAppPromptDialog.tsx +59 -0
  334. package/src/components/common/overlays/Modal.tsx +285 -0
  335. package/src/components/common/overlays/ModalChromeHeader.tsx +105 -0
  336. package/src/components/common/overlays/ObjectActionStrip.tsx +80 -0
  337. package/src/components/common/people/AutoUpdateReviewList.tsx +182 -0
  338. package/src/components/common/people/EntityList.tsx +122 -0
  339. package/src/components/common/people/EntityPortrait.tsx +64 -0
  340. package/src/components/common/people/LinkedDossierList.tsx +99 -0
  341. package/src/components/common/people/OpportunityList.tsx +94 -0
  342. package/src/components/common/people/PeopleSourceList.tsx +145 -0
  343. package/src/components/common/people/RelationshipDossier.tsx +168 -0
  344. package/src/components/common/people/RelationshipMap.tsx +362 -0
  345. package/src/components/common/people/index.ts +40 -0
  346. package/src/components/common/preview/FileAudioPreview.tsx +89 -0
  347. package/src/components/common/preview/FileImagePreview.tsx +88 -0
  348. package/src/components/common/preview/FilePdfPreview.tsx +87 -0
  349. package/src/components/common/preview/FileTextPreview.tsx +96 -0
  350. package/src/components/common/preview/FileVideoPreview.tsx +87 -0
  351. package/src/components/common/preview/PlatformFilePreview.tsx +166 -0
  352. package/src/components/common/preview/previewLayout.ts +27 -0
  353. package/src/components/common/render/Each.tsx +42 -0
  354. package/src/components/common/research/EvidenceDossier.tsx +894 -0
  355. package/src/components/common/research/index.ts +7 -0
  356. package/src/components/common/tiles/CatalogCard.tsx +116 -0
  357. package/src/components/common/tiles/Tile.tsx +45 -0
  358. package/src/components/common/tiles/TileGrid.tsx +13 -0
  359. package/src/components/common/typography/Heading.tsx +27 -0
  360. package/src/components/common/typography/Kicker.tsx +30 -0
  361. package/src/components/common/typography/Text.tsx +80 -0
  362. package/src/components/credentials/CredentialsPanel.tsx +169 -0
  363. package/src/components/credentials/credentialsTypes.ts +14 -0
  364. package/src/components/data/DataChart.tsx +913 -0
  365. package/src/components/data/dataTable.ts +99 -0
  366. package/src/components/editor/EditorCommentPromptDialog.tsx +124 -0
  367. package/src/components/editor/EditorLinkPromptDialog.tsx +65 -0
  368. package/src/components/print-design/PrintDesignPanel.test.tsx +1446 -0
  369. package/src/components/print-design/PrintDesignPanel.tsx +3043 -0
  370. package/src/components/print-design/index.ts +9 -0
  371. package/src/components/proofing/PagedProofDialog.tsx +453 -0
  372. package/src/components/proofing/index.ts +4 -0
  373. package/src/editor/DocumentEditor.tsx +28 -0
  374. package/src/editor/EditorViewModeToggle.tsx +35 -0
  375. package/src/editor/index.ts +97 -0
  376. package/src/editor/toolbarOverlays.ts +8 -0
  377. package/src/index.ts +209 -0
  378. package/src/theme/ThemeProvider.tsx +113 -0
  379. package/src/theme/appAccents.ts +87 -0
  380. package/src/theme/buildThemeBridgePayload.ts +23 -0
  381. package/src/theme/composePlatformTheme.test.ts +30 -0
  382. package/src/theme/composePlatformTheme.ts +23 -0
  383. package/src/theme/platformFont.d.mts +7 -0
  384. package/src/theme/platformFont.mjs +20 -0
  385. package/src/theme/platformFont.ts +6 -0
  386. package/src/theme/reportsShellChromeSelectors.ts +136 -0
  387. package/src/theme/styled.d.ts +5 -0
  388. package/src/theme/surfacePalettes.ts +178 -0
  389. package/src/theme/surfaceRoleSelectors.ts +113 -0
  390. package/src/theme/themeVars.ts +13 -0
  391. package/src/theme/themes/dark.ts +59 -0
  392. package/src/theme/themes/light.ts +60 -0
  393. package/src/theme/themes/palette.ts +44 -0
  394. package/src/theme/themes/shared.ts +68 -0
  395. package/src/theme/tokens.ts +114 -0
  396. package/src/theme/utils/GlobalStyles.ts +88 -0
  397. package/src/theme/utils/appFrameDocumentStyles.d.mts +5 -0
  398. package/src/theme/utils/appFrameDocumentStyles.mjs +89 -0
  399. package/src/theme/utils/appFrameDocumentStyles.ts +5 -0
  400. package/src/theme/utils/applyTheme.ts +50 -0
  401. package/src/theme/utils/cssVariables.ts +49 -0
  402. package/src/theme/utils/useInjectThemeIntoIframe.ts +40 -0
  403. package/src/utils/extensionIconMap.ts +123 -0
  404. package/src/utils/generalUtils.ts +157 -0
  405. package/src/writing/DraftAssistantDialog.tsx +279 -0
  406. package/src/writing/WritingStyleManager.tsx +416 -0
  407. package/src/writing/condenseWritingStyle.ts +103 -0
  408. package/src/writing/draftTextWithAgent.ts +134 -0
  409. package/src/writing/index.ts +39 -0
  410. package/src/writing/styleStore.ts +32 -0
  411. package/src/writing/writingStyle.test.ts +122 -0
  412. package/src/writing/writingStyle.ts +217 -0
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Plugin-side bridge client (plain JS, no build step).
3
+ * Import: @purescience/platform-ui/bridge/client
4
+ */
5
+
6
+ import { applyThemeFromHost } from './applyThemeFromHost.mjs'
7
+
8
+ export { PLATFORM_BRIDGE_EVENTS } from './events.mjs'
9
+ export {
10
+ PLATFORM_BRIDGE_METHODS,
11
+ PLATFORM_BRIDGE_METHOD_PREFIXES,
12
+ } from './methods.mjs'
13
+ export {
14
+ applyThemeFromHost,
15
+ PLATFORM_THEME_STYLE_ID,
16
+ } from './applyThemeFromHost.mjs'
17
+
18
+ const CHANNEL = 'purescience-plugin-bridge'
19
+ const HANDSHAKE_MS = 800
20
+
21
+ let readyPromise = null
22
+ let bridgeMeta = null
23
+ let eventListenerAttached = false
24
+ const eventListeners = new Map()
25
+
26
+ function postHandshake() {
27
+ window.parent.postMessage({ channel: CHANNEL, type: 'handshake' }, '*')
28
+ }
29
+
30
+ function ensureEventListener() {
31
+ if (eventListenerAttached) return
32
+ eventListenerAttached = true
33
+ window.addEventListener('message', event => {
34
+ const data = event.data
35
+ if (!data || data.channel !== CHANNEL || data.type !== 'event') return
36
+ const handlers = eventListeners.get(data.name)
37
+ if (!handlers) return
38
+ for (const handler of handlers) {
39
+ try {
40
+ handler(data.payload)
41
+ } catch (error) {
42
+ console.error('[plugin-bridge] event handler failed', error)
43
+ }
44
+ }
45
+ })
46
+ }
47
+
48
+ function onEvent(name, handler) {
49
+ ensureEventListener()
50
+ if (!eventListeners.has(name)) {
51
+ eventListeners.set(name, new Set())
52
+ }
53
+ eventListeners.get(name).add(handler)
54
+ return () => offEvent(name, handler)
55
+ }
56
+
57
+ function offEvent(name, handler) {
58
+ eventListeners.get(name)?.delete(handler)
59
+ }
60
+
61
+ function waitForReady() {
62
+ if (readyPromise) return readyPromise
63
+ readyPromise = new Promise((resolve, reject) => {
64
+ const timeout = window.setTimeout(() => {
65
+ window.clearInterval(handshakeTimer)
66
+ reject(
67
+ new Error(
68
+ 'PureScience plugin bridge ready timeout (is this page inside the desktop iframe?)',
69
+ ),
70
+ )
71
+ }, 15_000)
72
+
73
+ const onMessage = event => {
74
+ const data = event.data
75
+ if (!data || data.channel !== CHANNEL || data.type !== 'ready') return
76
+ window.clearTimeout(timeout)
77
+ window.clearInterval(handshakeTimer)
78
+ window.removeEventListener('message', onMessage)
79
+ const viewport =
80
+ data.viewport &&
81
+ typeof data.viewport === 'object' &&
82
+ typeof data.viewport.tabId === 'string'
83
+ ? {
84
+ tabId: data.viewport.tabId,
85
+ resource:
86
+ data.viewport.resource &&
87
+ typeof data.viewport.resource === 'object' &&
88
+ typeof data.viewport.resource.path === 'string'
89
+ ? data.viewport.resource
90
+ : null,
91
+ }
92
+ : undefined
93
+ bridgeMeta = {
94
+ version: data.version,
95
+ pluginId: data.pluginId,
96
+ appSlug: data.appSlug,
97
+ methods: Array.isArray(data.methods) ? data.methods : [],
98
+ theme: data.theme,
99
+ ...(viewport ? { viewport } : {}),
100
+ }
101
+ applyThemeFromHost(data.theme)
102
+ resolve(bridgeMeta)
103
+ }
104
+
105
+ window.addEventListener('message', onMessage)
106
+ postHandshake()
107
+ const handshakeTimer = window.setInterval(postHandshake, HANDSHAKE_MS)
108
+ })
109
+ return readyPromise
110
+ }
111
+
112
+ function call(method, args = []) {
113
+ return waitForReady().then(
114
+ () =>
115
+ new Promise((resolve, reject) => {
116
+ const id = `req-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
117
+ const onMessage = event => {
118
+ const data = event.data
119
+ if (
120
+ !data ||
121
+ data.channel !== CHANNEL ||
122
+ data.type !== 'response' ||
123
+ data.id !== id
124
+ ) {
125
+ return
126
+ }
127
+ window.removeEventListener('message', onMessage)
128
+ if (data.ok) resolve(data.result)
129
+ else reject(new Error(data.error || 'Bridge request failed'))
130
+ }
131
+ window.addEventListener('message', onMessage)
132
+ window.parent.postMessage(
133
+ { channel: CHANNEL, type: 'request', id, method, args },
134
+ '*',
135
+ )
136
+ }),
137
+ )
138
+ }
139
+
140
+ export const bridge = {
141
+ waitForReady,
142
+ call,
143
+ onEvent,
144
+ offEvent,
145
+ getMethods: () => bridgeMeta?.methods ?? [],
146
+ ping: () => call('ping'),
147
+ }
@@ -0,0 +1,63 @@
1
+ import type { Editor } from '@tiptap/core'
2
+ import {
3
+ insertCollectionImageFromBinaryResult,
4
+ insertInlineAssetKind,
5
+ } from '@purescience/platform-editor'
6
+ import type { CollectionAssetDocumentAdapter } from '../components/assets/asset-library/types.js'
7
+ import type { PlatformAssetSummary } from './types.js'
8
+
9
+ function isFileCollectionAsset(asset: PlatformAssetSummary): boolean {
10
+ return asset.kind === 'image' || asset.kind === 'vector'
11
+ }
12
+
13
+ function altForAsset(asset: PlatformAssetSummary): string {
14
+ return asset.label.trim() || asset.fileName
15
+ }
16
+
17
+ export interface CreateCollectionAssetDocumentAdapterOptions {
18
+ getEditor: () => Editor | null | undefined
19
+ readBinary: (
20
+ absolutePath: string,
21
+ ) => Promise<{ mimeType: string; base64: string }>
22
+ }
23
+
24
+ export async function insertCollectionAsset(
25
+ editor: Editor | null | undefined,
26
+ asset: PlatformAssetSummary,
27
+ readBinary: CreateCollectionAssetDocumentAdapterOptions['readBinary'],
28
+ ): Promise<void> {
29
+ if (!editor || editor.isDestroyed) return
30
+
31
+ if (isFileCollectionAsset(asset)) {
32
+ const binary = await readBinary(asset.absolutePath)
33
+ insertCollectionImageFromBinaryResult(editor, {
34
+ relativePath: asset.relativePath,
35
+ alt: altForAsset(asset),
36
+ mimeType: binary.mimeType,
37
+ base64: binary.base64,
38
+ })
39
+ return
40
+ }
41
+
42
+ if (
43
+ asset.kind === 'mermaid' ||
44
+ asset.kind === 'table' ||
45
+ asset.kind === 'math'
46
+ ) {
47
+ insertInlineAssetKind(editor, asset.kind, asset.sourceCode)
48
+ }
49
+ }
50
+
51
+ export function createCollectionAssetDocumentAdapter(
52
+ options: CreateCollectionAssetDocumentAdapterOptions,
53
+ ): CollectionAssetDocumentAdapter {
54
+ return {
55
+ insertAsset: async (asset: PlatformAssetSummary) => {
56
+ await insertCollectionAsset(
57
+ options.getEditor(),
58
+ asset,
59
+ options.readBinary,
60
+ )
61
+ },
62
+ }
63
+ }
@@ -0,0 +1 @@
1
+ export declare const DEFAULT_COLLECTION_FIGURES_DIR: 'assets/figures'
@@ -0,0 +1,2 @@
1
+ /** Default relative folder for collection file uploads (figures, pasted images). */
2
+ export const DEFAULT_COLLECTION_FIGURES_DIR = 'assets/figures'
@@ -0,0 +1 @@
1
+ export { DEFAULT_COLLECTION_FIGURES_DIR } from './collectionAssets.mjs'
@@ -0,0 +1,71 @@
1
+ import { describe, expect, it, vi } from 'vitest'
2
+ import {
3
+ absoluteCollectionAssetPath,
4
+ COLLECTION_ASSET_SRC_ATTR,
5
+ normalizeCollectionDocumentHtml,
6
+ prepareCollectionDocumentHtml,
7
+ relativeAssetSrcForHtml,
8
+ relativizeCollectionAssetPath,
9
+ } from './collectionDocumentHtml.js'
10
+
11
+ describe('collectionDocumentHtml', () => {
12
+ const collectionPath = '/home/user/MyBook.book'
13
+
14
+ it('resolves relative and in-collection absolute paths', () => {
15
+ expect(relativeAssetSrcForHtml('assets/fig.png', collectionPath)).toBe(
16
+ 'assets/fig.png',
17
+ )
18
+ expect(
19
+ relativeAssetSrcForHtml(
20
+ `${collectionPath}/assets/fig.png`,
21
+ collectionPath,
22
+ ),
23
+ ).toBe('assets/fig.png')
24
+ expect(
25
+ relativeAssetSrcForHtml('https://example.com/x.png', collectionPath),
26
+ ).toBe(null)
27
+ })
28
+
29
+ it('relativizes absolute paths under the collection root', () => {
30
+ expect(
31
+ relativizeCollectionAssetPath(
32
+ `${collectionPath}/assets/fig.png`,
33
+ collectionPath,
34
+ ),
35
+ ).toBe('assets/fig.png')
36
+ expect(
37
+ relativizeCollectionAssetPath('/tmp/other.png', collectionPath),
38
+ ).toBeNull()
39
+ })
40
+
41
+ it('builds absolute asset paths', () => {
42
+ expect(absoluteCollectionAssetPath(collectionPath, 'assets/fig.png')).toBe(
43
+ `${collectionPath}/assets/fig.png`,
44
+ )
45
+ })
46
+
47
+ it('prepareCollectionDocumentHtml injects data URLs and storage attribute', async () => {
48
+ const html = '<p><img src="assets/fig.png" alt="fig"></p>'
49
+ const readBinary = vi.fn().mockResolvedValue({
50
+ mimeType: 'image/png',
51
+ base64: 'abc',
52
+ })
53
+
54
+ const display = await prepareCollectionDocumentHtml(
55
+ html,
56
+ collectionPath,
57
+ readBinary,
58
+ )
59
+ expect(readBinary).toHaveBeenCalledWith(`${collectionPath}/assets/fig.png`)
60
+ expect(display).toContain('data:image/png;base64,abc')
61
+ expect(display).toContain(`${COLLECTION_ASSET_SRC_ATTR}="assets/fig.png"`)
62
+ })
63
+
64
+ it('normalizeCollectionDocumentHtml restores relative src from storage attribute', () => {
65
+ const html = `<p><img src="data:image/png;base64,abc" ${COLLECTION_ASSET_SRC_ATTR}="assets/fig.png"></p>`
66
+ const stored = normalizeCollectionDocumentHtml(html, collectionPath)
67
+ expect(stored).toContain('src="assets/fig.png"')
68
+ expect(stored).not.toContain(COLLECTION_ASSET_SRC_ATTR)
69
+ expect(stored).not.toContain('data:image/png')
70
+ })
71
+ })
@@ -0,0 +1,118 @@
1
+ import { COLLECTION_ASSET_SRC_ATTR } from '@purescience/platform-editor'
2
+
3
+ export { COLLECTION_ASSET_SRC_ATTR }
4
+
5
+ export function isDisplayableAbsoluteSource(src: string): boolean {
6
+ return (
7
+ src.startsWith('http://') ||
8
+ src.startsWith('https://') ||
9
+ src.startsWith('data:') ||
10
+ src.startsWith('blob:')
11
+ )
12
+ }
13
+
14
+ export function absoluteCollectionAssetPath(
15
+ collectionPath: string,
16
+ relativePath: string,
17
+ ): string {
18
+ const clean = relativePath.replace(/\\/g, '/').replace(/^\/+/, '')
19
+ return `${collectionPath.replace(/\/+$/, '')}/${clean}`
20
+ }
21
+
22
+ export function relativizeCollectionAssetPath(
23
+ absolutePath: string,
24
+ collectionPath: string,
25
+ ): string | null {
26
+ const normalizedAbsolute = absolutePath.replace(/\\/g, '/')
27
+ const normalizedRoot = collectionPath.replace(/\/+$/, '')
28
+ if (!normalizedAbsolute.startsWith(`${normalizedRoot}/`)) return null
29
+ return normalizedAbsolute.slice(normalizedRoot.length + 1)
30
+ }
31
+
32
+ /** Relative path stored in HTML, or null if src should be left unchanged. */
33
+ export function relativeAssetSrcForHtml(
34
+ src: string,
35
+ collectionPath: string,
36
+ ): string | null {
37
+ const trimmed = src.trim()
38
+ if (!trimmed || isDisplayableAbsoluteSource(trimmed)) return null
39
+ if (trimmed.startsWith('/')) {
40
+ return relativizeCollectionAssetPath(trimmed, collectionPath)
41
+ }
42
+ return trimmed.replace(/\\/g, '/').replace(/^\/+/, '')
43
+ }
44
+
45
+ export type ReadCollectionBinary = (
46
+ absolutePath: string,
47
+ ) => Promise<{ mimeType: string; base64: string }>
48
+
49
+ /** Load relative image paths as data URLs for the iframe editor. */
50
+ export async function prepareCollectionDocumentHtml(
51
+ html: string,
52
+ collectionPath: string,
53
+ readBinary: ReadCollectionBinary,
54
+ ): Promise<string> {
55
+ if (!html.trim() || !collectionPath.trim()) return html
56
+ const doc = new DOMParser().parseFromString(html, 'text/html')
57
+ let changed = false
58
+
59
+ for (const img of Array.from(doc.querySelectorAll('img'))) {
60
+ const src = img.getAttribute('src')?.trim()
61
+ if (!src || isDisplayableAbsoluteSource(src)) continue
62
+
63
+ const relative = relativeAssetSrcForHtml(src, collectionPath)
64
+ if (!relative) continue
65
+
66
+ try {
67
+ const absolute = absoluteCollectionAssetPath(collectionPath, relative)
68
+ const result = await readBinary(absolute)
69
+ img.setAttribute('src', `data:${result.mimeType};base64,${result.base64}`)
70
+ img.setAttribute(COLLECTION_ASSET_SRC_ATTR, relative)
71
+ changed = true
72
+ } catch {
73
+ // Keep relative src when the file is missing.
74
+ }
75
+ }
76
+
77
+ return changed ? doc.body.innerHTML : html
78
+ }
79
+
80
+ /** Persist relative `img src` values (strip injected data URLs). */
81
+ export function normalizeCollectionDocumentHtml(
82
+ html: string,
83
+ collectionPath: string,
84
+ ): string {
85
+ if (!html.trim() || !collectionPath.trim()) return html
86
+ const doc = new DOMParser().parseFromString(html, 'text/html')
87
+ let changed = false
88
+
89
+ for (const img of Array.from(doc.querySelectorAll('img'))) {
90
+ const stored = img.getAttribute(COLLECTION_ASSET_SRC_ATTR)?.trim()
91
+ if (stored) {
92
+ img.setAttribute('src', stored)
93
+ img.removeAttribute(COLLECTION_ASSET_SRC_ATTR)
94
+ changed = true
95
+ continue
96
+ }
97
+
98
+ const src = img.getAttribute('src')?.trim()
99
+ if (!src) continue
100
+
101
+ if (src.startsWith('file://')) {
102
+ try {
103
+ const relative = relativizeCollectionAssetPath(
104
+ decodeURIComponent(new URL(src).pathname),
105
+ collectionPath,
106
+ )
107
+ if (relative) {
108
+ img.setAttribute('src', relative)
109
+ changed = true
110
+ }
111
+ } catch {
112
+ // ignore malformed file URLs
113
+ }
114
+ }
115
+ }
116
+
117
+ return changed ? doc.body.innerHTML : html
118
+ }
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ buildPastedFigureRelativePath,
4
+ normalizeCollectionImageMimeType,
5
+ stemFromImageFileName,
6
+ } from './collectionImagePaste.js'
7
+
8
+ describe('collectionImagePaste helpers', () => {
9
+ it('normalizes mime types from file names', () => {
10
+ expect(normalizeCollectionImageMimeType(undefined, 'photo.PNG')).toBe(
11
+ 'image/png',
12
+ )
13
+ expect(normalizeCollectionImageMimeType('image/jpeg', 'x.bin')).toBe(
14
+ 'image/jpeg',
15
+ )
16
+ })
17
+
18
+ it('builds stable relative paths under the figures subdir', () => {
19
+ expect(
20
+ buildPastedFigureRelativePath(
21
+ 'assets/figures',
22
+ 'My Diagram.png',
23
+ 'image/png',
24
+ 1_700_000_000_000,
25
+ ),
26
+ ).toBe('assets/figures/My-Diagram-1700000000000.png')
27
+ })
28
+
29
+ it('derives stems from file names', () => {
30
+ expect(stemFromImageFileName('figures/My Photo.jpg')).toBe('My-Photo')
31
+ })
32
+ })
@@ -0,0 +1,130 @@
1
+ import type { Editor } from '@tiptap/core'
2
+ import { insertCollectionImage } from '@purescience/platform-editor'
3
+ import { DEFAULT_COLLECTION_FIGURES_DIR } from './collectionAssets.js'
4
+ import { absoluteCollectionAssetPath } from './collectionDocumentHtml.js'
5
+
6
+ const SUPPORTED_MIME = new Set([
7
+ 'image/png',
8
+ 'image/jpeg',
9
+ 'image/gif',
10
+ 'image/webp',
11
+ 'image/svg+xml',
12
+ ])
13
+
14
+ const EXT_FOR_MIME: Record<string, string> = {
15
+ 'image/png': 'png',
16
+ 'image/jpeg': 'jpg',
17
+ 'image/gif': 'gif',
18
+ 'image/webp': 'webp',
19
+ 'image/svg+xml': 'svg',
20
+ }
21
+
22
+ export interface CollectionImagePastePayload {
23
+ name: string
24
+ bytes: Uint8Array
25
+ mimeType: string
26
+ }
27
+
28
+ export interface CollectionImagePasteHandlerOptions {
29
+ collectionPath?: string
30
+ subdir?: string
31
+ onError?: (message: string) => void
32
+ getEditor: () => Editor | null | undefined
33
+ writeBinary: (absolutePath: string, bytes: Uint8Array) => Promise<void>
34
+ }
35
+
36
+ export function normalizeCollectionImageMimeType(
37
+ mimeType: string | undefined,
38
+ name: string,
39
+ ): string | undefined {
40
+ const trimmed = mimeType?.trim().toLowerCase()
41
+ if (trimmed && SUPPORTED_MIME.has(trimmed)) return trimmed
42
+ const ext = name.split('.').pop()?.toLowerCase()
43
+ if (ext === 'png') return 'image/png'
44
+ if (ext === 'jpg' || ext === 'jpeg') return 'image/jpeg'
45
+ if (ext === 'gif') return 'image/gif'
46
+ if (ext === 'webp') return 'image/webp'
47
+ if (ext === 'svg') return 'image/svg+xml'
48
+ return undefined
49
+ }
50
+
51
+ export function stemFromImageFileName(name: string): string {
52
+ const base = name.replace(/\\/g, '/').split('/').pop() ?? name
53
+ const dot = base.lastIndexOf('.')
54
+ const stem = dot > 0 ? base.slice(0, dot) : base
55
+ return stem.replace(/\s+/g, '-').replace(/[^\w.-]+/g, '') || 'image'
56
+ }
57
+
58
+ function extensionFromImageFileName(name: string): string | undefined {
59
+ const ext = name.split('.').pop()?.toLowerCase()
60
+ if (!ext) return undefined
61
+ if (['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'].includes(ext)) {
62
+ return ext === 'jpeg' ? 'jpg' : ext
63
+ }
64
+ return undefined
65
+ }
66
+
67
+ export function buildPastedFigureRelativePath(
68
+ subdir: string,
69
+ name: string,
70
+ mimeType?: string,
71
+ now = Date.now(),
72
+ ): string {
73
+ const normalizedMime =
74
+ normalizeCollectionImageMimeType(mimeType, name) ?? 'image/png'
75
+ if (!SUPPORTED_MIME.has(normalizedMime)) {
76
+ throw new Error(
77
+ 'Unsupported image type. Supported: PNG, JPEG, GIF, WebP, SVG.',
78
+ )
79
+ }
80
+ const ext =
81
+ EXT_FOR_MIME[normalizedMime] ?? extensionFromImageFileName(name) ?? 'png'
82
+ const stem = stemFromImageFileName(name) || 'image'
83
+ return `${subdir}/${stem}-${now}.${ext}`
84
+ }
85
+
86
+ /** Persist pasted bytes under the collection and insert into the active editor. */
87
+ export function createCollectionImagePasteHandler(
88
+ options: CollectionImagePasteHandlerOptions,
89
+ ): (payload: CollectionImagePastePayload) => Promise<void> {
90
+ const {
91
+ collectionPath,
92
+ subdir = DEFAULT_COLLECTION_FIGURES_DIR,
93
+ onError,
94
+ getEditor,
95
+ writeBinary,
96
+ } = options
97
+
98
+ return async ({ name, bytes, mimeType }) => {
99
+ if (!collectionPath?.trim()) {
100
+ onError?.('Open a collection before inserting images.')
101
+ throw new Error('No collection path')
102
+ }
103
+
104
+ const normalizedMime =
105
+ normalizeCollectionImageMimeType(mimeType, name) ?? 'image/png'
106
+ const relativePath = buildPastedFigureRelativePath(
107
+ subdir,
108
+ name,
109
+ normalizedMime,
110
+ )
111
+ const absolutePath = absoluteCollectionAssetPath(
112
+ collectionPath,
113
+ relativePath,
114
+ )
115
+ await writeBinary(absolutePath, bytes)
116
+
117
+ const stem = stemFromImageFileName(name)
118
+ const editor = getEditor()
119
+ if (
120
+ !insertCollectionImage(editor, {
121
+ relativePath,
122
+ alt: stem,
123
+ bytes,
124
+ mimeType: normalizedMime,
125
+ })
126
+ ) {
127
+ onError?.('Image nodes are not available in this editor.')
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,12 @@
1
+ import { bridge } from './client.mjs'
2
+ import { PLATFORM_BRIDGE_METHODS } from './methods.mjs'
3
+
4
+ /**
5
+ * Publish structured context from this tab for agent sessions.
6
+ * Requires `agents` permission.
7
+ *
8
+ * @param {{ summary: string, label?: string }} request
9
+ */
10
+ export async function publishContext(request) {
11
+ return bridge.call(PLATFORM_BRIDGE_METHODS.CONTEXT_PUBLISH, [request])
12
+ }
@@ -0,0 +1,22 @@
1
+ import { bridge } from './client.mjs'
2
+ import { PLATFORM_BRIDGE_METHODS } from './methods.mjs'
3
+
4
+ /** Credential vault bridge — status/list/set/delete only (no get). Requires `agents` permission. */
5
+
6
+ export async function readCredentialStorageStatus() {
7
+ return bridge.call(PLATFORM_BRIDGE_METHODS.CREDENTIALS_STORAGE_STATUS, [])
8
+ }
9
+
10
+ export async function listCredentials() {
11
+ return bridge.call(PLATFORM_BRIDGE_METHODS.CREDENTIALS_LIST, [])
12
+ }
13
+
14
+ /** @param {{ id: string, value: string }} request */
15
+ export async function setCredential(request) {
16
+ return bridge.call(PLATFORM_BRIDGE_METHODS.CREDENTIALS_SET, [request])
17
+ }
18
+
19
+ /** @param {{ id: string }} request */
20
+ export async function deleteCredential(request) {
21
+ return bridge.call(PLATFORM_BRIDGE_METHODS.CREDENTIALS_DELETE, [request])
22
+ }
@@ -0,0 +1,5 @@
1
+ export interface PlatformDialogPathResult {
2
+ path: string | null
3
+ }
4
+
5
+ export declare function openPlatformImageDialog(): Promise<string | null>
@@ -0,0 +1,13 @@
1
+ import { bridge } from './client.mjs'
2
+ import { PLATFORM_BRIDGE_METHODS } from './methods.mjs'
3
+
4
+ /**
5
+ * @returns {Promise<string | null>} Absolute path to the picked image, or null if cancelled.
6
+ */
7
+ export async function openPlatformImageDialog() {
8
+ const result = await bridge.call(
9
+ PLATFORM_BRIDGE_METHODS.DIALOG_OPEN_IMAGE,
10
+ [],
11
+ )
12
+ return result?.path ?? null
13
+ }
@@ -0,0 +1,14 @@
1
+ export declare const PLATFORM_BRIDGE_EVENTS: {
2
+ readonly RESOURCE_OPEN: 'resource.open'
3
+ readonly THEME_CHANGED: 'theme.changed'
4
+ readonly RENDER_PROGRESS: 'render.progress'
5
+ readonly WATCH_CHANGE: 'watch.change'
6
+ readonly AGENT_RUN_EVENT: 'agents.run.event'
7
+ readonly AGENT_TOOL_INVOKE: 'agents.tool.invoke'
8
+ readonly DEBUG_TEST: 'debug.test'
9
+ readonly SCHEDULE_FIRE: 'schedule.fire'
10
+ readonly WINDOW_CAMERA_SHORTCUT: 'window.cameraShortcut'
11
+ }
12
+
13
+ export type PlatformBridgeEventName =
14
+ (typeof PLATFORM_BRIDGE_EVENTS)[keyof typeof PLATFORM_BRIDGE_EVENTS]
@@ -0,0 +1,12 @@
1
+ /** Host → plugin bridge event names. Single source for shell + app SDK. */
2
+ export const PLATFORM_BRIDGE_EVENTS = {
3
+ RESOURCE_OPEN: 'resource.open',
4
+ THEME_CHANGED: 'theme.changed',
5
+ RENDER_PROGRESS: 'render.progress',
6
+ WATCH_CHANGE: 'watch.change',
7
+ AGENT_RUN_EVENT: 'agents.run.event',
8
+ AGENT_TOOL_INVOKE: 'agents.tool.invoke',
9
+ DEBUG_TEST: 'debug.test',
10
+ SCHEDULE_FIRE: 'schedule.fire',
11
+ WINDOW_CAMERA_SHORTCUT: 'window.cameraShortcut',
12
+ }