@arbidocs/blocks 0.5.120 → 0.5.122
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.
- package/dist/ClippingMediaViewer-IEIBT74X.cjs +156 -0
- package/dist/ClippingMediaViewer-IEIBT74X.cjs.map +1 -0
- package/dist/ClippingPdfViewer-OGYLMVCE.cjs +79 -0
- package/dist/ClippingPdfViewer-OGYLMVCE.cjs.map +1 -0
- package/dist/{ConnectedCallDock-WER7R222.cjs → ConnectedCallDock-XJOYVVAH.cjs} +9 -7
- package/dist/ConnectedCallDock-XJOYVVAH.cjs.map +1 -0
- package/dist/{ConnectedCallRoom-T7OBQTYP.cjs → ConnectedCallRoom-CUDYAPQK.cjs} +9 -7
- package/dist/{ConnectedCallRoom-T7OBQTYP.cjs.map → ConnectedCallRoom-CUDYAPQK.cjs.map} +1 -1
- package/dist/{ConnectedNativeDocxComparison-SGE4R3M4.cjs → ConnectedNativeDocxComparison-QKHCYD6W.cjs} +7 -7
- package/dist/{ConnectedNativeDocxComparison-SGE4R3M4.cjs.map → ConnectedNativeDocxComparison-QKHCYD6W.cjs.map} +1 -1
- package/dist/ConnectedPresentation-AU25OW25.cjs +321 -0
- package/dist/ConnectedPresentation-AU25OW25.cjs.map +1 -0
- package/dist/ConnectedSlidesTab-I75LKKN7.cjs +236 -0
- package/dist/ConnectedSlidesTab-I75LKKN7.cjs.map +1 -0
- package/dist/{ConnectedStreamedWordArtifact-6MCLJRIT.cjs → ConnectedStreamedWordArtifact-AG62232A.cjs} +7 -7
- package/dist/{ConnectedStreamedWordArtifact-6MCLJRIT.cjs.map → ConnectedStreamedWordArtifact-AG62232A.cjs.map} +1 -1
- package/dist/{DocumentReviewCard-BPIAJJED.cjs → DocumentReviewCard-M53XFLI2.cjs} +15 -14
- package/dist/DocumentReviewCard-M53XFLI2.cjs.map +1 -0
- package/dist/{FilesViewerDialog-67WK3TZC.cjs → FilesViewerDialog-4352KIOG.cjs} +30 -22
- package/dist/FilesViewerDialog-4352KIOG.cjs.map +1 -0
- package/dist/{GridViewImpl-ZMJJWTOM.cjs → GridViewImpl-LTYET77Q.cjs} +3 -3
- package/dist/GridViewImpl-LTYET77Q.cjs.map +1 -0
- package/dist/{WorkspaceTableView-J3C6VH4T.cjs → WorkspaceTableView-3XYAV4BV.cjs} +9 -8
- package/dist/WorkspaceTableView-3XYAV4BV.cjs.map +1 -0
- package/dist/agents/index.cjs +186 -186
- package/dist/{ai-IM4A23YT.cjs → ai-IEIR7SC2.cjs} +17 -15
- package/dist/ai-IEIR7SC2.cjs.map +1 -0
- package/dist/calls/connected/CallControls.d.ts +2 -0
- package/dist/calls/connected/CallControls.js +71 -9
- package/dist/calls/connected/CallControls.js.map +2 -2
- package/dist/calls/connected/ShareDocumentDialog.d.ts +14 -0
- package/dist/calls/connected/ShareDocumentDialog.js +77 -0
- package/dist/calls/connected/ShareDocumentDialog.js.map +7 -0
- package/dist/calls/index.cjs +2 -2
- package/dist/chat/index.cjs +8 -7
- package/dist/{chunk-557MJ4IH.cjs → chunk-2F57IO7S.cjs} +9 -146
- package/dist/chunk-2F57IO7S.cjs.map +1 -0
- package/dist/chunk-2RLA6QO2.cjs +174 -0
- package/dist/chunk-2RLA6QO2.cjs.map +1 -0
- package/dist/chunk-4TTX6ZNS.cjs +1374 -0
- package/dist/chunk-4TTX6ZNS.cjs.map +1 -0
- package/dist/chunk-5GCCUXMT.cjs +63 -0
- package/dist/chunk-5GCCUXMT.cjs.map +1 -0
- package/dist/{chunk-BUVIBHFU.cjs → chunk-5QERK4PK.cjs} +38 -38
- package/dist/chunk-5QERK4PK.cjs.map +1 -0
- package/dist/chunk-6OK7ESTL.cjs +134 -0
- package/dist/chunk-6OK7ESTL.cjs.map +1 -0
- package/dist/{chunk-ECLUFKYR.cjs → chunk-7557LDAN.cjs} +4 -4
- package/dist/{chunk-ECLUFKYR.cjs.map → chunk-7557LDAN.cjs.map} +1 -1
- package/dist/{chunk-KTQOGT2L.cjs → chunk-7L4X6QN2.cjs} +7 -7
- package/dist/{chunk-KTQOGT2L.cjs.map → chunk-7L4X6QN2.cjs.map} +1 -1
- package/dist/{chunk-BP3IJ34D.cjs → chunk-7NLO7ASW.cjs} +4 -4
- package/dist/{chunk-BP3IJ34D.cjs.map → chunk-7NLO7ASW.cjs.map} +1 -1
- package/dist/{chunk-3GWWOYGS.cjs → chunk-7PN3YENO.cjs} +4 -4
- package/dist/{chunk-3GWWOYGS.cjs.map → chunk-7PN3YENO.cjs.map} +1 -1
- package/dist/{chunk-DFBDQKGE.cjs → chunk-ADDQKZ5I.cjs} +4 -4
- package/dist/{chunk-DFBDQKGE.cjs.map → chunk-ADDQKZ5I.cjs.map} +1 -1
- package/dist/chunk-AFQTQC7A.cjs +35 -0
- package/dist/chunk-AFQTQC7A.cjs.map +1 -0
- package/dist/{chunk-NLSWDP42.cjs → chunk-ALK3PUUB.cjs} +21 -11
- package/dist/chunk-ALK3PUUB.cjs.map +1 -0
- package/dist/{chunk-775S43W7.cjs → chunk-CQQQIZU4.cjs} +8 -8
- package/dist/{chunk-775S43W7.cjs.map → chunk-CQQQIZU4.cjs.map} +1 -1
- package/dist/{chunk-TDO5UWXF.cjs → chunk-E3L5DB5F.cjs} +5 -5
- package/dist/{chunk-TDO5UWXF.cjs.map → chunk-E3L5DB5F.cjs.map} +1 -1
- package/dist/chunk-GFUOCGY6.cjs +32 -0
- package/dist/chunk-GFUOCGY6.cjs.map +1 -0
- package/dist/chunk-HYUKPHCT.cjs +66 -0
- package/dist/chunk-HYUKPHCT.cjs.map +1 -0
- package/dist/{chunk-QQWHG3KF.cjs → chunk-JJZOL5MG.cjs} +4 -4
- package/dist/{chunk-QQWHG3KF.cjs.map → chunk-JJZOL5MG.cjs.map} +1 -1
- package/dist/chunk-K4763GD5.cjs +301 -0
- package/dist/chunk-K4763GD5.cjs.map +1 -0
- package/dist/{chunk-B3LZITVF.cjs → chunk-KA3F6TIH.cjs} +2 -442
- package/dist/chunk-KA3F6TIH.cjs.map +1 -0
- package/dist/{chunk-PGJYD67F.cjs → chunk-KNGZZC4O.cjs} +7 -6
- package/dist/chunk-KNGZZC4O.cjs.map +1 -0
- package/dist/{chunk-YYPZOEED.cjs → chunk-LHWOKOMP.cjs} +23 -2
- package/dist/chunk-LHWOKOMP.cjs.map +1 -0
- package/dist/{chunk-HA66DHW6.cjs → chunk-MLA2BJ2Y.cjs} +28 -28
- package/dist/chunk-MLA2BJ2Y.cjs.map +1 -0
- package/dist/{chunk-CPR6EHG7.cjs → chunk-NG2EE7HR.cjs} +134 -10
- package/dist/chunk-NG2EE7HR.cjs.map +1 -0
- package/dist/{chunk-CKCG4NDB.cjs → chunk-OHHKBURG.cjs} +29 -29
- package/dist/chunk-OHHKBURG.cjs.map +1 -0
- package/dist/{chunk-4XJEHKVZ.cjs → chunk-PIYYBPHH.cjs} +4 -4
- package/dist/{chunk-4XJEHKVZ.cjs.map → chunk-PIYYBPHH.cjs.map} +1 -1
- package/dist/{chunk-YYSBWMDJ.cjs → chunk-QG2U2JGX.cjs} +4 -4
- package/dist/{chunk-YYSBWMDJ.cjs.map → chunk-QG2U2JGX.cjs.map} +1 -1
- package/dist/{chunk-5IZFSAEZ.cjs → chunk-SDRYSLOX.cjs} +4 -4
- package/dist/{chunk-5IZFSAEZ.cjs.map → chunk-SDRYSLOX.cjs.map} +1 -1
- package/dist/{chunk-NJCK6V6U.cjs → chunk-T73BHNQX.cjs} +2 -58
- package/dist/chunk-T73BHNQX.cjs.map +1 -0
- package/dist/{chunk-EOG6OT5I.cjs → chunk-TXP2N3DC.cjs} +4 -4
- package/dist/{chunk-EOG6OT5I.cjs.map → chunk-TXP2N3DC.cjs.map} +1 -1
- package/dist/{chunk-6UTKM5AB.cjs → chunk-UFT22LAB.cjs} +75 -75
- package/dist/chunk-UFT22LAB.cjs.map +1 -0
- package/dist/chunk-USNTRL22.cjs +4790 -0
- package/dist/chunk-USNTRL22.cjs.map +1 -0
- package/dist/{chunk-X36UR4A7.cjs → chunk-UTJ223TI.cjs} +413 -90
- package/dist/chunk-UTJ223TI.cjs.map +1 -0
- package/dist/{chunk-VZCPMXWC.cjs → chunk-V6MW7B4B.cjs} +4 -4
- package/dist/{chunk-VZCPMXWC.cjs.map → chunk-V6MW7B4B.cjs.map} +1 -1
- package/dist/{chunk-T23UTIRI.cjs → chunk-VWBYE3XY.cjs} +5 -5
- package/dist/{chunk-T23UTIRI.cjs.map → chunk-VWBYE3XY.cjs.map} +1 -1
- package/dist/chunk-WFMWKPGY.cjs +102 -0
- package/dist/chunk-WFMWKPGY.cjs.map +1 -0
- package/dist/chunk-WOOOMTTA.cjs +19 -0
- package/dist/chunk-WOOOMTTA.cjs.map +1 -0
- package/dist/{chunk-W6INT5GS.cjs → chunk-WRLGLZIA.cjs} +8 -8
- package/dist/{chunk-W6INT5GS.cjs.map → chunk-WRLGLZIA.cjs.map} +1 -1
- package/dist/{chunk-CJYWHUZR.cjs → chunk-XCKHOXRP.cjs} +4 -4
- package/dist/{chunk-CJYWHUZR.cjs.map → chunk-XCKHOXRP.cjs.map} +1 -1
- package/dist/chunk-XF355M7O.cjs +154 -0
- package/dist/chunk-XF355M7O.cjs.map +1 -0
- package/dist/{chunk-Q6CLWOP2.cjs → chunk-XGM4VVNV.cjs} +34 -34
- package/dist/{chunk-Q6CLWOP2.cjs.map → chunk-XGM4VVNV.cjs.map} +1 -1
- package/dist/{chunk-6WMMZLEV.cjs → chunk-XJEXFEWG.cjs} +2 -27
- package/dist/chunk-XJEXFEWG.cjs.map +1 -0
- package/dist/chunk-XK4TVJKD.cjs +73 -0
- package/dist/chunk-XK4TVJKD.cjs.map +1 -0
- package/dist/{chunk-E7TE2WVT.cjs → chunk-ZVEW3JBT.cjs} +3 -3
- package/dist/{chunk-E7TE2WVT.cjs.map → chunk-ZVEW3JBT.cjs.map} +1 -1
- package/dist/collaboration/index.cjs +70 -70
- package/dist/components/ZoomSlider.d.ts +25 -0
- package/dist/components/ZoomSlider.js +59 -0
- package/dist/components/ZoomSlider.js.map +7 -0
- package/dist/connect/docx.cjs +10 -10
- package/dist/connect/index.cjs +1066 -1147
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/viewer.cjs +19 -12
- package/dist/connection/ai.d.ts +1 -1
- package/dist/connection/conversation/ConnectedMessage.js +1 -1
- package/dist/connection/conversation/ConnectedMessage.js.map +2 -2
- package/dist/connection/files/ConnectedNewFileMenu.d.ts +2 -1
- package/dist/connection/files/ConnectedNewFileMenu.js +9 -3
- package/dist/connection/files/ConnectedNewFileMenu.js.map +2 -2
- package/dist/connection/files/explorer/ConnectedFilesExplorer.js +1 -1
- package/dist/connection/files/explorer/ConnectedFilesExplorer.js.map +2 -2
- package/dist/connection/files/explorer/FileTiles.js +1 -1
- package/dist/connection/files/explorer/FileTiles.js.map +2 -2
- package/dist/connection/files/fields/documentField.js +1 -1
- package/dist/connection/files/fields/documentField.js.map +2 -2
- package/dist/connection/files/grid/documentColumns.js +1 -1
- package/dist/connection/files/grid/documentColumns.js.map +2 -2
- package/dist/connection/files/page/DocumentsFilterBarActions.js +17 -28
- package/dist/connection/files/page/DocumentsFilterBarActions.js.map +2 -2
- package/dist/connection/mail/useMailboxDefaults.js +1 -1
- package/dist/connection/mail/useMailboxDefaults.js.map +2 -2
- package/dist/connection/panels/useSessionPanelMode.js +3 -2
- package/dist/connection/panels/useSessionPanelMode.js.map +2 -2
- package/dist/connection/presentation/ClippingMediaViewer.d.ts +12 -0
- package/dist/connection/presentation/ClippingMediaViewer.js +153 -0
- package/dist/connection/presentation/ClippingMediaViewer.js.map +7 -0
- package/dist/connection/presentation/ClippingPdfViewer.d.ts +22 -0
- package/dist/connection/presentation/ClippingPdfViewer.js +75 -0
- package/dist/connection/presentation/ClippingPdfViewer.js.map +7 -0
- package/dist/connection/presentation/ConnectedPresentation.d.ts +12 -0
- package/dist/connection/presentation/ConnectedPresentation.js +100 -0
- package/dist/connection/presentation/ConnectedPresentation.js.map +7 -0
- package/dist/connection/presentation/ConnectedSlidesTab.d.ts +20 -0
- package/dist/connection/presentation/ConnectedSlidesTab.js +222 -0
- package/dist/connection/presentation/ConnectedSlidesTab.js.map +7 -0
- package/dist/connection/presentation/CropClipDialog.d.ts +11 -0
- package/dist/connection/presentation/CropClipDialog.js +86 -0
- package/dist/connection/presentation/CropClipDialog.js.map +7 -0
- package/dist/connection/presentation/GenerateTile.d.ts +4 -0
- package/dist/connection/presentation/GenerateTile.js +24 -0
- package/dist/connection/presentation/GenerateTile.js.map +7 -0
- package/dist/connection/presentation/LabelColumnMenu.d.ts +8 -0
- package/dist/connection/presentation/LabelColumnMenu.js +38 -0
- package/dist/connection/presentation/LabelColumnMenu.js.map +7 -0
- package/dist/connection/presentation/PresentationDialogs.d.ts +39 -0
- package/dist/connection/presentation/PresentationDialogs.js +295 -0
- package/dist/connection/presentation/PresentationDialogs.js.map +7 -0
- package/dist/connection/presentation/PresentationFileCard.d.ts +10 -0
- package/dist/connection/presentation/PresentationFileCard.js +35 -0
- package/dist/connection/presentation/PresentationFileCard.js.map +7 -0
- package/dist/connection/presentation/PresentationPill.d.ts +8 -0
- package/dist/connection/presentation/PresentationPill.js +39 -0
- package/dist/connection/presentation/PresentationPill.js.map +7 -0
- package/dist/connection/presentation/PresentationSlot.d.ts +7 -0
- package/dist/connection/presentation/PresentationSlot.js +35 -0
- package/dist/connection/presentation/PresentationSlot.js.map +7 -0
- package/dist/connection/presentation/requests.d.ts +21 -0
- package/dist/connection/presentation/requests.js +27 -0
- package/dist/connection/presentation/requests.js.map +7 -0
- package/dist/connection/presentation/useImageGeneration.d.ts +11 -0
- package/dist/connection/presentation/useImageGeneration.js +66 -0
- package/dist/connection/presentation/useImageGeneration.js.map +7 -0
- package/dist/connection/presentation/useLabelColumns.d.ts +15 -0
- package/dist/connection/presentation/useLabelColumns.js +33 -0
- package/dist/connection/presentation/useLabelColumns.js.map +7 -0
- package/dist/connection/presentation/usePresentationController.d.ts +15 -0
- package/dist/connection/presentation/usePresentationController.js +118 -0
- package/dist/connection/presentation/usePresentationController.js.map +7 -0
- package/dist/connection/presentation/usePresentationFiles.d.ts +44 -0
- package/dist/connection/presentation/usePresentationFiles.js +307 -0
- package/dist/connection/presentation/usePresentationFiles.js.map +7 -0
- package/dist/connection/presentation/useViewerClipping.d.ts +14 -0
- package/dist/connection/presentation/useViewerClipping.js +18 -0
- package/dist/connection/presentation/useViewerClipping.js.map +7 -0
- package/dist/connection/shell/ConnectedAppShell.js +5 -1
- package/dist/connection/shell/ConnectedAppShell.js.map +2 -2
- package/dist/connection/viewer/CleanViewControl.d.ts +8 -0
- package/dist/connection/viewer/CleanViewControl.js +76 -0
- package/dist/connection/viewer/CleanViewControl.js.map +7 -0
- package/dist/connection/viewer/ConnectedDocumentViewer.js +28 -29
- package/dist/connection/viewer/ConnectedDocumentViewer.js.map +2 -2
- package/dist/connection/viewer/ViewerCropModal.d.ts +5 -0
- package/dist/connection/viewer/ViewerCropModal.js +22 -0
- package/dist/connection/viewer/ViewerCropModal.js.map +7 -0
- package/dist/connection/viewer/ViewerPdfPane.d.ts +3 -1
- package/dist/connection/viewer/ViewerPdfPane.js +41 -1
- package/dist/connection/viewer/ViewerPdfPane.js.map +2 -2
- package/dist/connection/viewer/documentPlace.d.ts +5 -4
- package/dist/connection/viewer/documentPlace.js +2 -2
- package/dist/connection/viewer/documentPlace.js.map +2 -2
- package/dist/connection/viewer/drafts.js +6 -4
- package/dist/connection/viewer/drafts.js.map +2 -2
- package/dist/connection/viewer/pdf/PdfClipOutlines.d.ts +11 -0
- package/dist/connection/viewer/pdf/PdfClipOutlines.js +42 -0
- package/dist/connection/viewer/pdf/PdfClipOutlines.js.map +7 -0
- package/dist/connection/viewer/pdf/clipOutlines.d.ts +17 -0
- package/dist/connection/viewer/pdf/clipOutlines.js +24 -0
- package/dist/connection/viewer/pdf/clipOutlines.js.map +7 -0
- package/dist/connection/viewer/pdf/passageHighlights.d.ts +2 -0
- package/dist/connection/viewer/pdf/passageHighlights.js +12 -1
- package/dist/connection/viewer/pdf/passageHighlights.js.map +2 -2
- package/dist/connection/viewer/pdf/usePdfClipOutlines.d.ts +7 -0
- package/dist/connection/viewer/pdf/usePdfClipOutlines.js +36 -0
- package/dist/connection/viewer/pdf/usePdfClipOutlines.js.map +7 -0
- package/dist/connection/viewer/pdf/usePdfPassageHighlights.js +11 -2
- package/dist/connection/viewer/pdf/usePdfPassageHighlights.js.map +2 -2
- package/dist/connection/viewer/shareDocument.d.ts +11 -0
- package/dist/connection/viewer/shareDocument.js +11 -0
- package/dist/connection/viewer/shareDocument.js.map +7 -0
- package/dist/connection/viewer/slidesTabChunk.d.ts +2 -0
- package/dist/connection/viewer/slidesTabChunk.js +9 -0
- package/dist/connection/viewer/slidesTabChunk.js.map +7 -0
- package/dist/connection/viewer/useCleanView.d.ts +14 -0
- package/dist/connection/viewer/useCleanView.js +29 -0
- package/dist/connection/viewer/useCleanView.js.map +7 -0
- package/dist/connection/viewer/useViewedDocument.d.ts +1 -0
- package/dist/connection/viewer/useViewedDocument.js +9 -1
- package/dist/connection/viewer/useViewedDocument.js.map +2 -2
- package/dist/connection/viewer/useViewerTabs.js +18 -4
- package/dist/connection/viewer/useViewerTabs.js.map +2 -2
- package/dist/connection/workflows/ConnectedWorkflowStart.d.ts +14 -2
- package/dist/connection/workflows/ConnectedWorkflowStart.js +5 -2
- package/dist/connection/workflows/ConnectedWorkflowStart.js.map +2 -2
- package/dist/databases/index.cjs +49 -49
- package/dist/document-tools/DocumentToolButtons.d.ts +2 -0
- package/dist/document-tools/DocumentToolButtons.js +22 -1
- package/dist/document-tools/DocumentToolButtons.js.map +2 -2
- package/dist/document-tools/index.cjs +16 -12
- package/dist/document-tools/index.d.ts +1 -1
- package/dist/document-tools/index.js +2 -0
- package/dist/document-tools/index.js.map +2 -2
- package/dist/embed/index.cjs +17 -15
- package/dist/files/GridViewImpl.js +1 -1
- package/dist/files/GridViewImpl.js.map +2 -2
- package/dist/files/ViewerChrome.d.ts +5 -1
- package/dist/files/ViewerChrome.js +16 -6
- package/dist/files/ViewerChrome.js.map +2 -2
- package/dist/files/index.cjs +224 -224
- package/dist/files/navigator/DocumentSearchList.js +1 -1
- package/dist/files/navigator/DocumentSearchList.js.map +2 -2
- package/dist/index.cjs +359 -356
- package/dist/index.cjs.map +1 -1
- package/dist/presentation/BoxActionBar.d.ts +11 -0
- package/dist/presentation/BoxActionBar.js +182 -0
- package/dist/presentation/BoxActionBar.js.map +7 -0
- package/dist/presentation/ClipActionBar.d.ts +26 -0
- package/dist/presentation/ClipActionBar.js +103 -0
- package/dist/presentation/ClipActionBar.js.map +7 -0
- package/dist/presentation/ClipCaptureOverlay.d.ts +14 -0
- package/dist/presentation/ClipCaptureOverlay.js +69 -0
- package/dist/presentation/ClipCaptureOverlay.js.map +7 -0
- package/dist/presentation/ClipImage.d.ts +21 -0
- package/dist/presentation/ClipImage.js +119 -0
- package/dist/presentation/ClipImage.js.map +7 -0
- package/dist/presentation/ClipTray.d.ts +11 -0
- package/dist/presentation/ClipTray.js +152 -0
- package/dist/presentation/ClipTray.js.map +7 -0
- package/dist/presentation/ClipViewControls.d.ts +11 -0
- package/dist/presentation/ClipViewControls.js +142 -0
- package/dist/presentation/ClipViewControls.js.map +7 -0
- package/dist/presentation/DesignMenu.d.ts +8 -0
- package/dist/presentation/DesignMenu.js +199 -0
- package/dist/presentation/DesignMenu.js.map +7 -0
- package/dist/presentation/LayoutMenu.d.ts +17 -0
- package/dist/presentation/LayoutMenu.js +122 -0
- package/dist/presentation/LayoutMenu.js.map +7 -0
- package/dist/presentation/MediaClip.d.ts +15 -0
- package/dist/presentation/MediaClip.js +67 -0
- package/dist/presentation/MediaClip.js.map +7 -0
- package/dist/presentation/Notice.d.ts +6 -0
- package/dist/presentation/Notice.js +43 -0
- package/dist/presentation/Notice.js.map +7 -0
- package/dist/presentation/PresentControls.d.ts +11 -0
- package/dist/presentation/PresentControls.js +117 -0
- package/dist/presentation/PresentControls.js.map +7 -0
- package/dist/presentation/PresentationStage.d.ts +32 -0
- package/dist/presentation/PresentationStage.js +232 -0
- package/dist/presentation/PresentationStage.js.map +7 -0
- package/dist/presentation/PresenterView.d.ts +16 -0
- package/dist/presentation/PresenterView.js +100 -0
- package/dist/presentation/PresenterView.js.map +7 -0
- package/dist/presentation/SlideDecor.d.ts +25 -0
- package/dist/presentation/SlideDecor.js +189 -0
- package/dist/presentation/SlideDecor.js.map +7 -0
- package/dist/presentation/SlidesEditor.d.ts +25 -0
- package/dist/presentation/SlidesEditor.js +375 -0
- package/dist/presentation/SlidesEditor.js.map +7 -0
- package/dist/presentation/StageBox.d.ts +30 -0
- package/dist/presentation/StageBox.js +281 -0
- package/dist/presentation/StageBox.js.map +7 -0
- package/dist/presentation/StageItem.d.ts +37 -0
- package/dist/presentation/StageItem.js +211 -0
- package/dist/presentation/StageItem.js.map +7 -0
- package/dist/presentation/box-style.d.ts +6 -0
- package/dist/presentation/box-style.js +19 -0
- package/dist/presentation/box-style.js.map +7 -0
- package/dist/presentation/boxes.d.ts +105 -0
- package/dist/presentation/boxes.js +103 -0
- package/dist/presentation/boxes.js.map +7 -0
- package/dist/presentation/draw-boxes.d.ts +8 -0
- package/dist/presentation/draw-boxes.js +130 -0
- package/dist/presentation/draw-boxes.js.map +7 -0
- package/dist/presentation/export-boxes.d.ts +10 -0
- package/dist/presentation/export-boxes.js +74 -0
- package/dist/presentation/export-boxes.js.map +7 -0
- package/dist/presentation/export-pptx.d.ts +16 -0
- package/dist/presentation/export-pptx.js +194 -0
- package/dist/presentation/export-pptx.js.map +7 -0
- package/dist/presentation/import-pptx.d.ts +35 -0
- package/dist/presentation/import-pptx.js +659 -0
- package/dist/presentation/import-pptx.js.map +7 -0
- package/dist/presentation/merge-outside-edits.d.ts +12 -0
- package/dist/presentation/merge-outside-edits.js +35 -0
- package/dist/presentation/merge-outside-edits.js.map +7 -0
- package/dist/presentation/model.d.ts +201 -0
- package/dist/presentation/model.js +271 -0
- package/dist/presentation/model.js.map +7 -0
- package/dist/presentation/panel-id.d.ts +44 -0
- package/dist/presentation/panel-id.js +29 -0
- package/dist/presentation/panel-id.js.map +7 -0
- package/dist/presentation/persistence.d.ts +121 -0
- package/dist/presentation/persistence.js +141 -0
- package/dist/presentation/persistence.js.map +7 -0
- package/dist/presentation/pptx-data.d.ts +22 -0
- package/dist/presentation/pptx-data.js +83 -0
- package/dist/presentation/pptx-data.js.map +7 -0
- package/dist/presentation/pptx-xml.d.ts +90 -0
- package/dist/presentation/pptx-xml.js +333 -0
- package/dist/presentation/pptx-xml.js.map +7 -0
- package/dist/presentation/render-slides.d.ts +58 -0
- package/dist/presentation/render-slides.js +309 -0
- package/dist/presentation/render-slides.js.map +7 -0
- package/dist/presentation/saved-boxes.d.ts +11 -0
- package/dist/presentation/saved-boxes.js +80 -0
- package/dist/presentation/saved-boxes.js.map +7 -0
- package/dist/presentation/slide-layout.d.ts +58 -0
- package/dist/presentation/slide-layout.js +133 -0
- package/dist/presentation/slide-layout.js.map +7 -0
- package/dist/presentation/stage-videos.d.ts +5 -0
- package/dist/presentation/stage-videos.js +19 -0
- package/dist/presentation/stage-videos.js.map +7 -0
- package/dist/presentation/store.d.ts +203 -0
- package/dist/presentation/store.js +429 -0
- package/dist/presentation/store.js.map +7 -0
- package/dist/presentation/theme.d.ts +102 -0
- package/dist/presentation/theme.js +276 -0
- package/dist/presentation/theme.js.map +7 -0
- package/dist/presentation/useClipDrag.d.ts +26 -0
- package/dist/presentation/useClipDrag.js +65 -0
- package/dist/presentation/useClipDrag.js.map +7 -0
- package/dist/presentation/useObjectUrl.d.ts +6 -0
- package/dist/presentation/useObjectUrl.js +15 -0
- package/dist/presentation/useObjectUrl.js.map +7 -0
- package/dist/presentation/usePresenterKeys.d.ts +13 -0
- package/dist/presentation/usePresenterKeys.js +62 -0
- package/dist/presentation/usePresenterKeys.js.map +7 -0
- package/dist/settings/index.cjs +149 -148
- package/dist/shell/PanelPortal.js +13 -3
- package/dist/shell/PanelPortal.js.map +2 -2
- package/dist/shell/index.cjs +49 -46
- package/dist/shell/panel-windows.d.ts +2 -0
- package/dist/shell/panel-windows.js +8 -2
- package/dist/shell/panel-windows.js.map +2 -2
- package/dist/studio/index.cjs +8 -6
- package/dist/studio/index.cjs.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/workflow-canvas/index.cjs +26 -25
- package/dist/workflow-canvas/index.cjs.map +1 -1
- package/node_modules/@arbidocs/coedit/package.json +1 -1
- package/package.json +6 -5
- package/dist/ConnectedCallDock-WER7R222.cjs.map +0 -1
- package/dist/DocumentReviewCard-BPIAJJED.cjs.map +0 -1
- package/dist/FilesViewerDialog-67WK3TZC.cjs.map +0 -1
- package/dist/GridViewImpl-ZMJJWTOM.cjs.map +0 -1
- package/dist/WorkspaceTableView-J3C6VH4T.cjs.map +0 -1
- package/dist/ai-IM4A23YT.cjs.map +0 -1
- package/dist/chunk-557MJ4IH.cjs.map +0 -1
- package/dist/chunk-6UTKM5AB.cjs.map +0 -1
- package/dist/chunk-6WMMZLEV.cjs.map +0 -1
- package/dist/chunk-B3LZITVF.cjs.map +0 -1
- package/dist/chunk-BUVIBHFU.cjs.map +0 -1
- package/dist/chunk-CKCG4NDB.cjs.map +0 -1
- package/dist/chunk-CPR6EHG7.cjs.map +0 -1
- package/dist/chunk-HA66DHW6.cjs.map +0 -1
- package/dist/chunk-NJCK6V6U.cjs.map +0 -1
- package/dist/chunk-NLSWDP42.cjs.map +0 -1
- package/dist/chunk-PGJYD67F.cjs.map +0 -1
- package/dist/chunk-X36UR4A7.cjs.map +0 -1
- package/dist/chunk-YYPZOEED.cjs.map +0 -1
|
@@ -10,8 +10,11 @@ import {
|
|
|
10
10
|
} from "@arbidocs/react/viewers";
|
|
11
11
|
import { friendlyFileType } from "@arbidocs/react/ui";
|
|
12
12
|
import { ViewerLoadError, ViewerMessage, ViewerPreviewUnavailable } from "../../files/index.js";
|
|
13
|
+
import { usePdfClipOutlines } from "./pdf/usePdfClipOutlines.js";
|
|
13
14
|
import { usePdfPassageHighlights } from "./pdf/usePdfPassageHighlights.js";
|
|
14
15
|
import { useViewerIsDetached } from "./support.js";
|
|
16
|
+
import { isLegacyPresentationFile } from "../../presentation/panel-id.js";
|
|
17
|
+
import { PresentationFileCard } from "../presentation/PresentationFileCard.js";
|
|
15
18
|
import { fileExtension } from "./useViewedDocument.js";
|
|
16
19
|
const AnnotatedPdfViewer = lazy(
|
|
17
20
|
() => import("../../document-tools/viewer/index.js").then((m) => ({ default: m.AnnotatedPdfViewer }))
|
|
@@ -22,6 +25,12 @@ const SignaturePlacementViewer = lazy(
|
|
|
22
25
|
const SignatureRequestEditor = lazy(
|
|
23
26
|
() => import("../../document-tools/viewer/index.js").then((m) => ({ default: m.SignatureRequestEditor }))
|
|
24
27
|
);
|
|
28
|
+
const ClippingPdfViewer = lazy(
|
|
29
|
+
() => import("../presentation/ClippingPdfViewer.js").then((m) => ({ default: m.ClippingPdfViewer }))
|
|
30
|
+
);
|
|
31
|
+
const ClippingMediaViewer = lazy(
|
|
32
|
+
() => import("../presentation/ClippingMediaViewer.js").then((m) => ({ default: m.ClippingMediaViewer }))
|
|
33
|
+
);
|
|
25
34
|
const GuidedSigningViewer = lazy(
|
|
26
35
|
() => import("../../document-tools/viewer/index.js").then((m) => ({ default: m.GuidedSigningViewer }))
|
|
27
36
|
);
|
|
@@ -36,13 +45,15 @@ function SessionPdfViewer(props) {
|
|
|
36
45
|
[session, viewingId]
|
|
37
46
|
);
|
|
38
47
|
const passageHighlights = usePdfPassageHighlights(viewingId);
|
|
48
|
+
const clipOutlines = usePdfClipOutlines(viewingId);
|
|
39
49
|
const { renderPageOverlay: modeOverlay } = props;
|
|
40
50
|
const renderPageOverlay = useCallback(
|
|
41
51
|
(page) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42
52
|
passageHighlights(page.pageNumber),
|
|
53
|
+
clipOutlines(page.pageNumber),
|
|
43
54
|
modeOverlay?.(page)
|
|
44
55
|
] }),
|
|
45
|
-
[passageHighlights, modeOverlay]
|
|
56
|
+
[passageHighlights, clipOutlines, modeOverlay]
|
|
46
57
|
);
|
|
47
58
|
return /* @__PURE__ */ jsx(
|
|
48
59
|
KitPdfViewer,
|
|
@@ -68,6 +79,7 @@ function ViewerPdfPane({
|
|
|
68
79
|
placingSignature,
|
|
69
80
|
onPlacingDone,
|
|
70
81
|
annotating,
|
|
82
|
+
clipping = false,
|
|
71
83
|
markup,
|
|
72
84
|
pdfSnapshotNumber,
|
|
73
85
|
isSigned,
|
|
@@ -75,6 +87,9 @@ function ViewerPdfPane({
|
|
|
75
87
|
}) {
|
|
76
88
|
const { documentData, isDownloadOnly, isVideo, isAudio, isImage, isDocx } = doc;
|
|
77
89
|
const showMarkup = markup.hasMarkup && !markup.showOriginal;
|
|
90
|
+
if (isLegacyPresentationFile(documentData?.file_name)) {
|
|
91
|
+
return /* @__PURE__ */ jsx(PresentationFileCard, { documentId, fileName: documentData?.file_name ?? "" });
|
|
92
|
+
}
|
|
78
93
|
if (isDownloadOnly) {
|
|
79
94
|
return /* @__PURE__ */ jsx(
|
|
80
95
|
ViewerPreviewUnavailable,
|
|
@@ -103,6 +118,17 @@ function ViewerPdfPane({
|
|
|
103
118
|
);
|
|
104
119
|
}
|
|
105
120
|
if (!pdfBlob) return /* @__PURE__ */ jsx(ViewerMessage, { children: loadingLabel });
|
|
121
|
+
if (clipping && (isImage || isVideo)) {
|
|
122
|
+
return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ViewerMessage, { children: "Loading\u2026" }), children: /* @__PURE__ */ jsx(
|
|
123
|
+
ClippingMediaViewer,
|
|
124
|
+
{
|
|
125
|
+
file: pdfBlob,
|
|
126
|
+
kind: isVideo ? "video" : "image",
|
|
127
|
+
documentId,
|
|
128
|
+
fileName: documentData?.file_name || (isVideo ? "Video" : "Image")
|
|
129
|
+
}
|
|
130
|
+
) });
|
|
131
|
+
}
|
|
106
132
|
if (isVideo) return /* @__PURE__ */ jsx(VideoViewer, { blob: pdfBlob, title: documentData?.file_name || "Video" });
|
|
107
133
|
if (isAudio) return /* @__PURE__ */ jsx(AudioViewer, { blob: pdfBlob, title: documentData?.file_name || "Audio" });
|
|
108
134
|
if (isImage) {
|
|
@@ -153,6 +179,20 @@ function ViewerPdfPane({
|
|
|
153
179
|
}
|
|
154
180
|
) });
|
|
155
181
|
}
|
|
182
|
+
if (clipping) {
|
|
183
|
+
return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ViewerMessage, { children: "Loading\u2026" }), children: /* @__PURE__ */ jsx(
|
|
184
|
+
ClippingPdfViewer,
|
|
185
|
+
{
|
|
186
|
+
...viewerProps,
|
|
187
|
+
file: pdfBlob,
|
|
188
|
+
documentId,
|
|
189
|
+
version: pdfSnapshotNumber,
|
|
190
|
+
fileName: documentData?.file_name || "Document",
|
|
191
|
+
hasMarkup: markup.hasMarkup,
|
|
192
|
+
viewer: SessionPdfViewer
|
|
193
|
+
}
|
|
194
|
+
) });
|
|
195
|
+
}
|
|
156
196
|
if (annotating || showMarkup) {
|
|
157
197
|
return /* @__PURE__ */ jsx(
|
|
158
198
|
Suspense,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/connection/viewer/ViewerPdfPane.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * The PDF (or media) body of the document viewer: loading and failure states, the image, audio and\n * video players, and the PDF with its tools \u2014 the markup layer, signature placement, a signature\n * request being prepared or signed.\n */\n\nimport { lazy, Suspense, useCallback } from 'react'\nimport { useArbiSessionState, useArbiSessionStore } from '@arbidocs/react'\nimport type {\n SignatureAppearance,\n SignatureRequest,\n useDocumentMarkup,\n} from '@arbidocs/react/document-tools'\nimport {\n AudioViewer,\n ImageViewer,\n PdfViewer as KitPdfViewer,\n VideoViewer,\n type PdfViewerProps,\n} from '@arbidocs/react/viewers'\nimport { friendlyFileType } from '@arbidocs/react/ui'\nimport { ViewerLoadError, ViewerMessage, ViewerPreviewUnavailable } from '../../files'\nimport { usePdfPassageHighlights } from './pdf/usePdfPassageHighlights'\nimport { useViewerIsDetached } from './support'\nimport { fileExtension, type ViewedDocument } from './useViewedDocument'\n\n// The markup viewer brings in pdf-lib, which nothing else needs: load it only when it is shown.\nconst AnnotatedPdfViewer = lazy(() =>\n import('../../document-tools/viewer').then((m) => ({ default: m.AnnotatedPdfViewer }))\n)\nconst SignaturePlacementViewer = lazy(() =>\n import('../../document-tools/viewer').then((m) => ({ default: m.SignaturePlacementViewer }))\n)\n// Signature requests: placing fields for signers, and a signer's guided signing.\nconst SignatureRequestEditor = lazy(() =>\n import('../../document-tools/viewer').then((m) => ({ default: m.SignatureRequestEditor }))\n)\nconst GuidedSigningViewer = lazy(() =>\n import('../../document-tools/viewer').then((m) => ({ default: m.GuidedSigningViewer }))\n)\n\n/** A signature request: the author placing fields, or a signer walked through theirs. */\nexport type SignatureFlow =\n | { kind: 'prepare'; draft?: SignatureRequest; self?: boolean }\n | { kind: 'sign'; request: SignatureRequest }\n | null\n\n/**\n * The kit PDF viewer, reporting the page read into the session state; in a detached window its\n * pages render without the opener's web fonts (canvas text in another document cannot use them).\n */\nfunction SessionPdfViewer(props: Omit<PdfViewerProps, 'onPageChange' | 'disableFontFace'>) {\n const session = useArbiSessionStore()\n const viewingId = useArbiSessionState((s) => s.viewingDocument?.id)\n const isDetached = useViewerIsDetached()\n const onPageChange = useCallback(\n (page: number) => {\n if (viewingId) session.setViewingDocument({ id: viewingId, page })\n },\n [session, viewingId]\n )\n // The passages a citation lights, over their pages, under whatever the mode draws (markup).\n const passageHighlights = usePdfPassageHighlights(viewingId)\n const { renderPageOverlay: modeOverlay } = props\n const renderPageOverlay = useCallback<NonNullable<PdfViewerProps['renderPageOverlay']>>(\n (page) => (\n <>\n {passageHighlights(page.pageNumber)}\n {modeOverlay?.(page)}\n </>\n ),\n [passageHighlights, modeOverlay]\n )\n return (\n <KitPdfViewer\n {...props}\n renderPageOverlay={renderPageOverlay}\n onPageChange={onPageChange}\n disableFontFace={!!isDetached}\n />\n )\n}\n\nexport interface ViewerPdfPaneProps {\n documentId: string\n doc: ViewedDocument\n pdfBlob: Blob | null\n isPdfLoading: boolean\n pdfError: unknown\n onDownload: () => void\n viewerProps: {\n currentPage: number | undefined\n onLoadSuccess: (pages: number) => void\n scrollTrigger: number\n zoom: number\n onZoomChange: (zoom: number) => void\n }\n signatureFlow: SignatureFlow\n onSignatureFlow: (flow: SignatureFlow) => void\n currentUserId: string | undefined\n placingSignature: boolean\n onPlacingDone: (appearance?: SignatureAppearance) => void\n annotating: boolean\n markup: ReturnType<typeof useDocumentMarkup>\n pdfSnapshotNumber: number | undefined\n isSigned: boolean\n /** One's saved signature or initials to place as a picture stamp (PDF tab \u203A My signature). */\n placeMark?: { image: string; label: string; key: number } | null\n}\n\nexport function ViewerPdfPane({\n documentId,\n doc,\n pdfBlob,\n isPdfLoading,\n pdfError,\n onDownload,\n viewerProps,\n signatureFlow,\n onSignatureFlow,\n currentUserId,\n placingSignature,\n onPlacingDone,\n annotating,\n markup,\n pdfSnapshotNumber,\n isSigned,\n placeMark = null,\n}: ViewerPdfPaneProps) {\n const { documentData, isDownloadOnly, isVideo, isAudio, isImage, isDocx } = doc\n const showMarkup = markup.hasMarkup && !markup.showOriginal\n\n // Files that cannot be previewed in the browser: the download, at once.\n if (isDownloadOnly) {\n return (\n <ViewerPreviewUnavailable\n extension={fileExtension(documentData?.file_name)}\n onDownload={onDownload}\n />\n )\n }\n\n // Text files are rendered to a PDF too, so name what the user opened, not the rendering.\n const loadingLabel = isVideo\n ? 'Loading video...'\n : isAudio\n ? 'Loading audio...'\n : isImage\n ? 'Loading image...'\n : !documentData\n ? 'Loading document...'\n : friendlyFileType(documentData.file_type, documentData.file_name) === 'Text'\n ? 'Loading text...'\n : 'Loading PDF...'\n const errorLabel = isVideo\n ? 'Failed to load video'\n : isAudio\n ? 'Failed to load audio'\n : isImage\n ? 'Failed to load image'\n : 'Failed to load PDF'\n\n if (\n isDocx &&\n !pdfSnapshotNumber &&\n documentData &&\n !['completed', 'low-content', 'failed'].includes(documentData.status || '')\n ) {\n return (\n <ViewerMessage>\n PDF and search indexing are processing. Open the DOCX tab to edit now.\n </ViewerMessage>\n )\n }\n if (isDocx && !pdfSnapshotNumber && documentData?.status === 'failed') {\n return (\n <ViewerMessage>\n PDF conversion failed. Open the Word editor or download the document.\n </ViewerMessage>\n )\n }\n if (isPdfLoading) return <ViewerMessage>{loadingLabel}</ViewerMessage>\n if (pdfError) {\n return (\n <ViewerLoadError\n message={\n isVideo || isAudio || isImage\n ? `${errorLabel} \u2014 download it instead.`\n : \"Couldn't display the PDF \u2014 download it instead.\"\n }\n onDownload={onDownload}\n />\n )\n }\n // Nothing is fetched yet: the PDF is read once the document record has loaded, so the viewer\n // would otherwise sit empty for that first stretch.\n if (!pdfBlob) return <ViewerMessage>{loadingLabel}</ViewerMessage>\n\n if (isVideo) return <VideoViewer blob={pdfBlob} title={documentData?.file_name || 'Video'} />\n if (isAudio) return <AudioViewer blob={pdfBlob} title={documentData?.file_name || 'Audio'} />\n if (isImage) {\n return <ImageViewer blob={pdfBlob} alt={documentData?.file_name || 'Document image'} />\n }\n if (signatureFlow?.kind === 'prepare') {\n return (\n <Suspense fallback={<ViewerMessage>Loading\u2026</ViewerMessage>}>\n <SignatureRequestEditor\n {...viewerProps}\n file={pdfBlob}\n documentId={documentId}\n version={pdfSnapshotNumber ?? documentData?.current_version ?? undefined}\n selfSigning={signatureFlow.self}\n draft={signatureFlow.draft}\n shareFile={!signatureFlow.draft}\n viewer={SessionPdfViewer}\n onCancel={() => onSignatureFlow(null)}\n onSaved={() => onSignatureFlow(null)}\n />\n </Suspense>\n )\n }\n if (signatureFlow?.kind === 'sign' && currentUserId) {\n const { currentPage: _page, scrollTrigger: _trigger, ...guidedProps } = viewerProps\n return (\n <Suspense fallback={<ViewerMessage>Loading\u2026</ViewerMessage>}>\n <GuidedSigningViewer\n {...guidedProps}\n file={pdfBlob}\n request={signatureFlow.request}\n signer={currentUserId}\n viewer={SessionPdfViewer}\n onClose={() => onSignatureFlow(null)}\n onSigned={() => onSignatureFlow(null)}\n />\n </Suspense>\n )\n }\n if (placingSignature) {\n return (\n <Suspense fallback={<ViewerMessage>Loading\u2026</ViewerMessage>}>\n <SignaturePlacementViewer\n {...viewerProps}\n file={pdfBlob}\n documentId={documentId}\n viewer={SessionPdfViewer}\n onCancel={() => onPlacingDone()}\n onContinue={(appearance) => onPlacingDone(appearance)}\n />\n </Suspense>\n )\n }\n // Annotate mode, or the markup as the reading view: one viewer, so leaving annotate mode keeps\n // (and saves) what was just drawn.\n if (annotating || showMarkup) {\n return (\n <Suspense\n fallback={\n <ViewerMessage>\n {annotating ? 'Loading annotation tools\u2026' : 'Loading markup\u2026'}\n </ViewerMessage>\n }\n >\n <AnnotatedPdfViewer\n key={`${documentId}@${pdfSnapshotNumber ?? 'current'}`}\n {...viewerProps}\n file={pdfBlob}\n documentId={documentId}\n version={pdfSnapshotNumber}\n editing={annotating}\n onMarkupChange={markup.onMarkupChange}\n fileName={documentData?.file_name}\n shared={documentData?.shared ?? false}\n folder={documentData?.folder}\n workspaceId={documentData?.workspace_ext_id}\n viewer={SessionPdfViewer}\n signed={isSigned}\n placeStamp={placeMark}\n />\n </Suspense>\n )\n }\n return <SessionPdfViewer {...viewerProps} file={pdfBlob} />\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["/**\n * The PDF (or media) body of the document viewer: loading and failure states, the image, audio and\n * video players, and the PDF with its tools \u2014 the markup layer, signature placement, a signature\n * request being prepared or signed.\n */\n\nimport { lazy, Suspense, useCallback } from 'react'\nimport { useArbiSessionState, useArbiSessionStore } from '@arbidocs/react'\nimport type {\n SignatureAppearance,\n SignatureRequest,\n useDocumentMarkup,\n} from '@arbidocs/react/document-tools'\nimport {\n AudioViewer,\n ImageViewer,\n PdfViewer as KitPdfViewer,\n VideoViewer,\n type PdfViewerProps,\n} from '@arbidocs/react/viewers'\nimport { friendlyFileType } from '@arbidocs/react/ui'\nimport { ViewerLoadError, ViewerMessage, ViewerPreviewUnavailable } from '../../files'\nimport { usePdfClipOutlines } from './pdf/usePdfClipOutlines'\nimport { usePdfPassageHighlights } from './pdf/usePdfPassageHighlights'\nimport { useViewerIsDetached } from './support'\nimport { isLegacyPresentationFile } from '../../presentation/panel-id'\nimport { PresentationFileCard } from '../presentation/PresentationFileCard'\nimport { fileExtension, type ViewedDocument } from './useViewedDocument'\n\n// The markup viewer brings in pdf-lib, which nothing else needs: load it only when it is shown.\nconst AnnotatedPdfViewer = lazy(() =>\n import('../../document-tools/viewer').then((m) => ({ default: m.AnnotatedPdfViewer }))\n)\nconst SignaturePlacementViewer = lazy(() =>\n import('../../document-tools/viewer').then((m) => ({ default: m.SignaturePlacementViewer }))\n)\n// Signature requests: placing fields for signers, and a signer's guided signing.\nconst SignatureRequestEditor = lazy(() =>\n import('../../document-tools/viewer').then((m) => ({ default: m.SignatureRequestEditor }))\n)\n// Clip mode for the presentation: its own chunk (the presentation module).\nconst ClippingPdfViewer = lazy(() =>\n import('../presentation/ClippingPdfViewer').then((m) => ({ default: m.ClippingPdfViewer }))\n)\nconst ClippingMediaViewer = lazy(() =>\n import('../presentation/ClippingMediaViewer').then((m) => ({ default: m.ClippingMediaViewer }))\n)\nconst GuidedSigningViewer = lazy(() =>\n import('../../document-tools/viewer').then((m) => ({ default: m.GuidedSigningViewer }))\n)\n\n/** A signature request: the author placing fields, or a signer walked through theirs. */\nexport type SignatureFlow =\n | { kind: 'prepare'; draft?: SignatureRequest; self?: boolean }\n | { kind: 'sign'; request: SignatureRequest }\n | null\n\n/**\n * The kit PDF viewer, reporting the page read into the session state; in a detached window its\n * pages render without the opener's web fonts (canvas text in another document cannot use them).\n */\nfunction SessionPdfViewer(props: Omit<PdfViewerProps, 'onPageChange' | 'disableFontFace'>) {\n const session = useArbiSessionStore()\n const viewingId = useArbiSessionState((s) => s.viewingDocument?.id)\n const isDetached = useViewerIsDetached()\n const onPageChange = useCallback(\n (page: number) => {\n if (viewingId) session.setViewingDocument({ id: viewingId, page })\n },\n [session, viewingId]\n )\n // The passages a citation lights, over their pages, under whatever the mode draws (markup).\n const passageHighlights = usePdfPassageHighlights(viewingId)\n // The open presentation's clips of this document, where they were cut.\n const clipOutlines = usePdfClipOutlines(viewingId)\n const { renderPageOverlay: modeOverlay } = props\n const renderPageOverlay = useCallback<NonNullable<PdfViewerProps['renderPageOverlay']>>(\n (page) => (\n <>\n {passageHighlights(page.pageNumber)}\n {clipOutlines(page.pageNumber)}\n {modeOverlay?.(page)}\n </>\n ),\n [passageHighlights, clipOutlines, modeOverlay]\n )\n return (\n <KitPdfViewer\n {...props}\n renderPageOverlay={renderPageOverlay}\n onPageChange={onPageChange}\n disableFontFace={!!isDetached}\n />\n )\n}\n\nexport interface ViewerPdfPaneProps {\n documentId: string\n doc: ViewedDocument\n pdfBlob: Blob | null\n isPdfLoading: boolean\n pdfError: unknown\n onDownload: () => void\n viewerProps: {\n currentPage: number | undefined\n onLoadSuccess: (pages: number) => void\n scrollTrigger: number\n zoom: number\n onZoomChange: (zoom: number) => void\n }\n signatureFlow: SignatureFlow\n onSignatureFlow: (flow: SignatureFlow) => void\n currentUserId: string | undefined\n placingSignature: boolean\n onPlacingDone: (appearance?: SignatureAppearance) => void\n annotating: boolean\n /** Clip mode: dragging over a page adds that area to the presentation. */\n clipping?: boolean\n markup: ReturnType<typeof useDocumentMarkup>\n pdfSnapshotNumber: number | undefined\n isSigned: boolean\n /** One's saved signature or initials to place as a picture stamp (PDF tab \u203A My signature). */\n placeMark?: { image: string; label: string; key: number } | null\n}\n\nexport function ViewerPdfPane({\n documentId,\n doc,\n pdfBlob,\n isPdfLoading,\n pdfError,\n onDownload,\n viewerProps,\n signatureFlow,\n onSignatureFlow,\n currentUserId,\n placingSignature,\n onPlacingDone,\n annotating,\n clipping = false,\n markup,\n pdfSnapshotNumber,\n isSigned,\n placeMark = null,\n}: ViewerPdfPaneProps) {\n const { documentData, isDownloadOnly, isVideo, isAudio, isImage, isDocx } = doc\n const showMarkup = markup.hasMarkup && !markup.showOriginal\n\n // A presentation saved before it was a PDF (its data alone): what it is, and Edit presentation.\n if (isLegacyPresentationFile(documentData?.file_name)) {\n return <PresentationFileCard documentId={documentId} fileName={documentData?.file_name ?? ''} />\n }\n\n // Files that cannot be previewed in the browser: the download, at once.\n if (isDownloadOnly) {\n return (\n <ViewerPreviewUnavailable\n extension={fileExtension(documentData?.file_name)}\n onDownload={onDownload}\n />\n )\n }\n\n // Text files are rendered to a PDF too, so name what the user opened, not the rendering.\n const loadingLabel = isVideo\n ? 'Loading video...'\n : isAudio\n ? 'Loading audio...'\n : isImage\n ? 'Loading image...'\n : !documentData\n ? 'Loading document...'\n : friendlyFileType(documentData.file_type, documentData.file_name) === 'Text'\n ? 'Loading text...'\n : 'Loading PDF...'\n const errorLabel = isVideo\n ? 'Failed to load video'\n : isAudio\n ? 'Failed to load audio'\n : isImage\n ? 'Failed to load image'\n : 'Failed to load PDF'\n\n if (\n isDocx &&\n !pdfSnapshotNumber &&\n documentData &&\n !['completed', 'low-content', 'failed'].includes(documentData.status || '')\n ) {\n return (\n <ViewerMessage>\n PDF and search indexing are processing. Open the DOCX tab to edit now.\n </ViewerMessage>\n )\n }\n if (isDocx && !pdfSnapshotNumber && documentData?.status === 'failed') {\n return (\n <ViewerMessage>\n PDF conversion failed. Open the Word editor or download the document.\n </ViewerMessage>\n )\n }\n if (isPdfLoading) return <ViewerMessage>{loadingLabel}</ViewerMessage>\n if (pdfError) {\n return (\n <ViewerLoadError\n message={\n isVideo || isAudio || isImage\n ? `${errorLabel} \u2014 download it instead.`\n : \"Couldn't display the PDF \u2014 download it instead.\"\n }\n onDownload={onDownload}\n />\n )\n }\n // Nothing is fetched yet: the PDF is read once the document record has loaded, so the viewer\n // would otherwise sit empty for that first stretch.\n if (!pdfBlob) return <ViewerMessage>{loadingLabel}</ViewerMessage>\n\n // Clip mode on an image or a video: drag over it for the presentation.\n if (clipping && (isImage || isVideo)) {\n return (\n <Suspense fallback={<ViewerMessage>Loading\u2026</ViewerMessage>}>\n <ClippingMediaViewer\n file={pdfBlob}\n kind={isVideo ? 'video' : 'image'}\n documentId={documentId}\n fileName={documentData?.file_name || (isVideo ? 'Video' : 'Image')}\n />\n </Suspense>\n )\n }\n if (isVideo) return <VideoViewer blob={pdfBlob} title={documentData?.file_name || 'Video'} />\n if (isAudio) return <AudioViewer blob={pdfBlob} title={documentData?.file_name || 'Audio'} />\n if (isImage) {\n return <ImageViewer blob={pdfBlob} alt={documentData?.file_name || 'Document image'} />\n }\n if (signatureFlow?.kind === 'prepare') {\n return (\n <Suspense fallback={<ViewerMessage>Loading\u2026</ViewerMessage>}>\n <SignatureRequestEditor\n {...viewerProps}\n file={pdfBlob}\n documentId={documentId}\n version={pdfSnapshotNumber ?? documentData?.current_version ?? undefined}\n selfSigning={signatureFlow.self}\n draft={signatureFlow.draft}\n shareFile={!signatureFlow.draft}\n viewer={SessionPdfViewer}\n onCancel={() => onSignatureFlow(null)}\n onSaved={() => onSignatureFlow(null)}\n />\n </Suspense>\n )\n }\n if (signatureFlow?.kind === 'sign' && currentUserId) {\n const { currentPage: _page, scrollTrigger: _trigger, ...guidedProps } = viewerProps\n return (\n <Suspense fallback={<ViewerMessage>Loading\u2026</ViewerMessage>}>\n <GuidedSigningViewer\n {...guidedProps}\n file={pdfBlob}\n request={signatureFlow.request}\n signer={currentUserId}\n viewer={SessionPdfViewer}\n onClose={() => onSignatureFlow(null)}\n onSigned={() => onSignatureFlow(null)}\n />\n </Suspense>\n )\n }\n if (placingSignature) {\n return (\n <Suspense fallback={<ViewerMessage>Loading\u2026</ViewerMessage>}>\n <SignaturePlacementViewer\n {...viewerProps}\n file={pdfBlob}\n documentId={documentId}\n viewer={SessionPdfViewer}\n onCancel={() => onPlacingDone()}\n onContinue={(appearance) => onPlacingDone(appearance)}\n />\n </Suspense>\n )\n }\n if (clipping) {\n return (\n <Suspense fallback={<ViewerMessage>Loading\u2026</ViewerMessage>}>\n <ClippingPdfViewer\n {...viewerProps}\n file={pdfBlob}\n documentId={documentId}\n version={pdfSnapshotNumber}\n fileName={documentData?.file_name || 'Document'}\n hasMarkup={markup.hasMarkup}\n viewer={SessionPdfViewer}\n />\n </Suspense>\n )\n }\n // Annotate mode, or the markup as the reading view: one viewer, so leaving annotate mode keeps\n // (and saves) what was just drawn.\n if (annotating || showMarkup) {\n return (\n <Suspense\n fallback={\n <ViewerMessage>\n {annotating ? 'Loading annotation tools\u2026' : 'Loading markup\u2026'}\n </ViewerMessage>\n }\n >\n <AnnotatedPdfViewer\n key={`${documentId}@${pdfSnapshotNumber ?? 'current'}`}\n {...viewerProps}\n file={pdfBlob}\n documentId={documentId}\n version={pdfSnapshotNumber}\n editing={annotating}\n onMarkupChange={markup.onMarkupChange}\n fileName={documentData?.file_name}\n shared={documentData?.shared ?? false}\n folder={documentData?.folder}\n workspaceId={documentData?.workspace_ext_id}\n viewer={SessionPdfViewer}\n signed={isSigned}\n placeStamp={placeMark}\n />\n </Suspense>\n )\n }\n return <SessionPdfViewer {...viewerProps} file={pdfBlob} />\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -20,11 +20,12 @@ export interface DocumentPlace {
|
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Open a place in the session's viewer: the document at its page (else the first), its passage
|
|
23
|
-
* marked (else none, so a passage of the document open before never carries over)
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* marked (else none, so a passage of the document open before never carries over), on the PDF: a
|
|
24
|
+
* passage is lit there, in the box the parser found it in. A passage the parser could not place on
|
|
25
|
+
* its page (a document parsed before passages had boxes) is shown on the text view instead
|
|
26
|
+
* (`usePdfPassageHighlights`).
|
|
26
27
|
*/
|
|
27
|
-
export declare function showDocumentPlace(session: Pick<ArbiSessionStore, 'setViewingDocument' | 'setDocumentViewer'>, { documentId, passageId, page
|
|
28
|
+
export declare function showDocumentPlace(session: Pick<ArbiSessionStore, 'setViewingDocument' | 'setDocumentViewer'>, { documentId, passageId, page }: DocumentPlace): void;
|
|
28
29
|
/** One key per place: two links to the same place are the same link. */
|
|
29
30
|
export declare function documentPlaceKey(place: DocumentPlace | null | undefined): string | null;
|
|
30
31
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
function showDocumentPlace(session, { documentId, passageId, page
|
|
2
|
+
function showDocumentPlace(session, { documentId, passageId, page }) {
|
|
3
3
|
session.setViewingDocument({ id: documentId, page: page ?? 1, passageId: passageId ?? null });
|
|
4
|
-
session.setDocumentViewer({ tab:
|
|
4
|
+
session.setDocumentViewer({ tab: "pdf" });
|
|
5
5
|
}
|
|
6
6
|
function documentPlaceKey(place) {
|
|
7
7
|
if (!place?.documentId) return null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/connection/viewer/documentPlace.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Where a link opens a document: the document (or one of its versions, `doc-\u2026@v{n}`), at a cited\n * passage or a page. Every way into a document goes through one rule, `showDocumentPlace`, so a\n * citation, a deep link, a production's QR code and a click in a list land the same way.\n */\n\nimport type { ArbiSessionStore } from '@arbidocs/react'\nimport type { DocumentViewerTab } from './documentViewerTabs'\n\nexport interface DocumentPlace {\n /** The document, or a version reference `doc-\u2026@v{n}` (that version opens read-only). */\n documentId: string\n /** A passage (`cnk-\u2026`) to scroll to and mark. */\n passageId?: string | null\n /** A page to open on. */\n page?: number | null\n /**\n * The document itself (`pdf`, as the environment's `openDocument` asks): a copy the document\n * tools just made, a signature request.\n */\n tab?: 'pdf' | null\n}\n\n/**\n * Open a place in the session's viewer: the document at its page (else the first), its passage\n * marked (else none, so a passage of the document open before never carries over)
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["/**\n * Where a link opens a document: the document (or one of its versions, `doc-\u2026@v{n}`), at a cited\n * passage or a page. Every way into a document goes through one rule, `showDocumentPlace`, so a\n * citation, a deep link, a production's QR code and a click in a list land the same way.\n */\n\nimport type { ArbiSessionStore } from '@arbidocs/react'\nimport type { DocumentViewerTab } from './documentViewerTabs'\n\nexport interface DocumentPlace {\n /** The document, or a version reference `doc-\u2026@v{n}` (that version opens read-only). */\n documentId: string\n /** A passage (`cnk-\u2026`) to scroll to and mark. */\n passageId?: string | null\n /** A page to open on. */\n page?: number | null\n /**\n * The document itself (`pdf`, as the environment's `openDocument` asks): a copy the document\n * tools just made, a signature request.\n */\n tab?: 'pdf' | null\n}\n\n/**\n * Open a place in the session's viewer: the document at its page (else the first), its passage\n * marked (else none, so a passage of the document open before never carries over), on the PDF: a\n * passage is lit there, in the box the parser found it in. A passage the parser could not place on\n * its page (a document parsed before passages had boxes) is shown on the text view instead\n * (`usePdfPassageHighlights`).\n */\nexport function showDocumentPlace(\n session: Pick<ArbiSessionStore, 'setViewingDocument' | 'setDocumentViewer'>,\n { documentId, passageId, page }: DocumentPlace\n): void {\n session.setViewingDocument({ id: documentId, page: page ?? 1, passageId: passageId ?? null })\n session.setDocumentViewer({ tab: 'pdf' })\n}\n\n/** One key per place: two links to the same place are the same link. */\nexport function documentPlaceKey(place: DocumentPlace | null | undefined): string | null {\n if (!place?.documentId) return null\n return `${place.documentId}|${place.passageId ?? ''}|${place.page ?? ''}|${place.tab ?? ''}`\n}\n\n/**\n * The dialog's first tab for a place: the PDF asked for, or a page alone, on the document itself; a\n * cited passage in the review; anything else on the first tab offered.\n */\nexport function dialogTabForPlace(\n place: DocumentPlace,\n tabs: readonly DocumentViewerTab[]\n): DocumentViewerTab | undefined {\n if (place.tab === 'pdf' && tabs.includes('document')) return 'document'\n if (place.passageId && tabs.includes('review')) return 'review'\n if (place.page && !place.passageId && tabs.includes('document')) return 'document'\n return tabs[0]\n}\n"],
|
|
5
|
+
"mappings": ";AA8BO,SAAS,kBACd,SACA,EAAE,YAAY,WAAW,KAAK,GACxB;AACN,UAAQ,mBAAmB,EAAE,IAAI,YAAY,MAAM,QAAQ,GAAG,WAAW,aAAa,KAAK,CAAC;AAC5F,UAAQ,kBAAkB,EAAE,KAAK,MAAM,CAAC;AAC1C;AAGO,SAAS,iBAAiB,OAAwD;AACvF,MAAI,CAAC,OAAO,WAAY,QAAO;AAC/B,SAAO,GAAG,MAAM,UAAU,IAAI,MAAM,aAAa,EAAE,IAAI,MAAM,QAAQ,EAAE,IAAI,MAAM,OAAO,EAAE;AAC5F;AAMO,SAAS,kBACd,OACA,MAC+B;AAC/B,MAAI,MAAM,QAAQ,SAAS,KAAK,SAAS,UAAU,EAAG,QAAO;AAC7D,MAAI,MAAM,aAAa,KAAK,SAAS,QAAQ,EAAG,QAAO;AACvD,MAAI,MAAM,QAAQ,CAAC,MAAM,aAAa,KAAK,SAAS,UAAU,EAAG,QAAO;AACxE,SAAO,KAAK,CAAC;AACf;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useMemo } from "react";
|
|
4
|
-
import { Camera, Check, FileText, Mic, Video as VideoIcon } from "lucide-react";
|
|
4
|
+
import { Camera, Check, FileText, Mic, Presentation, Video as VideoIcon } from "lucide-react";
|
|
5
5
|
import {
|
|
6
6
|
DOCUMENT_VIEWER_PANEL,
|
|
7
7
|
DraftTitleControls,
|
|
@@ -33,7 +33,8 @@ const KIND_OPTIONS = [
|
|
|
33
33
|
{ value: "text", icon: /* @__PURE__ */ jsx(FileText, { className: "w-4 h-4" }), label: "Doc" },
|
|
34
34
|
{ value: "audio", icon: /* @__PURE__ */ jsx(Mic, { className: "w-4 h-4" }), label: "Voice" },
|
|
35
35
|
{ value: "video", icon: /* @__PURE__ */ jsx(VideoIcon, { className: "w-4 h-4" }), label: "Video" },
|
|
36
|
-
{ value: "photo", icon: /* @__PURE__ */ jsx(Camera, { className: "w-4 h-4" }), label: "Photo" }
|
|
36
|
+
{ value: "photo", icon: /* @__PURE__ */ jsx(Camera, { className: "w-4 h-4" }), label: "Photo" },
|
|
37
|
+
{ value: "presentation", icon: /* @__PURE__ */ jsx(Presentation, { className: "w-4 h-4" }), label: "Presentation" }
|
|
37
38
|
];
|
|
38
39
|
const useDraft = (documentId) => useArbiSessionState((state) => state.draftDocuments[documentId]);
|
|
39
40
|
function ViewerDraftKind({ documentId }) {
|
|
@@ -72,7 +73,8 @@ const TITLE_PLACEHOLDER = {
|
|
|
72
73
|
text: "New Doc",
|
|
73
74
|
audio: "New Audio",
|
|
74
75
|
photo: "New Photo",
|
|
75
|
-
video: "New Video"
|
|
76
|
+
video: "New Video",
|
|
77
|
+
presentation: "New Presentation"
|
|
76
78
|
};
|
|
77
79
|
function selectTitleOnFocus(event) {
|
|
78
80
|
if (event.target instanceof HTMLInputElement) event.target.select();
|
|
@@ -115,7 +117,7 @@ function ViewerCaptureDraft({ documentId }) {
|
|
|
115
117
|
const { notify } = useBlocksEnvironment();
|
|
116
118
|
const upload = useUploadDocuments();
|
|
117
119
|
const { data: userSettings } = useUserSettings();
|
|
118
|
-
if (!draft || draft.kind === "text") return null;
|
|
120
|
+
if (!draft || draft.kind === "text" || draft.kind === "presentation") return null;
|
|
119
121
|
const kind = draft.kind;
|
|
120
122
|
const file = CAPTURE_FILE[kind];
|
|
121
123
|
const discard = () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/connection/viewer/drafts.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * The viewer's unsaved drafts (the session's `draftDocuments`, started with `startDraftDocument`)\n * and its related documents. A draft has no backend record until it is saved: its kind (Doc,\n * Voice, Photo, Video) replaces the tabs, its editable name replaces the title, and a capture\n * draft's recorder is the whole body. A capture saves straight to the workspace as its own\n * document (`useUploadDocuments`), with the user's active configuration, so the backend can name it\n * from its content.\n */\n\nimport { useMemo, type FocusEvent, type ReactNode } from 'react'\nimport { Camera, Check, FileText, Mic, Video as VideoIcon } from 'lucide-react'\nimport {\n DOCUMENT_VIEWER_PANEL,\n DraftTitleControls,\n PhotoCapture,\n VideoCapture,\n VoiceNoteRecorder,\n isPanelOpen,\n useArbi,\n useArbiSessionState,\n useArbiSessionStore,\n useConfig,\n useSaveConfigField,\n useUploadDocuments,\n useUserSettings,\n type ArbiDraftDocument,\n type ArbiDraftKind,\n type WorkspaceDocument,\n} from '@arbidocs/react'\nimport {\n useDictateContext,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@arbidocs/react/ui'\nimport { logger } from '@arbidocs/client'\nimport { useBlocksEnvironment } from '../../provider/environment'\nimport { RelatedDocumentsCell } from '../files/grid/cells'\nimport { useFilesDialogs } from '../files/store'\nimport type { ViewerDocumentRecord } from './support'\nimport { compareDocumentsIn } from './compareDocuments'\n\nconst KIND_OPTIONS: { value: ArbiDraftKind; icon: ReactNode; label: string }[] = [\n { value: 'text', icon: <FileText className=\"w-4 h-4\" />, label: 'Doc' },\n { value: 'audio', icon: <Mic className=\"w-4 h-4\" />, label: 'Voice' },\n { value: 'video', icon: <VideoIcon className=\"w-4 h-4\" />, label: 'Video' },\n { value: 'photo', icon: <Camera className=\"w-4 h-4\" />, label: 'Photo' },\n]\n\nconst useDraft = (documentId: string): ArbiDraftDocument | undefined =>\n useArbiSessionState((state) => state.draftDocuments[documentId])\n\n/**\n * A draft's kind, in place of the tabs: the icon of its kind opens the four kinds, so the user can\n * switch before capturing or typing anything.\n */\nexport function ViewerDraftKind({ documentId }: { documentId: string }) {\n const draft = useDraft(documentId)\n const session = useArbiSessionStore()\n if (!draft) return null\n const current = KIND_OPTIONS.find((option) => option.value === draft.kind) ?? KIND_OPTIONS[0]\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n title={`Draft kind: ${current.label}. Click to change.`}\n aria-label={`Draft kind: ${current.label}. Click to change.`}\n className=\"px-3 py-2 transition-all duration-200 font-medium border-b-2 border-transparent text-muted-foreground hover:text-foreground hover:border-border flex-shrink-0 flex items-center\"\n data-testid=\"draft-kind-trigger\"\n >\n {current.icon}\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" sideOffset={4} className=\"w-40\">\n {KIND_OPTIONS.map((option) => (\n <DropdownMenuItem\n key={option.value}\n onSelect={() => session.setDraftDocumentKind(documentId, option.value)}\n data-testid={`draft-kind-option-${option.value}`}\n >\n {option.icon}\n <span>{option.label}</span>\n {option.value === draft.kind && <Check className=\"ml-auto w-4 h-4\" />}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\nconst TITLE_PLACEHOLDER: Record<ArbiDraftKind, string> = {\n text: 'New Doc',\n audio: 'New Audio',\n photo: 'New Photo',\n video: 'New Video',\n}\n\n// The draft starts with a real default name (\"New Doc\"), not a placeholder, so focusing the field\n// selects it: typing then replaces it instead of appending to it.\nfunction selectTitleOnFocus(event: FocusEvent<HTMLDivElement>) {\n if (event.target instanceof HTMLInputElement) event.target.select()\n}\n\n/**\n * A draft's editable name, in place of the title, with the auto-name switch: the active\n * configuration's `DoctagLLM.AUTO_RENAME`, the same setting as the upload dialog's.\n */\nexport function ViewerDraftTitle({ documentId }: { documentId: string }) {\n const draft = useDraft(documentId)\n const session = useArbiSessionStore()\n const { data: userSettings } = useUserSettings()\n const { data: activeConfig } = useConfig(userSettings?.last_config || 'default')\n const configField = useSaveConfigField()\n const config = activeConfig as { DoctagLLM?: { AUTO_RENAME?: boolean } } | undefined\n const autoRename = config?.DoctagLLM?.AUTO_RENAME ?? false\n\n if (!draft) return null\n return (\n <div className=\"contents\" onFocus={selectTitleOnFocus}>\n <DraftTitleControls\n title={draft.title}\n onTitleChange={(title) => session.updateDraftDocument(documentId, { title })}\n placeholder={TITLE_PLACEHOLDER[draft.kind] ?? TITLE_PLACEHOLDER.text}\n autoRename={autoRename}\n onAutoRenameChange={(value) => configField.save('DoctagLLM', 'AUTO_RENAME', value)}\n />\n </div>\n )\n}\n\n/** Each capture's file name and the type used when the recorder reports none. */\nconst CAPTURE_FILE: Record<\n 'audio' | 'photo' | 'video',\n { name: string; fallbackType: string; saved: string; label: string }\n> = {\n audio: {\n name: 'voice-note.webm',\n fallbackType: 'audio/webm',\n saved: 'Voice note saved \u2014 transcribing\u2026',\n label: 'voice note',\n },\n photo: { name: 'photo.png', fallbackType: 'image/png', saved: 'Photo saved', label: 'photo' },\n video: { name: 'video.webm', fallbackType: 'video/webm', saved: 'Video saved', label: 'video' },\n}\n\n/**\n * A capture draft's recorder (voice, photo or video), the whole body of the viewer. The finished\n * capture is kept in the draft first, so it survives the viewer closing even if the save fails;\n * the draft is cleared once the saved document's id is known and the viewer moves to it.\n */\nexport function ViewerCaptureDraft({ documentId }: { documentId: string }) {\n const draft = useDraft(documentId)\n const session = useArbiSessionStore()\n const arbi = useArbi()\n const dictate = useDictateContext()\n const { notify } = useBlocksEnvironment()\n const upload = useUploadDocuments()\n const { data: userSettings } = useUserSettings()\n\n if (!draft || draft.kind === 'text') return null\n const kind = draft.kind\n const file = CAPTURE_FILE[kind]\n\n const discard = () => {\n session.clearDraftDocument(documentId)\n session.setViewingDocument(null)\n }\n const save = async (blob: Blob, mimeType: string | null) => {\n session.updateDraftDocument(documentId, { mediaBlob: blob })\n try {\n const response = await upload.mutateAsync({\n files: [new File([blob], file.name, { type: mimeType || file.fallbackType })],\n options: { configExtId: userSettings?.last_config ?? undefined },\n })\n // The id often arrives later (on the realtime channel): only leave the draft for a real one.\n const newDocId = response.doc_ext_ids?.[0]\n if (newDocId) {\n session.clearDraftDocument(documentId)\n session.setViewingDocument({ id: newDocId, page: null })\n }\n notify(file.saved, 'success')\n } catch (err) {\n logger.error(`[ViewerCaptureDraft] Saving the ${file.label} failed`, err)\n notify(`Could not save the ${file.label}. Please try again.`, 'error')\n }\n }\n const onError = (message: string) => notify(message, 'error')\n\n return (\n <div className=\"h-full overflow-hidden\" data-testid=\"media-capture-recorder\">\n {kind === 'audio' && (\n <VoiceNoteRecorder\n title={draft.title}\n isSaving={upload.isPending}\n onSave={save}\n onDiscard={discard}\n onError={onError}\n // A rough transcript while reviewing (the host's dictation, else the session's); the real\n // one comes with the saved document.\n transcribe={(blob) =>\n dictate ? dictate.transcribe(blob) : arbi.assistant.transcribe(blob)\n }\n />\n )}\n {kind === 'photo' && (\n <PhotoCapture\n onSave={save}\n onDiscard={discard}\n onError={onError}\n isSaving={upload.isPending}\n />\n )}\n {kind === 'video' && (\n <VideoCapture\n onSave={save}\n onDiscard={discard}\n onError={onError}\n isSaving={upload.isPending}\n />\n )}\n </div>\n )\n}\n\n/**\n * The documents related to the open one (its email family, copies, near duplicates), beside its\n * name: one opens in the viewer, and each can be deleted or compared with it.\n */\nexport function ViewerRelatedDocuments({ document }: { document: ViewerDocumentRecord }) {\n const session = useArbiSessionStore()\n const related = useArbiSessionState((state) => state.relatedDocuments.get(document.external_id))\n const viewerOpen = useArbiSessionState((state) => isPanelOpen(state, DOCUMENT_VIEWER_PANEL))\n const viewingId = useArbiSessionState((state) => state.viewingDocument?.id ?? null)\n const onCompare = useMemo(\n () => (id: string, otherId: string) => compareDocumentsIn(session, id, otherId),\n [session]\n )\n if (!related) return null\n return (\n <RelatedDocumentsCell\n currentDoc={document as WorkspaceDocument}\n related={related}\n viewingDocumentId={viewerOpen ? viewingId : null}\n onDocumentClick={(id) => session.setViewingDocument({ id, page: null })}\n onDelete={(doc) => useFilesDialogs.getState().requestDelete(doc)}\n onCompare={onCompare}\n />\n )\n}\n"],
|
|
5
|
-
"mappings": ";AA4CyB,
|
|
4
|
+
"sourcesContent": ["/**\n * The viewer's unsaved drafts (the session's `draftDocuments`, started with `startDraftDocument`)\n * and its related documents. A draft has no backend record until it is saved: its kind (Doc,\n * Voice, Photo, Video) replaces the tabs, its editable name replaces the title, and a capture\n * draft's recorder is the whole body. A capture saves straight to the workspace as its own\n * document (`useUploadDocuments`), with the user's active configuration, so the backend can name it\n * from its content.\n */\n\nimport { useMemo, type FocusEvent, type ReactNode } from 'react'\nimport { Camera, Check, FileText, Mic, Presentation, Video as VideoIcon } from 'lucide-react'\nimport {\n DOCUMENT_VIEWER_PANEL,\n DraftTitleControls,\n PhotoCapture,\n VideoCapture,\n VoiceNoteRecorder,\n isPanelOpen,\n useArbi,\n useArbiSessionState,\n useArbiSessionStore,\n useConfig,\n useSaveConfigField,\n useUploadDocuments,\n useUserSettings,\n type ArbiDraftDocument,\n type ArbiDraftKind,\n type WorkspaceDocument,\n} from '@arbidocs/react'\nimport {\n useDictateContext,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@arbidocs/react/ui'\nimport { logger } from '@arbidocs/client'\nimport { useBlocksEnvironment } from '../../provider/environment'\nimport { RelatedDocumentsCell } from '../files/grid/cells'\nimport { useFilesDialogs } from '../files/store'\nimport type { ViewerDocumentRecord } from './support'\nimport { compareDocumentsIn } from './compareDocuments'\n\nconst KIND_OPTIONS: { value: ArbiDraftKind; icon: ReactNode; label: string }[] = [\n { value: 'text', icon: <FileText className=\"w-4 h-4\" />, label: 'Doc' },\n { value: 'audio', icon: <Mic className=\"w-4 h-4\" />, label: 'Voice' },\n { value: 'video', icon: <VideoIcon className=\"w-4 h-4\" />, label: 'Video' },\n { value: 'photo', icon: <Camera className=\"w-4 h-4\" />, label: 'Photo' },\n { value: 'presentation', icon: <Presentation className=\"w-4 h-4\" />, label: 'Presentation' },\n]\n\nconst useDraft = (documentId: string): ArbiDraftDocument | undefined =>\n useArbiSessionState((state) => state.draftDocuments[documentId])\n\n/**\n * A draft's kind, in place of the tabs: the icon of its kind opens the four kinds, so the user can\n * switch before capturing or typing anything.\n */\nexport function ViewerDraftKind({ documentId }: { documentId: string }) {\n const draft = useDraft(documentId)\n const session = useArbiSessionStore()\n if (!draft) return null\n const current = KIND_OPTIONS.find((option) => option.value === draft.kind) ?? KIND_OPTIONS[0]\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n title={`Draft kind: ${current.label}. Click to change.`}\n aria-label={`Draft kind: ${current.label}. Click to change.`}\n className=\"px-3 py-2 transition-all duration-200 font-medium border-b-2 border-transparent text-muted-foreground hover:text-foreground hover:border-border flex-shrink-0 flex items-center\"\n data-testid=\"draft-kind-trigger\"\n >\n {current.icon}\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" sideOffset={4} className=\"w-40\">\n {KIND_OPTIONS.map((option) => (\n <DropdownMenuItem\n key={option.value}\n onSelect={() => session.setDraftDocumentKind(documentId, option.value)}\n data-testid={`draft-kind-option-${option.value}`}\n >\n {option.icon}\n <span>{option.label}</span>\n {option.value === draft.kind && <Check className=\"ml-auto w-4 h-4\" />}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n\nconst TITLE_PLACEHOLDER: Record<ArbiDraftKind, string> = {\n text: 'New Doc',\n audio: 'New Audio',\n photo: 'New Photo',\n video: 'New Video',\n presentation: 'New Presentation',\n}\n\n// The draft starts with a real default name (\"New Doc\"), not a placeholder, so focusing the field\n// selects it: typing then replaces it instead of appending to it.\nfunction selectTitleOnFocus(event: FocusEvent<HTMLDivElement>) {\n if (event.target instanceof HTMLInputElement) event.target.select()\n}\n\n/**\n * A draft's editable name, in place of the title, with the auto-name switch: the active\n * configuration's `DoctagLLM.AUTO_RENAME`, the same setting as the upload dialog's.\n */\nexport function ViewerDraftTitle({ documentId }: { documentId: string }) {\n const draft = useDraft(documentId)\n const session = useArbiSessionStore()\n const { data: userSettings } = useUserSettings()\n const { data: activeConfig } = useConfig(userSettings?.last_config || 'default')\n const configField = useSaveConfigField()\n const config = activeConfig as { DoctagLLM?: { AUTO_RENAME?: boolean } } | undefined\n const autoRename = config?.DoctagLLM?.AUTO_RENAME ?? false\n\n if (!draft) return null\n return (\n <div className=\"contents\" onFocus={selectTitleOnFocus}>\n <DraftTitleControls\n title={draft.title}\n onTitleChange={(title) => session.updateDraftDocument(documentId, { title })}\n placeholder={TITLE_PLACEHOLDER[draft.kind] ?? TITLE_PLACEHOLDER.text}\n autoRename={autoRename}\n onAutoRenameChange={(value) => configField.save('DoctagLLM', 'AUTO_RENAME', value)}\n />\n </div>\n )\n}\n\n/** Each capture's file name and the type used when the recorder reports none. */\nconst CAPTURE_FILE: Record<\n 'audio' | 'photo' | 'video',\n { name: string; fallbackType: string; saved: string; label: string }\n> = {\n audio: {\n name: 'voice-note.webm',\n fallbackType: 'audio/webm',\n saved: 'Voice note saved \u2014 transcribing\u2026',\n label: 'voice note',\n },\n photo: { name: 'photo.png', fallbackType: 'image/png', saved: 'Photo saved', label: 'photo' },\n video: { name: 'video.webm', fallbackType: 'video/webm', saved: 'Video saved', label: 'video' },\n}\n\n/**\n * A capture draft's recorder (voice, photo or video), the whole body of the viewer. The finished\n * capture is kept in the draft first, so it survives the viewer closing even if the save fails;\n * the draft is cleared once the saved document's id is known and the viewer moves to it.\n */\nexport function ViewerCaptureDraft({ documentId }: { documentId: string }) {\n const draft = useDraft(documentId)\n const session = useArbiSessionStore()\n const arbi = useArbi()\n const dictate = useDictateContext()\n const { notify } = useBlocksEnvironment()\n const upload = useUploadDocuments()\n const { data: userSettings } = useUserSettings()\n\n if (!draft || draft.kind === 'text' || draft.kind === 'presentation') return null\n const kind = draft.kind\n const file = CAPTURE_FILE[kind]\n\n const discard = () => {\n session.clearDraftDocument(documentId)\n session.setViewingDocument(null)\n }\n const save = async (blob: Blob, mimeType: string | null) => {\n session.updateDraftDocument(documentId, { mediaBlob: blob })\n try {\n const response = await upload.mutateAsync({\n files: [new File([blob], file.name, { type: mimeType || file.fallbackType })],\n options: { configExtId: userSettings?.last_config ?? undefined },\n })\n // The id often arrives later (on the realtime channel): only leave the draft for a real one.\n const newDocId = response.doc_ext_ids?.[0]\n if (newDocId) {\n session.clearDraftDocument(documentId)\n session.setViewingDocument({ id: newDocId, page: null })\n }\n notify(file.saved, 'success')\n } catch (err) {\n logger.error(`[ViewerCaptureDraft] Saving the ${file.label} failed`, err)\n notify(`Could not save the ${file.label}. Please try again.`, 'error')\n }\n }\n const onError = (message: string) => notify(message, 'error')\n\n return (\n <div className=\"h-full overflow-hidden\" data-testid=\"media-capture-recorder\">\n {kind === 'audio' && (\n <VoiceNoteRecorder\n title={draft.title}\n isSaving={upload.isPending}\n onSave={save}\n onDiscard={discard}\n onError={onError}\n // A rough transcript while reviewing (the host's dictation, else the session's); the real\n // one comes with the saved document.\n transcribe={(blob) =>\n dictate ? dictate.transcribe(blob) : arbi.assistant.transcribe(blob)\n }\n />\n )}\n {kind === 'photo' && (\n <PhotoCapture\n onSave={save}\n onDiscard={discard}\n onError={onError}\n isSaving={upload.isPending}\n />\n )}\n {kind === 'video' && (\n <VideoCapture\n onSave={save}\n onDiscard={discard}\n onError={onError}\n isSaving={upload.isPending}\n />\n )}\n </div>\n )\n}\n\n/**\n * The documents related to the open one (its email family, copies, near duplicates), beside its\n * name: one opens in the viewer, and each can be deleted or compared with it.\n */\nexport function ViewerRelatedDocuments({ document }: { document: ViewerDocumentRecord }) {\n const session = useArbiSessionStore()\n const related = useArbiSessionState((state) => state.relatedDocuments.get(document.external_id))\n const viewerOpen = useArbiSessionState((state) => isPanelOpen(state, DOCUMENT_VIEWER_PANEL))\n const viewingId = useArbiSessionState((state) => state.viewingDocument?.id ?? null)\n const onCompare = useMemo(\n () => (id: string, otherId: string) => compareDocumentsIn(session, id, otherId),\n [session]\n )\n if (!related) return null\n return (\n <RelatedDocumentsCell\n currentDoc={document as WorkspaceDocument}\n related={related}\n viewingDocumentId={viewerOpen ? viewingId : null}\n onDocumentClick={(id) => session.setViewingDocument({ id, page: null })}\n onDelete={(doc) => useFilesDialogs.getState().requestDelete(doc)}\n onCompare={onCompare}\n />\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The open presentation's clips of this document, outlined on its PDF pages: where each clip was
|
|
3
|
+
* cut, and the slide it is on. The outline takes no pointer events (the page stays selectable);
|
|
4
|
+
* its label goes to that slide in the presentation.
|
|
5
|
+
*/
|
|
6
|
+
import type { PdfClipOutline } from './clipOutlines';
|
|
7
|
+
export interface PdfClipOutlinesProps {
|
|
8
|
+
outlines: readonly PdfClipOutline[];
|
|
9
|
+
onShow: (outline: PdfClipOutline) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function PdfClipOutlines({ outlines, onShow }: PdfClipOutlinesProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Presentation } from "lucide-react";
|
|
4
|
+
function PdfClipOutlines({ outlines, onShow }) {
|
|
5
|
+
return /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0", "data-testid": "pdf-clip-outlines", children: outlines.map((outline) => {
|
|
6
|
+
const { rect } = outline;
|
|
7
|
+
const label = outline.slides.length ? `Slide ${outline.slides.map((index) => index + 1).join(", ")}` : "In the tray";
|
|
8
|
+
return /* @__PURE__ */ jsx(
|
|
9
|
+
"div",
|
|
10
|
+
{
|
|
11
|
+
className: "absolute rounded-sm border-2 border-dashed border-primary/70",
|
|
12
|
+
style: {
|
|
13
|
+
left: `${rect.x * 100}%`,
|
|
14
|
+
top: `${rect.y * 100}%`,
|
|
15
|
+
width: `${rect.w * 100}%`,
|
|
16
|
+
height: `${rect.h * 100}%`
|
|
17
|
+
},
|
|
18
|
+
"data-testid": "pdf-clip-outline",
|
|
19
|
+
"data-clip-id": outline.clipId,
|
|
20
|
+
children: /* @__PURE__ */ jsxs(
|
|
21
|
+
"button",
|
|
22
|
+
{
|
|
23
|
+
type: "button",
|
|
24
|
+
className: "pointer-events-auto absolute -top-2.5 left-1 inline-flex items-center gap-1 rounded-full bg-primary px-1.5 text-[10px] font-medium leading-4 text-primary-foreground shadow-sm hover:bg-primary/90",
|
|
25
|
+
title: outline.slides.length ? `Clipped for the presentation: go to ${label.toLowerCase()}` : "Clipped for the presentation: not on a slide yet",
|
|
26
|
+
onClick: () => onShow(outline),
|
|
27
|
+
"data-testid": "pdf-clip-outline-label",
|
|
28
|
+
children: [
|
|
29
|
+
/* @__PURE__ */ jsx(Presentation, { className: "size-2.5" }),
|
|
30
|
+
label
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
},
|
|
35
|
+
outline.clipId
|
|
36
|
+
);
|
|
37
|
+
}) });
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
PdfClipOutlines
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=PdfClipOutlines.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/connection/viewer/pdf/PdfClipOutlines.tsx"],
|
|
4
|
+
"sourcesContent": ["/**\n * The open presentation's clips of this document, outlined on its PDF pages: where each clip was\n * cut, and the slide it is on. The outline takes no pointer events (the page stays selectable);\n * its label goes to that slide in the presentation.\n */\n\nimport { Presentation } from 'lucide-react'\nimport type { PdfClipOutline } from './clipOutlines'\n\nexport interface PdfClipOutlinesProps {\n outlines: readonly PdfClipOutline[]\n onShow: (outline: PdfClipOutline) => void\n}\n\nexport function PdfClipOutlines({ outlines, onShow }: PdfClipOutlinesProps) {\n return (\n <div className=\"pointer-events-none absolute inset-0\" data-testid=\"pdf-clip-outlines\">\n {outlines.map((outline) => {\n const { rect } = outline\n const label = outline.slides.length\n ? `Slide ${outline.slides.map((index) => index + 1).join(', ')}`\n : 'In the tray'\n return (\n <div\n key={outline.clipId}\n className=\"absolute rounded-sm border-2 border-dashed border-primary/70\"\n style={{\n left: `${rect.x * 100}%`,\n top: `${rect.y * 100}%`,\n width: `${rect.w * 100}%`,\n height: `${rect.h * 100}%`,\n }}\n data-testid=\"pdf-clip-outline\"\n data-clip-id={outline.clipId}\n >\n <button\n type=\"button\"\n className=\"pointer-events-auto absolute -top-2.5 left-1 inline-flex items-center gap-1 rounded-full bg-primary px-1.5 text-[10px] font-medium leading-4 text-primary-foreground shadow-sm hover:bg-primary/90\"\n title={\n outline.slides.length\n ? `Clipped for the presentation: go to ${label.toLowerCase()}`\n : 'Clipped for the presentation: not on a slide yet'\n }\n onClick={() => onShow(outline)}\n data-testid=\"pdf-clip-outline-label\"\n >\n <Presentation className=\"size-2.5\" />\n {label}\n </button>\n </div>\n )\n })}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAmCY,SAWE,KAXF;AA7BZ,SAAS,oBAAoB;AAQtB,SAAS,gBAAgB,EAAE,UAAU,OAAO,GAAyB;AAC1E,SACE,oBAAC,SAAI,WAAU,wCAAuC,eAAY,qBAC/D,mBAAS,IAAI,CAAC,YAAY;AACzB,UAAM,EAAE,KAAK,IAAI;AACjB,UAAM,QAAQ,QAAQ,OAAO,SACzB,SAAS,QAAQ,OAAO,IAAI,CAAC,UAAU,QAAQ,CAAC,EAAE,KAAK,IAAI,CAAC,KAC5D;AACJ,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA,QACV,OAAO;AAAA,UACL,MAAM,GAAG,KAAK,IAAI,GAAG;AAAA,UACrB,KAAK,GAAG,KAAK,IAAI,GAAG;AAAA,UACpB,OAAO,GAAG,KAAK,IAAI,GAAG;AAAA,UACtB,QAAQ,GAAG,KAAK,IAAI,GAAG;AAAA,QACzB;AAAA,QACA,eAAY;AAAA,QACZ,gBAAc,QAAQ;AAAA,QAEtB;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,OACE,QAAQ,OAAO,SACX,uCAAuC,MAAM,YAAY,CAAC,KAC1D;AAAA,YAEN,SAAS,MAAM,OAAO,OAAO;AAAA,YAC7B,eAAY;AAAA,YAEZ;AAAA,kCAAC,gBAAa,WAAU,YAAW;AAAA,cAClC;AAAA;AAAA;AAAA,QACH;AAAA;AAAA,MAxBK,QAAQ;AAAA,IAyBf;AAAA,EAEJ,CAAC,GACH;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which of the open presentation's clips were cut from a document's pages, and the slides each is
|
|
3
|
+
* on: what the PDF outlines (`PdfClipOutlines`).
|
|
4
|
+
*/
|
|
5
|
+
import type { Clip, Rect, Slide } from '../../../presentation/model';
|
|
6
|
+
export interface PdfClipOutline {
|
|
7
|
+
clipId: string;
|
|
8
|
+
/** The clipped area, as fractions of the page. */
|
|
9
|
+
rect: Rect;
|
|
10
|
+
/** The slides (indexes) it is on; none while it waits in the tray. */
|
|
11
|
+
slides: number[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The page clips of the document `viewingId` names, by page number. A clip of another version of
|
|
15
|
+
* it is left out when a version is viewed; the parts of a slide's design (`plain`) are left out.
|
|
16
|
+
*/
|
|
17
|
+
export declare function clipOutlinesByPage(viewingId: string | null | undefined, clips: readonly Clip[], slides: readonly Pick<Slide, 'items'>[]): Map<number, PdfClipOutline[]>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
function parseRef(id) {
|
|
3
|
+
const match = /^(.*)@v(\d+)$/.exec(id);
|
|
4
|
+
return match ? { documentId: match[1], version: Number(match[2]) } : { documentId: id, version: null };
|
|
5
|
+
}
|
|
6
|
+
function clipOutlinesByPage(viewingId, clips, slides) {
|
|
7
|
+
const byPage = /* @__PURE__ */ new Map();
|
|
8
|
+
if (!viewingId) return byPage;
|
|
9
|
+
const { documentId, version } = parseRef(viewingId);
|
|
10
|
+
for (const clip of clips) {
|
|
11
|
+
if (clip.kind !== "pdf" || clip.plain || clip.documentId !== documentId) continue;
|
|
12
|
+
if (version !== null && clip.version !== void 0 && clip.version !== version) continue;
|
|
13
|
+
const on = slides.flatMap(
|
|
14
|
+
(slide, index) => slide.items.some((item) => item.clipId === clip.id) ? [index] : []
|
|
15
|
+
);
|
|
16
|
+
const outline = { clipId: clip.id, rect: clip.rect, slides: on };
|
|
17
|
+
byPage.set(clip.page, [...byPage.get(clip.page) ?? [], outline]);
|
|
18
|
+
}
|
|
19
|
+
return byPage;
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
clipOutlinesByPage
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=clipOutlines.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/connection/viewer/pdf/clipOutlines.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Which of the open presentation's clips were cut from a document's pages, and the slides each is\n * on: what the PDF outlines (`PdfClipOutlines`).\n */\n\nimport type { Clip, Rect, Slide } from '../../../presentation/model'\n\nexport interface PdfClipOutline {\n clipId: string\n /** The clipped area, as fractions of the page. */\n rect: Rect\n /** The slides (indexes) it is on; none while it waits in the tray. */\n slides: number[]\n}\n\n/** The document a viewer id names, and its version (`doc-\u2026@v{n}`), if one. */\nfunction parseRef(id: string): { documentId: string; version: number | null } {\n const match = /^(.*)@v(\\d+)$/.exec(id)\n return match\n ? { documentId: match[1], version: Number(match[2]) }\n : { documentId: id, version: null }\n}\n\n/**\n * The page clips of the document `viewingId` names, by page number. A clip of another version of\n * it is left out when a version is viewed; the parts of a slide's design (`plain`) are left out.\n */\nexport function clipOutlinesByPage(\n viewingId: string | null | undefined,\n clips: readonly Clip[],\n slides: readonly Pick<Slide, 'items'>[]\n): Map<number, PdfClipOutline[]> {\n const byPage = new Map<number, PdfClipOutline[]>()\n if (!viewingId) return byPage\n const { documentId, version } = parseRef(viewingId)\n for (const clip of clips) {\n if (clip.kind !== 'pdf' || clip.plain || clip.documentId !== documentId) continue\n if (version !== null && clip.version !== undefined && clip.version !== version) continue\n const on = slides.flatMap((slide, index) =>\n slide.items.some((item) => item.clipId === clip.id) ? [index] : []\n )\n const outline = { clipId: clip.id, rect: clip.rect, slides: on }\n byPage.set(clip.page, [...(byPage.get(clip.page) ?? []), outline])\n }\n return byPage\n}\n"],
|
|
5
|
+
"mappings": ";AAgBA,SAAS,SAAS,IAA4D;AAC5E,QAAM,QAAQ,gBAAgB,KAAK,EAAE;AACrC,SAAO,QACH,EAAE,YAAY,MAAM,CAAC,GAAG,SAAS,OAAO,MAAM,CAAC,CAAC,EAAE,IAClD,EAAE,YAAY,IAAI,SAAS,KAAK;AACtC;AAMO,SAAS,mBACd,WACA,OACA,QAC+B;AAC/B,QAAM,SAAS,oBAAI,IAA8B;AACjD,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,EAAE,YAAY,QAAQ,IAAI,SAAS,SAAS;AAClD,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,SAAS,KAAK,SAAS,KAAK,eAAe,WAAY;AACzE,QAAI,YAAY,QAAQ,KAAK,YAAY,UAAa,KAAK,YAAY,QAAS;AAChF,UAAM,KAAK,OAAO;AAAA,MAAQ,CAAC,OAAO,UAChC,MAAM,MAAM,KAAK,CAAC,SAAS,KAAK,WAAW,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC;AAAA,IACnE;AACA,UAAM,UAAU,EAAE,QAAQ,KAAK,IAAI,MAAM,KAAK,MAAM,QAAQ,GAAG;AAC/D,WAAO,IAAI,KAAK,MAAM,CAAC,GAAI,OAAO,IAAI,KAAK,IAAI,KAAK,CAAC,GAAI,OAAO,CAAC;AAAA,EACnE;AACA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -22,5 +22,7 @@ export interface PassageHighlightState {
|
|
|
22
22
|
}
|
|
23
23
|
/** How a passage is lit, or null when it is not. */
|
|
24
24
|
export declare function passageHighlight(chunkId: string, { cited, active }: PassageHighlightState): PassageHighlight | null;
|
|
25
|
+
/** The passage has a box on its page to light (not none, nor the whole page). */
|
|
26
|
+
export declare function passagePlaced(pages: readonly ProcessedPage[], chunkId: string): boolean;
|
|
25
27
|
/** The lit passages' boxes, by page number. Passages without a usable box are left out. */
|
|
26
28
|
export declare function passageBoxesByPage(pages: readonly ProcessedPage[], state: PassageHighlightState): Map<number, PdfPassageBox[]>;
|
|
@@ -15,6 +15,16 @@ function asBox(bbox) {
|
|
|
15
15
|
function isWholePage([x0, y0, x1, y1]) {
|
|
16
16
|
return x0 <= 0 && y0 <= 0 && x1 >= 1 && y1 >= 1;
|
|
17
17
|
}
|
|
18
|
+
function passagePlaced(pages, chunkId) {
|
|
19
|
+
for (const page of pages) {
|
|
20
|
+
const chunk = page.chunks.find((entry) => entry.chunkExtId === chunkId);
|
|
21
|
+
if (chunk) {
|
|
22
|
+
const box = asBox(chunk.pageBbox);
|
|
23
|
+
return !!box && !isWholePage(box);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
18
28
|
function passageBoxesByPage(pages, state) {
|
|
19
29
|
const byPage = /* @__PURE__ */ new Map();
|
|
20
30
|
if (state.cited.size === 0 && !state.active) return byPage;
|
|
@@ -35,6 +45,7 @@ function passageBoxesByPage(pages, state) {
|
|
|
35
45
|
}
|
|
36
46
|
export {
|
|
37
47
|
passageBoxesByPage,
|
|
38
|
-
passageHighlight
|
|
48
|
+
passageHighlight,
|
|
49
|
+
passagePlaced
|
|
39
50
|
};
|
|
40
51
|
//# sourceMappingURL=passageHighlights.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/connection/viewer/pdf/passageHighlights.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Which passages light up on the PDF, and where. The rules are the parsed view's (`ParsedPassage`):\n * while a fact's Citation popover is open its cited passages are light and the one being read is\n * strong; otherwise the passage a citation opened the document at is the one lit. A passage's box\n * is the parser's (`page_bbox`: fractions of its page, top-left origin), so it lands on the page\n * at any zoom.\n */\n\nimport type { ProcessedPage } from '@arbidocs/react/viewers'\nimport type { PassageHighlight } from '../../../files'\n\n/** [x0, y0, x1, y1] as fractions of the page. */\nexport type PageBox = readonly [number, number, number, number]\n\nexport interface PdfPassageBox {\n chunkId: string\n box: PageBox\n highlight: PassageHighlight\n}\n\nexport interface PassageHighlightState {\n /** A fact's cited passages (its Citation popover is open); empty when none is. */\n cited: ReadonlySet<string>\n /** The passage being read: the one a citation opened the document at. */\n active: string | null\n}\n\n/** How a passage is lit, or null when it is not. */\nexport function passageHighlight(\n chunkId: string,\n { cited, active }: PassageHighlightState\n): PassageHighlight | null {\n if (cited.size > 0) {\n if (!cited.has(chunkId)) return null\n return chunkId === active ? 'strong' : 'light'\n }\n return chunkId === active ? 'light' : null\n}\n\n/** A drawable box: four finite fractions with area. */\nfunction asBox(bbox: readonly number[] | undefined): PageBox | null {\n if (!bbox || bbox.length !== 4 || !bbox.every(Number.isFinite)) return null\n const [x0, y0, x1, y1] = bbox\n if (x1 <= x0 || y1 <= y0) return null\n return [x0, y0, x1, y1]\n}\n\n/**\n * The page's whole area: what the parser gives a chunk on a page where it recognized no block to\n * place it by. It says \"this page\", and a rectangle over the whole page would say nothing more.\n */\nfunction isWholePage([x0, y0, x1, y1]: PageBox): boolean {\n return x0 <= 0 && y0 <= 0 && x1 >= 1 && y1 >= 1\n}\n\n/** The lit passages' boxes, by page number. Passages without a usable box are left out. */\nexport function passageBoxesByPage(\n pages: readonly ProcessedPage[],\n state: PassageHighlightState\n): Map<number, PdfPassageBox[]> {\n const byPage = new Map<number, PdfPassageBox[]>()\n if (state.cited.size === 0 && !state.active) return byPage\n for (const page of pages) {\n if (page.pageNumber === null) continue\n for (const chunk of page.chunks) {\n const chunkId = chunk.chunkExtId\n if (!chunkId) continue\n const highlight = passageHighlight(chunkId, state)\n const box = highlight && asBox(chunk.pageBbox)\n if (!highlight || !box || isWholePage(box)) continue\n const boxes = byPage.get(page.pageNumber) ?? []\n boxes.push({ chunkId, box, highlight })\n byPage.set(page.pageNumber, boxes)\n }\n }\n return byPage\n}\n"],
|
|
5
|
-
"mappings": ";AA4BO,SAAS,iBACd,SACA,EAAE,OAAO,OAAO,GACS;AACzB,MAAI,MAAM,OAAO,GAAG;AAClB,QAAI,CAAC,MAAM,IAAI,OAAO,EAAG,QAAO;AAChC,WAAO,YAAY,SAAS,WAAW;AAAA,EACzC;AACA,SAAO,YAAY,SAAS,UAAU;AACxC;AAGA,SAAS,MAAM,MAAqD;AAClE,MAAI,CAAC,QAAQ,KAAK,WAAW,KAAK,CAAC,KAAK,MAAM,OAAO,QAAQ,EAAG,QAAO;AACvE,QAAM,CAAC,IAAI,IAAI,IAAI,EAAE,IAAI;AACzB,MAAI,MAAM,MAAM,MAAM,GAAI,QAAO;AACjC,SAAO,CAAC,IAAI,IAAI,IAAI,EAAE;AACxB;AAMA,SAAS,YAAY,CAAC,IAAI,IAAI,IAAI,EAAE,GAAqB;AACvD,SAAO,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM;AAChD;AAGO,SAAS,mBACd,OACA,OAC8B;AAC9B,QAAM,SAAS,oBAAI,IAA6B;AAChD,MAAI,MAAM,MAAM,SAAS,KAAK,CAAC,MAAM,OAAQ,QAAO;AACpD,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,eAAe,KAAM;AAC9B,eAAW,SAAS,KAAK,QAAQ;AAC/B,YAAM,UAAU,MAAM;AACtB,UAAI,CAAC,QAAS;AACd,YAAM,YAAY,iBAAiB,SAAS,KAAK;AACjD,YAAM,MAAM,aAAa,MAAM,MAAM,QAAQ;AAC7C,UAAI,CAAC,aAAa,CAAC,OAAO,YAAY,GAAG,EAAG;AAC5C,YAAM,QAAQ,OAAO,IAAI,KAAK,UAAU,KAAK,CAAC;AAC9C,YAAM,KAAK,EAAE,SAAS,KAAK,UAAU,CAAC;AACtC,aAAO,IAAI,KAAK,YAAY,KAAK;AAAA,IACnC;AAAA,EACF;AACA,SAAO;AACT;",
|
|
4
|
+
"sourcesContent": ["/**\n * Which passages light up on the PDF, and where. The rules are the parsed view's (`ParsedPassage`):\n * while a fact's Citation popover is open its cited passages are light and the one being read is\n * strong; otherwise the passage a citation opened the document at is the one lit. A passage's box\n * is the parser's (`page_bbox`: fractions of its page, top-left origin), so it lands on the page\n * at any zoom.\n */\n\nimport type { ProcessedPage } from '@arbidocs/react/viewers'\nimport type { PassageHighlight } from '../../../files'\n\n/** [x0, y0, x1, y1] as fractions of the page. */\nexport type PageBox = readonly [number, number, number, number]\n\nexport interface PdfPassageBox {\n chunkId: string\n box: PageBox\n highlight: PassageHighlight\n}\n\nexport interface PassageHighlightState {\n /** A fact's cited passages (its Citation popover is open); empty when none is. */\n cited: ReadonlySet<string>\n /** The passage being read: the one a citation opened the document at. */\n active: string | null\n}\n\n/** How a passage is lit, or null when it is not. */\nexport function passageHighlight(\n chunkId: string,\n { cited, active }: PassageHighlightState\n): PassageHighlight | null {\n if (cited.size > 0) {\n if (!cited.has(chunkId)) return null\n return chunkId === active ? 'strong' : 'light'\n }\n return chunkId === active ? 'light' : null\n}\n\n/** A drawable box: four finite fractions with area. */\nfunction asBox(bbox: readonly number[] | undefined): PageBox | null {\n if (!bbox || bbox.length !== 4 || !bbox.every(Number.isFinite)) return null\n const [x0, y0, x1, y1] = bbox\n if (x1 <= x0 || y1 <= y0) return null\n return [x0, y0, x1, y1]\n}\n\n/**\n * The page's whole area: what the parser gives a chunk on a page where it recognized no block to\n * place it by. It says \"this page\", and a rectangle over the whole page would say nothing more.\n */\nfunction isWholePage([x0, y0, x1, y1]: PageBox): boolean {\n return x0 <= 0 && y0 <= 0 && x1 >= 1 && y1 >= 1\n}\n\n/** The passage has a box on its page to light (not none, nor the whole page). */\nexport function passagePlaced(pages: readonly ProcessedPage[], chunkId: string): boolean {\n for (const page of pages) {\n const chunk = page.chunks.find((entry) => entry.chunkExtId === chunkId)\n if (chunk) {\n const box = asBox(chunk.pageBbox)\n return !!box && !isWholePage(box)\n }\n }\n return false\n}\n\n/** The lit passages' boxes, by page number. Passages without a usable box are left out. */\nexport function passageBoxesByPage(\n pages: readonly ProcessedPage[],\n state: PassageHighlightState\n): Map<number, PdfPassageBox[]> {\n const byPage = new Map<number, PdfPassageBox[]>()\n if (state.cited.size === 0 && !state.active) return byPage\n for (const page of pages) {\n if (page.pageNumber === null) continue\n for (const chunk of page.chunks) {\n const chunkId = chunk.chunkExtId\n if (!chunkId) continue\n const highlight = passageHighlight(chunkId, state)\n const box = highlight && asBox(chunk.pageBbox)\n if (!highlight || !box || isWholePage(box)) continue\n const boxes = byPage.get(page.pageNumber) ?? []\n boxes.push({ chunkId, box, highlight })\n byPage.set(page.pageNumber, boxes)\n }\n }\n return byPage\n}\n"],
|
|
5
|
+
"mappings": ";AA4BO,SAAS,iBACd,SACA,EAAE,OAAO,OAAO,GACS;AACzB,MAAI,MAAM,OAAO,GAAG;AAClB,QAAI,CAAC,MAAM,IAAI,OAAO,EAAG,QAAO;AAChC,WAAO,YAAY,SAAS,WAAW;AAAA,EACzC;AACA,SAAO,YAAY,SAAS,UAAU;AACxC;AAGA,SAAS,MAAM,MAAqD;AAClE,MAAI,CAAC,QAAQ,KAAK,WAAW,KAAK,CAAC,KAAK,MAAM,OAAO,QAAQ,EAAG,QAAO;AACvE,QAAM,CAAC,IAAI,IAAI,IAAI,EAAE,IAAI;AACzB,MAAI,MAAM,MAAM,MAAM,GAAI,QAAO;AACjC,SAAO,CAAC,IAAI,IAAI,IAAI,EAAE;AACxB;AAMA,SAAS,YAAY,CAAC,IAAI,IAAI,IAAI,EAAE,GAAqB;AACvD,SAAO,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM;AAChD;AAGO,SAAS,cAAc,OAAiC,SAA0B;AACvF,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,KAAK,OAAO,KAAK,CAAC,UAAU,MAAM,eAAe,OAAO;AACtE,QAAI,OAAO;AACT,YAAM,MAAM,MAAM,MAAM,QAAQ;AAChC,aAAO,CAAC,CAAC,OAAO,CAAC,YAAY,GAAG;AAAA,IAClC;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,mBACd,OACA,OAC8B;AAC9B,QAAM,SAAS,oBAAI,IAA6B;AAChD,MAAI,MAAM,MAAM,SAAS,KAAK,CAAC,MAAM,OAAQ,QAAO;AACpD,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,eAAe,KAAM;AAC9B,eAAW,SAAS,KAAK,QAAQ;AAC/B,YAAM,UAAU,MAAM;AACtB,UAAI,CAAC,QAAS;AACd,YAAM,YAAY,iBAAiB,SAAS,KAAK;AACjD,YAAM,MAAM,aAAa,MAAM,MAAM,QAAQ;AAC7C,UAAI,CAAC,aAAa,CAAC,OAAO,YAAY,GAAG,EAAG;AAC5C,YAAM,QAAQ,OAAO,IAAI,KAAK,UAAU,KAAK,CAAC;AAC9C,YAAM,KAAK,EAAE,SAAS,KAAK,UAAU,CAAC;AACtC,aAAO,IAAI,KAAK,YAAY,KAAK;AAAA,IACnC;AAAA,EACF;AACA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The open presentation's clips of the viewed document, outlined over its PDF pages
|
|
3
|
+
* (`clipOutlinesByPage`); a label goes to the clip's slide in the presentation.
|
|
4
|
+
*/
|
|
5
|
+
import { type ReactNode } from 'react';
|
|
6
|
+
/** A PDF viewer's page overlay: `(page) => outlines | null`. */
|
|
7
|
+
export declare function usePdfClipOutlines(viewingId: string | null | undefined): (pageNumber: number) => ReactNode;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useMemo } from "react";
|
|
4
|
+
import { useArbiSessionStore } from "@arbidocs/react";
|
|
5
|
+
import { usePresentation } from "../../../presentation/store.js";
|
|
6
|
+
import { showPresentation } from "../../presentation/requests.js";
|
|
7
|
+
import { PdfClipOutlines } from "./PdfClipOutlines.js";
|
|
8
|
+
import { clipOutlinesByPage } from "./clipOutlines.js";
|
|
9
|
+
function usePdfClipOutlines(viewingId) {
|
|
10
|
+
const session = useArbiSessionStore();
|
|
11
|
+
const clips = usePresentation((s) => s.clips);
|
|
12
|
+
const slides = usePresentation((s) => s.slides);
|
|
13
|
+
const byPage = useMemo(
|
|
14
|
+
() => clipOutlinesByPage(viewingId, clips, slides),
|
|
15
|
+
[viewingId, clips, slides]
|
|
16
|
+
);
|
|
17
|
+
const show = useCallback(
|
|
18
|
+
(outline) => {
|
|
19
|
+
const state = usePresentation.getState();
|
|
20
|
+
if (outline.slides.length) state.goTo(outline.slides[0]);
|
|
21
|
+
showPresentation(session, state.boundTo);
|
|
22
|
+
},
|
|
23
|
+
[session]
|
|
24
|
+
);
|
|
25
|
+
return useCallback(
|
|
26
|
+
(pageNumber) => {
|
|
27
|
+
const outlines = byPage.get(pageNumber);
|
|
28
|
+
return outlines ? /* @__PURE__ */ jsx(PdfClipOutlines, { outlines, onShow: show }) : null;
|
|
29
|
+
},
|
|
30
|
+
[byPage, show]
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
usePdfClipOutlines
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=usePdfClipOutlines.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/connection/viewer/pdf/usePdfClipOutlines.tsx"],
|
|
4
|
+
"sourcesContent": ["/**\n * The open presentation's clips of the viewed document, outlined over its PDF pages\n * (`clipOutlinesByPage`); a label goes to the clip's slide in the presentation.\n */\n\nimport { useCallback, useMemo, type ReactNode } from 'react'\nimport { useArbiSessionStore } from '@arbidocs/react'\nimport { usePresentation } from '../../../presentation/store'\nimport { showPresentation } from '../../presentation/requests'\nimport { PdfClipOutlines } from './PdfClipOutlines'\nimport { clipOutlinesByPage, type PdfClipOutline } from './clipOutlines'\n\n/** A PDF viewer's page overlay: `(page) => outlines | null`. */\nexport function usePdfClipOutlines(\n viewingId: string | null | undefined\n): (pageNumber: number) => ReactNode {\n const session = useArbiSessionStore()\n const clips = usePresentation((s) => s.clips)\n const slides = usePresentation((s) => s.slides)\n const byPage = useMemo(\n () => clipOutlinesByPage(viewingId, clips, slides),\n [viewingId, clips, slides]\n )\n const show = useCallback(\n (outline: PdfClipOutline) => {\n const state = usePresentation.getState()\n if (outline.slides.length) state.goTo(outline.slides[0])\n showPresentation(session, state.boundTo)\n },\n [session]\n )\n return useCallback(\n (pageNumber: number) => {\n const outlines = byPage.get(pageNumber)\n return outlines ? <PdfClipOutlines outlines={outlines} onShow={show} /> : null\n },\n [byPage, show]\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAkCwB;AA7BxB,SAAS,aAAa,eAA+B;AACrD,SAAS,2BAA2B;AACpC,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,uBAAuB;AAChC,SAAS,0BAA+C;AAGjD,SAAS,mBACd,WACmC;AACnC,QAAM,UAAU,oBAAoB;AACpC,QAAM,QAAQ,gBAAgB,CAAC,MAAM,EAAE,KAAK;AAC5C,QAAM,SAAS,gBAAgB,CAAC,MAAM,EAAE,MAAM;AAC9C,QAAM,SAAS;AAAA,IACb,MAAM,mBAAmB,WAAW,OAAO,MAAM;AAAA,IACjD,CAAC,WAAW,OAAO,MAAM;AAAA,EAC3B;AACA,QAAM,OAAO;AAAA,IACX,CAAC,YAA4B;AAC3B,YAAM,QAAQ,gBAAgB,SAAS;AACvC,UAAI,QAAQ,OAAO,OAAQ,OAAM,KAAK,QAAQ,OAAO,CAAC,CAAC;AACvD,uBAAiB,SAAS,MAAM,OAAO;AAAA,IACzC;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AACA,SAAO;AAAA,IACL,CAAC,eAAuB;AACtB,YAAM,WAAW,OAAO,IAAI,UAAU;AACtC,aAAO,WAAW,oBAAC,mBAAgB,UAAoB,QAAQ,MAAM,IAAK;AAAA,IAC5E;AAAA,IACA,CAAC,QAAQ,IAAI;AAAA,EACf;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|