@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,894 @@
1
+ import { useState, type ReactNode } from 'react'
2
+ import { styled } from 'styled-components'
3
+
4
+ export type EvidenceStrength = 'strong' | 'working' | 'weak'
5
+
6
+ export interface EvidenceSource {
7
+ id: string
8
+ label: string
9
+ title: string
10
+ url?: string
11
+ note: string
12
+ quote?: string
13
+ strength?: EvidenceStrength
14
+ }
15
+
16
+ export interface EvidenceFinding {
17
+ id: string
18
+ title: string
19
+ claim: string
20
+ confidence: EvidenceStrength
21
+ annotation?: string
22
+ evidence: EvidenceSource[]
23
+ }
24
+
25
+ export interface EvidenceDossierProps {
26
+ title: string
27
+ question: string
28
+ answer: string
29
+ updatedAt?: string
30
+ narrative?: string[]
31
+ findings: EvidenceFinding[]
32
+ uncertainties: string[]
33
+ nextSteps: string[]
34
+ onTitleChange?: (title: string) => void
35
+ onDeleteFinding?: (findingId: string) => void
36
+ footer?: ReactNode
37
+ className?: string
38
+ }
39
+
40
+ const strengthLabels: Record<EvidenceStrength, string> = {
41
+ strong: 'well supported',
42
+ working: 'working view',
43
+ weak: 'needs support',
44
+ }
45
+
46
+ type DossierRenderMode = 'read' | 'review' | 'audit'
47
+
48
+ const modeLabels: Record<DossierRenderMode, string> = {
49
+ read: 'Read',
50
+ review: 'Claims',
51
+ audit: 'Sources',
52
+ }
53
+
54
+ const modeDescriptions: Record<DossierRenderMode, string> = {
55
+ read: 'Polished dossier with quiet evidence links.',
56
+ review: 'Every claim opened with its supporting evidence.',
57
+ audit: 'Source-first trace for verification.',
58
+ }
59
+
60
+ function cleanDisplayText(value: unknown): string {
61
+ return String(value ?? '')
62
+ .replace(/\*\*([^*]+)\*\*/g, '$1')
63
+ .replace(/\*([^*]+)\*/g, '$1')
64
+ .replace(/__([^_]+)__/g, '$1')
65
+ .replace(/_([^_]+)_/g, '$1')
66
+ .replace(/\s*\[(?:\d+(?:,\s*)?)+\]/g, '')
67
+ .replace(/\s*\[\d+\](?=\[\d+\])/g, '')
68
+ .replace(/\[(\d+)\]/g, '')
69
+ .replace(/\s+([,.;:])/g, '$1')
70
+ .replace(/\s{2,}/g, ' ')
71
+ .trim()
72
+ }
73
+
74
+ function sourceUrlWithTextFragment(source: EvidenceSource): string | undefined {
75
+ if (!source.url) return undefined
76
+ const quote = cleanDisplayText(source.quote ?? '')
77
+ if (!quote) return source.url
78
+ const fragment = quote.slice(0, 140).trim()
79
+ if (!fragment) return source.url
80
+ const baseUrl = source.url.split('#')[0]
81
+ return `${baseUrl}#:~:text=${encodeURIComponent(fragment)}`
82
+ }
83
+
84
+ function formatUpdatedAt(value?: string): string | null {
85
+ if (!value) return null
86
+ const date = new Date(value)
87
+ if (Number.isNaN(date.getTime())) return value
88
+ return new Intl.DateTimeFormat(undefined, {
89
+ month: 'short',
90
+ day: 'numeric',
91
+ hour: 'numeric',
92
+ minute: '2-digit',
93
+ }).format(date)
94
+ }
95
+
96
+ function normalizeEvidenceSource(source: unknown, index: number): EvidenceSource {
97
+ const value =
98
+ source && typeof source === 'object'
99
+ ? (source as Partial<EvidenceSource>)
100
+ : {}
101
+ return {
102
+ id: cleanDisplayText(value.id) || `source-${index}`,
103
+ label: cleanDisplayText(value.label) || 'Evidence',
104
+ title: cleanDisplayText(value.title) || 'Untitled source',
105
+ url: cleanDisplayText(value.url) || undefined,
106
+ note: cleanDisplayText(value.note),
107
+ quote: cleanDisplayText(value.quote) || undefined,
108
+ strength:
109
+ value.strength === 'strong' ||
110
+ value.strength === 'working' ||
111
+ value.strength === 'weak'
112
+ ? value.strength
113
+ : 'working',
114
+ }
115
+ }
116
+
117
+ function normalizeEvidenceFinding(
118
+ finding: unknown,
119
+ index: number,
120
+ ): EvidenceFinding | null {
121
+ if (!finding || typeof finding !== 'object') return null
122
+ const value = finding as Partial<EvidenceFinding>
123
+ const title = cleanDisplayText(value.title)
124
+ const claim = cleanDisplayText(value.claim)
125
+ if (!title && !claim) return null
126
+ const evidence = Array.isArray(value.evidence)
127
+ ? value.evidence.map(normalizeEvidenceSource)
128
+ : []
129
+ return {
130
+ id: cleanDisplayText(value.id) || `finding-${index}`,
131
+ title: title || `Finding ${index + 1}`,
132
+ claim,
133
+ confidence:
134
+ value.confidence === 'strong' ||
135
+ value.confidence === 'working' ||
136
+ value.confidence === 'weak'
137
+ ? value.confidence
138
+ : evidence.length
139
+ ? 'working'
140
+ : 'weak',
141
+ annotation: cleanDisplayText(value.annotation) || undefined,
142
+ evidence,
143
+ }
144
+ }
145
+
146
+ function isEvidenceFinding(
147
+ finding: EvidenceFinding | null,
148
+ ): finding is EvidenceFinding {
149
+ return finding !== null
150
+ }
151
+
152
+ //#region styled-components
153
+
154
+ const Dossier = styled.article`
155
+ display: grid;
156
+ gap: 24px;
157
+ min-width: 0;
158
+ position: relative;
159
+ color: var(--platform-colors-text, #2e3338);
160
+ `
161
+
162
+ const Header = styled.header`
163
+ display: grid;
164
+ gap: 16px;
165
+ padding-bottom: 22px;
166
+ border-bottom: 1px solid rgb(46 51 56 / 12%);
167
+ `
168
+
169
+ const ModeDock = styled.div`
170
+ display: grid;
171
+ position: sticky;
172
+ top: 12px;
173
+ z-index: 3;
174
+ justify-self: end;
175
+ gap: 5px;
176
+
177
+ @media (max-width: 760px) {
178
+ justify-self: start;
179
+ }
180
+ `
181
+
182
+ const ModeSwitch = styled.div`
183
+ display: inline-flex;
184
+ gap: 4px;
185
+ padding: 3px;
186
+ border: 1px solid rgb(46 51 56 / 10%);
187
+ border-radius: 999px;
188
+ background: rgb(255 254 250 / 86%);
189
+ box-shadow: 0 10px 30px rgb(46 51 56 / 8%);
190
+ backdrop-filter: blur(10px);
191
+ `
192
+
193
+ const ModeDescription = styled.div`
194
+ max-width: 260px;
195
+ justify-self: end;
196
+ color: var(--platform-colors-text-tertiary, #8f98a4);
197
+ font-size: 11px;
198
+ line-height: 1.25;
199
+ text-align: right;
200
+
201
+ @media (max-width: 760px) {
202
+ justify-self: start;
203
+ text-align: left;
204
+ }
205
+ `
206
+
207
+ const ModeButton = styled.button<{ $active: boolean }>`
208
+ min-height: 26px;
209
+ border: 0;
210
+ border-radius: 999px;
211
+ background: ${({ $active }) =>
212
+ $active ? 'rgb(46 51 56 / 9%)' : 'transparent'};
213
+ padding: 0 10px;
214
+ color: ${({ $active }) =>
215
+ $active
216
+ ? 'var(--platform-colors-text, #2e3338)'
217
+ : 'var(--platform-colors-text-tertiary, #8f98a4)'};
218
+ font: inherit;
219
+ font-size: 12px;
220
+ font-weight: 750;
221
+ cursor: pointer;
222
+ `
223
+
224
+ const Kicker = styled.div`
225
+ color: var(--platform-colors-text-tertiary, #9aa2ad);
226
+ font-size: 11px;
227
+ font-weight: 800;
228
+ letter-spacing: 0.08em;
229
+ line-height: 1.3;
230
+ text-transform: uppercase;
231
+ `
232
+
233
+ const Title = styled.h1`
234
+ max-width: 22ch;
235
+ margin: 0;
236
+ color: var(--platform-colors-text, #20242a);
237
+ font-size: clamp(34px, 4.2vw, 54px);
238
+ font-weight: 800;
239
+ letter-spacing: 0;
240
+ line-height: 1.05;
241
+ overflow-wrap: anywhere;
242
+ `
243
+
244
+ const TitleInput = styled.textarea`
245
+ display: block;
246
+ width: 100%;
247
+ max-width: 22ch;
248
+ min-height: 1.1em;
249
+ margin: 0;
250
+ border: 0;
251
+ background: transparent;
252
+ color: var(--platform-colors-text, #20242a);
253
+ font: inherit;
254
+ font-size: clamp(34px, 4.2vw, 54px);
255
+ font-weight: 800;
256
+ letter-spacing: 0;
257
+ line-height: 1.05;
258
+ outline: none;
259
+ overflow: hidden;
260
+ overflow-wrap: anywhere;
261
+ resize: none;
262
+ field-sizing: content;
263
+
264
+ &:focus {
265
+ box-shadow: inset 0 -2px 0 rgb(46 51 56 / 18%);
266
+ }
267
+ `
268
+
269
+ const Question = styled.div`
270
+ max-width: 72ch;
271
+ color: var(--platform-colors-text-secondary, #68717c);
272
+ font-size: 14px;
273
+ line-height: 1.55;
274
+ `
275
+
276
+ const UpdatedStamp = styled.div`
277
+ color: var(--platform-colors-text-tertiary, #9aa2ad);
278
+ font-size: 11px;
279
+ font-weight: 750;
280
+ letter-spacing: 0.05em;
281
+ line-height: 1.3;
282
+ text-transform: uppercase;
283
+ `
284
+
285
+ const Narrative = styled.section`
286
+ display: grid;
287
+ gap: 18px;
288
+ max-width: 78ch;
289
+ padding: 2px 0 8px;
290
+ `
291
+
292
+ const Lead = styled.div`
293
+ margin: 0;
294
+ color: var(--platform-colors-text, #2e3338);
295
+ font-size: clamp(18px, 1.6vw, 22px);
296
+ line-height: 1.58;
297
+ `
298
+
299
+ const NarrativeText = styled.div`
300
+ margin: 0;
301
+ color: var(--platform-colors-text, #3a4047);
302
+ font-size: 14.5px;
303
+ line-height: 1.72;
304
+ `
305
+
306
+ const Findings = styled.section`
307
+ display: grid;
308
+ gap: 10px;
309
+ `
310
+
311
+ const Finding = styled.article`
312
+ display: grid;
313
+ grid-template-columns: 34px minmax(0, 1fr);
314
+ gap: 18px;
315
+ padding: 18px 0 20px;
316
+ border-top: 1px solid rgb(46 51 56 / 10%);
317
+ `
318
+
319
+ const FindingNumber = styled.div`
320
+ color: var(--platform-colors-text-tertiary, #9aa2ad);
321
+ font-size: 13px;
322
+ font-weight: 800;
323
+ letter-spacing: 0.05em;
324
+ `
325
+
326
+ const FindingBody = styled.div`
327
+ display: grid;
328
+ gap: 10px;
329
+ min-width: 0;
330
+ `
331
+
332
+ const FindingHeader = styled.div`
333
+ display: flex;
334
+ align-items: baseline;
335
+ justify-content: space-between;
336
+ gap: 18px;
337
+
338
+ @media (max-width: 760px) {
339
+ align-items: flex-start;
340
+ flex-direction: column;
341
+ gap: 8px;
342
+ }
343
+ `
344
+
345
+ const FindingTitleGroup = styled.div`
346
+ display: flex;
347
+ align-items: baseline;
348
+ gap: 12px;
349
+ min-width: 0;
350
+ max-width: 100%;
351
+ `
352
+
353
+ const FindingTitle = styled.h2`
354
+ margin: 0;
355
+ color: var(--platform-colors-text, #20242a);
356
+ font-size: clamp(17px, 1.5vw, 21px);
357
+ font-weight: 750;
358
+ letter-spacing: 0;
359
+ line-height: 1.2;
360
+ overflow-wrap: anywhere;
361
+ `
362
+
363
+ const DeleteAction = styled.button`
364
+ padding: 0;
365
+ border: 0;
366
+ background: transparent;
367
+ color: var(--platform-colors-text-tertiary, #9aa2ad);
368
+ font: inherit;
369
+ font-size: 11px;
370
+ font-weight: 800;
371
+ letter-spacing: 0.05em;
372
+ text-transform: uppercase;
373
+ opacity: 0;
374
+ cursor: pointer;
375
+ transition: opacity 120ms ease, color 120ms ease;
376
+
377
+ ${Finding}:hover &,
378
+ &:focus-visible {
379
+ opacity: 0.7;
380
+ }
381
+
382
+ &:hover {
383
+ color: #8a5f49;
384
+ opacity: 1;
385
+ }
386
+ `
387
+
388
+ const Confidence = styled.span<{ $strength: EvidenceStrength }>`
389
+ flex: 0 0 auto;
390
+ color: ${({ $strength }) =>
391
+ $strength === 'strong'
392
+ ? '#3d6b4a'
393
+ : $strength === 'weak'
394
+ ? '#9a694f'
395
+ : 'var(--platform-colors-text-secondary, #68717c)'};
396
+ font-size: 11px;
397
+ font-weight: 800;
398
+ letter-spacing: 0.05em;
399
+ text-transform: uppercase;
400
+ `
401
+
402
+ const Claim = styled.p`
403
+ max-width: 76ch;
404
+ margin: 0;
405
+ color: var(--platform-colors-text, #2e3338);
406
+ font-size: 14.5px;
407
+ line-height: 1.72;
408
+ `
409
+
410
+ const ClaimLine = styled.div`
411
+ display: inline;
412
+ `
413
+
414
+ const Annotation = styled.aside`
415
+ max-width: 72ch;
416
+ border-left: 3px solid rgb(82 90 101 / 36%);
417
+ padding: 4px 0 4px 14px;
418
+ color: var(--platform-colors-text-secondary, #68717c);
419
+ font-size: 13px;
420
+ line-height: 1.55;
421
+ `
422
+
423
+ const EvidenceHandle = styled.button<{ $strength: EvidenceStrength }>`
424
+ display: inline;
425
+ align-items: center;
426
+ min-height: 0;
427
+ margin-left: 6px;
428
+ transform: translateY(-1px);
429
+ border: 0;
430
+ border-bottom: 1px solid
431
+ ${({ $strength }) =>
432
+ $strength === 'strong'
433
+ ? 'rgb(61 107 74 / 38%)'
434
+ : $strength === 'weak'
435
+ ? 'rgb(154 105 79 / 42%)'
436
+ : 'rgb(46 51 56 / 26%)'};
437
+ border-radius: 0;
438
+ background: transparent;
439
+ padding: 0 1px;
440
+ color: ${({ $strength }) =>
441
+ $strength === 'strong'
442
+ ? '#3d6b4a'
443
+ : $strength === 'weak'
444
+ ? '#9a694f'
445
+ : 'var(--platform-colors-text-secondary, #68717c)'};
446
+ font: inherit;
447
+ font-size: 0.78em;
448
+ font-weight: 750;
449
+ letter-spacing: 0.02em;
450
+ cursor: pointer;
451
+ vertical-align: baseline;
452
+
453
+ &:hover,
454
+ &:focus-visible {
455
+ outline: none;
456
+ border-bottom-color: currentColor;
457
+ }
458
+ `
459
+
460
+ const EvidenceDrawer = styled.aside`
461
+ display: grid;
462
+ gap: 10px;
463
+ max-width: 64ch;
464
+ margin-top: 4px;
465
+ border-left: 2px solid rgb(46 51 56 / 18%);
466
+ padding: 2px 0 2px 14px;
467
+ `
468
+
469
+ const EvidenceGrid = styled.div`
470
+ display: grid;
471
+ grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
472
+ gap: 10px;
473
+ margin-top: 2px;
474
+ `
475
+
476
+ const AuditList = styled.section`
477
+ display: grid;
478
+ gap: 14px;
479
+ border-top: 1px solid rgb(46 51 56 / 10%);
480
+ padding-top: 18px;
481
+ `
482
+
483
+ const AuditIntro = styled.div`
484
+ max-width: 70ch;
485
+ color: var(--platform-colors-text-secondary, #68717c);
486
+ font-size: 13px;
487
+ line-height: 1.55;
488
+ `
489
+
490
+ const AuditFinding = styled.div`
491
+ display: grid;
492
+ gap: 9px;
493
+ `
494
+
495
+ const EvidenceCard = styled.div`
496
+ display: grid;
497
+ gap: 8px;
498
+ min-width: 0;
499
+ border: 1px solid rgb(46 51 56 / 10%);
500
+ border-radius: 8px;
501
+ background: rgb(255 255 255 / 56%);
502
+ padding: 12px 13px;
503
+ `
504
+
505
+ const EvidenceTop = styled.div`
506
+ display: flex;
507
+ align-items: baseline;
508
+ justify-content: space-between;
509
+ gap: 10px;
510
+ `
511
+
512
+ const EvidenceLabel = styled.span`
513
+ color: var(--platform-colors-text-tertiary, #8f98a4);
514
+ font-size: 10px;
515
+ font-weight: 850;
516
+ letter-spacing: 0.08em;
517
+ text-transform: uppercase;
518
+ `
519
+
520
+ const EvidenceTitle = styled.a`
521
+ min-width: 0;
522
+ overflow: hidden;
523
+ color: var(--platform-colors-text, #2e3338);
524
+ font-size: 14px;
525
+ font-weight: 750;
526
+ line-height: 1.3;
527
+ text-decoration: none;
528
+ text-overflow: ellipsis;
529
+ white-space: nowrap;
530
+
531
+ &:hover {
532
+ text-decoration: underline;
533
+ text-decoration-color: rgb(46 51 56 / 28%);
534
+ }
535
+ `
536
+
537
+ const EvidenceNote = styled.div`
538
+ color: var(--platform-colors-text-secondary, #68717c);
539
+ font-size: 13px;
540
+ line-height: 1.45;
541
+ `
542
+
543
+ const Quote = styled.blockquote`
544
+ margin: 0;
545
+ border-left: 2px solid rgb(46 51 56 / 16%);
546
+ padding-left: 10px;
547
+ color: var(--platform-colors-text, #3a4047);
548
+ font-size: 12.5px;
549
+ line-height: 1.45;
550
+ `
551
+
552
+ const BottomGrid = styled.div`
553
+ display: grid;
554
+ grid-template-columns: repeat(2, minmax(0, 1fr));
555
+ gap: 16px;
556
+
557
+ @media (max-width: 760px) {
558
+ grid-template-columns: 1fr;
559
+ }
560
+ `
561
+
562
+ const QuietList = styled.section`
563
+ display: grid;
564
+ align-content: start;
565
+ gap: 12px;
566
+ border-top: 1px solid rgb(46 51 56 / 10%);
567
+ padding-top: 16px;
568
+ `
569
+
570
+ const QuietListItems = styled.div`
571
+ display: grid;
572
+ gap: 9px;
573
+ `
574
+
575
+ const QuietListItem = styled.div`
576
+ color: var(--platform-colors-text-secondary, #68717c);
577
+ font-size: 14px;
578
+ line-height: 1.55;
579
+ `
580
+
581
+ const Footer = styled.footer`
582
+ border-top: 1px solid rgb(46 51 56 / 10%);
583
+ padding-top: 14px;
584
+ color: var(--platform-colors-text-tertiary, #8f98a4);
585
+ font-size: 12px;
586
+ line-height: 1.5;
587
+ `
588
+
589
+ //#endregion
590
+
591
+ function SourceTitle({
592
+ source,
593
+ }: {
594
+ source: EvidenceSource
595
+ }): React.ReactElement {
596
+ const content = cleanDisplayText(source.title)
597
+ const href = sourceUrlWithTextFragment(source)
598
+ if (href) {
599
+ return (
600
+ <EvidenceTitle href={href} target="_blank" rel="noreferrer">
601
+ {content}
602
+ </EvidenceTitle>
603
+ )
604
+ }
605
+ return <EvidenceTitle as="span">{content}</EvidenceTitle>
606
+ }
607
+
608
+ function evidenceHandleLabel(finding: EvidenceFinding): string {
609
+ const count = finding?.evidence?.length
610
+ const sourceLabel = count === 1 ? 'source' : 'sources'
611
+ if (finding.confidence === 'strong') return `checked · ${count}`
612
+ if (finding.confidence === 'weak') return `weak · ${count}`
613
+ return `${count} ${sourceLabel}`
614
+ }
615
+
616
+ function evidenceHandleTitle(finding: EvidenceFinding): string {
617
+ const strongest = finding?.evidence?.[0]
618
+ if (!strongest) return 'Show evidence'
619
+ return `Show evidence: ${cleanDisplayText(strongest.title)}`
620
+ }
621
+
622
+ function EvidenceCitation({
623
+ finding,
624
+ open,
625
+ onOpen,
626
+ onToggle,
627
+ }: {
628
+ finding: EvidenceFinding
629
+ open: boolean
630
+ onOpen: () => void
631
+ onToggle: () => void
632
+ }): React.ReactElement | null {
633
+ if (!finding?.evidence?.length) return null
634
+ return (
635
+ <EvidenceHandle
636
+ type="button"
637
+ $strength={finding.confidence}
638
+ onMouseEnter={onOpen}
639
+ onFocus={onOpen}
640
+ onClick={onToggle}
641
+ aria-expanded={open}
642
+ title={evidenceHandleTitle(finding)}
643
+ >
644
+ {evidenceHandleLabel(finding)}
645
+ </EvidenceHandle>
646
+ )
647
+ }
648
+
649
+ function EvidenceCards({
650
+ sources,
651
+ }: {
652
+ sources: EvidenceSource[]
653
+ }): React.ReactElement {
654
+ return (
655
+ <EvidenceGrid>
656
+ {sources.map(source => {
657
+ const showQuote = Boolean(
658
+ source.quote && source.label !== 'Web evidence',
659
+ )
660
+ return (
661
+ <EvidenceCard key={source.id}>
662
+ <EvidenceTop>
663
+ <EvidenceLabel>{source.label}</EvidenceLabel>
664
+ <Confidence $strength={source.strength ?? 'working'}>
665
+ {strengthLabels[source.strength ?? 'working']}
666
+ </Confidence>
667
+ </EvidenceTop>
668
+ <SourceTitle source={source} />
669
+ <EvidenceNote>{cleanDisplayText(source.note)}</EvidenceNote>
670
+ {showQuote ? (
671
+ <Quote>{cleanDisplayText(source.quote ?? '')}</Quote>
672
+ ) : null}
673
+ </EvidenceCard>
674
+ )
675
+ })}
676
+ </EvidenceGrid>
677
+ )
678
+ }
679
+
680
+ export function EvidenceDossier({
681
+ title,
682
+ question,
683
+ answer,
684
+ updatedAt,
685
+ narrative,
686
+ findings,
687
+ uncertainties,
688
+ nextSteps,
689
+ onTitleChange,
690
+ onDeleteFinding,
691
+ footer,
692
+ className,
693
+ }: EvidenceDossierProps): React.ReactElement {
694
+ const [mode, setMode] = useState<DossierRenderMode>('read')
695
+ const [openEvidenceId, setOpenEvidenceId] = useState<string | null>(null)
696
+ const updatedLabel = formatUpdatedAt(updatedAt)
697
+ const safeFindings = Array.isArray(findings)
698
+ ? findings.map(normalizeEvidenceFinding).filter(isEvidenceFinding)
699
+ : []
700
+ const safeNarrative = Array.isArray(narrative) ? narrative : []
701
+ const safeUncertainties = Array.isArray(uncertainties) ? uncertainties : []
702
+ const safeNextSteps = Array.isArray(nextSteps) ? nextSteps : []
703
+ const activeEvidence =
704
+ openEvidenceId && mode === 'read'
705
+ ? safeFindings.find(finding => finding.id === openEvidenceId)
706
+ : null
707
+ const toggleEvidence = (findingId: string): void => {
708
+ setOpenEvidenceId(current => (current === findingId ? null : findingId))
709
+ }
710
+ const leadEvidence = safeFindings[0]
711
+ const narrativeEvidence = (index: number): EvidenceFinding | undefined =>
712
+ safeFindings[index + 1] ?? safeFindings[index] ?? safeFindings[0]
713
+
714
+ return (
715
+ <Dossier className={className}>
716
+ <ModeDock>
717
+ <ModeSwitch aria-label="Dossier reading density">
718
+ {(['read', 'review', 'audit'] as DossierRenderMode[]).map(item => (
719
+ <ModeButton
720
+ key={item}
721
+ type="button"
722
+ $active={mode === item}
723
+ title={modeDescriptions[item]}
724
+ onClick={() => {
725
+ setMode(item)
726
+ setOpenEvidenceId(null)
727
+ }}
728
+ >
729
+ {modeLabels[item]}
730
+ </ModeButton>
731
+ ))}
732
+ </ModeSwitch>
733
+ <ModeDescription>{modeDescriptions[mode]}</ModeDescription>
734
+ </ModeDock>
735
+ <Header>
736
+ <Kicker>Living dossier</Kicker>
737
+ {onTitleChange ? (
738
+ <TitleInput
739
+ value={title}
740
+ rows={1}
741
+ onChange={event => onTitleChange(event.currentTarget.value)}
742
+ aria-label="Dossier title"
743
+ />
744
+ ) : (
745
+ <Title>{cleanDisplayText(title)}</Title>
746
+ )}
747
+ <Question>{cleanDisplayText(question)}</Question>
748
+ {updatedLabel ? (
749
+ <UpdatedStamp>Updated {updatedLabel}</UpdatedStamp>
750
+ ) : null}
751
+ </Header>
752
+
753
+ {mode !== 'audit' ? (
754
+ <Narrative aria-label="Dossier narrative">
755
+ <Kicker>Dossier</Kicker>
756
+ <Lead>
757
+ {cleanDisplayText(answer)}
758
+ {leadEvidence ? (
759
+ <EvidenceCitation
760
+ finding={leadEvidence}
761
+ open={openEvidenceId === leadEvidence.id}
762
+ onOpen={() => setOpenEvidenceId(leadEvidence.id)}
763
+ onToggle={() => toggleEvidence(leadEvidence.id)}
764
+ />
765
+ ) : null}
766
+ </Lead>
767
+ {activeEvidence && leadEvidence && activeEvidence.id === leadEvidence.id ? (
768
+ <EvidenceDrawer>
769
+ <EvidenceCards sources={activeEvidence?.evidence} />
770
+ </EvidenceDrawer>
771
+ ) : null}
772
+ {safeNarrative.map((item, index) => {
773
+ const relatedFinding = narrativeEvidence(index)
774
+ const paragraphEvidence =
775
+ activeEvidence && relatedFinding && activeEvidence.id === relatedFinding.id
776
+ ? activeEvidence
777
+ : null
778
+ return (
779
+ <NarrativeText key={`${index}-${item}`}>
780
+ {cleanDisplayText(item)}
781
+ {relatedFinding ? (
782
+ <EvidenceCitation
783
+ finding={relatedFinding}
784
+ open={openEvidenceId === relatedFinding.id}
785
+ onOpen={() => setOpenEvidenceId(relatedFinding.id)}
786
+ onToggle={() => toggleEvidence(relatedFinding.id)}
787
+ />
788
+ ) : null}
789
+ {paragraphEvidence ? (
790
+ <EvidenceDrawer>
791
+ <EvidenceCards sources={paragraphEvidence.evidence} />
792
+ </EvidenceDrawer>
793
+ ) : null}
794
+ </NarrativeText>
795
+ )
796
+ })}
797
+ </Narrative>
798
+ ) : null}
799
+
800
+ {mode === 'audit' ? (
801
+ <AuditList aria-label="Source audit">
802
+ <Kicker>Source trace</Kicker>
803
+ <AuditIntro>
804
+ Each claim is shown only to explain what the sources support. Use
805
+ this view to verify provenance, links, quotes, and weak spots.
806
+ </AuditIntro>
807
+ {safeFindings.map(finding => (
808
+ <AuditFinding key={finding.id}>
809
+ <FindingTitle>{cleanDisplayText(finding.title)}</FindingTitle>
810
+ <Claim>{cleanDisplayText(finding.claim)}</Claim>
811
+ <EvidenceCards sources={finding.evidence} />
812
+ </AuditFinding>
813
+ ))}
814
+ </AuditList>
815
+ ) : (
816
+ <Findings aria-label="Evidence-backed positions">
817
+ <Kicker>{mode === 'read' ? 'Argument' : 'Claim review'}</Kicker>
818
+ {safeFindings.map((finding, index) => (
819
+ <Finding key={finding.id}>
820
+ <FindingNumber>
821
+ {String(index + 1).padStart(2, '0')}
822
+ </FindingNumber>
823
+ <FindingBody>
824
+ <FindingHeader>
825
+ <FindingTitleGroup>
826
+ <FindingTitle>
827
+ {cleanDisplayText(finding.title)}
828
+ </FindingTitle>
829
+ {onDeleteFinding ? (
830
+ <DeleteAction
831
+ type="button"
832
+ onClick={() => onDeleteFinding(finding.id)}
833
+ aria-label={`Delete ${finding.title}`}
834
+ >
835
+ Delete
836
+ </DeleteAction>
837
+ ) : null}
838
+ </FindingTitleGroup>
839
+ {mode === 'review' ? (
840
+ <Confidence $strength={finding.confidence}>
841
+ {strengthLabels[finding.confidence]}
842
+ </Confidence>
843
+ ) : null}
844
+ </FindingHeader>
845
+ <Claim>
846
+ <ClaimLine>{cleanDisplayText(finding.claim)}</ClaimLine>
847
+ <EvidenceCitation
848
+ finding={finding}
849
+ open={openEvidenceId === finding.id}
850
+ onOpen={() => setOpenEvidenceId(finding.id)}
851
+ onToggle={() => toggleEvidence(finding.id)}
852
+ />
853
+ </Claim>
854
+ {finding.annotation ? (
855
+ <Annotation>
856
+ {cleanDisplayText(finding.annotation)}
857
+ </Annotation>
858
+ ) : null}
859
+ {mode === 'review' ? (
860
+ <EvidenceCards sources={finding.evidence} />
861
+ ) : activeEvidence?.id === finding.id ? (
862
+ <EvidenceDrawer>
863
+ <EvidenceCards sources={activeEvidence.evidence} />
864
+ </EvidenceDrawer>
865
+ ) : null}
866
+ </FindingBody>
867
+ </Finding>
868
+ ))}
869
+ </Findings>
870
+ )}
871
+
872
+ <BottomGrid>
873
+ <QuietList>
874
+ <Kicker>Uncertainty</Kicker>
875
+ <QuietListItems>
876
+ {safeUncertainties.map(item => (
877
+ <QuietListItem key={item}>{cleanDisplayText(item)}</QuietListItem>
878
+ ))}
879
+ </QuietListItems>
880
+ </QuietList>
881
+ <QuietList>
882
+ <Kicker>Next</Kicker>
883
+ <QuietListItems>
884
+ {safeNextSteps.map(item => (
885
+ <QuietListItem key={item}>{cleanDisplayText(item)}</QuietListItem>
886
+ ))}
887
+ </QuietListItems>
888
+ </QuietList>
889
+ </BottomGrid>
890
+
891
+ {footer ? <Footer>{footer}</Footer> : null}
892
+ </Dossier>
893
+ )
894
+ }