@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,128 @@
1
+ import type { PlatformAssetSummary } from '../../../bridge/types.js'
2
+ import type {
3
+ AssetLibraryFilter,
4
+ AssetLibrarySection,
5
+ AssetLibrarySortMode,
6
+ } from './utils/assetLibraryUtils.js'
7
+
8
+ /** App-owned mapping from collection asset → document editor (TipTap / HTML). */
9
+ export interface CollectionAssetDocumentAdapter {
10
+ insertAsset(asset: PlatformAssetSummary): Promise<void>
11
+ }
12
+
13
+ export interface AssetLibraryDialogProps {
14
+ open: boolean
15
+ onClose: () => void
16
+ /** Writer workspace or `.document` package root. */
17
+ collectionPath: string
18
+ /** App document adapter for library insert actions. */
19
+ documentAdapter?: CollectionAssetDocumentAdapter
20
+ /** Document path relative to `collectionPath` (e.g. `notes.doc.html`). */
21
+ currentDocumentFile?: string | null
22
+ /** App-supplied paths for `assets.listScoped` (e.g. book chapters). */
23
+ documentFiles?: string[]
24
+ currentDocumentTitle?: string | null
25
+ collectionLabel?: string
26
+ insertTargetLabel?: string
27
+ inlineAssetScope?: 'all' | 'current-document'
28
+ /** Relative path under `collectionPath` for uploads. Default `assets/figures`. */
29
+ uploadRelativeDir?: string
30
+ /** Called after inline source is saved to disk (mermaid/table/math). */
31
+ onDocumentSourceSaved?: (info: {
32
+ sourceDocumentPath?: string
33
+ relativePath: string
34
+ }) => void
35
+ }
36
+
37
+ export interface AssetLibraryWorkspaceProps
38
+ extends Omit<AssetLibraryDialogProps, 'onClose'> {
39
+ fill?: boolean
40
+ }
41
+
42
+ export type AssetLibraryStatusTone = 'error' | 'info' | 'success'
43
+
44
+ export type AssetLibraryBusyAction =
45
+ | 'uploading'
46
+ | 'saving'
47
+ | 'deleting'
48
+ | 'inserting'
49
+ | 'swapping'
50
+ | null
51
+
52
+ export interface AssetLibraryStatus {
53
+ tone: AssetLibraryStatusTone
54
+ message: string
55
+ }
56
+
57
+ export interface AssetLibraryListModel {
58
+ assets: PlatformAssetSummary[]
59
+ filteredAssets: PlatformAssetSummary[]
60
+ sectionedAssets: AssetLibrarySection[]
61
+ selectedAsset: PlatformAssetSummary | null
62
+ loading: boolean
63
+ status: AssetLibraryStatus | null
64
+ query: string
65
+ filter: AssetLibraryFilter
66
+ sortMode: AssetLibrarySortMode
67
+ sortOptions: readonly { value: AssetLibrarySortMode; label: string }[]
68
+ setQuery: (query: string) => void
69
+ setFilter: (filter: AssetLibraryFilter) => void
70
+ setSortMode: (sortMode: AssetLibrarySortMode) => void
71
+ selectAsset: (assetId: string) => void
72
+ }
73
+
74
+ import type { InlineAssetKind } from './helpers.js'
75
+
76
+ export interface AssetLibraryMetadataModel {
77
+ labelDraft: string
78
+ captionDraft: string
79
+ sourceDraft: string
80
+ setLabelDraft: (value: string) => void
81
+ setCaptionDraft: (value: string) => void
82
+ setSourceDraft: (value: string) => void
83
+ metadataDirty: boolean
84
+ sourceDirty: boolean
85
+ isDirty: boolean
86
+ canEdit: boolean
87
+ showSourceEditor: boolean
88
+ inlineKind: InlineAssetKind | null
89
+ }
90
+
91
+ export interface AssetLibraryActionsModel {
92
+ busy: AssetLibraryBusyAction
93
+ uploadAsset: () => Promise<void>
94
+ saveMetadata: () => Promise<void>
95
+ deleteSelected: () => Promise<void>
96
+ revealSelected: () => Promise<void>
97
+ insertSelected: () => Promise<void>
98
+ insertAsset: (asset: PlatformAssetSummary) => Promise<void>
99
+ canSaveMetadata: boolean
100
+ canDeleteSelected: boolean
101
+ canRevealSelected: boolean
102
+ canInsertSelected: boolean
103
+ canInsertAsset: (asset: PlatformAssetSummary) => boolean
104
+ insertTargetLabel: string
105
+ }
106
+
107
+ export interface AssetLibraryVersionsModel {
108
+ visible: boolean
109
+ versions: PlatformAssetSummary[]
110
+ selectedRelativePath: string | null
111
+ previewVersion: (assetId: string) => void
112
+ swapToVersion: (
113
+ target: PlatformAssetSummary,
114
+ scope: 'document' | 'collection',
115
+ ) => Promise<void>
116
+ busy: boolean
117
+ canSwapInDocument: boolean
118
+ collectionLabel: string
119
+ insertTargetLabel: string
120
+ }
121
+
122
+ export interface AssetLibraryUsageModel {
123
+ entries: PlatformAssetSummary['usedIn']
124
+ collectionLabel: string
125
+ insertTargetLabel: string
126
+ }
127
+
128
+ export type { AssetLibraryFilter, AssetLibrarySortMode, AssetLibrarySection }
@@ -0,0 +1,181 @@
1
+ import type {
2
+ PlatformAssetKind,
3
+ PlatformAssetSummary,
4
+ } from '../../../../bridge/types.js'
5
+
6
+ export type AssetLibraryFilter = 'all' | PlatformAssetKind
7
+
8
+ export type AssetLibrarySortMode = 'recent' | 'current-document' | 'used'
9
+
10
+ export interface AssetLibrarySection {
11
+ key: string
12
+ label: string
13
+ assets: PlatformAssetSummary[]
14
+ }
15
+
16
+ export function kindLabel(kind: PlatformAssetKind): string {
17
+ switch (kind) {
18
+ case 'image':
19
+ return 'Image'
20
+ case 'vector':
21
+ return 'SVG'
22
+ case 'mermaid':
23
+ return 'Mermaid'
24
+ case 'table':
25
+ return 'Table'
26
+ case 'math':
27
+ return 'Math'
28
+ default:
29
+ return 'File'
30
+ }
31
+ }
32
+
33
+ export function inlineKindShortLabel(kind: PlatformAssetKind): string {
34
+ if (kind === 'mermaid') return 'mmd'
35
+ if (kind === 'table') return 'tbl'
36
+ if (kind === 'math') return 'tex'
37
+ return 'file'
38
+ }
39
+
40
+ export function isFileThumbnailAsset(asset: PlatformAssetSummary): boolean {
41
+ return asset.kind === 'image' || asset.kind === 'vector'
42
+ }
43
+
44
+ export function formatAssetBytes(bytes: number): string {
45
+ if (bytes < 1024) return `${bytes} B`
46
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
47
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
48
+ }
49
+
50
+ export function formatAssetTimeAgo(value: string): string {
51
+ const diffMs = Date.now() - new Date(value).getTime()
52
+ const minutes = Math.max(1, Math.round(diffMs / 60_000))
53
+ if (minutes < 60) return `${minutes}m ago`
54
+ const hours = Math.round(minutes / 60)
55
+ if (hours < 24) return `${hours}h ago`
56
+ const days = Math.round(hours / 24)
57
+ return `${days}d ago`
58
+ }
59
+
60
+ export function filterCollectionAssets(
61
+ assets: readonly PlatformAssetSummary[],
62
+ options: {
63
+ query: string
64
+ filter: AssetLibraryFilter
65
+ currentDocumentFile?: string | null
66
+ inlineAssetScope?: 'all' | 'current-document'
67
+ },
68
+ ): PlatformAssetSummary[] {
69
+ const normalizedQuery = options.query.trim().toLowerCase()
70
+
71
+ return assets.filter(asset => {
72
+ if (
73
+ options.inlineAssetScope === 'current-document' &&
74
+ options.currentDocumentFile &&
75
+ asset.sourceType?.startsWith('inline-') &&
76
+ !asset.usedIn.some(
77
+ usage => usage.documentPath === options.currentDocumentFile,
78
+ )
79
+ ) {
80
+ return false
81
+ }
82
+ if (options.filter !== 'all' && asset.kind !== options.filter) {
83
+ return false
84
+ }
85
+ if (!normalizedQuery) return true
86
+ return (
87
+ asset.label.toLowerCase().includes(normalizedQuery) ||
88
+ asset.fileName.toLowerCase().includes(normalizedQuery) ||
89
+ asset.relativePath.toLowerCase().includes(normalizedQuery) ||
90
+ (asset.caption ?? '').toLowerCase().includes(normalizedQuery) ||
91
+ (asset.sourceCode ?? '').toLowerCase().includes(normalizedQuery)
92
+ )
93
+ })
94
+ }
95
+
96
+ export function sortCollectionAssets(
97
+ assets: readonly PlatformAssetSummary[],
98
+ sortMode: AssetLibrarySortMode,
99
+ currentDocumentFile?: string | null,
100
+ ): PlatformAssetSummary[] {
101
+ return [...assets].sort((left, right) => {
102
+ if (sortMode === 'current-document' && currentDocumentFile) {
103
+ const leftCurrent = left.usedIn.some(
104
+ usage => usage.documentPath === currentDocumentFile,
105
+ )
106
+ const rightCurrent = right.usedIn.some(
107
+ usage => usage.documentPath === currentDocumentFile,
108
+ )
109
+ if (leftCurrent !== rightCurrent) return leftCurrent ? -1 : 1
110
+ }
111
+ if (sortMode === 'used' || sortMode === 'current-document') {
112
+ const leftUsed = left.usedIn.length > 0
113
+ const rightUsed = right.usedIn.length > 0
114
+ if (leftUsed !== rightUsed) return leftUsed ? -1 : 1
115
+ }
116
+ const modifiedDiff = right.modifiedAt.localeCompare(left.modifiedAt)
117
+ if (modifiedDiff !== 0) return modifiedDiff
118
+ return left.label.localeCompare(right.label)
119
+ })
120
+ }
121
+
122
+ export function sectionCollectionAssets(
123
+ assets: readonly PlatformAssetSummary[],
124
+ sortMode: AssetLibrarySortMode,
125
+ currentDocumentFile?: string | null,
126
+ ): AssetLibrarySection[] {
127
+ if (sortMode === 'current-document' && currentDocumentFile) {
128
+ const inCurrentDocument = assets.filter(asset =>
129
+ asset.usedIn.some(usage => usage.documentPath === currentDocumentFile),
130
+ )
131
+ const usedElsewhere = assets.filter(
132
+ asset =>
133
+ !asset.usedIn.some(
134
+ usage => usage.documentPath === currentDocumentFile,
135
+ ) && asset.usedIn.length > 0,
136
+ )
137
+ const notPlaced = assets.filter(asset => asset.usedIn.length === 0)
138
+ return [
139
+ { key: 'current', label: 'In this document', assets: inCurrentDocument },
140
+ {
141
+ key: 'elsewhere',
142
+ label: 'Used elsewhere',
143
+ assets: usedElsewhere,
144
+ },
145
+ { key: 'unplaced', label: 'Not yet placed', assets: notPlaced },
146
+ ].filter(section => section.assets.length > 0)
147
+ }
148
+
149
+ if (sortMode === 'used') {
150
+ const used = assets.filter(asset => asset.usedIn.length > 0)
151
+ const notPlaced = assets.filter(asset => asset.usedIn.length === 0)
152
+ return [
153
+ { key: 'used', label: 'Used in collection', assets: used },
154
+ { key: 'unplaced', label: 'Not yet placed', assets: notPlaced },
155
+ ].filter(section => section.assets.length > 0)
156
+ }
157
+
158
+ return [{ key: 'all', label: '', assets: [...assets] }]
159
+ }
160
+
161
+ export const ASSET_KIND_FILTERS: readonly {
162
+ value: AssetLibraryFilter
163
+ label: string
164
+ }[] = [
165
+ { value: 'all', label: 'All' },
166
+ { value: 'image', label: 'Images' },
167
+ { value: 'vector', label: 'SVG' },
168
+ { value: 'mermaid', label: 'Mermaid' },
169
+ { value: 'table', label: 'Tables' },
170
+ { value: 'math', label: 'Math' },
171
+ { value: 'other', label: 'Other' },
172
+ ]
173
+
174
+ export const ASSET_SORT_OPTIONS: readonly {
175
+ value: AssetLibrarySortMode
176
+ label: string
177
+ }[] = [
178
+ { value: 'recent', label: 'Recent' },
179
+ { value: 'current-document', label: 'This document' },
180
+ { value: 'used', label: 'Used' },
181
+ ]
@@ -0,0 +1,5 @@
1
+ /** Basename without pulling in node:path in the UI bundle. */
2
+ export function basename(filePath: string): string {
3
+ const normalized = filePath.replace(/\\/g, '/')
4
+ return normalized.split('/').filter(Boolean).pop() ?? filePath
5
+ }
@@ -0,0 +1,7 @@
1
+ export function sanitizeUploadFileName(name: string): string {
2
+ const trimmed = name.trim() || 'uploaded-image.png'
3
+ const normalized = trimmed
4
+ .replace(/[^A-Za-z0-9._-]+/g, '-')
5
+ .replace(/-+/g, '-')
6
+ return normalized.replace(/^-+|-+$/g, '') || 'uploaded-image.png'
7
+ }
@@ -0,0 +1,52 @@
1
+ import { basename } from './pathBasename.js'
2
+ import { DEFAULT_COLLECTION_FIGURES_DIR } from '../../../../bridge/collectionAssets.mjs'
3
+ import {
4
+ createPlatformFolder,
5
+ readPlatformFileBinary,
6
+ writePlatformFileBinary,
7
+ } from '../../../../bridge/fs.mjs'
8
+ import { openPlatformImageDialog } from '../../../../bridge/dialog.mjs'
9
+ import { sanitizeUploadFileName } from './sanitizeUploadFileName.js'
10
+
11
+ /** @deprecated Use `DEFAULT_COLLECTION_FIGURES_DIR` from `@purescience/platform-ui/bridge/collectionAssets`. */
12
+ export const DEFAULT_UPLOAD_RELATIVE_DIR = DEFAULT_COLLECTION_FIGURES_DIR
13
+
14
+ export async function ensureCollectionSubdirs(
15
+ collectionPath: string,
16
+ relativeDir: string,
17
+ ): Promise<void> {
18
+ const root = collectionPath.replace(/\/+$/, '')
19
+ const parts = relativeDir.split('/').filter(Boolean)
20
+ let parent = root
21
+ for (const part of parts) {
22
+ try {
23
+ const created = await createPlatformFolder(parent, part)
24
+ parent = created.path
25
+ } catch {
26
+ parent = `${parent}/${part}`
27
+ }
28
+ }
29
+ }
30
+
31
+ export async function uploadCollectionAssetFromDialog(input: {
32
+ collectionPath: string
33
+ uploadRelativeDir?: string
34
+ }): Promise<string | null> {
35
+ const picked = await openPlatformImageDialog()
36
+ if (!picked) return null
37
+
38
+ const uploadRelativeDir =
39
+ input.uploadRelativeDir ?? DEFAULT_COLLECTION_FIGURES_DIR
40
+ const binary = await readPlatformFileBinary(picked)
41
+ const fileName = sanitizeUploadFileName(basename(picked))
42
+ const relativePath = `${uploadRelativeDir.replace(/\/+$/, '')}/${fileName}`
43
+ const absolutePath = `${input.collectionPath.replace(
44
+ /\/+$/,
45
+ '',
46
+ )}/${relativePath}`
47
+
48
+ await ensureCollectionSubdirs(input.collectionPath, uploadRelativeDir)
49
+ await writePlatformFileBinary(absolutePath, binary.base64)
50
+
51
+ return relativePath
52
+ }
@@ -0,0 +1,82 @@
1
+ import { act } from 'react'
2
+ import { createRoot, type Root } from 'react-dom/client'
3
+ import { styled } from 'styled-components'
4
+ import { afterEach, describe, expect, it, vi } from 'vitest'
5
+ import { PlatformIcon } from './PlatformIcon.js'
6
+ import { X } from 'lucide-react'
7
+ import {
8
+ OverlayActionCapsule,
9
+ OverlayActionCapsuleButton,
10
+ OverlayActionCapsuleCloseButton,
11
+ } from './OverlayActionCapsule.js'
12
+ import { ShellWindowControls } from './ShellWindowControls.js'
13
+
14
+ const StyledCapsuleHost = styled.div`
15
+ display: flex;
16
+ align-items: stretch;
17
+ height: 38px;
18
+ `
19
+
20
+ const StyledTitlebarHost = styled.div`
21
+ display: flex;
22
+ align-items: stretch;
23
+ height: 30px;
24
+ `
25
+
26
+ function render(ui: React.ReactElement): {
27
+ container: HTMLDivElement
28
+ root: Root
29
+ } {
30
+ const container = document.createElement('div')
31
+ document.body.appendChild(container)
32
+ const root = createRoot(container)
33
+ act(() => {
34
+ root.render(ui)
35
+ })
36
+ return { container, root }
37
+ }
38
+
39
+ afterEach(() => {
40
+ document.body.innerHTML = ''
41
+ })
42
+
43
+ describe('OverlayActionCapsule', () => {
44
+ it('renders custom actions and close control in one capsule', () => {
45
+ const onClose = vi.fn()
46
+ const { container } = render(
47
+ <StyledCapsuleHost>
48
+ <OverlayActionCapsule>
49
+ <OverlayActionCapsuleButton>Link</OverlayActionCapsuleButton>
50
+ <OverlayActionCapsuleCloseButton aria-label="Close" onClick={onClose}>
51
+ <PlatformIcon icon={X} size={14} strokeWidth={2} />
52
+ </OverlayActionCapsuleCloseButton>
53
+ </OverlayActionCapsule>
54
+ </StyledCapsuleHost>,
55
+ )
56
+
57
+ expect(container.querySelector('button')?.textContent).toContain('Link')
58
+ container
59
+ .querySelector('[aria-label="Close"]')
60
+ ?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
61
+ expect(onClose).toHaveBeenCalledOnce()
62
+ })
63
+ })
64
+
65
+ describe('ShellWindowControls', () => {
66
+ it('exposes minimize, maximize, and close inside a capsule', () => {
67
+ const { container } = render(
68
+ <StyledTitlebarHost>
69
+ <ShellWindowControls
70
+ onMinimize={() => undefined}
71
+ onMaximize={() => undefined}
72
+ onClose={() => undefined}
73
+ />
74
+ </StyledTitlebarHost>,
75
+ )
76
+
77
+ expect(container.querySelectorAll('button')).toHaveLength(3)
78
+ expect(container.querySelector('[aria-label="Minimize"]')).not.toBeNull()
79
+ expect(container.querySelector('[aria-label="Maximize"]')).not.toBeNull()
80
+ expect(container.querySelector('[aria-label="Close"]')).not.toBeNull()
81
+ })
82
+ })
@@ -0,0 +1,90 @@
1
+ import type { ComponentPropsWithoutRef } from 'react'
2
+ import { styled } from 'styled-components'
3
+ import { CleanButton } from '../common/buttons/CleanButton.js'
4
+
5
+ export type OverlayActionCapsuleProps = ComponentPropsWithoutRef<'div'>
6
+ export type OverlayActionCapsuleButtonProps = ComponentPropsWithoutRef<
7
+ typeof StyledOverlayActionCapsuleButton
8
+ >
9
+ export type OverlayActionCapsuleIconButtonProps = ComponentPropsWithoutRef<
10
+ typeof StyledOverlayActionCapsuleIconButton
11
+ >
12
+ export type OverlayActionCapsuleCloseButtonProps = ComponentPropsWithoutRef<
13
+ typeof StyledOverlayActionCapsuleCloseButton
14
+ >
15
+
16
+ const capsuleButtonBase = styled(CleanButton)`
17
+ display: inline-flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ gap: 0.375em;
21
+ height: 100%;
22
+ min-height: 0;
23
+ border: 0;
24
+ border-radius: 999px;
25
+ background: transparent;
26
+ color: var(--platform-colors-text-secondary);
27
+ font: inherit;
28
+ font-size: inherit;
29
+ line-height: 1;
30
+ cursor: pointer;
31
+ transition: background var(--platform-transition-fast),
32
+ color var(--platform-transition-fast);
33
+
34
+ &:hover:not(:disabled) {
35
+ background: var(--platform-colors-elevated);
36
+ color: var(--platform-colors-text);
37
+ }
38
+
39
+ &:disabled {
40
+ opacity: 0.45;
41
+ cursor: not-allowed;
42
+ }
43
+ `
44
+
45
+ export const StyledOverlayActionCapsule = styled.div`
46
+ display: inline-flex;
47
+ align-items: stretch;
48
+ height: 100%;
49
+ box-sizing: border-box;
50
+ gap: 0.1875em;
51
+ padding: 0.1875em;
52
+ border: 1px solid var(--platform-colors-border);
53
+ border-radius: 999px;
54
+ background: var(--platform-colors-surface);
55
+ box-shadow: var(--platform-shadow-sm);
56
+ width: fit-content;
57
+ font-size: var(--platform-typography-font-size-sm);
58
+ line-height: 1;
59
+ `
60
+
61
+ export const StyledOverlayActionCapsuleButton = styled(capsuleButtonBase)`
62
+ min-width: 2.125em;
63
+ padding: 0 0.625em;
64
+ `
65
+
66
+ export const StyledOverlayActionCapsuleIconButton = styled(capsuleButtonBase)`
67
+ aspect-ratio: 1;
68
+ width: auto;
69
+ min-width: 0;
70
+ padding: 0;
71
+ `
72
+
73
+ export const StyledOverlayActionCapsuleCloseButton = styled(
74
+ StyledOverlayActionCapsuleIconButton,
75
+ )`
76
+ background: var(--platform-colors-accent-hover);
77
+ color: var(--platform-colors-text-inverse);
78
+
79
+ &:hover:not(:disabled) {
80
+ background: var(--platform-colors-danger);
81
+ color: var(--platform-colors-text-inverse);
82
+ }
83
+ `
84
+
85
+ export const OverlayActionCapsule = StyledOverlayActionCapsule
86
+ export const OverlayActionCapsuleButton = StyledOverlayActionCapsuleButton
87
+ export const OverlayActionCapsuleIconButton =
88
+ StyledOverlayActionCapsuleIconButton
89
+ export const OverlayActionCapsuleCloseButton =
90
+ StyledOverlayActionCapsuleCloseButton
@@ -0,0 +1,113 @@
1
+ import { styled } from 'styled-components'
2
+
3
+ export interface PanelTabItem<T extends string = string> {
4
+ id: T
5
+ label: React.ReactNode
6
+ }
7
+
8
+ export interface PanelTabStripProps<T extends string = string> {
9
+ items: readonly PanelTabItem<T>[]
10
+ activeId: T
11
+ onSelect: (id: T) => void
12
+ className?: string
13
+ 'data-mode-count'?: number | string
14
+ 'aria-label'?: string
15
+ }
16
+
17
+ const StyledStrip = styled.nav`
18
+ display: flex;
19
+ align-items: flex-end;
20
+ min-width: 0;
21
+ overflow-x: auto;
22
+ overflow-y: hidden;
23
+ scrollbar-width: none;
24
+
25
+ &::-webkit-scrollbar {
26
+ display: none;
27
+ }
28
+ `
29
+
30
+ const StyledTab = styled.button`
31
+ position: relative;
32
+ box-sizing: border-box;
33
+ display: inline-flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ min-width: 112px;
37
+ max-width: 240px;
38
+ height: 31px;
39
+ margin-bottom: -1px;
40
+ padding: 0 12px;
41
+ border: 1px solid transparent;
42
+ border-bottom-color: transparent;
43
+ border-radius: 11px 11px 0 0;
44
+ background: transparent;
45
+ color: var(--platform-colors-text-secondary);
46
+ font: inherit;
47
+ font-size: var(--platform-typography-font-size-xs, 13px);
48
+ font-weight: 500;
49
+ line-height: 1;
50
+ white-space: nowrap;
51
+ cursor: pointer;
52
+
53
+ &[aria-selected='true'] {
54
+ border-color: var(--platform-colors-border);
55
+ border-bottom-color: var(
56
+ --platform-panel-tab-surface,
57
+ var(--platform-colors-surface)
58
+ );
59
+ background: var(
60
+ --platform-panel-tab-surface,
61
+ var(--platform-colors-surface)
62
+ );
63
+ color: var(--platform-colors-text);
64
+ font-weight: 600;
65
+ box-shadow: 0 -1px 0 rgb(255 255 255 / 70%);
66
+ }
67
+
68
+ &:hover:not([aria-selected='true']) {
69
+ border-color: var(--platform-colors-border-soft);
70
+ background: var(--platform-colors-surface-hover);
71
+ color: var(--platform-colors-text);
72
+ }
73
+
74
+ &:focus-visible {
75
+ outline: 2px solid var(--platform-colors-focus-ring);
76
+ outline-offset: 2px;
77
+ }
78
+ `
79
+
80
+ /** Compact local tab strip using the same visual scale as workspace tabs. */
81
+ export function PanelTabStrip<T extends string = string>({
82
+ items,
83
+ activeId,
84
+ onSelect,
85
+ className,
86
+ 'data-mode-count': dataModeCount,
87
+ 'aria-label': ariaLabel,
88
+ }: PanelTabStripProps<T>): React.ReactElement {
89
+ return (
90
+ <StyledStrip
91
+ className={className}
92
+ role="tablist"
93
+ aria-label={ariaLabel}
94
+ data-mode-count={dataModeCount}
95
+ >
96
+ {items.map(item => {
97
+ const active = item.id === activeId
98
+ return (
99
+ <StyledTab
100
+ key={item.id}
101
+ type="button"
102
+ role="tab"
103
+ tabIndex={active ? 0 : -1}
104
+ aria-selected={active}
105
+ onClick={() => onSelect(item.id)}
106
+ >
107
+ {item.label}
108
+ </StyledTab>
109
+ )
110
+ })}
111
+ </StyledStrip>
112
+ )
113
+ }
@@ -0,0 +1,27 @@
1
+ import type { LucideIcon } from 'lucide-react'
2
+
3
+ export interface PlatformIconProps {
4
+ icon: LucideIcon
5
+ /** Pixel size (width and height). */
6
+ size?: number
7
+ strokeWidth?: number
8
+ className?: string
9
+ }
10
+
11
+ /** Lucide wrapper — uses `currentColor` from parent. */
12
+ export function PlatformIcon({
13
+ icon: Icon,
14
+ size = 16,
15
+ strokeWidth = 1.75,
16
+ className,
17
+ }: PlatformIconProps): React.ReactElement {
18
+ return (
19
+ <Icon
20
+ size={size}
21
+ strokeWidth={strokeWidth}
22
+ className={className}
23
+ aria-hidden
24
+ focusable={false}
25
+ />
26
+ )
27
+ }