@arbidocs/blocks 0.5.124 → 0.5.126

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 (152) hide show
  1. package/dist/{ClippingMediaViewer-ZFEX5A3U.cjs → ClippingMediaViewer-47BAXNLO.cjs} +10 -11
  2. package/dist/ClippingMediaViewer-47BAXNLO.cjs.map +1 -0
  3. package/dist/{ClippingPdfViewer-3LRTEPHV.cjs → ClippingPdfViewer-D5BYHEAH.cjs} +16 -14
  4. package/dist/ClippingPdfViewer-D5BYHEAH.cjs.map +1 -0
  5. package/dist/{ConnectedCallDock-XJOYVVAH.cjs → ConnectedCallDock-733O5M36.cjs} +5 -5
  6. package/dist/{ConnectedCallDock-XJOYVVAH.cjs.map → ConnectedCallDock-733O5M36.cjs.map} +1 -1
  7. package/dist/{ConnectedCallRoom-CUDYAPQK.cjs → ConnectedCallRoom-TAUGSRGV.cjs} +5 -5
  8. package/dist/{ConnectedCallRoom-CUDYAPQK.cjs.map → ConnectedCallRoom-TAUGSRGV.cjs.map} +1 -1
  9. package/dist/{ConnectedNativeDocxComparison-QKHCYD6W.cjs → ConnectedNativeDocxComparison-HDE522YC.cjs} +9 -9
  10. package/dist/{ConnectedNativeDocxComparison-QKHCYD6W.cjs.map → ConnectedNativeDocxComparison-HDE522YC.cjs.map} +1 -1
  11. package/dist/{ConnectedPresentation-OV2KBOLK.cjs → ConnectedPresentation-SI3L77J4.cjs} +33 -34
  12. package/dist/ConnectedPresentation-SI3L77J4.cjs.map +1 -0
  13. package/dist/{ConnectedSlidesTab-LUAGZ6FD.cjs → ConnectedSlidesTab-JSPU6EY5.cjs} +37 -43
  14. package/dist/ConnectedSlidesTab-JSPU6EY5.cjs.map +1 -0
  15. package/dist/{ConnectedStreamedWordArtifact-AG62232A.cjs → ConnectedStreamedWordArtifact-4FHPOAFZ.cjs} +9 -9
  16. package/dist/{ConnectedStreamedWordArtifact-AG62232A.cjs.map → ConnectedStreamedWordArtifact-4FHPOAFZ.cjs.map} +1 -1
  17. package/dist/{DocumentReviewCard-M53XFLI2.cjs → DocumentReviewCard-BDNXP3D4.cjs} +4 -4
  18. package/dist/{DocumentReviewCard-M53XFLI2.cjs.map → DocumentReviewCard-BDNXP3D4.cjs.map} +1 -1
  19. package/dist/{FilesViewerDialog-JWKNJLWG.cjs → FilesViewerDialog-WQRNSKWM.cjs} +10 -11
  20. package/dist/FilesViewerDialog-WQRNSKWM.cjs.map +1 -0
  21. package/dist/{NativeDocxEditor-2LQY2CTV.cjs → NativeDocxEditor-SUDULUN3.cjs} +6 -6
  22. package/dist/{NativeDocxEditor-2LQY2CTV.cjs.map → NativeDocxEditor-SUDULUN3.cjs.map} +1 -1
  23. package/dist/{SpotlightStrip-ED22UYTM.cjs → SpotlightStrip-QQ2WUI32.cjs} +28 -29
  24. package/dist/SpotlightStrip-QQ2WUI32.cjs.map +1 -0
  25. package/dist/WorkspaceTableView-3XYAV4BV.cjs +1 -1
  26. package/dist/agents/index.cjs +1 -1
  27. package/dist/ai-IEIR7SC2.cjs +1 -1
  28. package/dist/calls/index.cjs +2 -2
  29. package/dist/{chunk-ZZZITHGK.cjs → chunk-26SFI2BH.cjs} +16 -3
  30. package/dist/chunk-26SFI2BH.cjs.map +1 -0
  31. package/dist/{chunk-NG2EE7HR.cjs → chunk-463QM764.cjs} +3 -3
  32. package/dist/{chunk-NG2EE7HR.cjs.map → chunk-463QM764.cjs.map} +1 -1
  33. package/dist/{chunk-XGM4VVNV.cjs → chunk-4KEQ62SL.cjs} +6 -6
  34. package/dist/{chunk-XGM4VVNV.cjs.map → chunk-4KEQ62SL.cjs.map} +1 -1
  35. package/dist/{chunk-YGBHY67W.cjs → chunk-6B4CCDDB.cjs} +5 -5
  36. package/dist/{chunk-YGBHY67W.cjs.map → chunk-6B4CCDDB.cjs.map} +1 -1
  37. package/dist/{chunk-JY7UUZQG.cjs → chunk-7Q46YAX4.cjs} +74 -71
  38. package/dist/chunk-7Q46YAX4.cjs.map +1 -0
  39. package/dist/{chunk-3ZZZO4F7.cjs → chunk-BY3QA3S5.cjs} +9 -9
  40. package/dist/{chunk-3ZZZO4F7.cjs.map → chunk-BY3QA3S5.cjs.map} +1 -1
  41. package/dist/{chunk-VKH3BEZE.cjs → chunk-CMPHJRVV.cjs} +3 -3
  42. package/dist/{chunk-VKH3BEZE.cjs.map → chunk-CMPHJRVV.cjs.map} +1 -1
  43. package/dist/{chunk-BX6G2ZCI.cjs → chunk-FB3JXSQT.cjs} +71 -43
  44. package/dist/chunk-FB3JXSQT.cjs.map +1 -0
  45. package/dist/{chunk-EMRY62HS.cjs → chunk-FGOJBOZX.cjs} +4 -4
  46. package/dist/{chunk-EMRY62HS.cjs.map → chunk-FGOJBOZX.cjs.map} +1 -1
  47. package/dist/{chunk-V6MW7B4B.cjs → chunk-I4X72WUT.cjs} +6 -6
  48. package/dist/{chunk-V6MW7B4B.cjs.map → chunk-I4X72WUT.cjs.map} +1 -1
  49. package/dist/{chunk-ORNLCBNE.cjs → chunk-JVEDKKNO.cjs} +43 -4
  50. package/dist/chunk-JVEDKKNO.cjs.map +1 -0
  51. package/dist/{chunk-7557LDAN.cjs → chunk-LOB2ATS5.cjs} +23 -10
  52. package/dist/chunk-LOB2ATS5.cjs.map +1 -0
  53. package/dist/{chunk-MXBXZXXQ.cjs → chunk-LXM5QXII.cjs} +3 -3
  54. package/dist/{chunk-MXBXZXXQ.cjs.map → chunk-LXM5QXII.cjs.map} +1 -1
  55. package/dist/chunk-OALQGO5C.cjs +18 -0
  56. package/dist/{chunk-SDRYSLOX.cjs.map → chunk-OALQGO5C.cjs.map} +1 -1
  57. package/dist/{chunk-2CSLFAGF.cjs → chunk-Q6N5D3R5.cjs} +34 -8
  58. package/dist/chunk-Q6N5D3R5.cjs.map +1 -0
  59. package/dist/{chunk-MVXHTAJ4.cjs → chunk-QWVUBTW3.cjs} +9 -9
  60. package/dist/{chunk-MVXHTAJ4.cjs.map → chunk-QWVUBTW3.cjs.map} +1 -1
  61. package/dist/{chunk-ADPRIBSH.cjs → chunk-X7ALF4MP.cjs} +7 -7
  62. package/dist/{chunk-ADPRIBSH.cjs.map → chunk-X7ALF4MP.cjs.map} +1 -1
  63. package/dist/{chunk-COD5ENHK.cjs → chunk-XNB27POJ.cjs} +4 -4
  64. package/dist/{chunk-COD5ENHK.cjs.map → chunk-XNB27POJ.cjs.map} +1 -1
  65. package/dist/{chunk-H236SNEV.cjs → chunk-ZCU72Q76.cjs} +191 -165
  66. package/dist/chunk-ZCU72Q76.cjs.map +1 -0
  67. package/dist/connect/docx.cjs +13 -13
  68. package/dist/connect/index.cjs +198 -163
  69. package/dist/connect/index.cjs.map +1 -1
  70. package/dist/connect/viewer.cjs +8 -9
  71. package/dist/connection/ai.d.ts +1 -0
  72. package/dist/connection/docx/ConnectedNativeDocxEditor.js +4 -5
  73. package/dist/connection/docx/ConnectedNativeDocxEditor.js.map +2 -2
  74. package/dist/connection/docx/save-native-version.d.ts +3 -0
  75. package/dist/connection/docx/save-native-version.js +19 -0
  76. package/dist/connection/docx/save-native-version.js.map +7 -0
  77. package/dist/connection/presentation/ClippingPdfViewer.js +8 -5
  78. package/dist/connection/presentation/ClippingPdfViewer.js.map +2 -2
  79. package/dist/connection/presentation/ConnectedSlidesTab.js +8 -13
  80. package/dist/connection/presentation/ConnectedSlidesTab.js.map +2 -2
  81. package/dist/connection/presentation/usePresentationController.js +24 -15
  82. package/dist/connection/presentation/usePresentationController.js.map +2 -2
  83. package/dist/connection/presentation/usePresentationFiles.d.ts +2 -1
  84. package/dist/connection/presentation/usePresentationFiles.js +15 -6
  85. package/dist/connection/presentation/usePresentationFiles.js.map +2 -2
  86. package/dist/connection/search/ConnectedSearchPage.d.ts +0 -11
  87. package/dist/connection/search/ConnectedSearchPage.js +3 -0
  88. package/dist/connection/search/ConnectedSearchPage.js.map +2 -2
  89. package/dist/connection/search/SearchResultRows.js +75 -43
  90. package/dist/connection/search/SearchResultRows.js.map +2 -2
  91. package/dist/connection/viewer/CleanViewControl.d.ts +3 -1
  92. package/dist/connection/viewer/CleanViewControl.js +5 -1
  93. package/dist/connection/viewer/CleanViewControl.js.map +2 -2
  94. package/dist/connection/viewer/ConnectedDocumentViewer.js +1 -1
  95. package/dist/connection/viewer/ConnectedDocumentViewer.js.map +2 -2
  96. package/dist/connection/viewer/VersionDropdown.js +1 -1
  97. package/dist/connection/viewer/VersionDropdown.js.map +1 -1
  98. package/dist/connection/viewer/ViewerPdfPane.js +2 -0
  99. package/dist/connection/viewer/ViewerPdfPane.js.map +2 -2
  100. package/dist/connection/viewer/pdf/usePassageTextFallback.d.ts +12 -0
  101. package/dist/connection/viewer/pdf/usePassageTextFallback.js +30 -0
  102. package/dist/connection/viewer/pdf/usePassageTextFallback.js.map +7 -0
  103. package/dist/connection/viewer/pdf/usePdfPassageHighlights.js +2 -11
  104. package/dist/connection/viewer/pdf/usePdfPassageHighlights.js.map +2 -2
  105. package/dist/connection/viewer/useCleanView.d.ts +1 -1
  106. package/dist/connection/viewer/useCleanView.js +2 -2
  107. package/dist/connection/viewer/useCleanView.js.map +2 -2
  108. package/dist/connection/viewer/useViewedDocument.d.ts +1 -0
  109. package/dist/connection/viewer/useViewerTabs.js +13 -1
  110. package/dist/connection/viewer/useViewerTabs.js.map +2 -2
  111. package/dist/docx/DocxCommandMenu.js +2 -1
  112. package/dist/docx/DocxCommandMenu.js.map +2 -2
  113. package/dist/docx/NativeDocxEditor.d.ts +2 -1
  114. package/dist/docx/NativeDocxEditor.js +30 -5
  115. package/dist/docx/NativeDocxEditor.js.map +2 -2
  116. package/dist/docx/index.cjs +38 -38
  117. package/dist/embed/index.cjs +2 -2
  118. package/dist/facts/index.cjs +43 -43
  119. package/dist/index.cjs +73 -73
  120. package/dist/presentation/ClipCaptureOverlay.js +14 -1
  121. package/dist/presentation/ClipCaptureOverlay.js.map +2 -2
  122. package/dist/presentation/ClipImage.js +7 -4
  123. package/dist/presentation/ClipImage.js.map +2 -2
  124. package/dist/presentation/ClipTray.js +11 -7
  125. package/dist/presentation/ClipTray.js.map +2 -2
  126. package/dist/presentation/SlidesEditor.js +6 -2
  127. package/dist/presentation/SlidesEditor.js.map +2 -2
  128. package/dist/presentation/store.d.ts +2 -0
  129. package/dist/presentation/store.js +15 -0
  130. package/dist/presentation/store.js.map +3 -3
  131. package/dist/settings/index.cjs +1 -1
  132. package/dist/studio/index.cjs +1 -1
  133. package/dist/styles.css +1 -1
  134. package/dist/workflow-canvas/index.cjs +12 -12
  135. package/node_modules/@arbidocs/coedit/package.json +1 -1
  136. package/package.json +5 -5
  137. package/dist/ClippingMediaViewer-ZFEX5A3U.cjs.map +0 -1
  138. package/dist/ClippingPdfViewer-3LRTEPHV.cjs.map +0 -1
  139. package/dist/ConnectedPresentation-OV2KBOLK.cjs.map +0 -1
  140. package/dist/ConnectedSlidesTab-LUAGZ6FD.cjs.map +0 -1
  141. package/dist/FilesViewerDialog-JWKNJLWG.cjs.map +0 -1
  142. package/dist/SpotlightStrip-ED22UYTM.cjs.map +0 -1
  143. package/dist/chunk-2CSLFAGF.cjs.map +0 -1
  144. package/dist/chunk-7557LDAN.cjs.map +0 -1
  145. package/dist/chunk-BX6G2ZCI.cjs.map +0 -1
  146. package/dist/chunk-GFUOCGY6.cjs +0 -32
  147. package/dist/chunk-GFUOCGY6.cjs.map +0 -1
  148. package/dist/chunk-H236SNEV.cjs.map +0 -1
  149. package/dist/chunk-JY7UUZQG.cjs.map +0 -1
  150. package/dist/chunk-ORNLCBNE.cjs.map +0 -1
  151. package/dist/chunk-SDRYSLOX.cjs +0 -18
  152. package/dist/chunk-ZZZITHGK.cjs.map +0 -1
@@ -1,13 +1,13 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var chunkXGM4VVNV_cjs = require('../chunk-XGM4VVNV.cjs');
4
+ var chunk4KEQ62SL_cjs = require('../chunk-4KEQ62SL.cjs');
5
5
  require('../chunk-YZ6YOOTF.cjs');
6
6
  require('../chunk-KWJW5TMH.cjs');
7
+ require('../chunk-AEQT2QQX.cjs');
7
8
  require('../chunk-7NLO7ASW.cjs');
8
9
  require('../chunk-7PN3YENO.cjs');
9
10
  var chunkUFT22LAB_cjs = require('../chunk-UFT22LAB.cjs');
10
- require('../chunk-AEQT2QQX.cjs');
11
11
  var chunkADDQKZ5I_cjs = require('../chunk-ADDQKZ5I.cjs');
12
12
  require('../chunk-5QERK4PK.cjs');
13
13
  require('../chunk-T73BHNQX.cjs');
@@ -15,7 +15,7 @@ require('../chunk-HYUKPHCT.cjs');
15
15
  require('../chunk-ALK3PUUB.cjs');
16
16
  require('../chunk-223D4727.cjs');
17
17
  require('../chunk-SNUBQMIS.cjs');
18
- var chunk7557LDAN_cjs = require('../chunk-7557LDAN.cjs');
18
+ var chunkLOB2ATS5_cjs = require('../chunk-LOB2ATS5.cjs');
19
19
  require('../chunk-4ONPEVTP.cjs');
20
20
  require('../chunk-V3KLUWQQ.cjs');
21
21
  var chunkP5AQYC4D_cjs = require('../chunk-P5AQYC4D.cjs');
@@ -29,7 +29,7 @@ require('../chunk-ORELHENJ.cjs');
29
29
  require('../chunk-NWNVWO6L.cjs');
30
30
  require('../chunk-SK6HQ46Q.cjs');
31
31
  require('../chunk-TG2GM7WJ.cjs');
32
- require('../chunk-VKH3BEZE.cjs');
32
+ require('../chunk-CMPHJRVV.cjs');
33
33
  var chunkNSD37OF5_cjs = require('../chunk-NSD37OF5.cjs');
34
34
  require('../chunk-OEJQFPWO.cjs');
35
35
  var chunkQG2U2JGX_cjs = require('../chunk-QG2U2JGX.cjs');
@@ -53,8 +53,6 @@ require('../chunk-YVCOQHCE.cjs');
53
53
  require('../chunk-JV5TVUOU.cjs');
54
54
  require('../chunk-ZVEW3JBT.cjs');
55
55
  require('../chunk-M74FWYNL.cjs');
56
- require('../chunk-QUMUAJ2P.cjs');
57
- require('../chunk-VZOOGA47.cjs');
58
56
  require('../chunk-SBWIA4PL.cjs');
59
57
  require('../chunk-QMIHLJMH.cjs');
60
58
  var chunkHQDR43JC_cjs = require('../chunk-HQDR43JC.cjs');
@@ -64,6 +62,8 @@ var chunk3YZKDCWY_cjs = require('../chunk-3YZKDCWY.cjs');
64
62
  require('../chunk-SJSGSHIT.cjs');
65
63
  require('../chunk-ZW6FPIFC.cjs');
66
64
  require('../chunk-ZT5MIFLC.cjs');
65
+ require('../chunk-QUMUAJ2P.cjs');
66
+ require('../chunk-VZOOGA47.cjs');
67
67
  require('../chunk-5SKHK3BG.cjs');
68
68
  require('../chunk-IDCD5TEZ.cjs');
69
69
  require('../chunk-M2FOXGJU.cjs');
@@ -3045,7 +3045,7 @@ function ConnectedWorkflowsPage({
3045
3045
  const config = host.workflows;
3046
3046
  const { openConversation, openFiles } = chunkTKHCIHE2_cjs.useBlocksEnvironment();
3047
3047
  const openWorkflow = chunkP5AQYC4D_cjs.useOpenWorkflow();
3048
- const renderPerson = chunkXGM4VVNV_cjs.useRenderTaskPerson();
3048
+ const renderPerson = chunk4KEQ62SL_cjs.useRenderTaskPerson();
3049
3049
  const refreshDocuments = react$2.useRefreshDocuments();
3050
3050
  const invalidateTags = react$2.useInvalidateTagData();
3051
3051
  const { data: tags = [] } = react$2.useTags();
@@ -3076,7 +3076,7 @@ function ConnectedWorkflowsPage({
3076
3076
  openHistory,
3077
3077
  closeHistoryTab,
3078
3078
  selectHistoryTab
3079
- } = chunkXGM4VVNV_cjs.useWorkflowsPageState({
3079
+ } = chunk4KEQ62SL_cjs.useWorkflowsPageState({
3080
3080
  historyWorkflowId: requestedHistoryId ?? historyWorkflowId,
3081
3081
  onHistoryWorkflowConsumed: consumeHistory
3082
3082
  });
@@ -3144,7 +3144,7 @@ function ConnectedWorkflowsPage({
3144
3144
  return (
3145
3145
  // The page in the Workflows page's frame: padded, scrolling.
3146
3146
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full min-h-0 w-full overflow-auto p-4 sm:p-6", children: /* @__PURE__ */ jsxRuntime.jsx(
3147
- chunkXGM4VVNV_cjs.ConnectedWorkflowRunPage,
3147
+ chunk4KEQ62SL_cjs.ConnectedWorkflowRunPage,
3148
3148
  {
3149
3149
  workflow: running.workflow,
3150
3150
  projectFrom: running.projectFrom,
@@ -3232,7 +3232,7 @@ function ConnectedWorkflowsPage({
3232
3232
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-0 flex-col", "data-testid": testId, children: [
3233
3233
  pageHeader && /* @__PURE__ */ jsxRuntime.jsx(chunkNSD37OF5_cjs.PageHeader, { ...pageHeader }),
3234
3234
  /* @__PURE__ */ jsxRuntime.jsx(
3235
- chunk7557LDAN_cjs.ConnectedWorkflowsTable,
3235
+ chunkLOB2ATS5_cjs.ConnectedWorkflowsTable,
3236
3236
  {
3237
3237
  onOpenTemplate: (id) => openBuilder({ kind: "template", id }),
3238
3238
  tabs: [
@@ -3262,7 +3262,7 @@ function ConnectedWorkflowsPage({
3262
3262
  }
3263
3263
  },
3264
3264
  content: hostTab ? hostTab.content : runsFor ? /* @__PURE__ */ jsxRuntime.jsx(
3265
- chunkXGM4VVNV_cjs.ConnectedWorkflowHistory,
3265
+ chunk4KEQ62SL_cjs.ConnectedWorkflowHistory,
3266
3266
  {
3267
3267
  inline: true,
3268
3268
  hideHeader: true,
@@ -3326,7 +3326,7 @@ function ConnectedWorkflowsPage({
3326
3326
  }
3327
3327
  ),
3328
3328
  /* @__PURE__ */ jsxRuntime.jsx(
3329
- chunkXGM4VVNV_cjs.ConnectedTaskComposer,
3329
+ chunk4KEQ62SL_cjs.ConnectedTaskComposer,
3330
3330
  {
3331
3331
  kind: "workflow",
3332
3332
  saveOnly: true,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arbidocs/coedit",
3
- "version": "0.5.124",
3
+ "version": "0.5.126",
4
4
  "description": "Real-time collaboration for the ARBI native DOCX editor: a Yjs mirror of the canonical OOXML package, session, and server runtime",
5
5
  "type": "module",
6
6
  "sideEffects": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arbidocs/blocks",
3
- "version": "0.5.124",
3
+ "version": "0.5.126",
4
4
  "description": "Composable, domain-neutral UI blocks for ARBI apps, organised by capability (retrieval, facts, agents, workspaces, citations, collaboration) plus shell, theme and site primitives, on top of @arbidocs/react.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -344,9 +344,9 @@
344
344
  "custom-site:test:e2e": "playwright test -c examples/custom-site/playwright.config.ts"
345
345
  },
346
346
  "dependencies": {
347
- "@arbidocs/client": "0.5.124",
348
- "@arbidocs/coedit": "0.5.124",
349
- "@arbidocs/sdk": "0.5.124",
347
+ "@arbidocs/client": "0.5.126",
348
+ "@arbidocs/coedit": "0.5.126",
349
+ "@arbidocs/sdk": "0.5.126",
350
350
  "@auth0/auth0-react": "^2.19.0",
351
351
  "@dnd-kit/core": "^6.3.1",
352
352
  "@dnd-kit/sortable": "^10.0.0",
@@ -397,7 +397,7 @@
397
397
  "y-protocols": "^1.0.6"
398
398
  },
399
399
  "peerDependencies": {
400
- "@arbidocs/react": "0.5.124",
400
+ "@arbidocs/react": "0.5.126",
401
401
  "@lexical/html": "0.35.0",
402
402
  "@lexical/link": "0.35.0",
403
403
  "@lexical/list": "0.35.0",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/connection/presentation/ClippingMediaViewer.tsx"],"names":["useObjectUrl","useRef","useState","useEffect","useCallback","useSpotlight","mediaCaption","jsxs","jsx","ClipCaptureOverlay","Button","Pause","Play","formatTime","Plus"],"mappings":"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","file":"ClippingMediaViewer-ZFEX5A3U.cjs","sourcesContent":["/**\n * An image or a video while clipping for a presentation: drag over it and that area becomes a clip\n * (of a video: from the moment on screen). A video has its own small bar (play / pause, scrub, and\n * \"Add whole frame\"), since the drag area covers the picture.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { Pause, Play, Plus } from 'lucide-react'\nimport { Button } from '@arbidocs/react/ui'\nimport { ClipCaptureOverlay } from '../../presentation/ClipCaptureOverlay'\nimport { formatTime, mediaCaption, type Rect } from '../../presentation/model'\nimport { useSpotlight } from '../../presentation/spotlight'\nimport { useObjectUrl } from '../../presentation/useObjectUrl'\n\nexport interface ClippingMediaViewerProps {\n file: Blob\n kind: 'image' | 'video'\n documentId: string\n fileName: string\n}\n\nconst WHOLE: Rect = { x: 0, y: 0, w: 1, h: 1 }\n\nexport function ClippingMediaViewer({\n file,\n kind,\n documentId,\n fileName,\n}: ClippingMediaViewerProps) {\n const url = useObjectUrl(file)\n const videoRef = useRef<HTMLVideoElement>(null)\n const imageRef = useRef<HTMLImageElement>(null)\n // The picture's own width / height, once known.\n const [natural, setNatural] = useState<number | null>(null)\n const [time, setTime] = useState(0)\n const [duration, setDuration] = useState(0)\n const [playing, setPlaying] = useState(false)\n\n // Its size, read as soon as it is known: at once if it already loaded (a cached file can load\n // before any handler is attached), else when it does.\n useEffect(() => {\n const media = videoRef.current ?? imageRef.current\n if (!media || !url) return\n const read = () => {\n const width = media instanceof HTMLVideoElement ? media.videoWidth : media.naturalWidth\n const height = media instanceof HTMLVideoElement ? media.videoHeight : media.naturalHeight\n if (width && height) setNatural(width / height)\n if (media instanceof HTMLVideoElement) setDuration(media.duration || 0)\n }\n read()\n const event = media instanceof HTMLVideoElement ? 'loadedmetadata' : 'load'\n media.addEventListener(event, read)\n return () => media.removeEventListener(event, read)\n }, [url])\n\n const capture = useCallback(\n ({ rect, aspect }: { rect: Rect; aspect: number }) => {\n const start = kind === 'video' ? (videoRef.current?.currentTime ?? 0) : undefined\n useSpotlight.getState().gather({\n kind,\n documentId,\n fileName,\n page: 1,\n rect,\n aspect,\n masks: [],\n source: file,\n start,\n caption: mediaCaption(fileName, start),\n })\n },\n [kind, documentId, fileName, file]\n )\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\" data-testid=\"clipping-media-viewer\">\n <div className=\"border-b border-border bg-muted px-3 py-1.5 text-xs text-muted-foreground\">\n {kind === 'video'\n ? 'Drag over the video to clip that area from this moment, or add the whole frame.'\n : 'Drag over the part of the image to clip it.'}\n </div>\n <div className=\"relative min-h-0 flex-1 bg-black\" style={{ containerType: 'size' }}>\n {/* The picture as large as fits, aspect kept; the drag area covers exactly it. */}\n <div\n className=\"absolute inset-0 m-auto\"\n style={\n natural\n ? {\n width: `min(100cqw, calc(100cqh * ${natural}))`,\n height: `min(100cqh, calc(100cqw / ${natural}))`,\n }\n : { width: '100%', height: '100%' }\n }\n >\n {kind === 'video' ? (\n <video\n ref={videoRef}\n src={url}\n playsInline\n preload=\"auto\"\n className=\"block h-full w-full\"\n onTimeUpdate={(event) => setTime(event.currentTarget.currentTime)}\n onPlay={() => setPlaying(true)}\n onPause={() => setPlaying(false)}\n />\n ) : (\n <img\n ref={imageRef}\n src={url}\n alt={fileName}\n draggable={false}\n className=\"block h-full w-full select-none\"\n />\n )}\n {natural && <ClipCaptureOverlay page={1} onCapture={capture} />}\n </div>\n </div>\n {kind === 'video' && (\n <div className=\"flex items-center gap-2 border-t border-border px-3 py-1.5\">\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n className=\"size-8\"\n onClick={() => {\n const v = videoRef.current\n if (!v) return\n if (v.paused) void v.play()\n else v.pause()\n }}\n aria-label={playing ? 'Pause' : 'Play'}\n >\n {playing ? <Pause className=\"size-4\" /> : <Play className=\"size-4\" />}\n </Button>\n <input\n type=\"range\"\n min={0}\n max={duration || 0}\n step={0.1}\n value={time}\n onChange={(event) => {\n const v = videoRef.current\n if (v) v.currentTime = Number(event.target.value)\n }}\n className=\"min-w-0 flex-1\"\n aria-label=\"Position in the video\"\n />\n <span className=\"text-xs tabular-nums text-muted-foreground\">\n {formatTime(time)} / {formatTime(duration)}\n </span>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n disabled={!natural}\n onClick={() => natural && capture({ rect: WHOLE, aspect: natural })}\n data-testid=\"clip-whole-frame\"\n >\n <Plus className=\"mr-1 size-4\" />\n Add whole frame\n </Button>\n </div>\n )}\n </div>\n )\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/connection/presentation/ClippingPdfViewer.tsx"],"names":["useDocumentTools","useState","useEffect","createMarkupStore","useParsedDocument","useProcessedChunks","useMemo","placedPassagesByPage","passageClipRect","jsx","useCallback","useSpotlight","ClipCaptureOverlay","jsxs"],"mappings":"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","file":"ClippingPdfViewer-3LRTEPHV.cjs","sourcesContent":["/**\n * The PDF while clipping: a click on a passage clips that passage (outlined under the pointer), a\n * drag over a page clips that area. Clips are gathered in the viewer's spotlight strip\n * (`SpotlightStrip`), to show over the document or keep as a slide.\n *\n * Redactions are honoured: when the version has markup, its redaction marks (all but rejected\n * ones) are read first and every clip carries those over its area as black masks, so a redacted\n * passage never reaches a shared screen. Clipping waits for them, and stays off if they cannot be\n * read.\n */\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useState,\n type ComponentType,\n type ReactNode,\n} from 'react'\nimport { useParsedDocument } from '@arbidocs/react'\nimport { createMarkupStore, useDocumentTools } from '@arbidocs/react/document-tools'\nimport { useProcessedChunks, type PdfViewerProps } from '@arbidocs/react/viewers'\nimport { ClipCaptureOverlay } from '../../presentation/ClipCaptureOverlay'\nimport type { Rect } from '../../presentation/model'\nimport { passageClipRect } from '../../presentation/passage-clip'\nimport { useSpotlight } from '../../presentation/spotlight'\nimport { placedPassagesByPage } from '../viewer/pdf/passageHighlights'\n\ntype MaskState =\n | { status: 'loading' }\n | { status: 'error' }\n | { status: 'ready'; masks: ReadonlyMap<number, Rect[]> }\n\nconst NO_MASKS: MaskState = { status: 'ready', masks: new Map() }\n\n/** The version's redaction rectangles by page; none to read when it has no markup. */\nfunction useRedactionMasks(documentId: string, version: number | undefined, hasMarkup: boolean) {\n const { client } = useDocumentTools()\n const [state, setState] = useState<MaskState>(hasMarkup ? { status: 'loading' } : NO_MASKS)\n useEffect(() => {\n if (!hasMarkup) {\n setState(NO_MASKS)\n return\n }\n let cancelled = false\n setState({ status: 'loading' })\n createMarkupStore({ version, client })\n .load(documentId)\n .then((layer) => {\n if (cancelled) return\n const masks = new Map<number, Rect[]>()\n for (const mark of layer.annotations) {\n if (mark.kind !== 'redaction' || mark.status === 'rejected') continue\n masks.set(mark.page, [...(masks.get(mark.page) ?? []), ...mark.rects])\n }\n setState({ status: 'ready', masks })\n })\n .catch(() => !cancelled && setState({ status: 'error' }))\n return () => {\n cancelled = true\n }\n }, [client, documentId, version, hasMarkup])\n return state\n}\n\nconst NO_FOOTNOTES: Record<string, string> = {}\nconst NO_PASSAGES: ReadonlyMap<number, Rect[]> = new Map()\n\n/**\n * The areas a click clips, by page: each passage the parser placed, with its margin. Only for the\n * current version (the passages are its own); an older one is clipped by dragging.\n */\nfunction usePassageAreas(documentId: string, current: boolean): ReadonlyMap<number, Rect[]> {\n const { data: parsed } = useParsedDocument(documentId, 'final', { enabled: current })\n const { processedChunks } = useProcessedChunks(parsed?.chunks, NO_FOOTNOTES)\n return useMemo(() => {\n if (!current || !processedChunks.length) return NO_PASSAGES\n const areas = new Map<number, Rect[]>()\n for (const [page, boxes] of placedPassagesByPage(processedChunks)) {\n areas.set(page, boxes.map(passageClipRect))\n }\n return areas\n }, [current, processedChunks])\n}\n\nexport interface ClippingPdfViewerProps extends Omit<PdfViewerProps, 'renderPageOverlay'> {\n file: Blob\n documentId: string\n /** The version on screen when it is an older one. */\n version?: number\n fileName: string\n /** The version has markup, which may hold redactions. */\n hasMarkup: boolean\n viewer: ComponentType<PdfViewerProps>\n}\n\nfunction Notice({ children }: { children: ReactNode }) {\n return (\n <div className=\"border-b border-border bg-muted px-3 py-1.5 text-xs text-muted-foreground\">\n {children}\n </div>\n )\n}\n\nexport function ClippingPdfViewer({\n documentId,\n version,\n fileName,\n hasMarkup,\n viewer: Viewer,\n ...viewerProps\n}: ClippingPdfViewerProps) {\n const masks = useRedactionMasks(documentId, version, hasMarkup)\n const passages = usePassageAreas(documentId, version === undefined)\n const file = viewerProps.file\n\n const capture = useCallback(\n ({ page, rect, aspect }: { page: number; rect: Rect; aspect: number }) => {\n if (masks.status !== 'ready') return\n useSpotlight.getState().gather({\n kind: 'pdf',\n documentId,\n version,\n fileName,\n page,\n rect,\n aspect,\n masks: masks.masks.get(page) ?? [],\n source: file,\n })\n // It joins the viewer's spotlight strip.\n },\n [masks, documentId, version, fileName, file]\n )\n\n const renderPageOverlay = useCallback(\n ({ pageNumber }: { pageNumber: number }) =>\n masks.status === 'ready' ? (\n <ClipCaptureOverlay\n page={pageNumber}\n passages={passages.get(pageNumber)}\n onCapture={capture}\n />\n ) : null,\n [masks.status, capture, passages]\n )\n\n return (\n <div\n className=\"flex h-full min-h-0 flex-col\"\n // The spotlight finds a clip's page here, to fly it from there.\n data-clip-document={documentId}\n data-testid=\"clipping-pdf-viewer\"\n >\n <Notice>\n {masks.status === 'loading'\n ? 'Reading the redactions before clipping…'\n : masks.status === 'error'\n ? \"Couldn't read this document's redactions, so it can't be clipped.\"\n : passages.size\n ? 'Click a passage to clip it, or drag over any area.'\n : 'Drag over the part of a page to clip it.'}\n </Notice>\n <div className=\"min-h-0 flex-1\">\n <Viewer {...viewerProps} renderPageOverlay={renderPageOverlay} />\n </div>\n </div>\n )\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/presentation/PresenterView.tsx","../src/connection/presentation/ConnectedPresentation.tsx"],"names":["useEffect","STAGE_ASPECT","usePresentation","useState","useCallback","usePresenterKeys","usePointerActive","jsxs","toggleFullscreen","jsx","PresentationStage","PresentControls","SlidesEditor","ConnectedDocumentToolsProvider","useArbiSessionStore","useArbiSessionState","isPanelOpen","PRESENTATION_PANEL","useArbiSession","useRef","usePresentationFiles","usePresentationController","presentationWindowOnlyPresents","ConnectedPanelHost","Fragment","Button","PanelLeftClose"],"mappings":"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","file":"ConnectedPresentation-OV2KBOLK.cjs","sourcesContent":["/**\n * The presentation in a window of its own, in one of two modes.\n *\n * Present: nothing but the stage on black, no controls and no pointer: what is shared in a call or\n * shown on a projector. Keys drive it (arrows / Page Up / Page Down change slide, B blanks, F or\n * double-click toggles full screen, Space plays videos, Escape stops presenting).\n *\n * Edit: the slides editor (`SlidesEditor`), the same as in the viewer's tab, when the presentation\n * is edited in its own window (beside the document its clips come from).\n */\n\nimport { useCallback, useEffect, useState } from 'react'\nimport { PresentationStage } from './PresentationStage'\nimport { SlidesEditor, type SlidesEditorProps } from './SlidesEditor'\nimport { STAGE_ASPECT } from './model'\nimport { usePresentation } from './store'\nimport { toggleFullscreen, usePresenterKeys } from './usePresenterKeys'\nimport { PresentControls } from './PresentControls'\nimport { usePointerActive } from './usePointerActive'\n\n/** The presenting window's page width, at most (smaller on a small screen). */\nconst PRESENT_WIDTH = 1280\n\n/**\n * While presenting in a pop-out, its page is made 16:9 and wide enough to share sharply, so a\n * shared window is all stage with no bars; editing again gives the window back its size. Only a\n * pop-out (a window this app opened) can be resized; the app's own window is left alone.\n */\nfunction useStageShapedWindow(view: Window | null, presenting: boolean) {\n useEffect(() => {\n if (!view || !presenting || !view.opener) return\n const before = { width: view.outerWidth, height: view.outerHeight }\n const chromeWidth = view.outerWidth - view.innerWidth\n const chromeHeight = view.outerHeight - view.innerHeight\n // Wide enough for a sharp share (a call receives about what the window shows), and 16:9.\n const width = Math.max(\n view.innerWidth,\n Math.min(PRESENT_WIDTH, Math.round((view.screen.availWidth - chromeWidth) * 0.6))\n )\n const height = Math.min(\n Math.round(width / STAGE_ASPECT),\n view.screen.availHeight - chromeHeight\n )\n try {\n view.resizeTo(Math.round(height * STAGE_ASPECT) + chromeWidth, height + chromeHeight)\n } catch {\n return // not resizable: the stage letterboxes in black\n }\n return () => {\n try {\n if (!view.closed) view.resizeTo(before.width, before.height)\n } catch {\n // gone or not resizable\n }\n }\n }, [view, presenting])\n}\n\nexport type PresenterViewProps = Omit<SlidesEditorProps, 'onPresent'> & {\n /** Present (default: here, in this window). */\n onPresent?: () => void\n}\n\nexport function PresenterView({ onPresent, ...editor }: PresenterViewProps) {\n const presenting = usePresentation((s) => s.presenting)\n const [view, setView] = useState<Window | null>(null)\n // The keys work in whichever window the presenter is in.\n const rootRef = useCallback((node: HTMLDivElement | null) => {\n if (node) setView(node.ownerDocument.defaultView)\n }, [])\n usePresenterKeys(presenting ? view : null, true)\n useStageShapedWindow(view, presenting)\n\n const active = usePointerActive(presenting ? view : null)\n\n if (presenting) {\n return (\n <div\n ref={rootRef}\n className=\"relative h-full w-full select-none bg-black\"\n // The pointer shows while it moves, with the controls; idle, nothing but the slide.\n style={{ cursor: active ? 'default' : 'none' }}\n // A click on the slide goes to the next one, as in a slideshow.\n onClick={() => {\n const state = usePresentation.getState()\n state.goTo(state.current + 1)\n }}\n onDoubleClick={() => view && toggleFullscreen(view)}\n data-testid=\"presentation-present\"\n >\n <PresentationStage />\n <PresentControls view={view} shown={active} />\n </div>\n )\n }\n return (\n <div ref={rootRef} className=\"h-full min-h-0\" data-testid=\"presenter-view\">\n <SlidesEditor\n {...editor}\n onPresent={onPresent ?? (() => usePresentation.getState().setPresenting(true))}\n />\n </div>\n )\n}\n","/**\n * The presentation's own window, mounted once by the app shell (the session's `PRESENTATION_PANEL`,\n * a pop-out). It presents: the slides alone, what is shared in a call or put full screen; or, when\n * the editor is popped out of the viewer's slides tab, it edits there, beside the documents its\n * clips come from. A window opened only to present closes when presenting ends.\n *\n * It also keeps what the presentation needs wherever it is edited: the theme's logo image, read\n * when the logo changes; and a clean slate when the session signs out or another user signs in\n * (the presentation holds decrypted files in memory).\n */\n\nimport { useCallback, useEffect, useRef } from 'react'\nimport { PanelLeftClose } from 'lucide-react'\nimport {\n isPanelOpen,\n useArbiSession,\n useArbiSessionState,\n useArbiSessionStore,\n} from '@arbidocs/react'\nimport { Button } from '@arbidocs/react/ui'\nimport { ConnectedPanelHost } from '../panels/ConnectedPanelHost'\nimport { ConnectedDocumentToolsProvider } from '../viewer/ConnectedDocumentToolsProvider'\nimport { PRESENTATION_PANEL, presentationWindowOnlyPresents } from '../../presentation/panel-id'\nimport { PresenterView } from '../../presentation/PresenterView'\nimport { usePresentation } from '../../presentation/store'\nimport { usePresentationController } from './usePresentationController'\nimport { usePresentationFiles } from './usePresentationFiles'\n\n/** The presentation's window, with the document tools it needs (sources, redactions). */\nexport function ConnectedPresentation() {\n return (\n <ConnectedDocumentToolsProvider>\n <PresentationWindow />\n </ConnectedDocumentToolsProvider>\n )\n}\n\nfunction PresentationWindow() {\n const session = useArbiSessionStore()\n const notify = usePresentation((s) => s.notify)\n const presenting = usePresentation((s) => s.presenting)\n const isOpen = useArbiSessionState((state) => isPanelOpen(state, PRESENTATION_PANEL))\n const { user } = useArbiSession()\n const userId = user?.external_id ?? null\n const shownFor = useRef(userId)\n const { downloadFile } = usePresentationFiles()\n const logoId = usePresentation((s) => s.theme.logo?.documentId ?? null)\n const controller = usePresentationController(isOpen && !presenting)\n\n useEffect(() => {\n if (shownFor.current === userId) return\n shownFor.current = userId\n usePresentation.getState().reset()\n session.closePanel(PRESENTATION_PANEL)\n }, [userId, session])\n\n // The theme's logo image, fetched whenever the logo changes (the theme keeps a reference only).\n useEffect(() => {\n if (!logoId) return\n let current = true\n downloadFile(logoId)\n .then((image) => current && usePresentation.getState().setLogoImage(image))\n .catch(() => current && notify('The logo could not be read', 'error'))\n return () => {\n current = false\n }\n }, [logoId, downloadFile, notify])\n\n // A closed window is not presenting.\n useEffect(() => {\n if (!isOpen) usePresentation.getState().setPresenting(false)\n }, [isOpen])\n\n // Opened only to present: closed when presenting ends (Escape).\n const wasPresenting = useRef(presenting)\n useEffect(() => {\n if (wasPresenting.current && !presenting && isOpen && presentationWindowOnlyPresents()) {\n session.closePanel(PRESENTATION_PANEL)\n }\n wasPresenting.current = presenting\n }, [presenting, isOpen, session])\n\n const onClose = useCallback(() => session.closePanel(PRESENTATION_PANEL), [session])\n\n return (\n <ConnectedPanelHost\n panelId={PRESENTATION_PANEL}\n isOpen={isOpen}\n onClose={onClose}\n title=\"Presentation\"\n defaultMode=\"popout\"\n defaultWidth={960}\n closeTestId=\"presentation-panel-close\"\n >\n {/* Inside the panel, so its dialogs open in its window. */}\n {() => (\n <>\n {!presenting && controller.dialogs}\n <PresenterView\n {...controller.editor}\n actions={\n <>\n {controller.editor.actions}\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"ghost\"\n className=\"size-8 text-muted-foreground hover:text-foreground pointer-coarse:size-11\"\n title=\"Back into the app's viewer\"\n aria-label=\"Back to the viewer\"\n onClick={onClose}\n data-testid=\"presentation-pop-in\"\n >\n <PanelLeftClose className=\"size-4\" />\n </Button>\n </>\n }\n />\n </>\n )}\n </ConnectedPanelHost>\n )\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/connection/presentation/ConnectedSlidesTab.tsx"],"names":["hasUnsavedChanges","usePresentation","useArbiSessionStore","usePanelHostSupport","useBlocksEnvironment","usePresentationFiles","useState","useArbiSessionState","isPanelOpen","PRESENTATION_PANEL","presentationWindowOnlyPresents","usePresentationController","useRef","useEffect","editPresentation","registerDocumentTurnFlush","useCallback","openPresentation","jsx","EmptyState","ExternalLink","jsxs","Button","PanelLeftClose","Presentation","Loader2","Fragment","SlidesEditor","DropdownMenuItem"],"mappings":"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","file":"ConnectedSlidesTab-LUAGZ6FD.cjs","sourcesContent":["/**\n * A presentation's slides tab in the document viewer: the slides editor, on the presentation the\n * viewer shows. Opening a saved presentation loads it (unless it is the one being edited); a new\n * one (a presentation draft) takes the clips gathered so far. Clips gathered with no presentation\n * open join the one opened next. Saving a draft makes it a document, and the tab moves to it.\n *\n * The editor can also move to the presentation's own window (Pop out), beside the documents its\n * clips come from; the tab then says so. Present opens that window with the slides alone.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { ExternalLink, Loader2, PanelLeftClose, Presentation } from 'lucide-react'\nimport {\n isPanelOpen,\n registerDocumentTurnFlush,\n useArbiSessionState,\n useArbiSessionStore,\n} from '@arbidocs/react'\nimport { Button, DropdownMenuItem } from '@arbidocs/react/ui'\nimport { useBlocksEnvironment } from '../../provider/environment'\nimport { usePanelHostSupport } from '../../shell/panel-host'\nimport {\n PRESENTATION_PANEL,\n openPresentation,\n presentationWindowOnlyPresents,\n} from '../../presentation/panel-id'\nimport { SlidesEditor } from '../../presentation/SlidesEditor'\nimport { hasUnsavedChanges, usePresentation } from '../../presentation/store'\nimport { EmptyState } from '../../components/EmptyState'\nimport { editPresentation } from './requests'\nimport { usePresentationController } from './usePresentationController'\nimport { usePresentationFiles } from './usePresentationFiles'\n\nexport interface ConnectedSlidesTabProps {\n /** The viewed document's id, or a presentation draft's (`draft-…`). */\n documentId: string\n /** The version on screen, when an older one. */\n version?: number\n /** The document's current version: a newer one than the one being edited is opened. */\n currentVersion?: number\n /** A new presentation, not saved yet: its draft title. */\n draftTitle?: string\n}\n\n/** \"2, 3 and 5\". */\nconst listOf = (numbers: number[]) =>\n numbers.length < 2\n ? String(numbers[0] ?? '')\n : `${numbers.slice(0, -1).join(', ')} and ${numbers[numbers.length - 1]}`\n\n/** A new presentation saves itself this long after its first content (typing settles first). */\nconst AUTO_SAVE_AFTER_MS = 2500\n/** The names a new presentation starts with (not chosen by anyone). */\nconst DEFAULT_NAMES = /^(new|untitled) presentation( \\d+)?$/i\n\nconst STALE = 'This presentation was changed outside the editor: Save to draw its slides again.'\n\n/** The presentation has anything in it worth asking about before it is replaced. */\nconst hasContent = () => hasUnsavedChanges(usePresentation.getState())\n\nexport function ConnectedSlidesTab({\n documentId,\n version,\n currentVersion,\n draftTitle,\n}: ConnectedSlidesTabProps) {\n const session = useArbiSessionStore()\n const { windows, popoutTitle } = usePanelHostSupport()\n const { confirm } = useBlocksEnvironment()\n const { open, save } = usePresentationFiles()\n const notify = usePresentation((s) => s.notify)\n const boundTo = usePresentation((s) => s.boundTo)\n const savedId = usePresentation((s) => s.savedId)\n const [loading, setLoading] = useState(false)\n const isDraft = draftTitle !== undefined\n const target = version ? `${documentId}@v${version}` : documentId\n const poppedOut = useArbiSessionState(\n (state) => isPanelOpen(state, PRESENTATION_PANEL) && !presentationWindowOnlyPresents()\n )\n const controller = usePresentationController(!poppedOut)\n const asked = useRef<string | null>(null)\n // Another presentation, with changes, is being edited and the user kept it.\n const [declined, setDeclined] = useState(false)\n const [force, setForce] = useState(0)\n\n // Show the presentation this tab is for (once per presentation: a remount does not ask again).\n useEffect(() => {\n const state = usePresentation.getState()\n if (state.boundTo === target || asked.current === `${target}#${force}`) return\n asked.current = `${target}#${force}`\n setDeclined(false)\n void (async () => {\n // Clips gathered with no presentation open join this one.\n const gathered = state.boundTo === null ? state.clips : []\n if (state.boundTo !== null && hasContent() && !force) {\n const ok = await confirm({\n title: 'Open presentation',\n message: 'Replace the presentation being edited? Save it first to keep its changes.',\n kind: 'confirm',\n })\n if (!ok) return setDeclined(true)\n }\n if (isDraft) {\n if (state.boundTo !== null) state.reset()\n usePresentation.getState().bind(target)\n usePresentation.getState().setTitle(draftTitle || 'Untitled presentation')\n return\n }\n setLoading(true)\n try {\n const { missing, stale, imported, changedOutside } = await open(documentId, version)\n usePresentation.getState().bind(target)\n if (gathered.length) usePresentation.getState().adoptClips(gathered)\n if (stale) notify(STALE, 'error')\n if (imported) {\n notify(\n ['Opened from PowerPoint, ready to edit.', ...imported.notes].join(' · '),\n 'success'\n )\n }\n if (changedOutside) {\n notify(\n `${changedOutside.length === 1 ? `Slide ${changedOutside[0]} was` : `Slides ${listOf(changedOutside)} were`} changed in PowerPoint: brought in as changed there.`,\n 'success'\n )\n }\n if (missing) {\n notify(\n `${missing} clip${missing === 1 ? '' : 's'} could not be opened (moved, deleted or not shared with you)`,\n 'error'\n )\n }\n } catch (error) {\n notify(\n error instanceof Error ? error.message : 'The presentation could not be opened',\n 'error'\n )\n } finally {\n setLoading(false)\n }\n })()\n // eslint-disable-next-line react-hooks/exhaustive-deps -- a draft's title changes as it is typed\n }, [target, documentId, version, isDraft, force, open, confirm, notify])\n\n // A draft's title is the presentation's.\n useEffect(() => {\n if (isDraft && boundTo === target && draftTitle) usePresentation.getState().setTitle(draftTitle)\n }, [isDraft, boundTo, target, draftTitle])\n\n // Saved as another document (a draft's first save, Save as, an older file saved as a\n // PowerPoint): the viewer follows it. Only once this tab's presentation was the one edited\n // (another, still bound as the tab opens, is not \"its\" save).\n const wasBound = useRef(false)\n useEffect(() => {\n if (boundTo === target) wasBound.current = true\n if (!wasBound.current || !savedId || boundTo !== savedId || savedId === target) return\n if (isDraft) session.clearDraftDocument(documentId)\n editPresentation(session, { kind: 'open', documentId: savedId })\n }, [isDraft, savedId, boundTo, target, documentId, session])\n\n // A newer version saved meanwhile (by ARBI, asked in the chat beside the deck, or by someone\n // else): shown at once, unless this one has changes of its own, which are never thrown away.\n const heldVersion = usePresentation((s) => (s.boundTo === target ? s.version : null))\n const toldNewer = useRef<number | null>(null)\n useEffect(() => {\n if (isDraft || version || !currentVersion || heldVersion === null) return\n if (currentVersion <= heldVersion || loading) return\n if (hasContent()) {\n if (toldNewer.current === currentVersion) return\n toldNewer.current = currentVersion\n notify(\n `A newer version (${currentVersion}) was saved while you edited. Save as… keeps yours; reopen the presentation to see it.`,\n 'error'\n )\n return\n }\n setLoading(true)\n void open(documentId)\n .then(() => {\n usePresentation.getState().bind(target)\n notify(`Updated to version ${currentVersion}.`, 'success')\n })\n .catch((error: unknown) =>\n notify(\n error instanceof Error ? error.message : 'The new version could not be opened',\n 'error'\n )\n )\n .finally(() => setLoading(false))\n }, [currentVersion, heldVersion, isDraft, version, loading, open, documentId, target, notify])\n\n // A new presentation saves itself once it has something in it (a clip, a title, a point): from\n // then on it is a document of Presentations, which a chat can work in and look at (its PDF), and\n // which is not lost with the tab. Later saves stay the person's (each a version they chose).\n const draftHasContent = usePresentation(\n (state) =>\n isDraft && state.boundTo === target && state.savedId === null && hasUnsavedChanges(state)\n )\n const autoSaved = useRef<string | null>(null)\n useEffect(() => {\n if (!draftHasContent || autoSaved.current === target) return\n const timer = setTimeout(() => {\n autoSaved.current = target\n const state = usePresentation.getState()\n // Still called what every new one is called: named after its first slide's title instead.\n const firstTitle = state.slides.find((slide) => slide.caption.trim())?.caption.trim()\n const named = state.title && !DEFAULT_NAMES.test(state.title) ? state.title : null\n const title = named ?? firstTitle ?? (state.title || 'Untitled presentation')\n if (title !== state.title) state.setTitle(title)\n void save(title).catch((error: unknown) => {\n autoSaved.current = null\n notify(\n error instanceof Error ? error.message : 'The presentation could not be saved',\n 'error'\n )\n })\n }, AUTO_SAVE_AFTER_MS)\n return () => clearTimeout(timer)\n }, [draftHasContent, target, save, draftTitle, notify])\n\n // A chat turn about this presentation (the chat beside it) saves its unsaved changes first, so\n // ARBI works on the slides as they are on screen.\n useEffect(() => {\n if (isDraft || version || savedId !== documentId || boundTo !== target) return\n return registerDocumentTurnFlush(documentId, async () => {\n const state = usePresentation.getState()\n if (state.savedId === documentId && hasUnsavedChanges(state)) await save(state.title)\n })\n }, [isDraft, version, savedId, boundTo, documentId, target, save])\n\n const present = useCallback(() => {\n openPresentation(session, { present: true, windows, title: popoutTitle('Presentation') })\n usePresentation.getState().setPresenting(true)\n }, [session, windows, popoutTitle])\n\n if (poppedOut) {\n return (\n <div className=\"flex h-full items-center justify-center p-6\" data-testid=\"slides-popped-out\">\n <EmptyState\n icon={ExternalLink}\n title=\"Editing in its own window\"\n description=\"The presentation is open in its own window, beside the documents you clip from.\"\n action={\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => session.closePanel(PRESENTATION_PANEL)}\n >\n <PanelLeftClose className=\"size-4\" />\n Bring it back here\n </Button>\n }\n />\n </div>\n )\n }\n if (declined && boundTo !== target) {\n return (\n <div className=\"flex h-full items-center justify-center p-6\" data-testid=\"slides-declined\">\n <EmptyState\n icon={Presentation}\n title=\"Another presentation is being edited\"\n description=\"It has changes. Save it first, or open this one in its place.\"\n action={\n <Button type=\"button\" variant=\"outline\" onClick={() => setForce((n) => n + 1)}>\n Open this one instead\n </Button>\n }\n />\n </div>\n )\n }\n if (loading || boundTo !== target) {\n return (\n <div className=\"flex h-full items-center justify-center gap-2 text-sm text-muted-foreground\">\n <Loader2 className=\"size-4 animate-spin\" />\n Opening the presentation…\n </div>\n )\n }\n return (\n <>\n {controller.dialogs}\n <SlidesEditor\n {...controller.editor}\n menuItems={\n <>\n {controller.editor.menuItems}\n <DropdownMenuItem\n onSelect={() =>\n openPresentation(session, { windows, title: popoutTitle('Presentation') })\n }\n data-testid=\"presentation-pop-out\"\n >\n <ExternalLink className=\"size-4\" />\n <span className=\"ml-2\">Edit in its own window</span>\n </DropdownMenuItem>\n </>\n }\n onPresent={present}\n />\n </>\n )\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/connection/viewer/DocumentViewerDialog.tsx","../src/connection/files/page/FilesViewerDialog.tsx"],"names":["jsx","Eye","PenLine","MessageSquareText","DEFAULT_DOCUMENT_VIEWER_TABS","DEFAULT_ASK_SUGGESTIONS","DEFAULT_DOCUMENT_VIEWER_LABELS","useConnectedWorkspace","useArbiSessionStore","useBlocksEnvironment","useArbiSessionState","useDocuments","parseVersionRef","tab","useState","question","useRef","useSlashField","Dialog","jsxs","DialogContent","DialogHeader","DialogTitle","FileText","DialogDescription","SegmentedControl","ErrorBoundary","DocumentReview","AskBar","ConnectedDocumentViewer"],"mappings":"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","file":"FilesViewerDialog-JWKNJLWG.cjs","sourcesContent":["/**\n * The document viewer as a dialog (the files page's own viewer): a title, the tabs\n * the host offers, and under them the review (`DocumentReview`), the whole viewer, or a question\n * (`AskBar`) that starts a thread about the document. Views of existing parts, not a new viewer.\n *\n * A draft started with Create New (the session's `draftDocuments`) has no server record yet: it\n * shows the whole viewer only (its kind, name and recorder), titled from the draft.\n */\n\nimport { useRef, useState, type ReactNode } from 'react'\nimport { Eye, FileText, MessageSquareText, PenLine } from 'lucide-react'\nimport {\n DocumentReview,\n useArbiSessionState,\n useArbiSessionStore,\n useDocuments,\n type WorkspaceDocument,\n} from '@arbidocs/react'\nimport { parseVersionRef } from '@arbidocs/react/artifacts'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '@arbidocs/react/ui'\nimport { AskBar } from '../../components/AskBar'\nimport { ErrorBoundary } from '../../components/ErrorBoundary'\nimport { SegmentedControl, type SegmentedOption } from '../../settings/SegmentedControl'\nimport { useBlocksEnvironment } from '../../provider/environment'\nimport { useConnectedWorkspace } from '../ConnectionProvider'\nimport { useSlashField } from '../ask/useSlashField'\nimport {\n DEFAULT_ASK_SUGGESTIONS,\n DEFAULT_DOCUMENT_VIEWER_LABELS,\n DEFAULT_DOCUMENT_VIEWER_TABS,\n type DocumentViewerDialogLabels,\n type DocumentViewerTab,\n} from './documentViewerTabs'\n\ntype ReviewDocument = Parameters<typeof DocumentReview>[0]['doc']\n\nexport interface DocumentViewerDialogProps {\n /** The document, or a version reference `doc-…@v{n}`. */\n documentId: string\n /** The views offered, in order. Default: review, document, ask. */\n tabs?: readonly DocumentViewerTab[]\n /** The view it opens on. Default: the first offered. */\n initialTab?: DocumentViewerTab\n labels?: DocumentViewerDialogLabels\n /** The ask view's suggested questions. */\n askSuggestions?: readonly string[]\n onClose: () => void\n /** The whole viewer, for the document view. */\n renderDocument: (documentId: string) => ReactNode\n}\n\nconst titleOf = (doc: WorkspaceDocument | undefined) =>\n doc?.doc_metadata?.title || doc?.file_name || 'Document'\n\nconst TAB_ICONS: Record<DocumentViewerTab, ReactNode> = {\n review: <Eye className=\"size-4\" />,\n document: <PenLine className=\"size-4\" />,\n ask: <MessageSquareText className=\"size-4\" />,\n}\n\nconst TAB_TEST_IDS: Record<DocumentViewerTab, string> = {\n review: 'review-tab-review',\n document: 'review-tab-pdf',\n ask: 'review-tab-qa',\n}\n\nexport function DocumentViewerDialog({\n documentId,\n tabs = DEFAULT_DOCUMENT_VIEWER_TABS,\n initialTab,\n labels: labelOverrides,\n askSuggestions = DEFAULT_ASK_SUGGESTIONS,\n onClose,\n renderDocument,\n}: DocumentViewerDialogProps) {\n const labels = { ...DEFAULT_DOCUMENT_VIEWER_LABELS, ...labelOverrides }\n const { workspaceId, live } = useConnectedWorkspace()\n const session = useArbiSessionStore()\n const { openNewConversation } = useBlocksEnvironment()\n const passageId = useArbiSessionState((s) => s.viewingDocument?.passageId ?? null)\n const draft = useArbiSessionState((s) => s.draftDocuments[documentId])\n const { data: documents } = useDocuments()\n const baseId = parseVersionRef(documentId).docId\n const doc = documents?.find((d) => d.external_id === baseId)\n const title = draft?.title || titleOf(doc)\n\n // The whole viewer needs a signed-in session (marking up, signing, versions). A draft is only\n // on the client: there is nothing to review or ask about until it is saved.\n const offered: readonly DocumentViewerTab[] = draft\n ? ['document']\n : tabs.filter((tab) => tab !== 'document' || live)\n const [chosen, setChosen] = useState<DocumentViewerTab | undefined>(initialTab)\n const tab = chosen && offered.includes(chosen) ? chosen : offered[0]\n\n const options: SegmentedOption<DocumentViewerTab>[] = offered.map((value) => ({\n value,\n label: labels[value],\n icon: TAB_ICONS[value],\n testId: TAB_TEST_IDS[value],\n }))\n\n // A question opens a new thread about the document, as the viewer's \"Chat\" does.\n const ask = (question: string) => {\n openNewConversation({ prompt: question })\n session.setNewChatDocumentId(baseId)\n }\n // The `/` menu: a skill's run is asked the same way, so its thread is about this document too.\n const [question, setQuestion] = useState('')\n const askInputRef = useRef<HTMLInputElement>(null)\n const slash = useSlashField({\n text: question,\n setText: setQuestion,\n inputRef: askInputRef,\n landing: 'chat',\n send: ask,\n })\n\n return (\n <Dialog open onOpenChange={(open) => !open && onClose()}>\n <DialogContent\n className=\"flex h-[90vh] max-w-[95vw] flex-col gap-0 p-0 sm:max-w-[95vw]\"\n data-testid=\"documents-dialog\"\n >\n <DialogHeader className=\"border-b px-4 py-3\">\n <DialogTitle className=\"flex min-w-0 items-center gap-2 pr-8 text-base\">\n <FileText className=\"size-4 shrink-0\" />\n <span className=\"truncate\" data-testid=\"documents-dialog-title\">\n {title}\n </span>\n </DialogTitle>\n <DialogDescription className=\"sr-only\">\n {offered.map((value) => labels[value]).join(', ')}: {title}\n </DialogDescription>\n {options.length > 1 && tab && (\n <SegmentedControl\n ariaLabel=\"Document view\"\n // A phone fits the three on one row: no label breaks over two lines.\n className=\"mt-2 w-fit max-w-full overflow-x-auto [&_button]:whitespace-nowrap max-sm:[&_button]:px-2.5\"\n value={tab}\n onChange={setChosen}\n options={options}\n />\n )}\n </DialogHeader>\n <div className=\"min-h-0 flex-1 overflow-hidden p-3\">\n {/* A view that fails to render says so here: the dialog and the page under it stay. */}\n <ErrorBoundary\n resetKey={`${documentId}|${tab}`}\n labels={{ title: \"This document couldn't be displayed\" }}\n testId=\"documents-dialog-error\"\n >\n {tab === 'review' && (\n <DocumentReview\n docId={baseId}\n doc={doc as ReviewDocument}\n workspaceId={workspaceId ?? undefined}\n activeChunkId={passageId}\n />\n )}\n {tab === 'document' && (\n <div className=\"h-full overflow-hidden\" data-testid=\"documents-dialog-viewer\">\n {renderDocument(documentId)}\n </div>\n )}\n {tab === 'ask' && (\n <div className=\"mx-auto max-w-2xl space-y-3 pt-6\" data-testid=\"doc-qa-panel\">\n <p className=\"text-sm text-muted-foreground\">{labels.askIntro}</p>\n {slash.launcher}\n <AskBar\n testId=\"doc-ask\"\n placeholder={labels.askPlaceholder.replace('{title}', title)}\n suggestions={[...askSuggestions]}\n onAsk={(text) => {\n if (!slash.startsSkill(text)) ask(text)\n }}\n value={question}\n onValueChange={setQuestion}\n onInputKeyDown={slash.onKeyDown}\n inputProps={slash.comboboxProps}\n inputRef={askInputRef}\n menu={slash.menu}\n />\n </div>\n )}\n </ErrorBoundary>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n","/**\n * The files page's own viewer (a page without a host viewer): `DocumentViewerDialog`, with the\n * whole `ConnectedDocumentViewer` as its document view. The page lazy-loads this module, so the\n * viewer stays out of the page's chunk.\n */\nimport { ConnectedDocumentViewer } from '../../viewer/ConnectedDocumentViewer'\nimport {\n DocumentViewerDialog,\n type DocumentViewerDialogProps,\n} from '../../viewer/DocumentViewerDialog'\n\nexport interface FilesViewerDialogProps extends Omit<DocumentViewerDialogProps, 'renderDocument'> {\n /** Where html/react artifacts load their sandbox runtime from. */\n artifactRuntimeUrl?: string\n}\n\nexport function FilesViewerDialog({ artifactRuntimeUrl, ...props }: FilesViewerDialogProps) {\n return (\n <DocumentViewerDialog\n {...props}\n renderDocument={(id) => (\n <ConnectedDocumentViewer documentId={id} artifactRuntimeUrl={artifactRuntimeUrl} />\n )}\n />\n )\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/presentation/SpotlightView.tsx","../src/connection/presentation/SpotlightStrip.tsx"],"names":["useSpotlight","usePresentation","useMemo","spotlightSteps","spotlightSlide","useState","useRef","useCallback","usePointerActive","useLayoutEffect","useEffect","jsxs","jsx","PresentationStage","cn","Control","ChevronLeft","ChevronRight","Check","Presentation","X","useArbiSessionStore","useDocuments","getDocumentTitle","Fragment","ClipImage","Popover","PopoverTrigger","Button","StickyNote","PopoverContent","Input","DocumentPicker","DropdownMenu","DropdownMenuTrigger","MoreHorizontal","DropdownMenuContent","DropdownMenuItem","Inbox","Eraser"],"mappings":"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","file":"SpotlightStrip-ED22UYTM.cjs","sourcesContent":["/**\n * The spotlight on screen: the document dims and the gathered clips fly from where they are on\n * their pages into a slide over it, in the same window (a call sharing it goes on sharing it). It is\n * the presentation's own stage (`PresentationStage`), so it looks as a slide of the deck would.\n *\n * ← → step through the clips together and each alone, Esc goes back to the document. Each move is\n * a FLIP: a clip is drawn where it ends and animated from where it was (its page, or its place on\n * the previous step) by a transform, about 300 ms; with reduced motion it simply appears.\n */\n\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'\nimport { Check, ChevronLeft, ChevronRight, Presentation, X } from 'lucide-react'\nimport { cn } from '@arbidocs/react/ui'\nimport type { Clip } from './model'\nimport { Control } from './PresentControls'\nimport { PresentationStage } from './PresentationStage'\nimport { spotlightSlide, spotlightSteps, useSpotlight } from './spotlight'\nimport { usePresentation } from './store'\nimport { usePointerActive } from './usePointerActive'\n\n/** How long a clip takes to fly into place. */\nexport const SPOTLIGHT_MS = 300\nconst EASING = 'cubic-bezier(0.2, 0, 0, 1)'\n\n/** A box on screen, in CSS pixels. */\nexport type ScreenRect = Pick<DOMRect, 'left' | 'top' | 'width' | 'height'>\n\nexport interface SpotlightViewProps {\n /** Where a clip is on screen before the spotlight (its passage on the page), if it shows. */\n fromRect: (clip: Clip) => ScreenRect | null\n}\n\nconst prefersReducedMotion = (view: Window | null) =>\n !!view?.matchMedia?.('(prefers-reduced-motion: reduce)').matches\n\nexport function SpotlightView({ fromRect }: SpotlightViewProps) {\n const clips = useSpotlight((s) => s.clips)\n const note = useSpotlight((s) => s.note)\n const step = useSpotlight((s) => s.step)\n const captions = usePresentation((s) => s.captions)\n const theme = usePresentation((s) => s.theme)\n const steps = useMemo(() => spotlightSteps(clips), [clips])\n const ids = steps[step] ?? []\n const stepKey = ids.join()\n const slide = useMemo(\n () => spotlightSlide(clips, ids, note, { captions, theme }),\n // `ids` is new each render; its key says when it changed.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [clips, stepKey, note, captions, theme]\n )\n const show = useMemo(() => ({ slide, clips }), [slide, clips])\n\n const [view, setView] = useState<Window | null>(null)\n const root = useRef<HTMLDivElement | null>(null)\n const rootRef = useCallback((node: HTMLDivElement | null) => {\n root.current = node\n if (node) setView(node.ownerDocument.defaultView)\n }, [])\n const active = usePointerActive(view)\n const [kept, setKept] = useState<string | null>(null)\n\n // FLIP: each clip from where it was (on its page, or on the step before) to where it is now.\n const last = useRef(new Map<string, ScreenRect>())\n const fromRectRef = useRef(fromRect)\n fromRectRef.current = fromRect\n useLayoutEffect(() => {\n const node = root.current\n if (!node) return\n const reduce = prefersReducedMotion(node.ownerDocument.defaultView)\n const next = new Map<string, ScreenRect>()\n for (const id of stepKey.split(',').filter(Boolean)) {\n const element = node.querySelector<HTMLElement>(`[data-testid=\"stage-item-${id}\"]`)\n const clip = useSpotlight.getState().clips.find((entry) => entry.id === id)\n if (!element || !clip) continue\n const to = element.getBoundingClientRect()\n next.set(id, to)\n const from = last.current.get(id) ?? fromRectRef.current(clip)\n if (reduce || !from || !to.width || !to.height || typeof element.animate !== 'function') {\n continue\n }\n element.animate(\n [\n {\n transform: `translate(${from.left - to.left}px, ${from.top - to.top}px) scale(${\n from.width / to.width\n }, ${from.height / to.height})`,\n },\n { transform: 'none' },\n ],\n { duration: SPOTLIGHT_MS, easing: EASING }\n )\n }\n last.current = next\n }, [stepKey])\n\n // The document dims as the clips arrive.\n const backdrop = useRef<HTMLDivElement>(null)\n useLayoutEffect(() => {\n const element = backdrop.current\n if (!element || typeof element.animate !== 'function') return\n if (prefersReducedMotion(element.ownerDocument.defaultView)) return\n element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: SPOTLIGHT_MS, easing: EASING })\n }, [])\n\n // Esc back to the document, ← → through the steps; taken before the viewer's own keys.\n useEffect(() => {\n if (!view) return\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.metaKey || event.ctrlKey || event.altKey) return\n const target = event.target as HTMLElement | null\n if (target?.closest?.('input, textarea, select, [contenteditable=\"true\"]')) return\n const state = useSpotlight.getState()\n if (event.key === 'Escape') state.hide()\n else if (event.key === 'ArrowRight' || event.key === 'PageDown') state.goTo(state.step + 1)\n else if (event.key === 'ArrowLeft' || event.key === 'PageUp') state.goTo(state.step - 1)\n else return\n event.preventDefault()\n event.stopPropagation()\n }\n view.addEventListener('keydown', onKeyDown, true)\n return () => view.removeEventListener('keydown', onKeyDown, true)\n }, [view])\n\n const actions = useSpotlight.getState()\n const isKept = kept === `${stepKey}|${note}`\n return (\n <div\n ref={rootRef}\n className=\"absolute inset-0 z-30 select-none\"\n style={{ cursor: active ? 'default' : 'none' }}\n role=\"dialog\"\n aria-label=\"Spotlight\"\n data-testid=\"spotlight\"\n >\n <div ref={backdrop} className=\"absolute inset-0 bg-black/85\" />\n <div className=\"absolute inset-0 p-4 pb-16\">\n <PresentationStage show={show} className=\"bg-transparent\" />\n </div>\n <div\n className={cn(\n 'absolute bottom-3 left-1/2 z-10 flex -translate-x-1/2 items-center gap-0.5 rounded-full bg-black/60 p-1 text-white shadow-lg backdrop-blur transition-opacity duration-300',\n active ? 'opacity-100' : 'pointer-events-none opacity-0'\n )}\n role=\"toolbar\"\n aria-label=\"Spotlight\"\n data-testid=\"spotlight-controls\"\n >\n <Control\n label=\"Previous (←)\"\n onClick={() => actions.goTo(step - 1)}\n disabled={step === 0}\n testId=\"spotlight-previous\"\n >\n <ChevronLeft className=\"size-4\" />\n </Control>\n <span\n className=\"min-w-12 text-center text-xs tabular-nums text-white/85\"\n data-testid=\"spotlight-position\"\n >\n {step + 1} / {steps.length}\n </span>\n <Control\n label=\"Next (→)\"\n onClick={() => actions.goTo(step + 1)}\n disabled={step >= steps.length - 1}\n testId=\"spotlight-next\"\n >\n <ChevronRight className=\"size-4\" />\n </Control>\n <div className=\"mx-1 h-4 w-px bg-white/25\" />\n <button\n type=\"button\"\n className=\"inline-flex h-8 items-center gap-1.5 whitespace-nowrap rounded-full px-3 text-xs text-white/85 hover:bg-white/15 hover:text-white disabled:opacity-60 disabled:hover:bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/60 pointer-coarse:h-11\"\n disabled={isKept}\n onClick={() => {\n actions.keep()\n setKept(`${stepKey}|${note}`)\n }}\n title=\"Add this as a slide of the open presentation\"\n data-testid=\"spotlight-keep\"\n >\n {isKept ? <Check className=\"size-3.5\" /> : <Presentation className=\"size-3.5\" />}\n {isKept ? 'Kept as slide' : 'Keep as slide'}\n </button>\n <Control label=\"Back to the document (Esc)\" onClick={actions.hide} testId=\"spotlight-close\">\n <X className=\"size-4\" />\n </Control>\n </div>\n </div>\n )\n}\n","/**\n * The clips gathered in the viewer, in a small strip over the document: each clip (× takes it\n * out), a note to show as the spotlight's title, another document to clip from, and Show (or S),\n * which puts them in the spotlight over this same viewer. Rarer actions — keep them as a slide,\n * add them to the presentation's tray, clear them — are in its \"…\" menu.\n *\n * The strip and the spotlight stay over whichever document the viewer shows, so clips are gathered\n * from several documents in a row. Esc brings the document back where it was scrolled to.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { Eraser, MoreHorizontal, Inbox, Presentation, StickyNote, X } from 'lucide-react'\nimport { useArbiSessionStore, useDocuments } from '@arbidocs/react'\nimport {\n Button,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n Input,\n Popover,\n PopoverContent,\n PopoverTrigger,\n cn,\n getDocumentTitle,\n} from '@arbidocs/react/ui'\nimport { ClipImage } from '../../presentation/ClipImage'\nimport type { Clip } from '../../presentation/model'\nimport { SpotlightView, type ScreenRect } from '../../presentation/SpotlightView'\nimport { useSpotlight } from '../../presentation/spotlight'\nimport { DocumentPicker, type PickerGroup } from '../../settings/DocumentPicker'\n\n/** The viewer this strip is over (its frame), found from the strip's own element. */\nconst frameOf = (node: HTMLElement | null) =>\n node?.closest<HTMLElement>('[data-slot=\"viewer-frame\"]') ?? null\n\nconst isTyping = (target: EventTarget | null) =>\n !!(target as HTMLElement | null)?.closest?.('input, textarea, select, [contenteditable=\"true\"]')\n\n/**\n * Where a clip is on screen now: its area of its page, while its document is clipped in the viewer\n * (`data-clip-document`) and that page is in view.\n */\nfunction clipOnPage(frame: HTMLElement, clip: Clip): ScreenRect | null {\n const clipped = Array.from(frame.querySelectorAll('[data-clip-document]')).find(\n (element) => element.getAttribute('data-clip-document') === clip.documentId\n )\n const page = clipped?.querySelector<HTMLElement>(`[data-page-number=\"${clip.page}\"]`)\n if (!page) return null\n const box = page.getBoundingClientRect()\n const view = frame.ownerDocument.defaultView\n const rect = {\n left: box.left + clip.rect.x * box.width,\n top: box.top + clip.rect.y * box.height,\n width: clip.rect.w * box.width,\n height: clip.rect.h * box.height,\n }\n const visible =\n rect.width > 0 &&\n rect.top + rect.height > 0 &&\n rect.left + rect.width > 0 &&\n (!view || (rect.top < view.innerHeight && rect.left < view.innerWidth))\n return visible ? rect : null\n}\n\nfunction useDocumentSwitcher(clips: readonly Clip[]) {\n const session = useArbiSessionStore()\n const { data: documents } = useDocuments()\n const groups = useMemo<PickerGroup[]>(() => {\n const all = (documents ?? [])\n .map((doc) => ({\n id: doc.external_id,\n title: getDocumentTitle(doc, 'Untitled'),\n fileName: doc.file_name,\n }))\n .sort((a, b) => a.title.localeCompare(b.title))\n const clipped = new Set(clips.map((clip) => clip.documentId))\n const from = all.filter((doc) => clipped.has(doc.id))\n return [\n ...(from.length ? [{ id: 'clipped', heading: 'Clipped from', docs: from }] : []),\n { id: 'all', heading: 'Documents', docs: all },\n ]\n }, [documents, clips])\n const open = (id: string) => session.setViewingDocument({ id, page: null })\n return { groups, open }\n}\n\nexport function SpotlightStrip() {\n const clips = useSpotlight((s) => s.clips)\n const note = useSpotlight((s) => s.note)\n const showing = useSpotlight((s) => s.showing)\n const actions = useSpotlight.getState()\n const switcher = useDocumentSwitcher(clips)\n const [noteOpen, setNoteOpen] = useState(false)\n\n const strip = useRef<HTMLDivElement | null>(null)\n const [view, setView] = useState<Window | null>(null)\n const stripRef = useCallback((node: HTMLDivElement | null) => {\n strip.current = node\n if (node) setView(node.ownerDocument.defaultView)\n }, [])\n\n // S shows the spotlight (not while typing).\n useEffect(() => {\n if (!view || showing) return\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.metaKey || event.ctrlKey || event.altKey || isTyping(event.target)) return\n if (event.key !== 's' && event.key !== 'S') return\n event.preventDefault()\n useSpotlight.getState().show()\n }\n view.addEventListener('keydown', onKeyDown)\n return () => view.removeEventListener('keydown', onKeyDown)\n }, [view, showing])\n\n // Back from the spotlight, the document is where it was scrolled to.\n const scrolled = useRef<{ element: HTMLElement; top: number; left: number } | null>(null)\n useEffect(() => {\n const scroller = frameOf(strip.current)?.querySelector<HTMLElement>(\n '[data-slot=\"pdf-scroller\"]'\n )\n if (showing) {\n scrolled.current = scroller\n ? { element: scroller, top: scroller.scrollTop, left: scroller.scrollLeft }\n : null\n return\n }\n const before = scrolled.current\n scrolled.current = null\n if (before?.element.isConnected) {\n before.element.scrollTop = before.top\n before.element.scrollLeft = before.left\n }\n }, [showing])\n\n const fromRect = useCallback((clip: Clip) => {\n const frame = frameOf(strip.current)\n const onPage = frame ? clipOnPage(frame, clip) : null\n // Not on screen (another document's clip): from its chip in the strip.\n return (\n onPage ??\n strip.current?.querySelector(`[data-spotlight-clip=\"${clip.id}\"]`)?.getBoundingClientRect() ??\n null\n )\n }, [])\n\n const count = `${clips.length} clip${clips.length === 1 ? '' : 's'}`\n return (\n <>\n <div\n ref={stripRef}\n className={cn(\n 'absolute bottom-[4.5rem] left-1/2 z-20 flex max-w-[calc(100%-1.5rem)] -translate-x-1/2 items-center gap-1.5 rounded-full border border-border bg-background/95 p-1 pl-1.5 shadow-lg backdrop-blur',\n showing && 'invisible'\n )}\n role=\"toolbar\"\n aria-label=\"Gathered clips\"\n data-testid=\"spotlight-strip\"\n >\n <ul className=\"flex min-w-0 items-center gap-1 overflow-x-auto [scrollbar-width:none]\">\n {clips.map((clip, index) => (\n <li\n key={clip.id}\n className=\"group relative h-8 shrink-0 overflow-hidden rounded-md border border-border bg-white\"\n style={{ width: `${Math.min(4.5, Math.max(1.5, 2 * clip.aspect))}rem` }}\n title={clip.caption}\n data-spotlight-clip={clip.id}\n data-testid={`spotlight-chip-${index}`}\n >\n <ClipImage clip={clip} className=\"h-full w-full\" />\n <button\n type=\"button\"\n className=\"absolute inset-0 flex items-center justify-center bg-black/50 text-white opacity-0 transition-opacity focus-visible:opacity-100 group-hover:opacity-100\"\n onClick={() => actions.remove(clip.id)}\n aria-label={`Remove ${clip.caption}`}\n data-testid={`spotlight-remove-${index}`}\n >\n <X className=\"size-3.5\" />\n </button>\n </li>\n ))}\n </ul>\n <span className=\"shrink-0 text-xs text-muted-foreground\" data-testid=\"spotlight-count\">\n {count}\n </span>\n <Popover open={noteOpen} onOpenChange={setNoteOpen}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant={note ? 'secondary' : 'ghost'}\n size=\"sm\"\n className=\"h-7 max-w-40 shrink-0 gap-1 rounded-full px-2 text-xs\"\n title={note ? `Note: ${note}` : 'Type a note to show as the title'}\n data-testid=\"spotlight-note\"\n >\n <StickyNote className=\"size-3.5 shrink-0\" />\n {note ? <span className=\"truncate\">{note}</span> : <span>Note</span>}\n </Button>\n </PopoverTrigger>\n <PopoverContent side=\"top\" className=\"w-72 p-2\">\n <Input\n autoFocus\n value={note}\n placeholder=\"Type a note\"\n onChange={(event) => actions.setNote(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter') setNoteOpen(false)\n }}\n data-testid=\"spotlight-note-input\"\n />\n </PopoverContent>\n </Popover>\n <div className=\"w-44 shrink-0 [&_[role=combobox]]:h-7 [&_[role=combobox]]:rounded-full [&_[role=combobox]]:text-xs\">\n <DocumentPicker\n groups={switcher.groups}\n value=\"\"\n onValueChange={switcher.open}\n labels={{ placeholder: 'Clip another document' }}\n />\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"h-7 shrink-0 gap-1 rounded-full px-3 text-xs\"\n onClick={actions.show}\n title=\"Show the clips over the document (S)\"\n data-testid=\"spotlight-show\"\n >\n <Presentation className=\"size-3.5\" />\n Show\n </Button>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-7 shrink-0 rounded-full\"\n aria-label=\"More\"\n data-testid=\"spotlight-more\"\n >\n <MoreHorizontal className=\"size-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" side=\"top\">\n <DropdownMenuItem onSelect={actions.keep} data-testid=\"spotlight-menu-keep\">\n <Presentation className=\"mr-2 size-4\" /> Keep as slide\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={actions.addToTray} data-testid=\"spotlight-menu-tray\">\n <Inbox className=\"mr-2 size-4\" /> Add to the presentation tray\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={actions.clear} data-testid=\"spotlight-menu-clear\">\n <Eraser className=\"mr-2 size-4\" /> Clear the clips\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n {showing && <SpotlightView fromRect={fromRect} />}\n </>\n )\n}\n"]}