@arbidocs/blocks 0.5.120 → 0.5.121
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-B6WG7Q64.cjs +156 -0
- package/dist/ClippingMediaViewer-B6WG7Q64.cjs.map +1 -0
- package/dist/ClippingPdfViewer-6GCVTV3V.cjs +79 -0
- package/dist/ClippingPdfViewer-6GCVTV3V.cjs.map +1 -0
- package/dist/{ConnectedCallDock-WER7R222.cjs → ConnectedCallDock-DC5V5OUJ.cjs} +25 -23
- package/dist/ConnectedCallDock-DC5V5OUJ.cjs.map +1 -0
- package/dist/{ConnectedCallRoom-T7OBQTYP.cjs → ConnectedCallRoom-FQLXWTFB.cjs} +18 -16
- package/dist/{ConnectedCallRoom-T7OBQTYP.cjs.map → ConnectedCallRoom-FQLXWTFB.cjs.map} +1 -1
- package/dist/{ConnectedNativeDocxComparison-SGE4R3M4.cjs → ConnectedNativeDocxComparison-4Q5S7VLD.cjs} +17 -17
- package/dist/{ConnectedNativeDocxComparison-SGE4R3M4.cjs.map → ConnectedNativeDocxComparison-4Q5S7VLD.cjs.map} +1 -1
- package/dist/ConnectedPresentation-4HLDZDWV.cjs +183 -0
- package/dist/ConnectedPresentation-4HLDZDWV.cjs.map +1 -0
- package/dist/{ConnectedProductionSetDialog-LHB4VQWC.cjs → ConnectedProductionSetDialog-4EAY6KAZ.cjs} +3 -3
- package/dist/{ConnectedProductionSetDialog-LHB4VQWC.cjs.map → ConnectedProductionSetDialog-4EAY6KAZ.cjs.map} +1 -1
- package/dist/ConnectedSlidesTab-7QDE2CZD.cjs +234 -0
- package/dist/ConnectedSlidesTab-7QDE2CZD.cjs.map +1 -0
- package/dist/{ConnectedStreamedWordArtifact-6MCLJRIT.cjs → ConnectedStreamedWordArtifact-FFOQJBOI.cjs} +17 -17
- package/dist/{ConnectedStreamedWordArtifact-6MCLJRIT.cjs.map → ConnectedStreamedWordArtifact-FFOQJBOI.cjs.map} +1 -1
- package/dist/{DocumentReviewCard-BPIAJJED.cjs → DocumentReviewCard-SNWVEZ7S.cjs} +17 -16
- package/dist/DocumentReviewCard-SNWVEZ7S.cjs.map +1 -0
- package/dist/{FilesViewerDialog-67WK3TZC.cjs → FilesViewerDialog-IZI63FOV.cjs} +52 -45
- package/dist/FilesViewerDialog-IZI63FOV.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-TEIRB6VJ.cjs} +11 -10
- package/dist/WorkspaceTableView-TEIRB6VJ.cjs.map +1 -0
- package/dist/agents/index.cjs +297 -297
- package/dist/{ai-IM4A23YT.cjs → ai-KBYNRNZ6.cjs} +28 -26
- package/dist/ai-KBYNRNZ6.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 +121 -121
- package/dist/chat/index.cjs +9 -8
- package/dist/{chunk-T23UTIRI.cjs → chunk-2PZT3J34.cjs} +6 -6
- package/dist/{chunk-T23UTIRI.cjs.map → chunk-2PZT3J34.cjs.map} +1 -1
- package/dist/{chunk-SGYSDABK.cjs → chunk-2W5E62EY.cjs} +6 -6
- package/dist/{chunk-SGYSDABK.cjs.map → chunk-2W5E62EY.cjs.map} +1 -1
- package/dist/{chunk-TDO5UWXF.cjs → chunk-3SCNXNHI.cjs} +5 -5
- package/dist/{chunk-TDO5UWXF.cjs.map → chunk-3SCNXNHI.cjs.map} +1 -1
- package/dist/{chunk-KTQOGT2L.cjs → chunk-474GPLCM.cjs} +7 -7
- package/dist/{chunk-KTQOGT2L.cjs.map → chunk-474GPLCM.cjs.map} +1 -1
- package/dist/{chunk-TP5PGWIO.cjs → chunk-4IOYQLKG.cjs} +3 -3
- package/dist/{chunk-TP5PGWIO.cjs.map → chunk-4IOYQLKG.cjs.map} +1 -1
- package/dist/{chunk-NY3YUCOW.cjs → chunk-4NPCDS2X.cjs} +3 -3
- package/dist/{chunk-NY3YUCOW.cjs.map → chunk-4NPCDS2X.cjs.map} +1 -1
- package/dist/{chunk-HA66DHW6.cjs → chunk-4RBQ44RY.cjs} +28 -28
- package/dist/chunk-4RBQ44RY.cjs.map +1 -0
- package/dist/{chunk-BQ5RIWWT.cjs → chunk-4VZZWYEC.cjs} +3 -3
- package/dist/{chunk-BQ5RIWWT.cjs.map → chunk-4VZZWYEC.cjs.map} +1 -1
- package/dist/{chunk-BP3IJ34D.cjs → chunk-52KGMAO5.cjs} +15 -15
- package/dist/{chunk-BP3IJ34D.cjs.map → chunk-52KGMAO5.cjs.map} +1 -1
- package/dist/{chunk-5IZFSAEZ.cjs → chunk-5534FYXP.cjs} +4 -4
- package/dist/{chunk-5IZFSAEZ.cjs.map → chunk-5534FYXP.cjs.map} +1 -1
- package/dist/chunk-5GCCUXMT.cjs +63 -0
- package/dist/chunk-5GCCUXMT.cjs.map +1 -0
- package/dist/chunk-5LTIZ44D.cjs +95 -0
- package/dist/chunk-5LTIZ44D.cjs.map +1 -0
- package/dist/{chunk-DFBDQKGE.cjs → chunk-734NPQNR.cjs} +4 -4
- package/dist/{chunk-DFBDQKGE.cjs.map → chunk-734NPQNR.cjs.map} +1 -1
- package/dist/{chunk-X36UR4A7.cjs → chunk-7VUJGHMY.cjs} +323 -96
- package/dist/chunk-7VUJGHMY.cjs.map +1 -0
- package/dist/chunk-AFQTQC7A.cjs +35 -0
- package/dist/chunk-AFQTQC7A.cjs.map +1 -0
- package/dist/{chunk-YYSBWMDJ.cjs → chunk-AY6EZSZB.cjs} +4 -4
- package/dist/{chunk-YYSBWMDJ.cjs.map → chunk-AY6EZSZB.cjs.map} +1 -1
- package/dist/{chunk-BUVIBHFU.cjs → chunk-BVAT4EVC.cjs} +38 -38
- package/dist/chunk-BVAT4EVC.cjs.map +1 -0
- package/dist/{chunk-ECLUFKYR.cjs → chunk-C2OICDMI.cjs} +10 -10
- package/dist/{chunk-ECLUFKYR.cjs.map → chunk-C2OICDMI.cjs.map} +1 -1
- package/dist/{chunk-CPR6EHG7.cjs → chunk-DR2AH7JL.cjs} +179 -55
- package/dist/chunk-DR2AH7JL.cjs.map +1 -0
- package/dist/{chunk-NJCK6V6U.cjs → chunk-EATIFPKP.cjs} +4 -60
- package/dist/chunk-EATIFPKP.cjs.map +1 -0
- package/dist/{chunk-B3LZITVF.cjs → chunk-EHP6OH5D.cjs} +11 -451
- package/dist/chunk-EHP6OH5D.cjs.map +1 -0
- package/dist/{chunk-775S43W7.cjs → chunk-ENGL7P3I.cjs} +31 -31
- package/dist/{chunk-775S43W7.cjs.map → chunk-ENGL7P3I.cjs.map} +1 -1
- package/dist/{chunk-VZCPMXWC.cjs → chunk-FKFQCK3C.cjs} +4 -4
- package/dist/{chunk-VZCPMXWC.cjs.map → chunk-FKFQCK3C.cjs.map} +1 -1
- package/dist/{chunk-6UTKM5AB.cjs → chunk-GKVE5JGB.cjs} +76 -76
- package/dist/chunk-GKVE5JGB.cjs.map +1 -0
- package/dist/{chunk-Q6CLWOP2.cjs → chunk-GPX5VC57.cjs} +50 -50
- package/dist/{chunk-Q6CLWOP2.cjs.map → chunk-GPX5VC57.cjs.map} +1 -1
- package/dist/{chunk-WAV762GP.cjs → chunk-GQXPNIRE.cjs} +6 -6
- package/dist/{chunk-WAV762GP.cjs.map → chunk-GQXPNIRE.cjs.map} +1 -1
- package/dist/chunk-GX2EBMJW.cjs +133 -0
- package/dist/chunk-GX2EBMJW.cjs.map +1 -0
- package/dist/{chunk-7OPOPYV6.cjs → chunk-H5OLHP2U.cjs} +3 -3
- package/dist/{chunk-7OPOPYV6.cjs.map → chunk-H5OLHP2U.cjs.map} +1 -1
- package/dist/chunk-HYUKPHCT.cjs +66 -0
- package/dist/chunk-HYUKPHCT.cjs.map +1 -0
- package/dist/{chunk-NLSWDP42.cjs → chunk-JQLWKNJO.cjs} +22 -12
- package/dist/chunk-JQLWKNJO.cjs.map +1 -0
- package/dist/{chunk-CKCG4NDB.cjs → chunk-JZEYHDWJ.cjs} +75 -75
- package/dist/{chunk-CKCG4NDB.cjs.map → chunk-JZEYHDWJ.cjs.map} +1 -1
- package/dist/{chunk-PGJYD67F.cjs → chunk-KNGZZC4O.cjs} +7 -6
- package/dist/chunk-KNGZZC4O.cjs.map +1 -0
- package/dist/{chunk-W6INT5GS.cjs → chunk-KP5AITQ4.cjs} +10 -10
- package/dist/{chunk-W6INT5GS.cjs.map → chunk-KP5AITQ4.cjs.map} +1 -1
- package/dist/{chunk-YYPZOEED.cjs → chunk-LHWOKOMP.cjs} +23 -2
- package/dist/chunk-LHWOKOMP.cjs.map +1 -0
- package/dist/{chunk-WXUU3COI.cjs → chunk-MZ2KZXNU.cjs} +3 -3
- package/dist/{chunk-WXUU3COI.cjs.map → chunk-MZ2KZXNU.cjs.map} +1 -1
- package/dist/chunk-NAJN32MF.cjs +154 -0
- package/dist/chunk-NAJN32MF.cjs.map +1 -0
- package/dist/{chunk-557MJ4IH.cjs → chunk-NLOETJCB.cjs} +17 -154
- package/dist/chunk-NLOETJCB.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-Q3XJUXID.cjs +4774 -0
- package/dist/chunk-Q3XJUXID.cjs.map +1 -0
- package/dist/{chunk-325GVZE6.cjs → chunk-Q6DQGGP4.cjs} +3 -3
- package/dist/{chunk-325GVZE6.cjs.map → chunk-Q6DQGGP4.cjs.map} +1 -1
- package/dist/chunk-QFXILJMJ.cjs +458 -0
- package/dist/chunk-QFXILJMJ.cjs.map +1 -0
- package/dist/{chunk-QQWHG3KF.cjs → chunk-RYVZDFVO.cjs} +5 -5
- package/dist/{chunk-QQWHG3KF.cjs.map → chunk-RYVZDFVO.cjs.map} +1 -1
- 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-KXIRVKCJ.cjs → chunk-UUENH4BJ.cjs} +3 -3
- package/dist/{chunk-KXIRVKCJ.cjs.map → chunk-UUENH4BJ.cjs.map} +1 -1
- package/dist/chunk-VJ5ZLRBD.cjs +27 -0
- package/dist/chunk-VJ5ZLRBD.cjs.map +1 -0
- package/dist/{chunk-3GWWOYGS.cjs → chunk-W6B3RYTX.cjs} +4 -4
- package/dist/{chunk-3GWWOYGS.cjs.map → chunk-W6B3RYTX.cjs.map} +1 -1
- package/dist/chunk-WOOOMTTA.cjs +19 -0
- package/dist/chunk-WOOOMTTA.cjs.map +1 -0
- 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-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-XTR26TNE.cjs +1358 -0
- package/dist/chunk-XTR26TNE.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 +20 -20
- package/dist/connect/index.cjs +1150 -1232
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/viewer.cjs +40 -34
- package/dist/connection/ai.d.ts +1 -1
- 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 +220 -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 +44 -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 +16 -0
- package/dist/connection/presentation/requests.js +20 -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 +37 -0
- package/dist/connection/viewer/ViewerPdfPane.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/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/dashboards/index.cjs +1 -1
- 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 +41 -39
- 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 +225 -225
- package/dist/files/navigator/DocumentSearchList.js +1 -1
- package/dist/files/navigator/DocumentSearchList.js.map +2 -2
- package/dist/index.cjs +1666 -1664
- 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/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 +69 -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 +359 -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 +34 -0
- package/dist/presentation/panel-id.js +24 -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 +54 -0
- package/dist/presentation/slide-layout.js +117 -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 +151 -150
- package/dist/shell/PanelPortal.js +1 -0
- package/dist/shell/PanelPortal.js.map +2 -2
- package/dist/shell/index.cjs +112 -110
- package/dist/shell/panel-windows.d.ts +2 -0
- package/dist/shell/panel-windows.js +7 -2
- package/dist/shell/panel-windows.js.map +2 -2
- package/dist/studio/index.cjs +28 -26
- package/dist/studio/index.cjs.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/workflow-canvas/index.cjs +55 -54
- 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-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
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { closePdfRegionImage, renderPdfRegion } from "@arbidocs/react/viewers";
|
|
3
|
+
import { STAGE_ASPECT, clipLabel, frameHeight } from "./model.js";
|
|
4
|
+
import { slideGeometry, textLines } from "./slide-layout.js";
|
|
5
|
+
import {
|
|
6
|
+
FONTS,
|
|
7
|
+
TEXT_COLORS,
|
|
8
|
+
accentColor,
|
|
9
|
+
paintBackground,
|
|
10
|
+
paintClipEdge,
|
|
11
|
+
paintSlideBackground
|
|
12
|
+
} from "./theme.js";
|
|
13
|
+
import { boxWords, drawBox } from "./draw-boxes.js";
|
|
14
|
+
const SLIDE_PIXELS = { width: 1920, height: Math.round(1920 / STAGE_ASPECT) };
|
|
15
|
+
async function videoFrame(source, seconds) {
|
|
16
|
+
const video = document.createElement("video");
|
|
17
|
+
video.muted = true;
|
|
18
|
+
video.playsInline = true;
|
|
19
|
+
video.preload = "auto";
|
|
20
|
+
const url = URL.createObjectURL(source);
|
|
21
|
+
video.src = url;
|
|
22
|
+
const ready = new Promise((resolve) => {
|
|
23
|
+
const timer = setTimeout(() => resolve(false), 8e3);
|
|
24
|
+
video.addEventListener(
|
|
25
|
+
"loadedmetadata",
|
|
26
|
+
() => {
|
|
27
|
+
video.currentTime = Math.min(seconds, Math.max(0, (video.duration || 0) - 0.05));
|
|
28
|
+
},
|
|
29
|
+
{ once: true }
|
|
30
|
+
);
|
|
31
|
+
video.addEventListener(
|
|
32
|
+
"seeked",
|
|
33
|
+
() => {
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
resolve(true);
|
|
36
|
+
},
|
|
37
|
+
{ once: true }
|
|
38
|
+
);
|
|
39
|
+
video.addEventListener("error", () => resolve(false), { once: true });
|
|
40
|
+
});
|
|
41
|
+
const ok = await ready;
|
|
42
|
+
URL.revokeObjectURL(url);
|
|
43
|
+
return ok ? video : null;
|
|
44
|
+
}
|
|
45
|
+
async function drawClip(context, clip, box) {
|
|
46
|
+
const { rect } = clip;
|
|
47
|
+
if (clip.kind === "pdf") {
|
|
48
|
+
const image = await renderPdfRegion({
|
|
49
|
+
file: clip.source,
|
|
50
|
+
page: clip.page,
|
|
51
|
+
rect,
|
|
52
|
+
masks: clip.masks,
|
|
53
|
+
width: Math.ceil(box.width)
|
|
54
|
+
});
|
|
55
|
+
context.drawImage(image, box.x, box.y, box.width, box.height);
|
|
56
|
+
closePdfRegionImage(image);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (clip.kind === "image") {
|
|
60
|
+
const bitmap = await createImageBitmap(clip.source);
|
|
61
|
+
context.drawImage(
|
|
62
|
+
bitmap,
|
|
63
|
+
rect.x * bitmap.width,
|
|
64
|
+
rect.y * bitmap.height,
|
|
65
|
+
rect.w * bitmap.width,
|
|
66
|
+
rect.h * bitmap.height,
|
|
67
|
+
box.x,
|
|
68
|
+
box.y,
|
|
69
|
+
box.width,
|
|
70
|
+
box.height
|
|
71
|
+
);
|
|
72
|
+
bitmap.close();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const video = await videoFrame(clip.source, clip.start ?? 0);
|
|
76
|
+
if (video) {
|
|
77
|
+
const w = video.videoWidth;
|
|
78
|
+
const h = video.videoHeight;
|
|
79
|
+
context.drawImage(
|
|
80
|
+
video,
|
|
81
|
+
rect.x * w,
|
|
82
|
+
rect.y * h,
|
|
83
|
+
rect.w * w,
|
|
84
|
+
rect.h * h,
|
|
85
|
+
box.x,
|
|
86
|
+
box.y,
|
|
87
|
+
box.width,
|
|
88
|
+
box.height
|
|
89
|
+
);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
context.fillStyle = "#262626";
|
|
93
|
+
context.fillRect(box.x, box.y, box.width, box.height);
|
|
94
|
+
context.fillStyle = "#e5e5e5";
|
|
95
|
+
context.font = `${Math.round(box.height * 0.12)}px sans-serif`;
|
|
96
|
+
context.textAlign = "center";
|
|
97
|
+
context.textBaseline = "middle";
|
|
98
|
+
context.fillText(
|
|
99
|
+
`\u25B6 ${clip.caption}`,
|
|
100
|
+
box.x + box.width / 2,
|
|
101
|
+
box.y + box.height / 2,
|
|
102
|
+
box.width * 0.9
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
const CLIP_RADIUS = 6e-3;
|
|
106
|
+
async function drawRoundedClip(context, clip, box, stageWidth, shadow = true) {
|
|
107
|
+
const radius = stageWidth * CLIP_RADIUS;
|
|
108
|
+
const outline = () => {
|
|
109
|
+
context.beginPath();
|
|
110
|
+
context.roundRect(box.x, box.y, box.width, box.height, radius);
|
|
111
|
+
};
|
|
112
|
+
context.save();
|
|
113
|
+
if (shadow) {
|
|
114
|
+
context.shadowColor = "rgba(0,0,0,0.5)";
|
|
115
|
+
context.shadowBlur = stageWidth * 0.016;
|
|
116
|
+
context.shadowOffsetY = stageWidth * 5e-3;
|
|
117
|
+
}
|
|
118
|
+
context.fillStyle = "#fff";
|
|
119
|
+
outline();
|
|
120
|
+
context.fill();
|
|
121
|
+
context.restore();
|
|
122
|
+
context.save();
|
|
123
|
+
outline();
|
|
124
|
+
context.clip();
|
|
125
|
+
await drawClip(context, clip, box);
|
|
126
|
+
if (clip.kind === "pdf") paintClipEdge(context, box, stageWidth);
|
|
127
|
+
context.restore();
|
|
128
|
+
context.save();
|
|
129
|
+
context.strokeStyle = "rgba(255,255,255,0.15)";
|
|
130
|
+
context.lineWidth = Math.max(1, stageWidth / 1280);
|
|
131
|
+
outline();
|
|
132
|
+
context.stroke();
|
|
133
|
+
context.restore();
|
|
134
|
+
}
|
|
135
|
+
function wrapLines(context, text, width, max) {
|
|
136
|
+
const lines = [];
|
|
137
|
+
let line = "";
|
|
138
|
+
for (const word of text.split(/\s+/).filter(Boolean)) {
|
|
139
|
+
const next = line ? `${line} ${word}` : word;
|
|
140
|
+
if (context.measureText(next).width <= width || !line) {
|
|
141
|
+
line = next;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
lines.push(line);
|
|
145
|
+
line = word;
|
|
146
|
+
}
|
|
147
|
+
if (line) lines.push(line);
|
|
148
|
+
if (lines.length <= max) return lines;
|
|
149
|
+
const kept = lines.slice(0, max);
|
|
150
|
+
let last = kept[max - 1];
|
|
151
|
+
while (last && context.measureText(`${last}\u2026`).width > width) last = last.slice(0, -1);
|
|
152
|
+
kept[max - 1] = `${last}\u2026`;
|
|
153
|
+
return kept;
|
|
154
|
+
}
|
|
155
|
+
function drawText(context, box, lines, theme, width, height) {
|
|
156
|
+
const size = box.size * width;
|
|
157
|
+
const lineHeight = size * box.leading;
|
|
158
|
+
context.font = `${box.weight} ${size}px ${FONTS[theme.font].css}`;
|
|
159
|
+
context.textBaseline = "top";
|
|
160
|
+
const indent = box.bullets ? size * 1.22 : 0;
|
|
161
|
+
const left = box.x * width;
|
|
162
|
+
const boxWidth = box.w * width - indent;
|
|
163
|
+
const gap = box.bullets ? size * 0.3 : 0;
|
|
164
|
+
const room = box.h * height;
|
|
165
|
+
const paragraphs = [];
|
|
166
|
+
let used = 0;
|
|
167
|
+
for (const paragraph of lines) {
|
|
168
|
+
const fits = Math.floor((room - used + 1e-6) / lineHeight);
|
|
169
|
+
if (fits < 1) break;
|
|
170
|
+
const wrapped = wrapLines(context, paragraph, boxWidth, fits);
|
|
171
|
+
paragraphs.push(wrapped);
|
|
172
|
+
used += wrapped.length * lineHeight + gap;
|
|
173
|
+
}
|
|
174
|
+
used = Math.max(0, used - gap);
|
|
175
|
+
const offset = box.anchor === "bottom" ? room - used : box.anchor === "middle" ? (room - used) / 2 : 0;
|
|
176
|
+
let y = box.y * height + offset + (lineHeight - size) / 2;
|
|
177
|
+
for (const wrapped of paragraphs) {
|
|
178
|
+
if (box.bullets) {
|
|
179
|
+
const square = size * 0.42;
|
|
180
|
+
context.fillStyle = accentColor(theme.accent);
|
|
181
|
+
context.fillRect(left, y + size * 0.5 - square / 2, square, square);
|
|
182
|
+
}
|
|
183
|
+
context.fillStyle = TEXT_COLORS[box.tone];
|
|
184
|
+
context.textAlign = box.align === "center" ? "center" : "left";
|
|
185
|
+
for (const line of wrapped) {
|
|
186
|
+
const x = box.align === "center" ? left + box.w * width / 2 : left + indent;
|
|
187
|
+
context.fillText(line, x, y);
|
|
188
|
+
y += lineHeight;
|
|
189
|
+
}
|
|
190
|
+
y += gap;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function drawLogo(context, logo, box, width, height) {
|
|
194
|
+
const image = await loadImage(logo);
|
|
195
|
+
const scale = Math.min(box.w * width / image.width, box.h * height / image.height);
|
|
196
|
+
const w = image.width * scale;
|
|
197
|
+
const h = image.height * scale;
|
|
198
|
+
const x = box.x < 0.5 ? box.x * width : (box.x + box.w) * width - w;
|
|
199
|
+
context.globalAlpha = 0.9;
|
|
200
|
+
context.drawImage(image, x, (box.y + box.h) * height - h, w, h);
|
|
201
|
+
context.globalAlpha = 1;
|
|
202
|
+
}
|
|
203
|
+
async function loadImage(file) {
|
|
204
|
+
const url = URL.createObjectURL(file);
|
|
205
|
+
try {
|
|
206
|
+
const image = new Image();
|
|
207
|
+
image.src = url;
|
|
208
|
+
await image.decode();
|
|
209
|
+
if (!image.naturalWidth) {
|
|
210
|
+
image.width = 600;
|
|
211
|
+
image.height = 200;
|
|
212
|
+
}
|
|
213
|
+
return image;
|
|
214
|
+
} finally {
|
|
215
|
+
URL.revokeObjectURL(url);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function slideWords(slide) {
|
|
219
|
+
const own = slide.layout === "free" ? [] : [slide.caption.trim(), ...textLines(slide.points)];
|
|
220
|
+
return [...own, ...slide.boxes.flatMap(boxWords)].filter(Boolean);
|
|
221
|
+
}
|
|
222
|
+
function drawSlideText(context, slide, look, width, height) {
|
|
223
|
+
const geometry = slideGeometry(slide, {
|
|
224
|
+
labels: look.labels,
|
|
225
|
+
logo: look.theme.logo?.corner ?? null
|
|
226
|
+
});
|
|
227
|
+
const title = slide.caption.trim();
|
|
228
|
+
if (title && geometry.title) {
|
|
229
|
+
drawText(context, geometry.title, [title], look.theme, width, height);
|
|
230
|
+
if (geometry.rule) {
|
|
231
|
+
context.fillStyle = accentColor(look.theme.accent);
|
|
232
|
+
const r = geometry.rule;
|
|
233
|
+
context.fillRect(r.x * width, r.y * height, r.w * width, r.h * height);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (geometry.text) {
|
|
237
|
+
drawText(context, geometry.text, textLines(slide.points), look.theme, width, height);
|
|
238
|
+
}
|
|
239
|
+
return geometry;
|
|
240
|
+
}
|
|
241
|
+
async function renderSlide(slide, clips, look) {
|
|
242
|
+
const { width, height } = SLIDE_PIXELS;
|
|
243
|
+
const canvas = document.createElement("canvas");
|
|
244
|
+
canvas.width = width;
|
|
245
|
+
canvas.height = height;
|
|
246
|
+
const context = canvas.getContext("2d");
|
|
247
|
+
if (!context) throw new Error("Could not draw the slides");
|
|
248
|
+
if (slide.background) {
|
|
249
|
+
await paintSlideBackground(context, width, height, slide.background, look.media);
|
|
250
|
+
} else {
|
|
251
|
+
paintBackground(context, width, height, look.theme.background);
|
|
252
|
+
}
|
|
253
|
+
const geometry = drawSlideText(context, slide, look, width, height);
|
|
254
|
+
const text = slideWords(slide);
|
|
255
|
+
const font = FONTS[look.theme.font].css;
|
|
256
|
+
const drawBoxes = (front) => {
|
|
257
|
+
for (const box of slide.boxes) {
|
|
258
|
+
if (!!box.front === front) drawBox(context, box, width, height, font);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
drawBoxes(false);
|
|
262
|
+
for (const item of slide.items) {
|
|
263
|
+
const clip = clips.find((entry) => entry.id === item.clipId);
|
|
264
|
+
if (!clip) continue;
|
|
265
|
+
const box = {
|
|
266
|
+
x: item.frame.x * width,
|
|
267
|
+
y: item.frame.y * height,
|
|
268
|
+
width: item.frame.w * width,
|
|
269
|
+
height: frameHeight(item.frame, clip.aspect) * height
|
|
270
|
+
};
|
|
271
|
+
if (clip.plain) {
|
|
272
|
+
await drawClip(context, clip, box);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
await drawRoundedClip(context, clip, box, width);
|
|
276
|
+
const label = clipLabel(clip, look.columnValues);
|
|
277
|
+
if (look.labels && label) {
|
|
278
|
+
context.fillStyle = TEXT_COLORS.muted;
|
|
279
|
+
context.font = `${Math.round(width * 0.017)}px ${font}`;
|
|
280
|
+
context.textAlign = "left";
|
|
281
|
+
context.textBaseline = "top";
|
|
282
|
+
context.fillText(label, box.x, box.y + box.height + width * 5e-3, box.width);
|
|
283
|
+
text.push(label);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
drawBoxes(true);
|
|
287
|
+
if (geometry.logo && look.logo) await drawLogo(context, look.logo, geometry.logo, width, height);
|
|
288
|
+
const image = await new Promise(
|
|
289
|
+
(resolve, reject) => canvas.toBlob(
|
|
290
|
+
(blob) => blob ? resolve(blob) : reject(new Error("Could not draw the slides")),
|
|
291
|
+
"image/jpeg",
|
|
292
|
+
0.92
|
|
293
|
+
)
|
|
294
|
+
);
|
|
295
|
+
canvas.width = canvas.height = 0;
|
|
296
|
+
return { image, text };
|
|
297
|
+
}
|
|
298
|
+
export {
|
|
299
|
+
CLIP_RADIUS,
|
|
300
|
+
SLIDE_PIXELS,
|
|
301
|
+
drawClip,
|
|
302
|
+
drawRoundedClip,
|
|
303
|
+
drawSlideText,
|
|
304
|
+
loadImage,
|
|
305
|
+
renderSlide,
|
|
306
|
+
slideWords,
|
|
307
|
+
wrapLines
|
|
308
|
+
};
|
|
309
|
+
//# sourceMappingURL=render-slides.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/presentation/render-slides.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * A presentation's slides as images, exactly as they are presented: the theme's background, the\n * slide's title, rule and points by its layout, each clip in its frame (a PDF clip drawn from its\n * PDF with its redactions burnt in, an image from its file, a video as its frame at the clip's\n * start), the source labels under them and the logo. For the saved PDF (`imagesToPdf`), where they\n * are the pages; the PowerPoint export draws its parts with the same pieces.\n */\n\nimport { closePdfRegionImage, renderPdfRegion } from '@arbidocs/react/viewers'\nimport { STAGE_ASPECT, clipLabel, frameHeight, type Clip, type Slide } from './model'\nimport { slideGeometry, textLines, type Box, type TextBox } from './slide-layout'\nimport {\n FONTS,\n TEXT_COLORS,\n accentColor,\n paintBackground,\n paintClipEdge,\n paintSlideBackground,\n type Theme,\n} from './theme'\nimport { boxWords, drawBox } from './draw-boxes'\n\n/** The rendered slide's size in pixels (a 1080p screen). */\nexport const SLIDE_PIXELS = { width: 1920, height: Math.round(1920 / STAGE_ASPECT) }\n\nexport interface RenderedSlide {\n /** A JPEG of the slide. */\n image: Blob\n /** The slide's words (title, labels), for a searchable PDF. */\n text: string[]\n}\n\n/** A video's frame at `seconds`; null when it cannot be read in time. */\nasync function videoFrame(source: Blob, seconds: number): Promise<HTMLVideoElement | null> {\n const video = document.createElement('video')\n video.muted = true\n video.playsInline = true\n video.preload = 'auto'\n const url = URL.createObjectURL(source)\n video.src = url\n const ready = new Promise<boolean>((resolve) => {\n const timer = setTimeout(() => resolve(false), 8000)\n video.addEventListener(\n 'loadedmetadata',\n () => {\n video.currentTime = Math.min(seconds, Math.max(0, (video.duration || 0) - 0.05))\n },\n { once: true }\n )\n video.addEventListener(\n 'seeked',\n () => {\n clearTimeout(timer)\n resolve(true)\n },\n { once: true }\n )\n video.addEventListener('error', () => resolve(false), { once: true })\n })\n const ok = await ready\n URL.revokeObjectURL(url)\n return ok ? video : null\n}\n\nexport async function drawClip(\n context: CanvasRenderingContext2D,\n clip: Clip,\n box: { x: number; y: number; width: number; height: number }\n) {\n const { rect } = clip\n if (clip.kind === 'pdf') {\n const image = await renderPdfRegion({\n file: clip.source,\n page: clip.page,\n rect,\n masks: clip.masks,\n width: Math.ceil(box.width),\n })\n context.drawImage(image, box.x, box.y, box.width, box.height)\n closePdfRegionImage(image)\n return\n }\n if (clip.kind === 'image') {\n const bitmap = await createImageBitmap(clip.source)\n context.drawImage(\n bitmap,\n rect.x * bitmap.width,\n rect.y * bitmap.height,\n rect.w * bitmap.width,\n rect.h * bitmap.height,\n box.x,\n box.y,\n box.width,\n box.height\n )\n bitmap.close()\n return\n }\n const video = await videoFrame(clip.source, clip.start ?? 0)\n if (video) {\n const w = video.videoWidth\n const h = video.videoHeight\n context.drawImage(\n video,\n rect.x * w,\n rect.y * h,\n rect.w * w,\n rect.h * h,\n box.x,\n box.y,\n box.width,\n box.height\n )\n return\n }\n // The frame could not be read: a placeholder that still says what plays there.\n context.fillStyle = '#262626'\n context.fillRect(box.x, box.y, box.width, box.height)\n context.fillStyle = '#e5e5e5'\n context.font = `${Math.round(box.height * 0.12)}px sans-serif`\n context.textAlign = 'center'\n context.textBaseline = 'middle'\n context.fillText(\n `\u25B6 ${clip.caption}`,\n box.x + box.width / 2,\n box.y + box.height / 2,\n box.width * 0.9\n )\n}\n\n/** A clip's corner radius, a fraction of the stage width (as the stage's 0.6cqw). */\nexport const CLIP_RADIUS = 0.006\n\n/**\n * A clip as the stage shows it: lifted off the background by a soft shadow, its corners softly\n * rounded, a faint light edge around it.\n */\nexport async function drawRoundedClip(\n context: CanvasRenderingContext2D,\n clip: Clip,\n box: { x: number; y: number; width: number; height: number },\n stageWidth: number,\n shadow = true\n) {\n const radius = stageWidth * CLIP_RADIUS\n const outline = () => {\n context.beginPath()\n context.roundRect(box.x, box.y, box.width, box.height, radius)\n }\n context.save()\n if (shadow) {\n context.shadowColor = 'rgba(0,0,0,0.5)'\n context.shadowBlur = stageWidth * 0.016\n context.shadowOffsetY = stageWidth * 0.005\n }\n context.fillStyle = '#fff'\n outline()\n context.fill()\n context.restore()\n context.save()\n outline()\n context.clip()\n await drawClip(context, clip, box)\n if (clip.kind === 'pdf') paintClipEdge(context, box, stageWidth)\n context.restore()\n context.save()\n context.strokeStyle = 'rgba(255,255,255,0.15)'\n context.lineWidth = Math.max(1, stageWidth / 1280)\n outline()\n context.stroke()\n context.restore()\n}\n\n/** What a slide is drawn with besides itself and its clips. */\nexport interface SlideLook {\n labels: boolean\n /** Each document's value in the label column (see `clipLabel`). */\n columnValues: Readonly<Record<string, string>>\n theme: Theme\n /** The theme's logo image, if it has one. */\n logo: Blob | null\n /** Images embedded in the presentation (slide backgrounds, imported pictures), by key. */\n media: Readonly<Record<string, string>>\n}\n\n/** The lines of `text` that fit `width` (words wrapped), at most `max`, the last one cut short. */\nexport function wrapLines(\n context: CanvasRenderingContext2D,\n text: string,\n width: number,\n max: number\n): string[] {\n const lines: string[] = []\n let line = ''\n for (const word of text.split(/\\s+/).filter(Boolean)) {\n const next = line ? `${line} ${word}` : word\n if (context.measureText(next).width <= width || !line) {\n line = next\n continue\n }\n lines.push(line)\n line = word\n }\n if (line) lines.push(line)\n if (lines.length <= max) return lines\n const kept = lines.slice(0, max)\n let last = kept[max - 1]\n while (last && context.measureText(`${last}\u2026`).width > width) last = last.slice(0, -1)\n kept[max - 1] = `${last}\u2026`\n return kept\n}\n\n/** A text box drawn on a `width \u00D7 height` canvas (as the stage draws it). */\nfunction drawText(\n context: CanvasRenderingContext2D,\n box: TextBox,\n lines: readonly string[],\n theme: Theme,\n width: number,\n height: number\n) {\n const size = box.size * width\n const lineHeight = size * box.leading\n context.font = `${box.weight} ${size}px ${FONTS[theme.font].css}`\n context.textBaseline = 'top'\n const indent = box.bullets ? size * 1.22 : 0\n const left = box.x * width\n const boxWidth = box.w * width - indent\n const gap = box.bullets ? size * 0.3 : 0\n const room = box.h * height\n // The lines that fit, then placed in the box by its anchor (as the stage places them).\n const paragraphs: string[][] = []\n let used = 0\n for (const paragraph of lines) {\n const fits = Math.floor((room - used + 1e-6) / lineHeight)\n if (fits < 1) break\n const wrapped = wrapLines(context, paragraph, boxWidth, fits)\n paragraphs.push(wrapped)\n used += wrapped.length * lineHeight + gap\n }\n used = Math.max(0, used - gap)\n const offset =\n box.anchor === 'bottom' ? room - used : box.anchor === 'middle' ? (room - used) / 2 : 0\n let y = box.y * height + offset + (lineHeight - size) / 2\n for (const wrapped of paragraphs) {\n if (box.bullets) {\n const square = size * 0.42\n context.fillStyle = accentColor(theme.accent)\n context.fillRect(left, y + size * 0.5 - square / 2, square, square)\n }\n context.fillStyle = TEXT_COLORS[box.tone]\n context.textAlign = box.align === 'center' ? 'center' : 'left'\n for (const line of wrapped) {\n const x = box.align === 'center' ? left + (box.w * width) / 2 : left + indent\n context.fillText(line, x, y)\n y += lineHeight\n }\n y += gap\n }\n}\n\n/** The logo, contained in its box and set to the box's outer side. */\nasync function drawLogo(\n context: CanvasRenderingContext2D,\n logo: Blob,\n box: Box,\n width: number,\n height: number\n) {\n const image = await loadImage(logo)\n const scale = Math.min((box.w * width) / image.width, (box.h * height) / image.height)\n const w = image.width * scale\n const h = image.height * scale\n const x = box.x < 0.5 ? box.x * width : (box.x + box.w) * width - w\n context.globalAlpha = 0.9\n context.drawImage(image, x, (box.y + box.h) * height - h, w, h)\n context.globalAlpha = 1\n}\n\n/** An image file, decoded (any format the browser shows, SVG included). */\nexport async function loadImage(file: Blob): Promise<HTMLImageElement> {\n const url = URL.createObjectURL(file)\n try {\n const image = new Image()\n image.src = url\n await image.decode()\n // An SVG without a size of its own is drawn at a usable one.\n if (!image.naturalWidth) {\n image.width = 600\n image.height = 200\n }\n return image\n } finally {\n URL.revokeObjectURL(url)\n }\n}\n\n/** A slide's title, rule and text, as `drawSlideText` draws them: for the searchable PDF's text. */\nexport function slideWords(slide: Slide): string[] {\n // A blank slide's title is in its boxes.\n const own = slide.layout === 'free' ? [] : [slide.caption.trim(), ...textLines(slide.points)]\n return [...own, ...slide.boxes.flatMap(boxWords)].filter(Boolean)\n}\n\n/** The slide's own text (title, rule, points or subtitle) on a `width \u00D7 height` canvas. */\nexport function drawSlideText(\n context: CanvasRenderingContext2D,\n slide: Slide,\n look: SlideLook,\n width: number,\n height: number\n) {\n const geometry = slideGeometry(slide, {\n labels: look.labels,\n logo: look.theme.logo?.corner ?? null,\n })\n const title = slide.caption.trim()\n if (title && geometry.title) {\n drawText(context, geometry.title, [title], look.theme, width, height)\n if (geometry.rule) {\n context.fillStyle = accentColor(look.theme.accent)\n const r = geometry.rule\n context.fillRect(r.x * width, r.y * height, r.w * width, r.h * height)\n }\n }\n if (geometry.text) {\n drawText(context, geometry.text, textLines(slide.points), look.theme, width, height)\n }\n return geometry\n}\n\nexport async function renderSlide(\n slide: Slide,\n clips: readonly Clip[],\n look: SlideLook\n): Promise<RenderedSlide> {\n const { width, height } = SLIDE_PIXELS\n const canvas = document.createElement('canvas')\n canvas.width = width\n canvas.height = height\n const context = canvas.getContext('2d')\n if (!context) throw new Error('Could not draw the slides')\n if (slide.background) {\n await paintSlideBackground(context, width, height, slide.background, look.media)\n } else {\n paintBackground(context, width, height, look.theme.background)\n }\n const geometry = drawSlideText(context, slide, look, width, height)\n const text: string[] = slideWords(slide)\n const font = FONTS[look.theme.font].css\n const drawBoxes = (front: boolean) => {\n for (const box of slide.boxes) {\n if (!!box.front === front) drawBox(context, box, width, height, font)\n }\n }\n drawBoxes(false)\n\n for (const item of slide.items) {\n const clip = clips.find((entry) => entry.id === item.clipId)\n if (!clip) continue\n const box = {\n x: item.frame.x * width,\n y: item.frame.y * height,\n width: item.frame.w * width,\n height: frameHeight(item.frame, clip.aspect) * height,\n }\n if (clip.plain) {\n await drawClip(context, clip, box)\n continue\n }\n await drawRoundedClip(context, clip, box, width)\n const label = clipLabel(clip, look.columnValues)\n if (look.labels && label) {\n context.fillStyle = TEXT_COLORS.muted\n context.font = `${Math.round(width * 0.017)}px ${font}`\n context.textAlign = 'left'\n context.textBaseline = 'top'\n context.fillText(label, box.x, box.y + box.height + width * 0.005, box.width)\n text.push(label)\n }\n }\n\n drawBoxes(true)\n if (geometry.logo && look.logo) await drawLogo(context, look.logo, geometry.logo, width, height)\n\n const image = await new Promise<Blob>((resolve, reject) =>\n canvas.toBlob(\n (blob) => (blob ? resolve(blob) : reject(new Error('Could not draw the slides'))),\n 'image/jpeg',\n 0.92\n )\n )\n canvas.width = canvas.height = 0\n return { image, text }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slide boxes and backgrounds read from saved data (written by the app, an agent or an import):
|
|
3
|
+
* every field checked, anything malformed dropped rather than trusted.
|
|
4
|
+
*/
|
|
5
|
+
import type { SlideBackground, SlideBox } from './boxes';
|
|
6
|
+
/** A box as saved: no session id. */
|
|
7
|
+
export type SavedBox = Omit<SlideBox, 'id'>;
|
|
8
|
+
export declare function parseBox(value: unknown): SavedBox | null;
|
|
9
|
+
export declare function parseBoxes(value: unknown): SavedBox[];
|
|
10
|
+
/** A slide's background, if valid (an image only if `media` has it). */
|
|
11
|
+
export declare function parseBackground(value: unknown, media: Readonly<Record<string, string>>): SlideBackground | undefined;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
const HEX = /^#[0-9a-f]{6}$/i;
|
|
3
|
+
const SHAPES = ["text", "rect", "roundRect", "ellipse", "line"];
|
|
4
|
+
const ALIGNS = ["left", "center", "right", "justify"];
|
|
5
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v);
|
|
6
|
+
const color = (v) => typeof v === "string" && HEX.test(v) ? v.toLowerCase() : void 0;
|
|
7
|
+
function parseRun(value) {
|
|
8
|
+
const run = value;
|
|
9
|
+
if (!run || typeof run.text !== "string") return null;
|
|
10
|
+
return {
|
|
11
|
+
text: run.text,
|
|
12
|
+
...num(run.size) && run.size > 0 && run.size < 400 ? { size: run.size } : {},
|
|
13
|
+
...run.bold === true ? { bold: true } : {},
|
|
14
|
+
...run.italic === true ? { italic: true } : {},
|
|
15
|
+
...run.underline === true ? { underline: true } : {},
|
|
16
|
+
...color(run.color) ? { color: color(run.color) } : {},
|
|
17
|
+
...typeof run.font === "string" && run.font ? { font: run.font.slice(0, 64) } : {}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function parseParagraph(value) {
|
|
21
|
+
const paragraph = value;
|
|
22
|
+
if (!paragraph || !Array.isArray(paragraph.runs)) return null;
|
|
23
|
+
return {
|
|
24
|
+
runs: paragraph.runs.map(parseRun).filter((run) => run !== null),
|
|
25
|
+
...ALIGNS.includes(paragraph.align) ? { align: paragraph.align } : {},
|
|
26
|
+
...num(paragraph.level) && paragraph.level > 0 ? { level: Math.min(8, Math.round(paragraph.level)) } : {},
|
|
27
|
+
...paragraph.bullet === true ? { bullet: true } : {}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function parseBox(value) {
|
|
31
|
+
const box = value;
|
|
32
|
+
const frame = box?.frame;
|
|
33
|
+
if (!box || !frame || !num(frame.x) || !num(frame.y) || !num(frame.w) || !num(frame.h) || frame.w < 0 || frame.h < 0 || !SHAPES.includes(box.shape)) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const line = box.line;
|
|
37
|
+
return {
|
|
38
|
+
frame: { x: frame.x, y: frame.y, w: frame.w, h: frame.h },
|
|
39
|
+
shape: box.shape,
|
|
40
|
+
paragraphs: Array.isArray(box.paragraphs) ? box.paragraphs.map(parseParagraph).filter((p) => p !== null) : [],
|
|
41
|
+
...num(box.rotation) && box.rotation % 360 !== 0 ? { rotation: box.rotation % 360 } : {},
|
|
42
|
+
...box.flipH === true ? { flipH: true } : {},
|
|
43
|
+
...box.flipV === true ? { flipV: true } : {},
|
|
44
|
+
...color(box.fill) ? { fill: color(box.fill) } : {},
|
|
45
|
+
...num(box.fillAlpha) && box.fillAlpha >= 0 && box.fillAlpha < 1 ? { fillAlpha: box.fillAlpha } : {},
|
|
46
|
+
...line && color(line.color) && num(line.width) && line.width >= 0 ? {
|
|
47
|
+
line: {
|
|
48
|
+
color: color(line.color),
|
|
49
|
+
width: line.width,
|
|
50
|
+
...line.dash === true ? { dash: true } : {}
|
|
51
|
+
}
|
|
52
|
+
} : {},
|
|
53
|
+
...box.anchor === "middle" || box.anchor === "bottom" || box.anchor === "top" ? { anchor: box.anchor } : {},
|
|
54
|
+
...num(box.inset) && box.inset >= 0 ? { inset: box.inset } : {},
|
|
55
|
+
...box.role === "title" || box.role === "body" ? { role: box.role } : {},
|
|
56
|
+
...box.front === true ? { front: true } : {}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function parseBoxes(value) {
|
|
60
|
+
return Array.isArray(value) ? value.map(parseBox).filter((b) => b !== null) : [];
|
|
61
|
+
}
|
|
62
|
+
function parseBackground(value, media) {
|
|
63
|
+
const background = value;
|
|
64
|
+
if (!background || typeof background !== "object") return void 0;
|
|
65
|
+
const gradient = background.gradient;
|
|
66
|
+
const parsed = {
|
|
67
|
+
...color(background.color) ? { color: color(background.color) } : {},
|
|
68
|
+
...gradient && color(gradient.from) && color(gradient.to) && num(gradient.angle) ? {
|
|
69
|
+
gradient: { from: color(gradient.from), to: color(gradient.to), angle: gradient.angle }
|
|
70
|
+
} : {},
|
|
71
|
+
...typeof background.image === "string" && media[background.image] ? { image: background.image } : {}
|
|
72
|
+
};
|
|
73
|
+
return Object.keys(parsed).length ? parsed : void 0;
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
parseBackground,
|
|
77
|
+
parseBox,
|
|
78
|
+
parseBoxes
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=saved-boxes.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/presentation/saved-boxes.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Slide boxes and backgrounds read from saved data (written by the app, an agent or an import):\n * every field checked, anything malformed dropped rather than trusted.\n */\n\nimport type { BoxShape, Paragraph, SlideBackground, SlideBox, TextAlign, TextRun } from './boxes'\n\n/** A box as saved: no session id. */\nexport type SavedBox = Omit<SlideBox, 'id'>\n\nconst HEX = /^#[0-9a-f]{6}$/i\nconst SHAPES: readonly BoxShape[] = ['text', 'rect', 'roundRect', 'ellipse', 'line']\nconst ALIGNS: readonly TextAlign[] = ['left', 'center', 'right', 'justify']\n\nconst num = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v)\nconst color = (v: unknown): string | undefined =>\n typeof v === 'string' && HEX.test(v) ? v.toLowerCase() : undefined\n\nfunction parseRun(value: unknown): TextRun | null {\n const run = value as Partial<TextRun> | null\n if (!run || typeof run.text !== 'string') return null\n return {\n text: run.text,\n ...(num(run.size) && run.size > 0 && run.size < 400 ? { size: run.size } : {}),\n ...(run.bold === true ? { bold: true } : {}),\n ...(run.italic === true ? { italic: true } : {}),\n ...(run.underline === true ? { underline: true } : {}),\n ...(color(run.color) ? { color: color(run.color) } : {}),\n ...(typeof run.font === 'string' && run.font ? { font: run.font.slice(0, 64) } : {}),\n }\n}\n\nfunction parseParagraph(value: unknown): Paragraph | null {\n const paragraph = value as Partial<Paragraph> | null\n if (!paragraph || !Array.isArray(paragraph.runs)) return null\n return {\n runs: paragraph.runs.map(parseRun).filter((run): run is TextRun => run !== null),\n ...(ALIGNS.includes(paragraph.align as TextAlign) ? { align: paragraph.align } : {}),\n ...(num(paragraph.level) && paragraph.level > 0\n ? { level: Math.min(8, Math.round(paragraph.level)) }\n : {}),\n ...(paragraph.bullet === true ? { bullet: true } : {}),\n }\n}\n\nexport function parseBox(value: unknown): SavedBox | null {\n const box = value as Partial<SavedBox> | null\n const frame = box?.frame\n if (\n !box ||\n !frame ||\n !num(frame.x) ||\n !num(frame.y) ||\n !num(frame.w) ||\n !num(frame.h) ||\n frame.w < 0 ||\n frame.h < 0 ||\n !SHAPES.includes(box.shape as BoxShape)\n ) {\n return null\n }\n const line = box.line\n return {\n frame: { x: frame.x, y: frame.y, w: frame.w, h: frame.h },\n shape: box.shape as BoxShape,\n paragraphs: Array.isArray(box.paragraphs)\n ? box.paragraphs.map(parseParagraph).filter((p): p is Paragraph => p !== null)\n : [],\n ...(num(box.rotation) && box.rotation % 360 !== 0 ? { rotation: box.rotation % 360 } : {}),\n ...(box.flipH === true ? { flipH: true } : {}),\n ...(box.flipV === true ? { flipV: true } : {}),\n ...(color(box.fill) ? { fill: color(box.fill) } : {}),\n ...(num(box.fillAlpha) && box.fillAlpha >= 0 && box.fillAlpha < 1\n ? { fillAlpha: box.fillAlpha }\n : {}),\n ...(line && color(line.color) && num(line.width) && line.width >= 0\n ? {\n line: {\n color: color(line.color)!,\n width: line.width,\n ...(line.dash === true ? { dash: true } : {}),\n },\n }\n : {}),\n ...(box.anchor === 'middle' || box.anchor === 'bottom' || box.anchor === 'top'\n ? { anchor: box.anchor }\n : {}),\n ...(num(box.inset) && box.inset >= 0 ? { inset: box.inset } : {}),\n ...(box.role === 'title' || box.role === 'body' ? { role: box.role } : {}),\n ...(box.front === true ? { front: true } : {}),\n }\n}\n\nexport function parseBoxes(value: unknown): SavedBox[] {\n return Array.isArray(value) ? value.map(parseBox).filter((b): b is SavedBox => b !== null) : []\n}\n\n/** A slide's background, if valid (an image only if `media` has it). */\nexport function parseBackground(\n value: unknown,\n media: Readonly<Record<string, string>>\n): SlideBackground | undefined {\n const background = value as Partial<SlideBackground> | null\n if (!background || typeof background !== 'object') return undefined\n const gradient = background.gradient\n const parsed: SlideBackground = {\n ...(color(background.color) ? { color: color(background.color) } : {}),\n ...(gradient && color(gradient.from) && color(gradient.to) && num(gradient.angle)\n ? {\n gradient: { from: color(gradient.from)!, to: color(gradient.to)!, angle: gradient.angle },\n }\n : {}),\n ...(typeof background.image === 'string' && media[background.image]\n ? { image: background.image }\n : {}),\n }\n return Object.keys(parsed).length ? parsed : undefined\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a slide's parts go, by its layout: its title, the accent rule, its text (points, or a
|
|
3
|
+
* subtitle), the logo in the footer, and the room its clips are arranged in. One description,
|
|
4
|
+
* drawn by the stage, the saved PDF and the PowerPoint export alike.
|
|
5
|
+
*
|
|
6
|
+
* All positions are fractions of the stage (x, w of its width; y, h of its height); type sizes
|
|
7
|
+
* are fractions of the stage width.
|
|
8
|
+
*/
|
|
9
|
+
import { type Reserve, type Slide, type SlideLayout } from './model';
|
|
10
|
+
import type { LogoCorner } from './theme';
|
|
11
|
+
export interface TextBox {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
w: number;
|
|
15
|
+
h: number;
|
|
16
|
+
/** Type size, a fraction of the stage width. */
|
|
17
|
+
size: number;
|
|
18
|
+
weight: 400 | 600 | 700;
|
|
19
|
+
align: 'left' | 'center';
|
|
20
|
+
tone: 'title' | 'body' | 'muted';
|
|
21
|
+
/** Lines start with a bullet in the accent colour. */
|
|
22
|
+
bullets?: boolean;
|
|
23
|
+
/** Line height, a multiple of the type size. */
|
|
24
|
+
leading: number;
|
|
25
|
+
/** Where the text sits in its box, top to bottom (default top). */
|
|
26
|
+
anchor?: 'top' | 'middle' | 'bottom';
|
|
27
|
+
}
|
|
28
|
+
export interface Box {
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
w: number;
|
|
32
|
+
h: number;
|
|
33
|
+
}
|
|
34
|
+
export interface SlideGeometry {
|
|
35
|
+
/** Where its title goes (none on a blank slide: its text is in its boxes). */
|
|
36
|
+
title: TextBox | null;
|
|
37
|
+
/** The accent rule (under or above the title), when the slide shows a title. */
|
|
38
|
+
rule: Box | null;
|
|
39
|
+
/** Where its points (or subtitle) go, if this layout has text. */
|
|
40
|
+
text: TextBox | null;
|
|
41
|
+
logo: Box | null;
|
|
42
|
+
/** The room its clips keep free (they are arranged in the rest). */
|
|
43
|
+
reserve: Reserve;
|
|
44
|
+
}
|
|
45
|
+
export declare const LAYOUT_INFO: Record<SlideLayout, {
|
|
46
|
+
label: string;
|
|
47
|
+
text?: string;
|
|
48
|
+
}>;
|
|
49
|
+
export declare function slideGeometry(slide: Pick<Slide, 'layout' | 'caption'>, options: {
|
|
50
|
+
labels: boolean;
|
|
51
|
+
logo: LogoCorner | null;
|
|
52
|
+
}): SlideGeometry;
|
|
53
|
+
/** The lines of a slide's text (blank lines dropped). */
|
|
54
|
+
export declare const textLines: (text: string) => string[];
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { CAPTION_SPACE, STAGE_MARGIN } from "./model.js";
|
|
3
|
+
const TEXT_INSET = 0.06;
|
|
4
|
+
const LAYOUT_INFO = {
|
|
5
|
+
clips: { label: "Title and clips" },
|
|
6
|
+
points: { label: "Clips and points", text: "One point per line" },
|
|
7
|
+
compare: { label: "Side by side" },
|
|
8
|
+
title: { label: "Title slide", text: "Subtitle" },
|
|
9
|
+
section: { label: "Section", text: "Subtitle" },
|
|
10
|
+
free: { label: "Blank" }
|
|
11
|
+
};
|
|
12
|
+
const FOOTER = 0.085;
|
|
13
|
+
function slideGeometry(slide, options) {
|
|
14
|
+
const below = options.labels ? CAPTION_SPACE : 0;
|
|
15
|
+
const bottom = options.logo ? FOOTER : 0;
|
|
16
|
+
const logo = options.logo ? {
|
|
17
|
+
x: options.logo === "bl" ? TEXT_INSET : 1 - TEXT_INSET - 0.16,
|
|
18
|
+
y: 1 - 0.03 - 0.055,
|
|
19
|
+
w: 0.16,
|
|
20
|
+
h: 0.055
|
|
21
|
+
} : null;
|
|
22
|
+
const hasTitle = slide.caption.trim() !== "";
|
|
23
|
+
if (slide.layout === "free") {
|
|
24
|
+
return { title: null, rule: null, text: null, logo, reserve: { top: 0, below, bottom } };
|
|
25
|
+
}
|
|
26
|
+
if (slide.layout === "title" || slide.layout === "section") {
|
|
27
|
+
const centred = slide.layout === "title";
|
|
28
|
+
const title2 = centred ? {
|
|
29
|
+
x: 0.1,
|
|
30
|
+
y: 0.28,
|
|
31
|
+
w: 0.8,
|
|
32
|
+
h: 0.2,
|
|
33
|
+
size: 0.052,
|
|
34
|
+
weight: 700,
|
|
35
|
+
align: "center",
|
|
36
|
+
tone: "title",
|
|
37
|
+
leading: 1.1,
|
|
38
|
+
// Just above the rule, however many lines it takes.
|
|
39
|
+
anchor: "bottom"
|
|
40
|
+
} : {
|
|
41
|
+
x: TEXT_INSET + 0.02,
|
|
42
|
+
y: 0.36,
|
|
43
|
+
w: 0.8,
|
|
44
|
+
h: 0.18,
|
|
45
|
+
size: 0.046,
|
|
46
|
+
weight: 700,
|
|
47
|
+
align: "left",
|
|
48
|
+
tone: "title",
|
|
49
|
+
leading: 1.1
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
title: title2,
|
|
53
|
+
rule: centred ? { x: 0.47, y: 0.505, w: 0.06, h: 7e-3 } : { x: TEXT_INSET + 0.02, y: 0.33, w: 0.07, h: 8e-3 },
|
|
54
|
+
text: {
|
|
55
|
+
x: title2.x,
|
|
56
|
+
y: centred ? 0.545 : 0.555,
|
|
57
|
+
w: title2.w,
|
|
58
|
+
h: 0.1,
|
|
59
|
+
size: 0.021,
|
|
60
|
+
weight: 400,
|
|
61
|
+
align: title2.align,
|
|
62
|
+
tone: "muted",
|
|
63
|
+
leading: 1.3
|
|
64
|
+
},
|
|
65
|
+
logo,
|
|
66
|
+
// Clips, if any, go under the text.
|
|
67
|
+
reserve: { top: 0.62, below, bottom }
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const title = {
|
|
71
|
+
x: TEXT_INSET,
|
|
72
|
+
y: 0.05,
|
|
73
|
+
w: 1 - 2 * TEXT_INSET,
|
|
74
|
+
h: 0.075,
|
|
75
|
+
size: 0.03,
|
|
76
|
+
weight: 600,
|
|
77
|
+
align: "left",
|
|
78
|
+
tone: "title",
|
|
79
|
+
leading: 1.15
|
|
80
|
+
};
|
|
81
|
+
const top = hasTitle ? 0.14 : 0;
|
|
82
|
+
const inset = hasTitle || slide.layout === "points" ? TEXT_INSET - STAGE_MARGIN : 0;
|
|
83
|
+
const geometry = {
|
|
84
|
+
title,
|
|
85
|
+
rule: hasTitle ? { x: TEXT_INSET, y: 0.128, w: 0.045, h: 6e-3 } : null,
|
|
86
|
+
text: null,
|
|
87
|
+
logo,
|
|
88
|
+
reserve: { top, below, bottom, inset }
|
|
89
|
+
};
|
|
90
|
+
if (slide.layout === "points") {
|
|
91
|
+
const y = STAGE_MARGIN + top;
|
|
92
|
+
geometry.text = {
|
|
93
|
+
x: 0.6,
|
|
94
|
+
y,
|
|
95
|
+
w: 1 - 0.6 - TEXT_INSET,
|
|
96
|
+
h: 1 - y - STAGE_MARGIN - bottom - below,
|
|
97
|
+
// Level with the clips, which are centred in their room.
|
|
98
|
+
anchor: "middle",
|
|
99
|
+
size: 0.0195,
|
|
100
|
+
weight: 400,
|
|
101
|
+
align: "left",
|
|
102
|
+
tone: "body",
|
|
103
|
+
bullets: true,
|
|
104
|
+
leading: 1.45
|
|
105
|
+
};
|
|
106
|
+
geometry.reserve = { ...geometry.reserve, right: 0.4 - inset };
|
|
107
|
+
}
|
|
108
|
+
if (slide.layout === "compare") geometry.reserve = { ...geometry.reserve, columns: 2 };
|
|
109
|
+
return geometry;
|
|
110
|
+
}
|
|
111
|
+
const textLines = (text) => text.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
112
|
+
export {
|
|
113
|
+
LAYOUT_INFO,
|
|
114
|
+
slideGeometry,
|
|
115
|
+
textLines
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=slide-layout.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/presentation/slide-layout.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Where a slide's parts go, by its layout: its title, the accent rule, its text (points, or a\n * subtitle), the logo in the footer, and the room its clips are arranged in. One description,\n * drawn by the stage, the saved PDF and the PowerPoint export alike.\n *\n * All positions are fractions of the stage (x, w of its width; y, h of its height); type sizes\n * are fractions of the stage width.\n */\n\nimport { CAPTION_SPACE, STAGE_MARGIN, type Reserve, type Slide, type SlideLayout } from './model'\nimport type { LogoCorner } from './theme'\n\n/** Text sits this far in from the stage's sides: a generous margin. */\nconst TEXT_INSET = 0.06\n\nexport interface TextBox {\n x: number\n y: number\n w: number\n h: number\n /** Type size, a fraction of the stage width. */\n size: number\n weight: 400 | 600 | 700\n align: 'left' | 'center'\n tone: 'title' | 'body' | 'muted'\n /** Lines start with a bullet in the accent colour. */\n bullets?: boolean\n /** Line height, a multiple of the type size. */\n leading: number\n /** Where the text sits in its box, top to bottom (default top). */\n anchor?: 'top' | 'middle' | 'bottom'\n}\n\nexport interface Box {\n x: number\n y: number\n w: number\n h: number\n}\n\nexport interface SlideGeometry {\n /** Where its title goes (none on a blank slide: its text is in its boxes). */\n title: TextBox | null\n /** The accent rule (under or above the title), when the slide shows a title. */\n rule: Box | null\n /** Where its points (or subtitle) go, if this layout has text. */\n text: TextBox | null\n logo: Box | null\n /** The room its clips keep free (they are arranged in the rest). */\n reserve: Reserve\n}\n\nexport const LAYOUT_INFO: Record<SlideLayout, { label: string; text?: string }> = {\n clips: { label: 'Title and clips' },\n points: { label: 'Clips and points', text: 'One point per line' },\n compare: { label: 'Side by side' },\n title: { label: 'Title slide', text: 'Subtitle' },\n section: { label: 'Section', text: 'Subtitle' },\n free: { label: 'Blank' },\n}\n\n/** The footer's height while a logo shows. */\nconst FOOTER = 0.085\n\nexport function slideGeometry(\n slide: Pick<Slide, 'layout' | 'caption'>,\n options: { labels: boolean; logo: LogoCorner | null }\n): SlideGeometry {\n const below = options.labels ? CAPTION_SPACE : 0\n const bottom = options.logo ? FOOTER : 0\n const logo: Box | null = options.logo\n ? {\n x: options.logo === 'bl' ? TEXT_INSET : 1 - TEXT_INSET - 0.16,\n y: 1 - 0.03 - 0.055,\n w: 0.16,\n h: 0.055,\n }\n : null\n const hasTitle = slide.caption.trim() !== ''\n\n // Blank: nothing of its own but the logo; whatever is placed on it goes anywhere.\n if (slide.layout === 'free') {\n return { title: null, rule: null, text: null, logo, reserve: { top: 0, below, bottom } }\n }\n\n if (slide.layout === 'title' || slide.layout === 'section') {\n const centred = slide.layout === 'title'\n const title: TextBox = centred\n ? {\n x: 0.1,\n y: 0.28,\n w: 0.8,\n h: 0.2,\n size: 0.052,\n weight: 700,\n align: 'center',\n tone: 'title',\n leading: 1.1,\n // Just above the rule, however many lines it takes.\n anchor: 'bottom',\n }\n : {\n x: TEXT_INSET + 0.02,\n y: 0.36,\n w: 0.8,\n h: 0.18,\n size: 0.046,\n weight: 700,\n align: 'left',\n tone: 'title',\n leading: 1.1,\n }\n return {\n title,\n rule: centred\n ? { x: 0.47, y: 0.505, w: 0.06, h: 0.007 }\n : { x: TEXT_INSET + 0.02, y: 0.33, w: 0.07, h: 0.008 },\n text: {\n x: title.x,\n y: centred ? 0.545 : 0.555,\n w: title.w,\n h: 0.1,\n size: 0.021,\n weight: 400,\n align: title.align,\n tone: 'muted',\n leading: 1.3,\n },\n logo,\n // Clips, if any, go under the text.\n reserve: { top: 0.62, below, bottom },\n }\n }\n\n const title: TextBox = {\n x: TEXT_INSET,\n y: 0.05,\n w: 1 - 2 * TEXT_INSET,\n h: 0.075,\n size: 0.03,\n weight: 600,\n align: 'left',\n tone: 'title',\n leading: 1.15,\n }\n const top = hasTitle ? 0.14 : 0\n // Clips line up with the slide's text (its title, its points); with none, they take the room.\n const inset = hasTitle || slide.layout === 'points' ? TEXT_INSET - STAGE_MARGIN : 0\n const geometry: SlideGeometry = {\n title,\n rule: hasTitle ? { x: TEXT_INSET, y: 0.128, w: 0.045, h: 0.006 } : null,\n text: null,\n logo,\n reserve: { top, below, bottom, inset },\n }\n if (slide.layout === 'points') {\n const y = STAGE_MARGIN + top\n geometry.text = {\n x: 0.6,\n y,\n w: 1 - 0.6 - TEXT_INSET,\n h: 1 - y - STAGE_MARGIN - bottom - below,\n // Level with the clips, which are centred in their room.\n anchor: 'middle',\n size: 0.0195,\n weight: 400,\n align: 'left',\n tone: 'body',\n bullets: true,\n leading: 1.45,\n }\n geometry.reserve = { ...geometry.reserve, right: 0.4 - inset }\n }\n if (slide.layout === 'compare') geometry.reserve = { ...geometry.reserve, columns: 2 }\n return geometry\n}\n\n/** The lines of a slide's text (blank lines dropped). */\nexport const textLines = (text: string): string[] =>\n text\n .split('\\n')\n .map((line) => line.trim())\n .filter(Boolean)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["title"]
|
|
7
|
+
}
|