@arbidocs/blocks 0.5.125 → 0.5.127

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 (200) hide show
  1. package/README.md +19 -0
  2. package/dist/{ClippingMediaViewer-ZFEX5A3U.cjs → ClippingMediaViewer-47BAXNLO.cjs} +10 -11
  3. package/dist/ClippingMediaViewer-47BAXNLO.cjs.map +1 -0
  4. package/dist/{ClippingPdfViewer-3LRTEPHV.cjs → ClippingPdfViewer-D5BYHEAH.cjs} +16 -14
  5. package/dist/ClippingPdfViewer-D5BYHEAH.cjs.map +1 -0
  6. package/dist/ConnectedCallDock-XJOYVVAH.cjs +6 -6
  7. package/dist/ConnectedCallRoom-CUDYAPQK.cjs +6 -6
  8. package/dist/{ConnectedNativeDocxComparison-7QQ55JIO.cjs → ConnectedNativeDocxComparison-JUF2TI2P.cjs} +12 -12
  9. package/dist/{ConnectedNativeDocxComparison-7QQ55JIO.cjs.map → ConnectedNativeDocxComparison-JUF2TI2P.cjs.map} +1 -1
  10. package/dist/{ConnectedPresentation-L32JPRFI.cjs → ConnectedPresentation-SI3L77J4.cjs} +35 -36
  11. package/dist/ConnectedPresentation-SI3L77J4.cjs.map +1 -0
  12. package/dist/{ConnectedProductionSetDialog-4EAY6KAZ.cjs → ConnectedProductionSetDialog-LHB4VQWC.cjs} +3 -3
  13. package/dist/{ConnectedProductionSetDialog-4EAY6KAZ.cjs.map → ConnectedProductionSetDialog-LHB4VQWC.cjs.map} +1 -1
  14. package/dist/{ConnectedSlidesTab-P7GGNUTO.cjs → ConnectedSlidesTab-E76OVY34.cjs} +38 -44
  15. package/dist/ConnectedSlidesTab-E76OVY34.cjs.map +1 -0
  16. package/dist/{ConnectedStreamedWordArtifact-7NJ3N52V.cjs → ConnectedStreamedWordArtifact-Q64DRRQD.cjs} +12 -12
  17. package/dist/{ConnectedStreamedWordArtifact-7NJ3N52V.cjs.map → ConnectedStreamedWordArtifact-Q64DRRQD.cjs.map} +1 -1
  18. package/dist/{DocumentReviewCard-BFPOFQXI.cjs → DocumentReviewCard-M53XFLI2.cjs} +16 -16
  19. package/dist/{DocumentReviewCard-BFPOFQXI.cjs.map → DocumentReviewCard-M53XFLI2.cjs.map} +1 -1
  20. package/dist/{FilesViewerDialog-MHUVEZ7M.cjs → FilesViewerDialog-V6FSRQTP.cjs} +33 -34
  21. package/dist/FilesViewerDialog-V6FSRQTP.cjs.map +1 -0
  22. package/dist/{NativeDocxEditor-SIRYPMBN.cjs → NativeDocxEditor-2PTEK6ND.cjs} +6 -6
  23. package/dist/{NativeDocxEditor-SIRYPMBN.cjs.map → NativeDocxEditor-2PTEK6ND.cjs.map} +1 -1
  24. package/dist/{SpotlightStrip-ED22UYTM.cjs → SpotlightStrip-QQ2WUI32.cjs} +28 -29
  25. package/dist/SpotlightStrip-QQ2WUI32.cjs.map +1 -0
  26. package/dist/{WorkspaceTableView-MIFYXV3G.cjs → WorkspaceTableView-3XYAV4BV.cjs} +8 -8
  27. package/dist/{WorkspaceTableView-MIFYXV3G.cjs.map → WorkspaceTableView-3XYAV4BV.cjs.map} +1 -1
  28. package/dist/agents/index.cjs +215 -215
  29. package/dist/ai-IEIR7SC2.cjs +3 -3
  30. package/dist/auth/index.cjs +1 -1
  31. package/dist/calls/index.cjs +2 -2
  32. package/dist/chat/index.cjs +6 -6
  33. package/dist/{chunk-GAMRVB3I.cjs → chunk-23I5EUZA.cjs} +65 -10
  34. package/dist/chunk-23I5EUZA.cjs.map +1 -0
  35. package/dist/{chunk-ZZZITHGK.cjs → chunk-26SFI2BH.cjs} +16 -3
  36. package/dist/chunk-26SFI2BH.cjs.map +1 -0
  37. package/dist/{chunk-OGK377NL.cjs → chunk-2SU37RZA.cjs} +409 -42
  38. package/dist/chunk-2SU37RZA.cjs.map +1 -0
  39. package/dist/{chunk-CQ2RJYE2.cjs → chunk-3IH55CDE.cjs} +3 -3
  40. package/dist/{chunk-CQ2RJYE2.cjs.map → chunk-3IH55CDE.cjs.map} +1 -1
  41. package/dist/{chunk-AS6QZETU.cjs → chunk-5QERK4PK.cjs} +35 -35
  42. package/dist/{chunk-AS6QZETU.cjs.map → chunk-5QERK4PK.cjs.map} +1 -1
  43. package/dist/{chunk-AISYXX4M.cjs → chunk-7NLO7ASW.cjs} +4 -4
  44. package/dist/{chunk-AISYXX4M.cjs.map → chunk-7NLO7ASW.cjs.map} +1 -1
  45. package/dist/{chunk-QIK7E6VF.cjs → chunk-7PN3YENO.cjs} +4 -4
  46. package/dist/{chunk-QIK7E6VF.cjs.map → chunk-7PN3YENO.cjs.map} +1 -1
  47. package/dist/{chunk-JY7UUZQG.cjs → chunk-7Q46YAX4.cjs} +74 -71
  48. package/dist/chunk-7Q46YAX4.cjs.map +1 -0
  49. package/dist/{chunk-J3FFSEKO.cjs → chunk-ADDQKZ5I.cjs} +4 -4
  50. package/dist/{chunk-J3FFSEKO.cjs.map → chunk-ADDQKZ5I.cjs.map} +1 -1
  51. package/dist/{chunk-L6HTZIBD.cjs → chunk-ALK3PUUB.cjs} +3 -3
  52. package/dist/{chunk-L6HTZIBD.cjs.map → chunk-ALK3PUUB.cjs.map} +1 -1
  53. package/dist/{chunk-3ZZZO4F7.cjs → chunk-BY3QA3S5.cjs} +9 -9
  54. package/dist/{chunk-3ZZZO4F7.cjs.map → chunk-BY3QA3S5.cjs.map} +1 -1
  55. package/dist/{chunk-3UP32FM3.cjs → chunk-CQQQIZU4.cjs} +5 -5
  56. package/dist/{chunk-3UP32FM3.cjs.map → chunk-CQQQIZU4.cjs.map} +1 -1
  57. package/dist/{chunk-YOEP3I4V.cjs → chunk-E3L5DB5F.cjs} +5 -5
  58. package/dist/{chunk-YOEP3I4V.cjs.map → chunk-E3L5DB5F.cjs.map} +1 -1
  59. package/dist/{chunk-UA7HOLAZ.cjs → chunk-ESVRVZ53.cjs} +5 -5
  60. package/dist/{chunk-UA7HOLAZ.cjs.map → chunk-ESVRVZ53.cjs.map} +1 -1
  61. package/dist/{chunk-2K6IMIVL.cjs → chunk-GNZAH6UI.cjs} +3 -3
  62. package/dist/{chunk-2K6IMIVL.cjs.map → chunk-GNZAH6UI.cjs.map} +1 -1
  63. package/dist/{chunk-KA3F6TIH.cjs → chunk-IWHP7FAB.cjs} +12 -5
  64. package/dist/chunk-IWHP7FAB.cjs.map +1 -0
  65. package/dist/{chunk-ORNLCBNE.cjs → chunk-JVEDKKNO.cjs} +43 -4
  66. package/dist/chunk-JVEDKKNO.cjs.map +1 -0
  67. package/dist/{chunk-6JMW3IJ2.cjs → chunk-JVLOIIA7.cjs} +103 -100
  68. package/dist/chunk-JVLOIIA7.cjs.map +1 -0
  69. package/dist/{chunk-UUENH4BJ.cjs → chunk-KXIRVKCJ.cjs} +3 -3
  70. package/dist/{chunk-UUENH4BJ.cjs.map → chunk-KXIRVKCJ.cjs.map} +1 -1
  71. package/dist/{chunk-YVO6Z5BS.cjs → chunk-M6WVIGYP.cjs} +10 -10
  72. package/dist/{chunk-YVO6Z5BS.cjs.map → chunk-M6WVIGYP.cjs.map} +1 -1
  73. package/dist/{chunk-PSB64T43.cjs → chunk-MLA2BJ2Y.cjs} +28 -28
  74. package/dist/{chunk-PSB64T43.cjs.map → chunk-MLA2BJ2Y.cjs.map} +1 -1
  75. package/dist/{chunk-B3R62764.cjs → chunk-MMSHIIKG.cjs} +6 -6
  76. package/dist/{chunk-B3R62764.cjs.map → chunk-MMSHIIKG.cjs.map} +1 -1
  77. package/dist/{chunk-ENOPKX7E.cjs → chunk-MXBXZXXQ.cjs} +3 -3
  78. package/dist/{chunk-ENOPKX7E.cjs.map → chunk-MXBXZXXQ.cjs.map} +1 -1
  79. package/dist/chunk-NTDQQLDD.cjs +18 -0
  80. package/dist/{chunk-IRGRMJPP.cjs.map → chunk-NTDQQLDD.cjs.map} +1 -1
  81. package/dist/{chunk-PVJ5ODDB.cjs → chunk-Q6IKXWPG.cjs} +4 -4
  82. package/dist/{chunk-PVJ5ODDB.cjs.map → chunk-Q6IKXWPG.cjs.map} +1 -1
  83. package/dist/{chunk-4LP5MJGQ.cjs → chunk-QG2U2JGX.cjs} +4 -4
  84. package/dist/{chunk-4LP5MJGQ.cjs.map → chunk-QG2U2JGX.cjs.map} +1 -1
  85. package/dist/{chunk-MVXHTAJ4.cjs → chunk-QWVUBTW3.cjs} +9 -9
  86. package/dist/{chunk-MVXHTAJ4.cjs.map → chunk-QWVUBTW3.cjs.map} +1 -1
  87. package/dist/{chunk-OHWHYVKG.cjs → chunk-T73BHNQX.cjs} +3 -3
  88. package/dist/{chunk-OHWHYVKG.cjs.map → chunk-T73BHNQX.cjs.map} +1 -1
  89. package/dist/{chunk-PWBRE7JA.cjs → chunk-VLQCLQAS.cjs} +23 -10
  90. package/dist/chunk-VLQCLQAS.cjs.map +1 -0
  91. package/dist/{chunk-ADPRIBSH.cjs → chunk-X7ALF4MP.cjs} +7 -7
  92. package/dist/{chunk-ADPRIBSH.cjs.map → chunk-X7ALF4MP.cjs.map} +1 -1
  93. package/dist/{chunk-7RJZIPLE.cjs → chunk-YP5EBGLU.cjs} +33 -33
  94. package/dist/{chunk-7RJZIPLE.cjs.map → chunk-YP5EBGLU.cjs.map} +1 -1
  95. package/dist/{chunk-TSFNLY3K.cjs → chunk-ZCU72Q76.cjs} +192 -166
  96. package/dist/chunk-ZCU72Q76.cjs.map +1 -0
  97. package/dist/{chunk-BLIO3B7Z.cjs → chunk-ZYGAXD3N.cjs} +3 -3
  98. package/dist/{chunk-BLIO3B7Z.cjs.map → chunk-ZYGAXD3N.cjs.map} +1 -1
  99. package/dist/connect/docx.cjs +16 -16
  100. package/dist/connect/index.cjs +1095 -1114
  101. package/dist/connect/index.cjs.map +1 -1
  102. package/dist/connect/viewer.cjs +26 -27
  103. package/dist/connection/ai.d.ts +1 -0
  104. package/dist/connection/docx/ConnectedNativeDocxEditor.js +4 -5
  105. package/dist/connection/docx/ConnectedNativeDocxEditor.js.map +2 -2
  106. package/dist/connection/docx/save-native-version.d.ts +3 -0
  107. package/dist/connection/docx/save-native-version.js +19 -0
  108. package/dist/connection/docx/save-native-version.js.map +7 -0
  109. package/dist/connection/presentation/ClippingPdfViewer.js +8 -5
  110. package/dist/connection/presentation/ClippingPdfViewer.js.map +2 -2
  111. package/dist/connection/presentation/ConnectedSlidesTab.js +8 -13
  112. package/dist/connection/presentation/ConnectedSlidesTab.js.map +2 -2
  113. package/dist/connection/presentation/usePresentationController.js +24 -15
  114. package/dist/connection/presentation/usePresentationController.js.map +2 -2
  115. package/dist/connection/presentation/usePresentationFiles.d.ts +2 -1
  116. package/dist/connection/presentation/usePresentationFiles.js +15 -6
  117. package/dist/connection/presentation/usePresentationFiles.js.map +2 -2
  118. package/dist/connection/remote-control/ConnectedRemoteControlConsent.js +6 -3
  119. package/dist/connection/remote-control/ConnectedRemoteControlConsent.js.map +2 -2
  120. package/dist/connection/remote-control/ConnectedRemoteControlIndicator.js +12 -2
  121. package/dist/connection/remote-control/ConnectedRemoteControlIndicator.js.map +2 -2
  122. package/dist/connection/remote-control/ConnectedRemoteControlSwitch.js +1 -1
  123. package/dist/connection/remote-control/ConnectedRemoteControlSwitch.js.map +1 -1
  124. package/dist/connection/remote-control/browser-diagnostics.d.ts +7 -0
  125. package/dist/connection/remote-control/browser-diagnostics.js +109 -0
  126. package/dist/connection/remote-control/browser-diagnostics.js.map +7 -0
  127. package/dist/connection/remote-control/remoteControl.d.ts +9 -1
  128. package/dist/connection/remote-control/remoteControl.js +130 -10
  129. package/dist/connection/remote-control/remoteControl.js.map +2 -2
  130. package/dist/connection/remote-control/screen-sharing.d.ts +11 -0
  131. package/dist/connection/remote-control/screen-sharing.js +78 -0
  132. package/dist/connection/remote-control/screen-sharing.js.map +7 -0
  133. package/dist/connection/report-problem/screen-capture.d.ts +1 -0
  134. package/dist/connection/report-problem/screen-capture.js +2 -1
  135. package/dist/connection/report-problem/screen-capture.js.map +2 -2
  136. package/dist/connection/search/ConnectedSearchPage.d.ts +0 -11
  137. package/dist/connection/search/ConnectedSearchPage.js +3 -0
  138. package/dist/connection/search/ConnectedSearchPage.js.map +2 -2
  139. package/dist/connection/search/SearchResultRows.js +75 -43
  140. package/dist/connection/search/SearchResultRows.js.map +2 -2
  141. package/dist/connection/viewer/CleanViewControl.d.ts +3 -1
  142. package/dist/connection/viewer/CleanViewControl.js +5 -1
  143. package/dist/connection/viewer/CleanViewControl.js.map +2 -2
  144. package/dist/connection/viewer/ConnectedDocumentViewer.js +1 -1
  145. package/dist/connection/viewer/ConnectedDocumentViewer.js.map +2 -2
  146. package/dist/connection/viewer/useCleanView.d.ts +1 -1
  147. package/dist/connection/viewer/useCleanView.js +2 -2
  148. package/dist/connection/viewer/useCleanView.js.map +2 -2
  149. package/dist/connection/viewer/useViewedDocument.d.ts +1 -0
  150. package/dist/document-tools/index.cjs +20 -20
  151. package/dist/docx/NativeDocxEditor.d.ts +2 -1
  152. package/dist/docx/NativeDocxEditor.js +34 -7
  153. package/dist/docx/NativeDocxEditor.js.map +2 -2
  154. package/dist/docx/index.cjs +38 -38
  155. package/dist/docx/useVersionAutosave.d.ts +10 -0
  156. package/dist/docx/useVersionAutosave.js +36 -0
  157. package/dist/docx/useVersionAutosave.js.map +7 -0
  158. package/dist/embed/index.cjs +19 -19
  159. package/dist/facts/index.cjs +64 -64
  160. package/dist/files/index.cjs +223 -223
  161. package/dist/index.cjs +542 -542
  162. package/dist/presentation/ClipCaptureOverlay.js +14 -1
  163. package/dist/presentation/ClipCaptureOverlay.js.map +2 -2
  164. package/dist/presentation/ClipImage.js +7 -4
  165. package/dist/presentation/ClipImage.js.map +2 -2
  166. package/dist/presentation/ClipTray.js +11 -7
  167. package/dist/presentation/ClipTray.js.map +2 -2
  168. package/dist/presentation/SlidesEditor.js +6 -2
  169. package/dist/presentation/SlidesEditor.js.map +2 -2
  170. package/dist/presentation/store.d.ts +2 -0
  171. package/dist/presentation/store.js +15 -0
  172. package/dist/presentation/store.js.map +3 -3
  173. package/dist/settings/index.cjs +146 -146
  174. package/dist/shell/RemoteControlPill.d.ts +2 -1
  175. package/dist/shell/RemoteControlPill.js +10 -3
  176. package/dist/shell/RemoteControlPill.js.map +2 -2
  177. package/dist/shell/index.cjs +21 -21
  178. package/dist/site/index.cjs +6 -6
  179. package/dist/studio/index.cjs +9 -9
  180. package/dist/workflow-canvas/index.cjs +34 -34
  181. package/node_modules/@arbidocs/coedit/package.json +1 -1
  182. package/package.json +5 -5
  183. package/dist/ClippingMediaViewer-ZFEX5A3U.cjs.map +0 -1
  184. package/dist/ClippingPdfViewer-3LRTEPHV.cjs.map +0 -1
  185. package/dist/ConnectedPresentation-L32JPRFI.cjs.map +0 -1
  186. package/dist/ConnectedSlidesTab-P7GGNUTO.cjs.map +0 -1
  187. package/dist/FilesViewerDialog-MHUVEZ7M.cjs.map +0 -1
  188. package/dist/SpotlightStrip-ED22UYTM.cjs.map +0 -1
  189. package/dist/chunk-6JMW3IJ2.cjs.map +0 -1
  190. package/dist/chunk-GAMRVB3I.cjs.map +0 -1
  191. package/dist/chunk-GFUOCGY6.cjs +0 -32
  192. package/dist/chunk-GFUOCGY6.cjs.map +0 -1
  193. package/dist/chunk-IRGRMJPP.cjs +0 -18
  194. package/dist/chunk-JY7UUZQG.cjs.map +0 -1
  195. package/dist/chunk-KA3F6TIH.cjs.map +0 -1
  196. package/dist/chunk-OGK377NL.cjs.map +0 -1
  197. package/dist/chunk-ORNLCBNE.cjs.map +0 -1
  198. package/dist/chunk-PWBRE7JA.cjs.map +0 -1
  199. package/dist/chunk-TSFNLY3K.cjs.map +0 -1
  200. package/dist/chunk-ZZZITHGK.cjs.map +0 -1
@@ -1,40 +1,38 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var chunk6JMW3IJ2_cjs = require('../chunk-6JMW3IJ2.cjs');
4
+ var chunkJVLOIIA7_cjs = require('../chunk-JVLOIIA7.cjs');
5
5
  require('../chunk-JITYC4OS.cjs');
6
6
  require('../chunk-WVZLAAIZ.cjs');
7
- require('../chunk-MVXHTAJ4.cjs');
8
- require('../chunk-ADPRIBSH.cjs');
9
- require('../chunk-ORNLCBNE.cjs');
7
+ require('../chunk-X7ALF4MP.cjs');
10
8
  require('../chunk-4CIVQWAX.cjs');
11
9
  var chunkPL7HUTM3_cjs = require('../chunk-PL7HUTM3.cjs');
12
- require('../chunk-GFUOCGY6.cjs');
13
- require('../chunk-J3FFSEKO.cjs');
14
- require('../chunk-PWBRE7JA.cjs');
15
- require('../chunk-4ONPEVTP.cjs');
16
- require('../chunk-V3KLUWQQ.cjs');
17
- require('../chunk-CQ2RJYE2.cjs');
18
- require('../chunk-2K6IMIVL.cjs');
19
- require('../chunk-JH6JTI76.cjs');
20
- require('../chunk-ZESMRGDV.cjs');
21
- require('../chunk-UUENH4BJ.cjs');
22
- require('../chunk-AS6QZETU.cjs');
23
- require('../chunk-OHWHYVKG.cjs');
10
+ require('../chunk-QWVUBTW3.cjs');
11
+ require('../chunk-JVEDKKNO.cjs');
12
+ require('../chunk-ADDQKZ5I.cjs');
13
+ require('../chunk-KXIRVKCJ.cjs');
14
+ require('../chunk-5QERK4PK.cjs');
15
+ require('../chunk-T73BHNQX.cjs');
24
16
  require('../chunk-HYUKPHCT.cjs');
25
- require('../chunk-L6HTZIBD.cjs');
17
+ require('../chunk-ALK3PUUB.cjs');
26
18
  require('../chunk-223D4727.cjs');
27
19
  require('../chunk-SNUBQMIS.cjs');
20
+ require('../chunk-VLQCLQAS.cjs');
21
+ require('../chunk-4ONPEVTP.cjs');
22
+ require('../chunk-V3KLUWQQ.cjs');
28
23
  require('../chunk-P5AQYC4D.cjs');
29
24
  require('../chunk-WAV762GP.cjs');
25
+ require('../chunk-3IH55CDE.cjs');
26
+ require('../chunk-GNZAH6UI.cjs');
27
+ require('../chunk-JH6JTI76.cjs');
30
28
  require('../chunk-QQMMCXBP.cjs');
31
29
  require('../chunk-DTMBAFIL.cjs');
30
+ require('../chunk-ZESMRGDV.cjs');
32
31
  require('../chunk-VCEX7COE.cjs');
33
32
  require('../chunk-34HQO4ZQ.cjs');
34
- require('../chunk-KA3F6TIH.cjs');
33
+ require('../chunk-IWHP7FAB.cjs');
35
34
  require('../chunk-K4763GD5.cjs');
36
35
  require('../chunk-2RLA6QO2.cjs');
37
- require('../chunk-BLIO3B7Z.cjs');
38
36
  require('../chunk-MZZ3SBQL.cjs');
39
37
  require('../chunk-NY3YUCOW.cjs');
40
38
  require('../chunk-325GVZE6.cjs');
@@ -42,12 +40,15 @@ require('../chunk-WQMR3O2D.cjs');
42
40
  require('../chunk-ORELHENJ.cjs');
43
41
  require('../chunk-NWNVWO6L.cjs');
44
42
  require('../chunk-SK6HQ46Q.cjs');
45
- require('../chunk-OEJQFPWO.cjs');
43
+ require('../chunk-MNIQDXM6.cjs');
44
+ require('../chunk-E3Y7RI3J.cjs');
46
45
  require('../chunk-TG2GM7WJ.cjs');
47
- require('../chunk-JJZOL5MG.cjs');
46
+ require('../chunk-ZYGAXD3N.cjs');
47
+ require('../chunk-OEJQFPWO.cjs');
48
48
  require('../chunk-OYXWQTHF.cjs');
49
49
  require('../chunk-S237ORM3.cjs');
50
50
  require('../chunk-II2OEXHW.cjs');
51
+ require('../chunk-JJZOL5MG.cjs');
51
52
  require('../chunk-3YEXZX4H.cjs');
52
53
  require('../chunk-4PA6VCCR.cjs');
53
54
  require('../chunk-WXUU3COI.cjs');
@@ -56,13 +57,13 @@ require('../chunk-RL2HI6NP.cjs');
56
57
  require('../chunk-6UZC4XVV.cjs');
57
58
  require('../chunk-YVCOQHCE.cjs');
58
59
  require('../chunk-JV5TVUOU.cjs');
59
- require('../chunk-VZOOGA47.cjs');
60
60
  require('../chunk-ZVEW3JBT.cjs');
61
61
  require('../chunk-EG7EJFUE.cjs');
62
62
  require('../chunk-VG24E4JJ.cjs');
63
63
  require('../chunk-U2FFG2B3.cjs');
64
64
  require('../chunk-KKYNTWDU.cjs');
65
65
  require('../chunk-M74FWYNL.cjs');
66
+ require('../chunk-VZOOGA47.cjs');
66
67
  require('../chunk-HQDR43JC.cjs');
67
68
  require('../chunk-JR2YSBD5.cjs');
68
69
  require('../chunk-3YZKDCWY.cjs');
@@ -79,23 +80,21 @@ require('../chunk-KQF7POTG.cjs');
79
80
  require('../chunk-75BNMCUX.cjs');
80
81
  require('../chunk-FX3K6LNZ.cjs');
81
82
  require('../chunk-QGVOT7E4.cjs');
82
- require('../chunk-IPSHAMO2.cjs');
83
83
  require('../chunk-QRAQZTJE.cjs');
84
84
  require('../chunk-UDSHVNU3.cjs');
85
+ require('../chunk-IPSHAMO2.cjs');
85
86
  require('../chunk-PPCATBOY.cjs');
86
- require('../chunk-LJJJ6CYH.cjs');
87
- require('../chunk-MNIQDXM6.cjs');
88
- require('../chunk-E3Y7RI3J.cjs');
89
87
  require('../chunk-TKHCIHE2.cjs');
90
88
  require('../chunk-JDFBEGQO.cjs');
91
89
  require('../chunk-ULWRNGGE.cjs');
90
+ require('../chunk-LJJJ6CYH.cjs');
92
91
  require('../chunk-PLIF5CUV.cjs');
93
92
 
94
93
 
95
94
 
96
95
  Object.defineProperty(exports, "ConnectedDocumentViewer", {
97
96
  enumerable: true,
98
- get: function () { return chunk6JMW3IJ2_cjs.ConnectedDocumentViewer; }
97
+ get: function () { return chunkJVLOIIA7_cjs.ConnectedDocumentViewer; }
99
98
  });
100
99
  Object.defineProperty(exports, "ConnectedDocumentToolsProvider", {
101
100
  enumerable: true,
@@ -57,6 +57,7 @@ export declare function useWorkspaceDocs(): {
57
57
  config_ext_id?: string | null;
58
58
  parent_ext_id?: string | null;
59
59
  current_version: number;
60
+ autosave: boolean;
60
61
  versions: import("@arbidocs/client").components["schemas"]["DocVersion"][];
61
62
  thread_id?: string | null;
62
63
  derived_from: import("@arbidocs/client").components["schemas"]["DerivedFrom"][];
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { saveNativeVersion } from "./save-native-version.js";
3
4
  import { useMemo, useRef, useState } from "react";
4
5
  import { useQueryClient } from "@tanstack/react-query";
5
6
  import { refreshDocuments, useArbi, useWorkspaceUsers } from "@arbidocs/react";
@@ -9,6 +10,7 @@ import {
9
10
  useOptionalDocumentTools
10
11
  } from "@arbidocs/react/document-tools";
11
12
  import { NativeDocxEditor } from "../../docx/lazy.js";
13
+ import { registerDocumentTurnFlush } from "@arbidocs/react";
12
14
  import { useRegisterOpenDocument } from "@arbidocs/react/shell";
13
15
  import { useBlocksEnvironment } from "../../provider/environment.js";
14
16
  import { useConnectedWorkspace } from "../ConnectionProvider.js";
@@ -94,16 +96,13 @@ function useConnectedNativeDocxAdapter(fontBaseUrl = "/native-docx/fonts") {
94
96
  await ensureWorkspaceOpen();
95
97
  return (await arbi.editor.request(`/${encodeURIComponent(id)}/session`, { method: "POST" })).json();
96
98
  },
97
- save: async (id, state) => (await arbi.editor.request(`/${encodeURIComponent(id)}/save`, {
98
- method: "POST",
99
- headers: { "Content-Type": "application/json" },
100
- body: JSON.stringify({ state })
101
- })).json(),
99
+ save: (id, state, autosave) => saveNativeVersion(arbi.editor.request, id, state, autosave),
102
100
  loadFonts: () => import("../../docx/fonts.js").then((m) => m.loadDocxFonts(fontBaseUrl)),
103
101
  baseUrl: arbi.getConnectionInfo()?.baseUrl || window.location.origin,
104
102
  author,
105
103
  subscribeTokenChanges: tokenChanges(arbi),
106
104
  onDirtyChange: support.onDirtyChange,
105
+ registerTurnFlush: registerDocumentTurnFlush,
107
106
  // The document's row moves to the saved version in place: through the host when it keeps
108
107
  // the lists (its document tools refresh them), else here.
109
108
  onVersionSaved: async (id, version) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/connection/docx/ConnectedNativeDocxEditor.tsx"],
4
- "sourcesContent": ["import { useMemo, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { refreshDocuments, useArbi, useWorkspaceUsers } from '@arbidocs/react'\nimport {\n fetchSavedSignature,\n storeSavedSignature,\n useOptionalDocumentTools,\n} from '@arbidocs/react/document-tools'\nimport type { NativeDocxAdapter, NativeDocxEditorProps } from '../../docx/NativeDocxEditor'\nimport { NativeDocxEditor } from '../../docx/lazy'\nimport { useRegisterOpenDocument } from '@arbidocs/react/shell'\nimport { useBlocksEnvironment } from '../../provider/environment'\nimport { useConnectedWorkspace } from '../ConnectionProvider'\nimport { useDocxCommentTarget, useNativeDocxSupport } from './support'\nimport { useConnectedDocxReferences } from './useConnectedDocxReferences'\nimport { useConnectedHouseTemplate } from './useConnectedHouseTemplate'\n\nexport type ConnectedNativeDocxEditorProps = Omit<NativeDocxEditorProps, 'adapter' | 'compact'> & {\n fontBaseUrl?: string\n}\n\n/**\n * The native Word editor on the signed-in ARBI session, under any host (`defaultHost`'s sign-in\n * layer, or the host's own `arbi` and session state). Until a live session has opened, a failure to\n * open one falls back to the saved copy, read-only, so the document is still readable; later token\n * refreshes are not guarded (switching modes would drop unsaved edits).\n */\nexport function ConnectedNativeDocxEditor({\n fontBaseUrl = '/native-docx/fonts',\n ...props\n}: ConnectedNativeDocxEditorProps) {\n const adapter = useConnectedNativeDocxAdapter(fontBaseUrl)\n const { workspaceId } = useConnectedWorkspace()\n const commentTarget = useDocxCommentTarget()\n const [openFailed, setOpenFailed] = useState(false)\n const hasOpened = useRef(false)\n const guardedAdapter = useMemo<NativeDocxAdapter>(\n () => ({\n ...adapter,\n open: async (documentId) => {\n try {\n const session = await adapter.open(documentId)\n hasOpened.current = true\n return session\n } catch (error) {\n if (!hasOpened.current) setOpenFailed(true)\n throw error\n }\n },\n }),\n [adapter]\n )\n // Drafting mode: the saved Word document being edited is what the chat is about. A history\n // snapshot, a streamed preview, an unsaved draft and an embedded body field are not.\n const live = !props.snapshot && !props.stream && !props.draftContent\n useRegisterOpenDocument({ documentId: live ? props.documentId : null, workspaceId })\n const initialCommentId =\n props.initialCommentId ??\n (commentTarget?.documentId === props.documentId ? commentTarget.commentId : undefined)\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n {openFailed && (\n <p role=\"status\" className=\"text-muted-foreground border-b px-3 py-2 text-xs\">\n Co-editing isn&apos;t available for this document right now, so this is a read-only copy\n of the last saved version.\n </p>\n )}\n <div className=\"min-h-0 flex-1\">\n <NativeDocxEditor\n {...props}\n snapshot={props.snapshot || openFailed}\n initialCommentId={initialCommentId}\n adapter={guardedAdapter}\n />\n </div>\n </div>\n )\n}\n\n/** The access token changed (a refresh): the live room is told the new one. */\nfunction tokenChanges(arbi: ReturnType<typeof useArbi>) {\n return (listener: () => void) => {\n if (!arbi.isLoggedIn) return () => {}\n const session = arbi.getClient().session\n let token = session.getState().accessToken\n return session.subscribe((state) => {\n if (state.accessToken === token) return\n token = state.accessToken\n listener()\n })\n }\n}\n\n/**\n * The native Word adapter on the signed-in session: the editor transport, the people to mention,\n * cross-references, the house template, and the host's extras (its host config's `docx`).\n */\nexport function useConnectedNativeDocxAdapter(fontBaseUrl = '/native-docx/fonts') {\n const arbi = useArbi()\n const { workspaceId, live, user, ensureWorkspaceOpen } = useConnectedWorkspace()\n const queries = useQueryClient()\n const { notify } = useBlocksEnvironment()\n const tools = useOptionalDocumentTools()\n const support = useNativeDocxSupport()\n const references = useConnectedDocxReferences()\n const houseTemplate = useConnectedHouseTemplate()\n const { data: members } = useWorkspaceUsers(workspaceId ?? undefined, {\n enabled: live && !!workspaceId,\n beforeFetch: ensureWorkspaceOpen,\n })\n const onDocumentsChanged = tools?.onDocumentsChanged\n // Tracked changes and comments are authored by the user's first name.\n const author = (live && arbi.user.profile()?.givenName) || user?.name || 'You'\n const adapter = useMemo<NativeDocxAdapter>(\n () => ({\n request: async (path, options) => {\n await ensureWorkspaceOpen()\n return arbi.editor.request(path, options)\n },\n open: async (id) => {\n await ensureWorkspaceOpen()\n return (\n await arbi.editor.request(`/${encodeURIComponent(id)}/session`, { method: 'POST' })\n ).json()\n },\n save: async (id, state) =>\n (\n await arbi.editor.request(`/${encodeURIComponent(id)}/save`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ state }),\n })\n ).json(),\n loadFonts: () => import('../../docx/fonts').then((m) => m.loadDocxFonts(fontBaseUrl)),\n baseUrl: arbi.getConnectionInfo()?.baseUrl || window.location.origin,\n author,\n subscribeTokenChanges: tokenChanges(arbi),\n onDirtyChange: support.onDirtyChange,\n // The document's row moves to the saved version in place: through the host when it keeps\n // the lists (its document tools refresh them), else here.\n onVersionSaved: async (id, version) => {\n if (onDocumentsChanged) await onDocumentsChanged([id], 'version')\n if (version || !onDocumentsChanged) {\n const fresh = await refreshDocuments(queries, arbi, [id])\n if (\n version &&\n !fresh.some((doc) => doc.external_id === id && (doc.current_version ?? 0) >= version)\n ) {\n throw new Error(\n 'The version was saved, but its current status could not be confirmed. Please try again.'\n )\n }\n }\n },\n notify,\n mentions: members?.map(({ user: member }) => ({\n id: member.external_id,\n name: [member.given_name, member.family_name].filter(Boolean).join(' '),\n kind: member.external_id.startsWith('agt-') ? 'agent' : 'human',\n })),\n savedSignature: {\n load: () => fetchSavedSignature(arbi.getClient().fetch),\n save: (signature) => storeSavedSignature(arbi.getClient().fetch, signature),\n defaultName: user?.name ?? '',\n },\n signatureSigners: members\n ?.filter(({ user: member }) => member.external_id.startsWith('usr-'))\n .map(({ user: member }) => ({\n email: member.email,\n name: [member.given_name, member.family_name].filter(Boolean).join(' '),\n })),\n requestAgent: async (id, request) => {\n const response = await arbi.editor.request(`/${encodeURIComponent(id)}/mentions`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n comment_id: request.commentId,\n instruction: request.instruction,\n request_id: request.requestId,\n recipient_ids: request.recipientIds,\n }),\n })\n const delivery = (await response.json()) as { response_ids: string[] }\n notify('Mention delivered.', 'success')\n return { responseIds: delivery.response_ids || [] }\n },\n getAgentStatus: async (responseId) => (await arbi.assistant.getResponse(responseId)).status,\n definitions: support.definitions,\n references,\n imageGeneration: support.imageGeneration,\n houseTemplate,\n }),\n [\n arbi,\n author,\n members,\n ensureWorkspaceOpen,\n fontBaseUrl,\n queries,\n notify,\n onDocumentsChanged,\n support,\n references,\n houseTemplate,\n user?.name,\n ]\n )\n return adapter\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { saveNativeVersion } from './save-native-version'\nimport { useMemo, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { refreshDocuments, useArbi, useWorkspaceUsers } from '@arbidocs/react'\nimport {\n fetchSavedSignature,\n storeSavedSignature,\n useOptionalDocumentTools,\n} from '@arbidocs/react/document-tools'\nimport type { NativeDocxAdapter, NativeDocxEditorProps } from '../../docx/NativeDocxEditor'\nimport { NativeDocxEditor } from '../../docx/lazy'\nimport { registerDocumentTurnFlush } from '@arbidocs/react'\nimport { useRegisterOpenDocument } from '@arbidocs/react/shell'\nimport { useBlocksEnvironment } from '../../provider/environment'\nimport { useConnectedWorkspace } from '../ConnectionProvider'\nimport { useDocxCommentTarget, useNativeDocxSupport } from './support'\nimport { useConnectedDocxReferences } from './useConnectedDocxReferences'\nimport { useConnectedHouseTemplate } from './useConnectedHouseTemplate'\n\nexport type ConnectedNativeDocxEditorProps = Omit<NativeDocxEditorProps, 'adapter' | 'compact'> & {\n fontBaseUrl?: string\n}\n\n/**\n * The native Word editor on the signed-in ARBI session, under any host (`defaultHost`'s sign-in\n * layer, or the host's own `arbi` and session state). Until a live session has opened, a failure to\n * open one falls back to the saved copy, read-only, so the document is still readable; later token\n * refreshes are not guarded (switching modes would drop unsaved edits).\n */\nexport function ConnectedNativeDocxEditor({\n fontBaseUrl = '/native-docx/fonts',\n ...props\n}: ConnectedNativeDocxEditorProps) {\n const adapter = useConnectedNativeDocxAdapter(fontBaseUrl)\n const { workspaceId } = useConnectedWorkspace()\n const commentTarget = useDocxCommentTarget()\n const [openFailed, setOpenFailed] = useState(false)\n const hasOpened = useRef(false)\n const guardedAdapter = useMemo<NativeDocxAdapter>(\n () => ({\n ...adapter,\n open: async (documentId) => {\n try {\n const session = await adapter.open(documentId)\n hasOpened.current = true\n return session\n } catch (error) {\n if (!hasOpened.current) setOpenFailed(true)\n throw error\n }\n },\n }),\n [adapter]\n )\n // Drafting mode: the saved Word document being edited is what the chat is about. A history\n // snapshot, a streamed preview, an unsaved draft and an embedded body field are not.\n const live = !props.snapshot && !props.stream && !props.draftContent\n useRegisterOpenDocument({ documentId: live ? props.documentId : null, workspaceId })\n const initialCommentId =\n props.initialCommentId ??\n (commentTarget?.documentId === props.documentId ? commentTarget.commentId : undefined)\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n {openFailed && (\n <p role=\"status\" className=\"text-muted-foreground border-b px-3 py-2 text-xs\">\n Co-editing isn&apos;t available for this document right now, so this is a read-only copy\n of the last saved version.\n </p>\n )}\n <div className=\"min-h-0 flex-1\">\n <NativeDocxEditor\n {...props}\n snapshot={props.snapshot || openFailed}\n initialCommentId={initialCommentId}\n adapter={guardedAdapter}\n />\n </div>\n </div>\n )\n}\n\n/** The access token changed (a refresh): the live room is told the new one. */\nfunction tokenChanges(arbi: ReturnType<typeof useArbi>) {\n return (listener: () => void) => {\n if (!arbi.isLoggedIn) return () => {}\n const session = arbi.getClient().session\n let token = session.getState().accessToken\n return session.subscribe((state) => {\n if (state.accessToken === token) return\n token = state.accessToken\n listener()\n })\n }\n}\n\n/**\n * The native Word adapter on the signed-in session: the editor transport, the people to mention,\n * cross-references, the house template, and the host's extras (its host config's `docx`).\n */\nexport function useConnectedNativeDocxAdapter(fontBaseUrl = '/native-docx/fonts') {\n const arbi = useArbi()\n const { workspaceId, live, user, ensureWorkspaceOpen } = useConnectedWorkspace()\n const queries = useQueryClient()\n const { notify } = useBlocksEnvironment()\n const tools = useOptionalDocumentTools()\n const support = useNativeDocxSupport()\n const references = useConnectedDocxReferences()\n const houseTemplate = useConnectedHouseTemplate()\n const { data: members } = useWorkspaceUsers(workspaceId ?? undefined, {\n enabled: live && !!workspaceId,\n beforeFetch: ensureWorkspaceOpen,\n })\n const onDocumentsChanged = tools?.onDocumentsChanged\n // Tracked changes and comments are authored by the user's first name.\n const author = (live && arbi.user.profile()?.givenName) || user?.name || 'You'\n const adapter = useMemo<NativeDocxAdapter>(\n () => ({\n request: async (path, options) => {\n await ensureWorkspaceOpen()\n return arbi.editor.request(path, options)\n },\n open: async (id) => {\n await ensureWorkspaceOpen()\n return (\n await arbi.editor.request(`/${encodeURIComponent(id)}/session`, { method: 'POST' })\n ).json()\n },\n save: (id, state, autosave) => saveNativeVersion(arbi.editor.request, id, state, autosave),\n loadFonts: () => import('../../docx/fonts').then((m) => m.loadDocxFonts(fontBaseUrl)),\n baseUrl: arbi.getConnectionInfo()?.baseUrl || window.location.origin,\n author,\n subscribeTokenChanges: tokenChanges(arbi),\n onDirtyChange: support.onDirtyChange,\n registerTurnFlush: registerDocumentTurnFlush,\n // The document's row moves to the saved version in place: through the host when it keeps\n // the lists (its document tools refresh them), else here.\n onVersionSaved: async (id, version) => {\n if (onDocumentsChanged) await onDocumentsChanged([id], 'version')\n if (version || !onDocumentsChanged) {\n const fresh = await refreshDocuments(queries, arbi, [id])\n if (\n version &&\n !fresh.some((doc) => doc.external_id === id && (doc.current_version ?? 0) >= version)\n ) {\n throw new Error(\n 'The version was saved, but its current status could not be confirmed. Please try again.'\n )\n }\n }\n },\n notify,\n mentions: members?.map(({ user: member }) => ({\n id: member.external_id,\n name: [member.given_name, member.family_name].filter(Boolean).join(' '),\n kind: member.external_id.startsWith('agt-') ? 'agent' : 'human',\n })),\n savedSignature: {\n load: () => fetchSavedSignature(arbi.getClient().fetch),\n save: (signature) => storeSavedSignature(arbi.getClient().fetch, signature),\n defaultName: user?.name ?? '',\n },\n signatureSigners: members\n ?.filter(({ user: member }) => member.external_id.startsWith('usr-'))\n .map(({ user: member }) => ({\n email: member.email,\n name: [member.given_name, member.family_name].filter(Boolean).join(' '),\n })),\n requestAgent: async (id, request) => {\n const response = await arbi.editor.request(`/${encodeURIComponent(id)}/mentions`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n comment_id: request.commentId,\n instruction: request.instruction,\n request_id: request.requestId,\n recipient_ids: request.recipientIds,\n }),\n })\n const delivery = (await response.json()) as { response_ids: string[] }\n notify('Mention delivered.', 'success')\n return { responseIds: delivery.response_ids || [] }\n },\n getAgentStatus: async (responseId) => (await arbi.assistant.getResponse(responseId)).status,\n definitions: support.definitions,\n references,\n imageGeneration: support.imageGeneration,\n houseTemplate,\n }),\n [\n arbi,\n author,\n members,\n ensureWorkspaceOpen,\n fontBaseUrl,\n queries,\n notify,\n onDocumentsChanged,\n support,\n references,\n houseTemplate,\n user?.name,\n ]\n )\n return adapter\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,3 @@
1
+ import type { NativeDocxAdapter } from '../../docx/NativeDocxEditor';
2
+ /** Older servers reject the new body field; retain their ordinary version save during rollout. */
3
+ export declare function saveNativeVersion(request: NativeDocxAdapter['request'], documentId: string, state?: string, autosave?: boolean): ReturnType<NativeDocxAdapter['save']>;
@@ -0,0 +1,19 @@
1
+ "use client";
2
+ async function saveNativeVersion(request, documentId, state, autosave = false) {
3
+ const send = async (rolling) => (await request(`/${encodeURIComponent(documentId)}/save`, {
4
+ method: "POST",
5
+ headers: { "Content-Type": "application/json" },
6
+ body: JSON.stringify({ state, ...rolling ? { autosave: true } : {} })
7
+ })).json();
8
+ try {
9
+ return await send(autosave);
10
+ } catch (error) {
11
+ if (!autosave || !error || typeof error !== "object" || !("status" in error) || error.status !== 422)
12
+ throw error;
13
+ return send(false);
14
+ }
15
+ }
16
+ export {
17
+ saveNativeVersion
18
+ };
19
+ //# sourceMappingURL=save-native-version.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/connection/docx/save-native-version.ts"],
4
+ "sourcesContent": ["import type { NativeDocxAdapter } from '../../docx/NativeDocxEditor'\n\n/** Older servers reject the new body field; retain their ordinary version save during rollout. */\nexport async function saveNativeVersion(\n request: NativeDocxAdapter['request'],\n documentId: string,\n state?: string,\n autosave = false\n): ReturnType<NativeDocxAdapter['save']> {\n const send = async (rolling: boolean) =>\n (\n await request(`/${encodeURIComponent(documentId)}/save`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ state, ...(rolling ? { autosave: true } : {}) }),\n })\n ).json()\n try {\n return await send(autosave)\n } catch (error) {\n if (\n !autosave ||\n !error ||\n typeof error !== 'object' ||\n !('status' in error) ||\n error.status !== 422\n )\n throw error\n return send(false)\n }\n}\n"],
5
+ "mappings": ";AAGA,eAAsB,kBACpB,SACA,YACA,OACA,WAAW,OAC4B;AACvC,QAAM,OAAO,OAAO,aAEhB,MAAM,QAAQ,IAAI,mBAAmB,UAAU,CAAC,SAAS;AAAA,IACvD,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,GAAI,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC,EAAG,CAAC;AAAA,EACxE,CAAC,GACD,KAAK;AACT,MAAI;AACF,WAAO,MAAM,KAAK,QAAQ;AAAA,EAC5B,SAAS,OAAO;AACd,QACE,CAAC,YACD,CAAC,SACD,OAAO,UAAU,YACjB,EAAE,YAAY,UACd,MAAM,WAAW;AAEjB,YAAM;AACR,WAAO,KAAK,KAAK;AAAA,EACnB;AACF;",
6
+ "names": []
7
+ }
@@ -41,17 +41,20 @@ function useRedactionMasks(documentId, version, hasMarkup) {
41
41
  }
42
42
  const NO_FOOTNOTES = {};
43
43
  const NO_PASSAGES = /* @__PURE__ */ new Map();
44
- function usePassageAreas(documentId, current) {
45
- const { data: parsed } = useParsedDocument(documentId, "final", { enabled: current });
44
+ function usePassageAreas(documentId, version) {
45
+ const { data: parsed } = useParsedDocument(
46
+ version ? `${documentId}@v${version}` : documentId,
47
+ "final"
48
+ );
46
49
  const { processedChunks } = useProcessedChunks(parsed?.chunks, NO_FOOTNOTES);
47
50
  return useMemo(() => {
48
- if (!current || !processedChunks.length) return NO_PASSAGES;
51
+ if (!processedChunks.length) return NO_PASSAGES;
49
52
  const areas = /* @__PURE__ */ new Map();
50
53
  for (const [page, boxes] of placedPassagesByPage(processedChunks)) {
51
54
  areas.set(page, boxes.map(passageClipRect));
52
55
  }
53
56
  return areas;
54
- }, [current, processedChunks]);
57
+ }, [processedChunks]);
55
58
  }
56
59
  function Notice({ children }) {
57
60
  return /* @__PURE__ */ jsx("div", { className: "border-b border-border bg-muted px-3 py-1.5 text-xs text-muted-foreground", children });
@@ -65,7 +68,7 @@ function ClippingPdfViewer({
65
68
  ...viewerProps
66
69
  }) {
67
70
  const masks = useRedactionMasks(documentId, version, hasMarkup);
68
- const passages = usePassageAreas(documentId, version === void 0);
71
+ const passages = usePassageAreas(documentId, version);
69
72
  const file = viewerProps.file;
70
73
  const capture = useCallback(
71
74
  ({ page, rect, aspect }) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/connection/presentation/ClippingPdfViewer.tsx"],
4
- "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\u2026'\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"],
5
- "mappings": "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",
4
+ "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, version?: number): ReadonlyMap<number, Rect[]> {\n const { data: parsed } = useParsedDocument(\n version ? `${documentId}@v${version}` : documentId,\n 'final'\n )\n const { processedChunks } = useProcessedChunks(parsed?.chunks, NO_FOOTNOTES)\n return useMemo(() => {\n if (!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 }, [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)\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\u2026'\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"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -140,21 +140,15 @@ function ConnectedSlidesTab({
140
140
  )
141
141
  ).finally(() => setLoading(false));
142
142
  }, [currentVersion, heldVersion, isDraft, version, loading, open, documentId, target, notify]);
143
- const draftHasContent = usePresentation(
144
- (state) => isDraft && state.boundTo === target && state.savedId === null && hasUnsavedChanges(state)
145
- );
146
- const autoSaved = useRef(null);
143
+ const editState = usePresentation((state) => state);
147
144
  useEffect(() => {
148
- if (!draftHasContent || autoSaved.current === target) return;
145
+ if (version || editState.boundTo !== target || !hasUnsavedChanges(editState)) return;
149
146
  const timer = setTimeout(() => {
150
- autoSaved.current = target;
151
147
  const state = usePresentation.getState();
152
148
  const firstTitle = state.slides.find((slide) => slide.caption.trim())?.caption.trim();
153
- const named = state.title && !DEFAULT_NAMES.test(state.title) ? state.title : null;
154
- const title = named ?? firstTitle ?? (state.title || "Untitled presentation");
149
+ const title = state.title && !DEFAULT_NAMES.test(state.title) ? state.title : firstTitle ?? (state.title || "Untitled presentation");
155
150
  if (title !== state.title) state.setTitle(title);
156
- void save(title).catch((error) => {
157
- autoSaved.current = null;
151
+ void save(title, { autosave: true }).catch((error) => {
158
152
  notify(
159
153
  error instanceof Error ? error.message : "The presentation could not be saved",
160
154
  "error"
@@ -162,12 +156,13 @@ function ConnectedSlidesTab({
162
156
  });
163
157
  }, AUTO_SAVE_AFTER_MS);
164
158
  return () => clearTimeout(timer);
165
- }, [draftHasContent, target, save, draftTitle, notify]);
159
+ }, [editState, target, version, save, notify]);
166
160
  useEffect(() => {
167
- if (isDraft || version || savedId !== documentId || boundTo !== target) return;
161
+ if (version || boundTo !== target || !isDraft && savedId !== documentId) return;
168
162
  return registerDocumentTurnFlush(documentId, async () => {
169
163
  const state = usePresentation.getState();
170
- if (state.savedId === documentId && hasUnsavedChanges(state)) await save(state.title);
164
+ if ((isDraft || state.savedId === documentId) && hasUnsavedChanges(state))
165
+ await save(state.title);
171
166
  });
172
167
  }, [isDraft, version, savedId, boundTo, documentId, target, save]);
173
168
  const present = useCallback(() => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/connection/presentation/ConnectedSlidesTab.tsx"],
4
- "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-\u2026`). */\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(' \u00B7 '),\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\u2026 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\u2026\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"],
5
- "mappings": "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",
4
+ "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-\u2026`). */\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(' \u00B7 '),\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\u2026 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 editState = usePresentation((state) => state)\n useEffect(() => {\n if (version || editState.boundTo !== target || !hasUnsavedChanges(editState)) return\n const timer = setTimeout(() => {\n const state = usePresentation.getState()\n const firstTitle = state.slides.find((slide) => slide.caption.trim())?.caption.trim()\n const title =\n state.title && !DEFAULT_NAMES.test(state.title)\n ? state.title\n : (firstTitle ?? (state.title || 'Untitled presentation'))\n if (title !== state.title) state.setTitle(title)\n void save(title, { autosave: true }).catch((error: unknown) => {\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 }, [editState, target, version, save, 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 (version || boundTo !== target || (!isDraft && savedId !== documentId)) return\n return registerDocumentTurnFlush(documentId, async () => {\n const state = usePresentation.getState()\n if ((isDraft || state.savedId === documentId) && hasUnsavedChanges(state))\n 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\u2026\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"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -19,6 +19,8 @@ import { useLabelColumns } from "./useLabelColumns.js";
19
19
  import { usePresentationFiles } from "./usePresentationFiles.js";
20
20
  function usePresentationController(active) {
21
21
  const session = useArbiSessionStore();
22
+ const autosave = usePresentation((s) => s.autosave);
23
+ const version = usePresentation((s) => s.version);
22
24
  const notify = usePresentation((s) => s.notify);
23
25
  const { save, canSave } = usePresentationFiles();
24
26
  const labelColumns = useLabelColumns(active);
@@ -79,21 +81,28 @@ function usePresentationController(active) {
79
81
  )
80
82
  ] });
81
83
  const editor = {
82
- actions: canSave && /* @__PURE__ */ jsx(
83
- Button,
84
- {
85
- type: "button",
86
- size: "icon",
87
- variant: "ghost",
88
- className: "size-8 text-muted-foreground hover:text-foreground pointer-coarse:size-11",
89
- title: "Save (in Files \u203A Presentations)",
90
- "aria-label": "Save",
91
- disabled: saving,
92
- onClick: () => void quickSave(),
93
- "data-testid": "presentation-save",
94
- children: /* @__PURE__ */ jsx(Save, { className: "size-4" })
95
- }
96
- ),
84
+ actions: canSave && /* @__PURE__ */ jsxs(Fragment, { children: [
85
+ autosave && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
86
+ "v",
87
+ version,
88
+ " \xB7 Autosaved"
89
+ ] }),
90
+ /* @__PURE__ */ jsx(
91
+ Button,
92
+ {
93
+ type: "button",
94
+ size: "icon",
95
+ variant: "ghost",
96
+ className: "size-8 text-muted-foreground hover:text-foreground pointer-coarse:size-11",
97
+ title: "Save (in Files \u203A Presentations)",
98
+ "aria-label": "Save",
99
+ disabled: saving,
100
+ onClick: () => void quickSave(),
101
+ "data-testid": "presentation-save",
102
+ children: /* @__PURE__ */ jsx(Save, { className: "size-4" })
103
+ }
104
+ )
105
+ ] }),
97
106
  menuItems: canSave && /* @__PURE__ */ jsxs(Fragment, { children: [
98
107
  /* @__PURE__ */ jsx(DropdownMenuItem, { onSelect: () => setDialog("open"), children: "Open\u2026" }),
99
108
  /* @__PURE__ */ jsx(DropdownMenuItem, { onSelect: () => setDialog("fromTemplate"), children: "New from template\u2026" }),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/connection/presentation/usePresentationController.tsx"],
4
- "sourcesContent": ["/**\n * What the presentation editor gets from the session, wherever it is mounted (the viewer's slides\n * tab, or the presentation's own window): Save, the \"\u2026\" menu (Open, New from template, Save as,\n * Save as template, Export PowerPoint, Export PDF, label column), Generate image, crop and the logo\n * picker, and the dialogs they open (rendered by the host, in its own window).\n */\n\nimport { useCallback, useState, type ReactNode } from 'react'\nimport { Save } from 'lucide-react'\nimport { useArbiSessionStore } from '@arbidocs/react'\nimport { Button, DropdownMenuItem } from '@arbidocs/react/ui'\nimport type { SlidesEditorProps } from '../../presentation/SlidesEditor'\nimport { usePresentation } from '../../presentation/store'\nimport {\n ChooseLogoDialog,\n GenerateImageDialog,\n OpenPresentationDialog,\n SavePresentationDialog,\n type SaveMode,\n} from './PresentationDialogs'\nimport { CropClipDialog } from './CropClipDialog'\nimport { LabelColumnMenu } from './LabelColumnMenu'\nimport { GenerateTile } from './GenerateTile'\nimport { editPresentation } from './requests'\nimport { useLabelColumns } from './useLabelColumns'\nimport { usePresentationFiles } from './usePresentationFiles'\n\ntype Dialog = SaveMode | 'open' | 'fromTemplate' | 'generate' | 'logo'\n\nexport interface PresentationController {\n /** The editor's session parts (the host adds its own actions and Present). */\n editor: Omit<SlidesEditorProps, 'onPresent'>\n /** The dialogs, for the host to render in its window. */\n dialogs: ReactNode\n}\n\nexport function usePresentationController(active: boolean): PresentationController {\n const session = useArbiSessionStore()\n const notify = usePresentation((s) => s.notify)\n const { save, canSave } = usePresentationFiles()\n const labelColumns = useLabelColumns(active)\n const [dialog, setDialog] = useState<Dialog | null>(null)\n const [reference, setReference] = useState<string | null>(null)\n const [cropping, setCropping] = useState<string | null>(null)\n const cropClip = usePresentation((s) => s.clips.find((c) => c.id === cropping) ?? null)\n const [saving, setSaving] = useState(false)\n const close = (open: boolean) => !open && setDialog(null)\n\n // Saved before: save again (a new version) at once; never saved: ask for a title.\n const quickSave = useCallback(async () => {\n const { savedId, title } = usePresentation.getState()\n if (!savedId) return setDialog('save')\n setSaving(true)\n try {\n await save(title)\n notify('Presentation saved', 'success')\n } catch (error) {\n notify(\n error instanceof Error ? error.message : 'The presentation could not be saved',\n 'error'\n )\n } finally {\n setSaving(false)\n }\n }, [save, notify])\n\n const generate = useCallback((referenceId: string | null) => {\n setReference(referenceId)\n setDialog('generate')\n }, [])\n\n const saveModes: SaveMode[] = ['save', 'saveAs', 'export', 'template']\n const dialogs = (\n <>\n <SavePresentationDialog\n open={saveModes.includes(dialog as SaveMode)}\n mode={saveModes.includes(dialog as SaveMode) ? (dialog as SaveMode) : 'save'}\n onOpenChange={close}\n />\n <OpenPresentationDialog\n open={dialog === 'open' || dialog === 'fromTemplate'}\n templates={dialog === 'fromTemplate'}\n // A saved one opens in the viewer's slides tab (its document); a template, as a new one.\n onOpened={(id) =>\n editPresentation(session, id ? { kind: 'open', documentId: id } : { kind: 'new' })\n }\n onOpenChange={close}\n />\n <ChooseLogoDialog open={dialog === 'logo'} onOpenChange={close} />\n <CropClipDialog clip={cropClip} onOpenChange={(open) => !open && setCropping(null)} />\n <GenerateImageDialog\n open={dialog === 'generate'}\n referenceId={reference}\n onOpenChange={close}\n />\n </>\n )\n\n const editor: PresentationController['editor'] = {\n actions: canSave && (\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=\"Save (in Files \u203A Presentations)\"\n aria-label=\"Save\"\n disabled={saving}\n onClick={() => void quickSave()}\n data-testid=\"presentation-save\"\n >\n <Save className=\"size-4\" />\n </Button>\n ),\n menuItems: canSave && (\n <>\n <DropdownMenuItem onSelect={() => setDialog('open')}>Open\u2026</DropdownMenuItem>\n <DropdownMenuItem onSelect={() => setDialog('fromTemplate')}>\n New from template\u2026\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={() => setDialog('saveAs')}>Save as\u2026</DropdownMenuItem>\n <DropdownMenuItem onSelect={() => setDialog('template')}>\n Save as template\u2026\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={() => setDialog('export')}>Export PDF\u2026</DropdownMenuItem>\n <LabelColumnMenu columns={labelColumns} />\n </>\n ),\n trayStart: canSave && <GenerateTile onClick={() => generate(null)} />,\n onChooseLogo: canSave ? () => setDialog('logo') : undefined,\n onEditImage: (clipId) => {\n const clip = usePresentation.getState().clips.find((c) => c.id === clipId)\n if (clip) generate(clip.documentId)\n },\n onCropImage: setCropping,\n }\n return { editor, dialogs }\n}\n"],
5
- "mappings": ";AAyEI,mBACE,KADF;AAlEJ,SAAS,aAAa,gBAAgC;AACtD,SAAS,YAAY;AACrB,SAAS,2BAA2B;AACpC,SAAS,QAAQ,wBAAwB;AAEzC,SAAS,uBAAuB;AAChC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,sBAAsB;AAC/B,SAAS,uBAAuB;AAChC,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AACjC,SAAS,uBAAuB;AAChC,SAAS,4BAA4B;AAW9B,SAAS,0BAA0B,QAAyC;AACjF,QAAM,UAAU,oBAAoB;AACpC,QAAM,SAAS,gBAAgB,CAAC,MAAM,EAAE,MAAM;AAC9C,QAAM,EAAE,MAAM,QAAQ,IAAI,qBAAqB;AAC/C,QAAM,eAAe,gBAAgB,MAAM;AAC3C,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAwB,IAAI;AACxD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAwB,IAAI;AAC9D,QAAM,CAAC,UAAU,WAAW,IAAI,SAAwB,IAAI;AAC5D,QAAM,WAAW,gBAAgB,CAAC,MAAM,EAAE,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ,KAAK,IAAI;AACtF,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,QAAM,QAAQ,CAAC,SAAkB,CAAC,QAAQ,UAAU,IAAI;AAGxD,QAAM,YAAY,YAAY,YAAY;AACxC,UAAM,EAAE,SAAS,MAAM,IAAI,gBAAgB,SAAS;AACpD,QAAI,CAAC,QAAS,QAAO,UAAU,MAAM;AACrC,cAAU,IAAI;AACd,QAAI;AACF,YAAM,KAAK,KAAK;AAChB,aAAO,sBAAsB,SAAS;AAAA,IACxC,SAAS,OAAO;AACd;AAAA,QACE,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACzC;AAAA,MACF;AAAA,IACF,UAAE;AACA,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,MAAM,MAAM,CAAC;AAEjB,QAAM,WAAW,YAAY,CAAC,gBAA+B;AAC3D,iBAAa,WAAW;AACxB,cAAU,UAAU;AAAA,EACtB,GAAG,CAAC,CAAC;AAEL,QAAM,YAAwB,CAAC,QAAQ,UAAU,UAAU,UAAU;AACrE,QAAM,UACJ,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,UAAU,SAAS,MAAkB;AAAA,QAC3C,MAAM,UAAU,SAAS,MAAkB,IAAK,SAAsB;AAAA,QACtE,cAAc;AAAA;AAAA,IAChB;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,WAAW,UAAU,WAAW;AAAA,QACtC,WAAW,WAAW;AAAA,QAEtB,UAAU,CAAC,OACT,iBAAiB,SAAS,KAAK,EAAE,MAAM,QAAQ,YAAY,GAAG,IAAI,EAAE,MAAM,MAAM,CAAC;AAAA,QAEnF,cAAc;AAAA;AAAA,IAChB;AAAA,IACA,oBAAC,oBAAiB,MAAM,WAAW,QAAQ,cAAc,OAAO;AAAA,IAChE,oBAAC,kBAAe,MAAM,UAAU,cAAc,CAAC,SAAS,CAAC,QAAQ,YAAY,IAAI,GAAG;AAAA,IACpF;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,WAAW;AAAA,QACjB,aAAa;AAAA,QACb,cAAc;AAAA;AAAA,IAChB;AAAA,KACF;AAGF,QAAM,SAA2C;AAAA,IAC/C,SAAS,WACP;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,WAAU;AAAA,QACV,OAAM;AAAA,QACN,cAAW;AAAA,QACX,UAAU;AAAA,QACV,SAAS,MAAM,KAAK,UAAU;AAAA,QAC9B,eAAY;AAAA,QAEZ,8BAAC,QAAK,WAAU,UAAS;AAAA;AAAA,IAC3B;AAAA,IAEF,WAAW,WACT,iCACE;AAAA,0BAAC,oBAAiB,UAAU,MAAM,UAAU,MAAM,GAAG,wBAAK;AAAA,MAC1D,oBAAC,oBAAiB,UAAU,MAAM,UAAU,cAAc,GAAG,qCAE7D;AAAA,MACA,oBAAC,oBAAiB,UAAU,MAAM,UAAU,QAAQ,GAAG,2BAAQ;AAAA,MAC/D,oBAAC,oBAAiB,UAAU,MAAM,UAAU,UAAU,GAAG,oCAEzD;AAAA,MACA,oBAAC,oBAAiB,UAAU,MAAM,UAAU,QAAQ,GAAG,8BAAW;AAAA,MAClE,oBAAC,mBAAgB,SAAS,cAAc;AAAA,OAC1C;AAAA,IAEF,WAAW,WAAW,oBAAC,gBAAa,SAAS,MAAM,SAAS,IAAI,GAAG;AAAA,IACnE,cAAc,UAAU,MAAM,UAAU,MAAM,IAAI;AAAA,IAClD,aAAa,CAAC,WAAW;AACvB,YAAM,OAAO,gBAAgB,SAAS,EAAE,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM;AACzE,UAAI,KAAM,UAAS,KAAK,UAAU;AAAA,IACpC;AAAA,IACA,aAAa;AAAA,EACf;AACA,SAAO,EAAE,QAAQ,QAAQ;AAC3B;",
4
+ "sourcesContent": ["/**\n * What the presentation editor gets from the session, wherever it is mounted (the viewer's slides\n * tab, or the presentation's own window): Save, the \"\u2026\" menu (Open, New from template, Save as,\n * Save as template, Export PowerPoint, Export PDF, label column), Generate image, crop and the logo\n * picker, and the dialogs they open (rendered by the host, in its own window).\n */\n\nimport { useCallback, useState, type ReactNode } from 'react'\nimport { Save } from 'lucide-react'\nimport { useArbiSessionStore } from '@arbidocs/react'\nimport { Button, DropdownMenuItem } from '@arbidocs/react/ui'\nimport type { SlidesEditorProps } from '../../presentation/SlidesEditor'\nimport { usePresentation } from '../../presentation/store'\nimport {\n ChooseLogoDialog,\n GenerateImageDialog,\n OpenPresentationDialog,\n SavePresentationDialog,\n type SaveMode,\n} from './PresentationDialogs'\nimport { CropClipDialog } from './CropClipDialog'\nimport { LabelColumnMenu } from './LabelColumnMenu'\nimport { GenerateTile } from './GenerateTile'\nimport { editPresentation } from './requests'\nimport { useLabelColumns } from './useLabelColumns'\nimport { usePresentationFiles } from './usePresentationFiles'\n\ntype Dialog = SaveMode | 'open' | 'fromTemplate' | 'generate' | 'logo'\n\nexport interface PresentationController {\n /** The editor's session parts (the host adds its own actions and Present). */\n editor: Omit<SlidesEditorProps, 'onPresent'>\n /** The dialogs, for the host to render in its window. */\n dialogs: ReactNode\n}\n\nexport function usePresentationController(active: boolean): PresentationController {\n const session = useArbiSessionStore()\n const autosave = usePresentation((s) => s.autosave)\n const version = usePresentation((s) => s.version)\n const notify = usePresentation((s) => s.notify)\n const { save, canSave } = usePresentationFiles()\n const labelColumns = useLabelColumns(active)\n const [dialog, setDialog] = useState<Dialog | null>(null)\n const [reference, setReference] = useState<string | null>(null)\n const [cropping, setCropping] = useState<string | null>(null)\n const cropClip = usePresentation((s) => s.clips.find((c) => c.id === cropping) ?? null)\n const [saving, setSaving] = useState(false)\n const close = (open: boolean) => !open && setDialog(null)\n\n // Saved before: save again (a new version) at once; never saved: ask for a title.\n const quickSave = useCallback(async () => {\n const { savedId, title } = usePresentation.getState()\n if (!savedId) return setDialog('save')\n setSaving(true)\n try {\n await save(title)\n notify('Presentation saved', 'success')\n } catch (error) {\n notify(\n error instanceof Error ? error.message : 'The presentation could not be saved',\n 'error'\n )\n } finally {\n setSaving(false)\n }\n }, [save, notify])\n\n const generate = useCallback((referenceId: string | null) => {\n setReference(referenceId)\n setDialog('generate')\n }, [])\n\n const saveModes: SaveMode[] = ['save', 'saveAs', 'export', 'template']\n const dialogs = (\n <>\n <SavePresentationDialog\n open={saveModes.includes(dialog as SaveMode)}\n mode={saveModes.includes(dialog as SaveMode) ? (dialog as SaveMode) : 'save'}\n onOpenChange={close}\n />\n <OpenPresentationDialog\n open={dialog === 'open' || dialog === 'fromTemplate'}\n templates={dialog === 'fromTemplate'}\n // A saved one opens in the viewer's slides tab (its document); a template, as a new one.\n onOpened={(id) =>\n editPresentation(session, id ? { kind: 'open', documentId: id } : { kind: 'new' })\n }\n onOpenChange={close}\n />\n <ChooseLogoDialog open={dialog === 'logo'} onOpenChange={close} />\n <CropClipDialog clip={cropClip} onOpenChange={(open) => !open && setCropping(null)} />\n <GenerateImageDialog\n open={dialog === 'generate'}\n referenceId={reference}\n onOpenChange={close}\n />\n </>\n )\n\n const editor: PresentationController['editor'] = {\n actions: canSave && (\n <>\n {autosave && <span className=\"text-xs text-muted-foreground\">v{version} \u00B7 Autosaved</span>}\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=\"Save (in Files \u203A Presentations)\"\n aria-label=\"Save\"\n disabled={saving}\n onClick={() => void quickSave()}\n data-testid=\"presentation-save\"\n >\n <Save className=\"size-4\" />\n </Button>\n </>\n ),\n menuItems: canSave && (\n <>\n <DropdownMenuItem onSelect={() => setDialog('open')}>Open\u2026</DropdownMenuItem>\n <DropdownMenuItem onSelect={() => setDialog('fromTemplate')}>\n New from template\u2026\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={() => setDialog('saveAs')}>Save as\u2026</DropdownMenuItem>\n <DropdownMenuItem onSelect={() => setDialog('template')}>\n Save as template\u2026\n </DropdownMenuItem>\n <DropdownMenuItem onSelect={() => setDialog('export')}>Export PDF\u2026</DropdownMenuItem>\n <LabelColumnMenu columns={labelColumns} />\n </>\n ),\n trayStart: canSave && <GenerateTile onClick={() => generate(null)} />,\n onChooseLogo: canSave ? () => setDialog('logo') : undefined,\n onEditImage: (clipId) => {\n const clip = usePresentation.getState().clips.find((c) => c.id === clipId)\n if (clip) generate(clip.documentId)\n },\n onCropImage: setCropping,\n }\n return { editor, dialogs }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }