camox 0.43.0 → 0.45.0
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/bin/camox.mjs +9 -1
- package/dist/components/AuthGate.js +0 -6
- package/dist/components/DeviceSelector.js +96 -0
- package/dist/components/PlatformLabel.js +20 -24
- package/dist/core/components/lexical/HighlightNode.js +1 -1
- package/dist/core/components/lexical/InlineContentEditable.js +15 -19
- package/dist/core/components/lexical/InlineLexicalEditor.js +54 -74
- package/dist/core/components/lexical/InlineStylesPlugin.js +57 -41
- package/dist/core/components/lexical/MaxLengthPlugin.js +59 -0
- package/dist/core/components/lexical/SelectionBroadcaster.js +55 -56
- package/dist/core/components/lexical/SidebarLexicalEditor.js +327 -234
- package/dist/core/components/lexical/SummaryToggleGuard.js +42 -0
- package/dist/core/components/lexical/TextLinkPopover.js +169 -180
- package/dist/core/createApp.d.ts +254 -299
- package/dist/core/createApp.js +123 -29
- package/dist/core/createBlock.d.ts +5 -5
- package/dist/core/createBlock.js +399 -152
- package/dist/core/createCollection.d.ts +55 -0
- package/dist/core/createCollection.js +93 -0
- package/dist/core/createLayout.d.ts +197 -61
- package/dist/core/createLayout.js +114 -56
- package/dist/core/defineDocument.d.ts +3 -5
- package/dist/core/derivedRoutes.js +49 -1
- package/dist/core/dom.d.ts +13 -4
- package/dist/core/dom.js +44 -1
- package/dist/core/editing/BlockEditingRuntime.d.ts +16 -0
- package/dist/core/editing/BlockEditingRuntime.js +6 -15
- package/dist/core/editing/CompleteBlockEditingRuntime.js +28 -23
- package/dist/core/editing/createEditableBlock.d.ts +820 -565
- package/dist/core/editing/createEditableBlock.js +2153 -971
- package/dist/core/editing/referenceWrites.js +51 -0
- package/dist/core/editing/useLatestRecord.js +90 -0
- package/dist/core/hooks/useArtifactHost.js +139 -0
- package/dist/core/hooks/useFieldSelection.js +43 -18
- package/dist/core/hooks/useIsEditable.js +12 -10
- package/dist/core/hooks/useOverlayMessage.js +15 -19
- package/dist/core/lib/artifact.d.ts +21 -0
- package/dist/core/lib/artifact.js +154 -0
- package/dist/core/lib/artifactColors.js +123 -0
- package/dist/core/lib/artifactData.d.ts +1 -0
- package/dist/core/lib/artifactData.js +131 -0
- package/dist/core/lib/artifactHost.js +205 -0
- package/dist/core/lib/artifactRuntime.js +36 -0
- package/dist/core/lib/artifactTheme.js +57 -0
- package/dist/core/lib/contentType.d.ts +304 -103
- package/dist/core/lib/contentType.js +207 -168
- package/dist/core/lib/fieldTypes.js +4 -0
- package/dist/core/lib/iconTypes.d.ts +13 -0
- package/dist/core/lib/icons.d.ts +6 -0
- package/dist/core/lib/icons.js +126 -0
- package/dist/core/lib/imagePlaceholder.js +177 -0
- package/dist/core/lib/imageTransform.js +1 -1
- package/dist/core/lib/lexicalReact.d.ts +3 -1
- package/dist/core/lib/lexicalState.js +7 -7
- package/dist/core/lib/modifierFormats.js +1 -1
- package/dist/core/lib/previewPaused.js +9 -0
- package/dist/core/lib/recordScope.js +316 -0
- package/dist/core/lib/reference.d.ts +81 -0
- package/dist/core/lib/reference.js +29 -0
- package/dist/core/lib/textLinks.js +7 -5
- package/dist/core/lib/textStyles.d.ts +6 -8
- package/dist/core/lib/textStyles.js +5 -5
- package/dist/core/notFound.d.ts +7 -0
- package/dist/core/notFound.js +11 -0
- package/dist/core/pageDestinations.js +6 -3
- package/dist/core/recordItem.js +34 -0
- package/dist/core/requestContext.d.ts +26 -0
- package/dist/core/requestContext.js +20 -0
- package/dist/features/canvas/CamoxCanvas.js +1734 -0
- package/dist/features/canvas/CanvasBlockFrame.js +353 -0
- package/dist/features/canvas/CanvasBlockHeader.js +55 -0
- package/dist/features/canvas/CanvasLoadingScreen.js +35 -0
- package/dist/features/canvas/CanvasOverlays.js +254 -0
- package/dist/features/canvas/CanvasPageEdge.js +87 -0
- package/dist/features/canvas/CanvasPageFrame.js +335 -0
- package/dist/features/canvas/CanvasPageHeader.js +125 -0
- package/dist/features/canvas/CanvasPreviewPageSlot.js +103 -0
- package/dist/features/canvas/CanvasShelfMarker.js +255 -0
- package/dist/features/canvas/DerivedPagePathInput.js +238 -0
- package/dist/features/canvas/canvasCamera.js +229 -0
- package/dist/features/canvas/canvasFlight.js +104 -0
- package/dist/features/canvas/canvasFrameCapture.js +364 -0
- package/dist/features/canvas/canvasFrameDocument.js +317 -0
- package/dist/features/canvas/canvasFrameMode.js +53 -0
- package/dist/features/canvas/canvasInsertionSeams.js +44 -0
- package/dist/features/canvas/canvasItems.js +120 -0
- package/dist/features/canvas/canvasOverlayGeometry.js +240 -0
- package/dist/features/canvas/canvasPages.js +79 -0
- package/dist/features/canvas/canvasScreenSpace.js +26 -0
- package/dist/features/canvas/canvasSelection.js +18 -0
- package/dist/features/canvas/canvasStore.js +149 -0
- package/dist/features/canvas/canvasThumbnails.js +304 -0
- package/dist/features/canvas/canvasZoom.js +10 -0
- package/dist/features/canvas/sidebar/CanvasInspector.js +202 -0
- package/dist/features/canvas/sidebar/CanvasNavigator.js +226 -0
- package/dist/features/canvas/sidebar/CanvasSidebar.js +60 -0
- package/dist/features/canvas/sidebar/CanvasSidebarFrame.js +48 -0
- package/dist/features/canvas/sidebar/CanvasSidebarSkeleton.js +39 -0
- package/dist/features/canvas/sidebar/CatalogBlockInspector.js +189 -0
- package/dist/features/canvas/sidebar/CuratedPageInspector.js +69 -0
- package/dist/features/canvas/sidebar/DerivedPageInspector.js +37 -0
- package/dist/features/canvas/sidebar/LayoutPageBlocks.js +78 -0
- package/dist/features/canvas/sidebar/LayoutPageOverview.js +71 -0
- package/dist/features/canvas/sidebar/PageInspector.js +157 -0
- package/dist/features/canvas/sidebar/PageInspectorSkeleton.js +90 -0
- package/dist/features/canvas/sidebar/PageOverview.js +27 -0
- package/dist/features/canvas/sidebar/SingletonPageInspector.js +31 -0
- package/dist/features/canvas/useCanvasCamera.js +803 -0
- package/dist/features/canvas/useCanvasFrame.js +149 -0
- package/dist/features/canvas/useCanvasFrameInput.js +97 -0
- package/dist/features/canvas/useCanvasNearness.js +57 -0
- package/dist/features/canvas/useDiscoveredPreviewPaths.js +62 -0
- package/dist/features/content/CamoxContent.d.ts +2 -5
- package/dist/features/content/CamoxContent.js +174 -271
- package/dist/features/content/CollectionItemSheetContext.js +106 -0
- package/dist/features/content/ContentAssets.js +315 -0
- package/dist/features/content/ContentCollection.js +663 -0
- package/dist/features/content/collection-form.js +58 -0
- package/dist/features/content/components/AssetCard.js +101 -69
- package/dist/features/content/components/AssetCardSkeleton.js +4 -8
- package/dist/features/content/components/ContentSidebar.js +129 -18
- package/dist/features/content/components/ContentSidebarGroup.js +37 -0
- package/dist/features/content/components/DeleteCollectionItemButton.js +202 -0
- package/dist/features/content/components/MissingFromLocale.js +282 -0
- package/dist/features/content/components/UploadDropZone.js +27 -31
- package/dist/features/content/components/UploadProgressDrawer.js +90 -98
- package/dist/features/navigation/navigation.d.ts +66 -20
- package/dist/features/navigation/navigation.js +257 -90
- package/dist/features/og/defaultFonts.js +8 -8
- package/dist/features/og/geist-variable-ttf-base64.js +1 -1
- package/dist/features/og/imageResponse.d.ts +8 -0
- package/dist/features/og/imageResponse.js +26 -0
- package/dist/features/page/CuratedPageContent.js +167 -0
- package/dist/features/page/DerivedPageContent.js +26 -30
- package/dist/features/page/SharedLayoutContent.js +25 -27
- package/dist/features/preview/CamoxPreview.d.ts +23 -61
- package/dist/features/preview/CamoxPreview.js +811 -507
- package/dist/features/preview/EditablePageContent.js +1 -1
- package/dist/features/preview/EditablePageExperience.js +16 -22
- package/dist/features/preview/components/AddBlockDialog.js +270 -0
- package/dist/features/preview/components/AssetFieldEditor.js +173 -154
- package/dist/features/preview/components/AssetLightbox.js +194 -136
- package/dist/features/preview/components/AssetPickerModal.js +153 -170
- package/dist/features/preview/components/BlockActionsPopover.js +150 -149
- package/dist/features/preview/components/BlockErrorBoundary.js +8 -12
- package/dist/features/preview/components/BlockThumbnail.js +176 -100
- package/dist/features/preview/components/CanvasPreview.js +243 -0
- package/dist/features/preview/components/CreatePageModal.js +128 -137
- package/dist/features/preview/components/DebouncedFieldEditor.js +29 -33
- package/dist/features/preview/components/DerivedLayoutDialog.js +46 -29
- package/dist/features/preview/components/DrillRow.js +150 -0
- package/dist/features/preview/components/FieldOverlayStyles.js +33 -10
- package/dist/features/preview/components/FieldToolbar.js +166 -116
- package/dist/features/preview/components/Frame.js +108 -20
- package/dist/features/preview/components/IconFieldEditor.js +227 -0
- package/dist/features/preview/components/ItemFieldsEditor.js +238 -205
- package/dist/features/preview/components/LinkFieldEditor.js +135 -139
- package/dist/features/preview/components/Metadata.js +22 -30
- package/dist/features/preview/components/MobilePreviewDrawer.js +89 -108
- package/dist/features/preview/components/MultipleAssetFieldEditor.js +334 -246
- package/dist/features/preview/components/OverlayTracker.js +10 -42
- package/dist/features/preview/components/Overlays.js +81 -69
- package/dist/features/preview/components/PageInfo.js +1748 -0
- package/dist/features/preview/components/PageInfoSkeleton.js +51 -0
- package/dist/features/preview/components/PageLocationFieldset.js +112 -116
- package/dist/features/preview/components/PageNavigatorHeader.js +24 -0
- package/dist/features/preview/components/PageNicknameField.js +22 -26
- package/dist/features/preview/components/PagePicker.js +145 -147
- package/dist/features/preview/components/PageTree.js +525 -532
- package/dist/features/preview/components/PreviewActivation.js +28 -32
- package/dist/features/preview/components/PreviewPanel.js +620 -319
- package/dist/features/preview/components/PreviewToolbar.js +111 -340
- package/dist/features/preview/components/RecordCard.js +80 -0
- package/dist/features/preview/components/RecordCombobox.js +178 -0
- package/dist/features/preview/components/ReferenceFieldEditor.js +149 -0
- package/dist/features/preview/components/ReferenceListFieldEditor.js +432 -0
- package/dist/features/preview/components/RemovedBlockPlaceholder.js +95 -0
- package/dist/features/preview/components/RepeatableItemsList.js +125 -138
- package/dist/features/preview/components/SelectionEditor.js +993 -0
- package/dist/features/preview/components/ShikiMarkdown.js +14 -18
- package/dist/features/preview/components/SidebarSection.js +52 -64
- package/dist/features/preview/components/StudioEditingFallback.js +56 -0
- package/dist/features/preview/components/UnlinkAssetButton.js +104 -107
- package/dist/features/preview/components/blockGridNavigation.js +15 -0
- package/dist/features/preview/components/contentFieldSchema.js +8 -0
- package/dist/features/preview/components/useFieldWriter.js +200 -0
- package/dist/features/preview/components/useRecordView.js +383 -0
- package/dist/features/preview/components/useRepeatableItemActions.js +35 -37
- package/dist/features/preview/components/useUpdateBlockPosition.js +37 -37
- package/dist/features/preview/editingFrame.js +197 -0
- package/dist/features/preview/overlayHighlights.js +27 -0
- package/dist/features/preview/overlayMessages.js +72 -1
- package/dist/features/preview/pageRecord.js +189 -0
- package/dist/features/preview/previewConstants.js +6 -2
- package/dist/features/preview/previewPageFrame.js +95 -0
- package/dist/features/preview/previewPreparation.js +7 -0
- package/dist/features/preview/previewSelection.js +33 -59
- package/dist/features/preview/previewStore.js +225 -123
- package/dist/features/preview/referencePickerFocus.js +29 -0
- package/dist/features/preview/repeatableItemTargets.js +56 -0
- package/dist/features/preview/selectionReveal.js +21 -0
- package/dist/features/preview/usePreviewViewportActions.js +114 -0
- package/dist/features/preview/viewportContinuity.js +109 -0
- package/dist/features/provider/AuthenticatedCamoxProvider.js +69 -44
- package/dist/features/provider/CamoxProvider.d.ts +3 -6
- package/dist/features/provider/CamoxProvider.js +38 -50
- package/dist/features/provider/CoreCamoxProvider.js +71 -23
- package/dist/features/provider/LocalhostPreviewProvider.js +73 -73
- package/dist/features/provider/PreviewSignInPage.js +54 -0
- package/dist/features/provider/components/CamoxAppContext.js +11 -11
- package/dist/features/provider/components/CommandPalette.js +87 -143
- package/dist/features/provider/useAdminShortcuts.js +7 -11
- package/dist/features/routes/pageRuntime.d.ts +44 -4
- package/dist/features/routes/pageRuntime.js +244 -137
- package/dist/features/runtime/RuntimeChrome.js +117 -115
- package/dist/features/runtime/alternates.js +64 -0
- package/dist/features/runtime/analytics.d.ts +7 -0
- package/dist/features/runtime/analytics.js +66 -0
- package/dist/features/runtime/artifactSources.js +87 -0
- package/dist/features/runtime/editingFallback.js +20 -0
- package/dist/features/runtime/linkPrefetch.js +97 -0
- package/dist/features/runtime/livePages.js +62 -0
- package/dist/features/runtime/llmsTxt.js +40 -0
- package/dist/features/runtime/loopbackFetch.js +18 -0
- package/dist/features/runtime/navigationAccessibility.js +51 -0
- package/dist/features/runtime/navigationTarget.js +23 -2
- package/dist/features/runtime/pageApp.js +216 -141
- package/dist/features/runtime/pageBlockTypes.js +20 -0
- package/dist/features/runtime/pageClient.d.ts +2 -4
- package/dist/features/runtime/pageClient.js +18 -3
- package/dist/features/runtime/pageNavigation.js +254 -32
- package/dist/features/runtime/pageServer.d.ts +2 -4
- package/dist/features/runtime/pageServer.js +8 -1
- package/dist/features/runtime/pageViewport.js +7 -0
- package/dist/features/runtime/runtime.d.ts +60 -23
- package/dist/features/runtime/runtime.js +779 -166
- package/dist/features/runtime/runtimeChromeLoader.js +58 -0
- package/dist/features/runtime/runtimeRoutes.d.ts +19 -0
- package/dist/features/runtime/runtimeRoutes.js +25 -0
- package/dist/features/runtime/scrollRestoration.js +133 -0
- package/dist/features/runtime/studioApp.d.ts +4 -3
- package/dist/features/runtime/studioApp.js +5 -9
- package/dist/features/runtime/studioClient.d.ts +2 -4
- package/dist/features/runtime/studioClient.js +12 -3
- package/dist/features/runtime/studioServer.d.ts +2 -4
- package/dist/features/runtime/studioServer.js +6 -1
- package/dist/features/runtime/worker.d.ts +6 -0
- package/dist/features/runtime/worker.js +23 -0
- package/dist/features/runtime/workerRequest.js +44 -0
- package/dist/features/studio/CamoxStudio.d.ts +2 -6
- package/dist/features/studio/CamoxStudio.js +34 -49
- package/dist/features/studio/FollowAgentProvider.js +338 -0
- package/dist/features/studio/components/ChatSidebar.js +356 -0
- package/dist/features/studio/components/DraftsControl.js +676 -0
- package/dist/features/studio/components/FaviconDialog.js +318 -0
- package/dist/features/studio/components/LocalePicker.js +785 -0
- package/dist/features/studio/components/Navbar.js +123 -103
- package/dist/features/studio/components/ProjectMenu.js +239 -260
- package/dist/features/studio/components/RenameDraftDialog.js +206 -0
- package/dist/features/studio/components/ReviewDraftSheet.js +1472 -0
- package/dist/features/studio/components/ThreadComposer.js +157 -0
- package/dist/features/studio/components/ThreadMarkdown.js +113 -0
- package/dist/features/studio/components/ThreadMessages.js +270 -0
- package/dist/features/studio/components/UserButton.js +34 -23
- package/dist/features/studio/draftLabel.js +33 -0
- package/dist/features/studio/draftNotice.js +13 -0
- package/dist/features/studio/followAgent.js +250 -0
- package/dist/features/studio/followAgentContext.js +7 -0
- package/dist/features/studio/localeOptions.js +118 -0
- package/dist/features/studio/localeSwitch.js +19 -0
- package/dist/features/studio/routes.js +48 -1
- package/dist/features/studio/studioStore.js +64 -1
- package/dist/features/studio/useTheme.js +24 -27
- package/dist/features/vite/artifactRuntimeMinification.js +36 -0
- package/dist/features/vite/auth.js +1 -50
- package/dist/features/vite/blockBoilerplate.js +6 -5
- package/dist/features/vite/blockChunks.js +31 -0
- package/dist/features/vite/bundlerLogs.js +17 -0
- package/dist/features/vite/definitionsSync.js +158 -226
- package/dist/features/vite/devAuthentication.js +4 -16
- package/dist/features/vite/devSignIn.js +87 -0
- package/dist/features/vite/iconGeneration.js +16 -0
- package/dist/features/vite/layoutGeneration.js +11 -1
- package/dist/features/vite/publishSeededContent.js +30 -0
- package/dist/features/vite/routeGeneration.js +6 -7
- package/dist/features/vite/runtimeDev.js +80 -99
- package/dist/features/vite/vite.d.ts +53 -31
- package/dist/features/vite/vite.js +230 -116
- package/dist/features/vite/workerBuild.js +39 -0
- package/dist/hooks/use-debounced-field.js +116 -47
- package/dist/hooks/use-file-upload.js +114 -67
- package/dist/hooks/use-marquee-selection.js +32 -36
- package/dist/hooks/use-page-destinations.js +69 -37
- package/dist/lib/api-client-server.d.ts +5 -0
- package/dist/lib/api-client-server.js +10 -4
- package/dist/lib/api-client.d.ts +6 -0
- package/dist/lib/api-client.js +51 -16
- package/dist/lib/api-socket.js +17 -0
- package/dist/lib/auth-page-styles.js +39 -0
- package/dist/lib/auth.js +139 -60
- package/dist/lib/collection-cache.js +23 -0
- package/dist/lib/draft-scope.d.ts +4 -0
- package/dist/lib/draft-scope.js +222 -0
- package/dist/lib/html.js +12 -0
- package/dist/lib/link-pages.js +154 -0
- package/dist/lib/locale-path.d.ts +22 -0
- package/dist/lib/locale-path.js +57 -0
- package/dist/lib/normalized-data.js +252 -152
- package/dist/lib/preview-handoff.js +13 -0
- package/dist/lib/queries.d.ts +11 -0
- package/dist/lib/queries.js +71 -131
- package/dist/lib/studio-reads.d.ts +14 -0
- package/dist/lib/studio-reads.js +574 -0
- package/dist/lib/use-project-room.js +50 -47
- package/dist/lib/use-thread.js +310 -0
- package/dist/lib/utils.js +25 -29
- package/dist/lib/view-locale-context.js +22 -0
- package/dist/lib/view-locale.js +46 -0
- package/dist/package.js +5 -0
- package/dist/studio-overlays.css +41 -147
- package/dist/studio.css +2 -2
- package/package.json +70 -72
- package/skills/camox/SKILL.md +16 -5
- package/skills/camox/references/artifacts.md +179 -0
- package/skills/camox/references/block-definitions.md +98 -55
- package/skills/camox/references/cli-common.md +18 -6
- package/skills/camox/references/cli-content.md +92 -58
- package/skills/camox/references/cli-publishing.md +26 -0
- package/skills/camox/references/dom-integrations.md +27 -0
- package/skills/camox/references/field-styling.md +7 -7
- package/skills/camox/references/layout-definitions.md +16 -14
- package/skills/camox/references/localization.md +56 -0
- package/skills/camox/references/routed-layouts.md +133 -15
- package/skills/camox-expand/SKILL.md +69 -0
- package/skills/camox-expand/references/jobs.md +39 -0
- package/skills/camoxify/SKILL.md +38 -2
- package/dist/core/components/AddBlockControlBar.js +0 -78
- package/dist/core/hooks/useRequireDraftSource.js +0 -23
- package/dist/features/metadata/sitemap.d.ts +0 -8
- package/dist/features/metadata/sitemap.js +0 -17
- package/dist/features/og/imageResponse.node.d.ts +0 -12
- package/dist/features/og/imageResponse.node.js +0 -49
- package/dist/features/og/imageResponse.workerd.d.ts +0 -12
- package/dist/features/og/imageResponse.workerd.js +0 -40
- package/dist/features/page/PublishedPageContent.js +0 -205
- package/dist/features/page/PublishedPageExperience.js +0 -23
- package/dist/features/preview/DerivedPreview.js +0 -134
- package/dist/features/preview/commentsEnabled.js +0 -8
- package/dist/features/preview/components/AddBlockSidebar.js +0 -381
- package/dist/features/preview/components/AttachedComments.js +0 -194
- package/dist/features/preview/components/CommentHeader.js +0 -122
- package/dist/features/preview/components/CommentSidebar.js +0 -29
- package/dist/features/preview/components/CommentTargetQuote.js +0 -166
- package/dist/features/preview/components/DerivedLayoutSidebar.d.ts +0 -5
- package/dist/features/preview/components/DerivedLayoutSidebar.js +0 -179
- package/dist/features/preview/components/DerivedPageInfoSidebar.js +0 -69
- package/dist/features/preview/components/LeftSidebar.js +0 -68
- package/dist/features/preview/components/PageEditorSidebar.js +0 -563
- package/dist/features/preview/components/PageInfoSidebar.js +0 -1551
- package/dist/features/preview/components/PageNavigatorSidebar.js +0 -78
- package/dist/features/preview/components/PageStatusBadge.js +0 -97
- package/dist/features/preview/components/PeekedBlock.js +0 -217
- package/dist/features/preview/components/PreviewComments.js +0 -56
- package/dist/features/preview/components/PreviewToolbarSpacer.js +0 -43
- package/dist/features/preview/components/PublicationRow.js +0 -422
- package/dist/features/preview/components/PublishDialog.js +0 -194
- package/dist/features/preview/components/RightSidebar.js +0 -59
- package/dist/features/preview/previewCommentsStore.js +0 -115
- package/dist/features/preview/publication.js +0 -81
- package/dist/features/runtime/SharedChromeContext.js +0 -7
- package/dist/features/studio/components/EnvironmentMenu.js +0 -413
- package/dist/features/vite/appGeneration.js +0 -89
- package/dist/lib/view-queries.js +0 -28
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/Matches.d.ts +0 -114
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/RouterProvider.d.ts +0 -25
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/config.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/defer.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/fileRoute.d.ts +0 -20
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/global.d.ts +0 -8
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/hash-scroll.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/index.d.ts +0 -18
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/link.d.ts +0 -161
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/load-matches.d.ts +0 -6
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/location.d.ts +0 -54
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/lru-cache.d.ts +0 -8
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/manifest.d.ts +0 -32
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/new-process-route-tree.d.ts +0 -115
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/not-found.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/redirect.d.ts +0 -49
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/rewrite.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/root.d.ts +0 -6
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/route.d.ts +0 -343
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/routeInfo.d.ts +0 -48
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/router.d.ts +0 -695
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/scroll-restoration.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/searchMiddleware.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/searchParams.d.ts +0 -5
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/ssr/serializer/RawStream.d.ts +0 -38
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/ssr/serializer/seroval-plugins.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/ssr/serializer/transformer.d.ts +0 -65
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/stores.d.ts +0 -69
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/structuralSharing.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/typePrimitives.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useLoaderData.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useLoaderDeps.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useNavigate.d.ts +0 -10
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useParams.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useRouteContext.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useSearch.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/utils.d.ts +0 -28
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/validators.d.ts +0 -49
- package/skills/camox/references/cli-environments.md +0 -37
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { BLOCK_OVERLAP_SELECTOR } from "./canvasFrameDocument.js";
|
|
2
|
+
|
|
3
|
+
//#region src/features/canvas/canvasFrameCapture.ts
|
|
4
|
+
const XHTML = "http://www.w3.org/1999/xhtml";
|
|
5
|
+
const URL_PATTERN = /url\(\s*(["']?)([^"')]+)\1\s*\)/g;
|
|
6
|
+
/**
|
|
7
|
+
* Draws same-origin frames into images, with the browser's own rendering: a frame's content
|
|
8
|
+
* and stylesheets go into an SVG `<foreignObject>`, which the browser lays out and paints like
|
|
9
|
+
* any page. Canvas frames are documents we build, so their stylesheets copy over as text, and
|
|
10
|
+
* nothing is inlined element by element. An SVG drawn as an image loads nothing, so fonts and
|
|
11
|
+
* images are embedded as data URLs, fetched once for every frame the capturer draws.
|
|
12
|
+
*/
|
|
13
|
+
function createFrameCapturer() {
|
|
14
|
+
const resources = /* @__PURE__ */ new Map();
|
|
15
|
+
const sheets = /* @__PURE__ */ new Map();
|
|
16
|
+
function fetchBlob(url) {
|
|
17
|
+
return fetch(url, {
|
|
18
|
+
mode: "cors",
|
|
19
|
+
credentials: "omit"
|
|
20
|
+
}).then((response) => response.ok ? response.blob() : null).catch(() => null);
|
|
21
|
+
}
|
|
22
|
+
function embed(url) {
|
|
23
|
+
if (url.startsWith("data:")) return Promise.resolve(url);
|
|
24
|
+
let embedded = resources.get(url);
|
|
25
|
+
if (!embedded) {
|
|
26
|
+
embedded = fetchBlob(url).then((blob) => blob && dataUrl(blob));
|
|
27
|
+
resources.set(url, embedded);
|
|
28
|
+
}
|
|
29
|
+
return embedded;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* An image at no more than the size it's drawn at, so large photos don't make the browser
|
|
33
|
+
* decode them in full to draw a small thumbnail. Resizing happens off the main thread.
|
|
34
|
+
*/
|
|
35
|
+
function embedImage(url, width, height) {
|
|
36
|
+
if (url.startsWith("data:") || !width || !height) return embed(url);
|
|
37
|
+
const key = `${url}#${width}x${height}`;
|
|
38
|
+
let embedded = resources.get(key);
|
|
39
|
+
if (!embedded) {
|
|
40
|
+
embedded = fetchBlob(url).then(async (blob) => {
|
|
41
|
+
if (!blob) return null;
|
|
42
|
+
try {
|
|
43
|
+
const bitmap = await createImageBitmap(blob);
|
|
44
|
+
const factor = Math.max(width / bitmap.width, height / bitmap.height);
|
|
45
|
+
if (factor >= 1) {
|
|
46
|
+
bitmap.close();
|
|
47
|
+
return dataUrl(blob);
|
|
48
|
+
}
|
|
49
|
+
const canvas = new OffscreenCanvas(Math.ceil(bitmap.width * factor), Math.ceil(bitmap.height * factor));
|
|
50
|
+
const context = canvas.getContext("2d");
|
|
51
|
+
if (!context) return dataUrl(blob);
|
|
52
|
+
context.imageSmoothingQuality = "high";
|
|
53
|
+
context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
|
54
|
+
bitmap.close();
|
|
55
|
+
return dataUrl(await canvas.convertToBlob({
|
|
56
|
+
type: "image/webp",
|
|
57
|
+
quality: .9
|
|
58
|
+
}));
|
|
59
|
+
} catch {
|
|
60
|
+
return dataUrl(blob);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
resources.set(key, embedded);
|
|
64
|
+
}
|
|
65
|
+
return embedded;
|
|
66
|
+
}
|
|
67
|
+
/** A cross-origin stylesheet's text, which its CSSOM doesn't expose. */
|
|
68
|
+
function sheetText(href) {
|
|
69
|
+
let text = sheets.get(href);
|
|
70
|
+
if (!text) {
|
|
71
|
+
text = fetch(href, {
|
|
72
|
+
mode: "cors",
|
|
73
|
+
credentials: "omit"
|
|
74
|
+
}).then((response) => response.ok ? response.text() : null).catch(() => null);
|
|
75
|
+
sheets.set(href, text);
|
|
76
|
+
}
|
|
77
|
+
return text;
|
|
78
|
+
}
|
|
79
|
+
async function embedUrls(css) {
|
|
80
|
+
const urls = /* @__PURE__ */ new Set();
|
|
81
|
+
for (const match of css.matchAll(URL_PATTERN)) if (!match[2].startsWith("data:") && !match[2].startsWith("#")) urls.add(match[2]);
|
|
82
|
+
if (!urls.size) return css;
|
|
83
|
+
const embedded = /* @__PURE__ */ new Map();
|
|
84
|
+
await Promise.all(Array.from(urls, async (url) => {
|
|
85
|
+
const data = await embed(url);
|
|
86
|
+
if (data) embedded.set(url, data);
|
|
87
|
+
}));
|
|
88
|
+
return css.replace(URL_PATTERN, (match, _quote, url) => {
|
|
89
|
+
const data = embedded.get(url);
|
|
90
|
+
return data ? `url("${data}")` : match;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async function collectStyles(doc) {
|
|
94
|
+
const collected = {
|
|
95
|
+
rules: [],
|
|
96
|
+
fontFaces: []
|
|
97
|
+
};
|
|
98
|
+
const pending = [];
|
|
99
|
+
for (const sheet of [...Array.from(doc.styleSheets), ...doc.adoptedStyleSheets]) {
|
|
100
|
+
if (sheet.disabled) continue;
|
|
101
|
+
const media = sheet.media.mediaText;
|
|
102
|
+
const wrap = media && media !== "all" ? (css) => `@media ${media} {${css}}` : noWrap;
|
|
103
|
+
const base = sheet.href ?? doc.baseURI;
|
|
104
|
+
let rules;
|
|
105
|
+
try {
|
|
106
|
+
rules = sheet.cssRules;
|
|
107
|
+
} catch {
|
|
108
|
+
if (!sheet.href) continue;
|
|
109
|
+
const index = collected.rules.push("") - 1;
|
|
110
|
+
const href = sheet.href;
|
|
111
|
+
pending.push(sheetText(href).then((text) => {
|
|
112
|
+
if (text == null) return;
|
|
113
|
+
const parsed = new (doc.defaultView ?? window).CSSStyleSheet();
|
|
114
|
+
parsed.replaceSync(text.replace(/@import[^;]+;/g, ""));
|
|
115
|
+
const own = {
|
|
116
|
+
rules: [],
|
|
117
|
+
fontFaces: []
|
|
118
|
+
};
|
|
119
|
+
collectRules(parsed.cssRules, href, own, wrap);
|
|
120
|
+
collected.rules[index] = own.rules.join("\n");
|
|
121
|
+
collected.fontFaces.push(...own.fontFaces);
|
|
122
|
+
}));
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
collectRules(rules, base, collected, wrap);
|
|
126
|
+
}
|
|
127
|
+
await Promise.all(pending);
|
|
128
|
+
return collected;
|
|
129
|
+
}
|
|
130
|
+
/** The `@font-face` rules of faces the frame loaded, each with its first source embedded. */
|
|
131
|
+
async function embedFonts(doc, fontFaces) {
|
|
132
|
+
const loaded = [];
|
|
133
|
+
doc.fonts.forEach((face) => {
|
|
134
|
+
if (face.status === "loaded") loaded.push(face);
|
|
135
|
+
});
|
|
136
|
+
return (await Promise.all(fontFaces.map(async ({ family, css }) => {
|
|
137
|
+
const faces = loaded.filter((face) => unquote(face.family) === family);
|
|
138
|
+
if (!faces.length) return "";
|
|
139
|
+
const range = /unicode-range:\s*([^;]+);/.exec(css)?.[1].replace(/\s/g, "").toUpperCase();
|
|
140
|
+
const weight = /font-weight:\s*([^;]+);/.exec(css)?.[1].trim();
|
|
141
|
+
const style = /font-style:\s*([^;]+);/.exec(css)?.[1].trim();
|
|
142
|
+
if (!faces.some((face) => (!range || face.unicodeRange.replace(/\s/g, "").toUpperCase() === range) && (!weight || face.weight === weight) && (!style || face.style === style))) return "";
|
|
143
|
+
const source = /src:\s*([^;]+);/.exec(css);
|
|
144
|
+
const first = source && /url\("([^"]+)"\)(\s*format\([^)]*\))?/.exec(source[1]);
|
|
145
|
+
if (!source || !first) return css;
|
|
146
|
+
const data = await embed(first[1]);
|
|
147
|
+
if (!data) return "";
|
|
148
|
+
return css.replace(source[0], `src: url("${data}")${first[2] ?? ""};`);
|
|
149
|
+
}))).filter(Boolean).join("\n");
|
|
150
|
+
}
|
|
151
|
+
/** The SVG drawing `doc`'s content at `width` by `height` CSS pixels, at `scale`. */
|
|
152
|
+
async function documentSvg(doc, width, height, scale, blockOverlap) {
|
|
153
|
+
const view = doc.defaultView;
|
|
154
|
+
if (!view || !doc.body) throw new Error("The frame has no document to capture.");
|
|
155
|
+
const html = doc.documentElement.cloneNode(false);
|
|
156
|
+
const body = doc.body.cloneNode(true);
|
|
157
|
+
html.appendChild(body);
|
|
158
|
+
const originals = doc.body.querySelectorAll("*");
|
|
159
|
+
const clones = body.querySelectorAll("*");
|
|
160
|
+
const clonesOf = /* @__PURE__ */ new Map();
|
|
161
|
+
const fixes = [];
|
|
162
|
+
for (let index = 0; index < originals.length; index++) {
|
|
163
|
+
const original = originals[index];
|
|
164
|
+
const clone = clones[index];
|
|
165
|
+
clonesOf.set(original, clone);
|
|
166
|
+
const tag = original.localName;
|
|
167
|
+
if (tag === "img") {
|
|
168
|
+
const image = original;
|
|
169
|
+
const url = image.currentSrc || image.src;
|
|
170
|
+
clone.removeAttribute("srcset");
|
|
171
|
+
clone.removeAttribute("sizes");
|
|
172
|
+
clone.removeAttribute("loading");
|
|
173
|
+
fixes.push(embedImage(url, Math.ceil(image.clientWidth * scale), Math.ceil(image.clientHeight * scale)).then((data) => data ? clone.setAttribute("src", data) : clone.removeAttribute("src")));
|
|
174
|
+
} else if (tag === "source" && original.parentElement?.localName === "picture") clone.remove();
|
|
175
|
+
else if (tag === "iframe") {
|
|
176
|
+
const frame = original;
|
|
177
|
+
const replacement = replaceElement(clone, original, "img");
|
|
178
|
+
replacement.removeAttribute("src");
|
|
179
|
+
replacement.removeAttribute("srcdoc");
|
|
180
|
+
const nested = sameOriginDocument(frame);
|
|
181
|
+
if (nested && frame.clientWidth && frame.clientHeight) fixes.push(documentSvg(nested, frame.clientWidth, frame.clientHeight, scale).then((svg) => replacement.setAttribute("src", svgUrl(svg))).catch(() => {}));
|
|
182
|
+
} else if (tag === "canvas" || tag === "video") {
|
|
183
|
+
const replacement = replaceElement(clone, original, "img");
|
|
184
|
+
replacement.removeAttribute("src");
|
|
185
|
+
try {
|
|
186
|
+
const drawn = doc.createElement("canvas");
|
|
187
|
+
const media = original;
|
|
188
|
+
drawn.width = "videoWidth" in media ? media.videoWidth : media.width;
|
|
189
|
+
drawn.height = "videoHeight" in media ? media.videoHeight : media.height;
|
|
190
|
+
drawn.getContext("2d")?.drawImage(media, 0, 0);
|
|
191
|
+
replacement.setAttribute("src", drawn.toDataURL());
|
|
192
|
+
} catch {
|
|
193
|
+
const poster = original.poster;
|
|
194
|
+
if (poster) fixes.push(embed(poster).then((data) => data && replacement.setAttribute("src", data)));
|
|
195
|
+
}
|
|
196
|
+
} else if (tag === "object" || tag === "embed") replaceElement(clone, original, "div").style.setProperty("display", view.getComputedStyle(original).display);
|
|
197
|
+
else if (tag === "input") {
|
|
198
|
+
const input = original;
|
|
199
|
+
if (input.type === "checkbox" || input.type === "radio") {
|
|
200
|
+
if (input.checked) clone.setAttribute("checked", "");
|
|
201
|
+
else clone.removeAttribute("checked");
|
|
202
|
+
} else clone.setAttribute("value", input.value);
|
|
203
|
+
} else if (tag === "textarea") clone.textContent = original.value;
|
|
204
|
+
else if (tag === "option") {
|
|
205
|
+
if (original.selected) clone.setAttribute("selected", "");
|
|
206
|
+
else clone.removeAttribute("selected");
|
|
207
|
+
} else if (tag === "script" || tag === "noscript") clone.remove();
|
|
208
|
+
const inline = clone.getAttribute("style");
|
|
209
|
+
if (inline?.includes("url(")) fixes.push(embedUrls(absoluteUrls(inline, doc.baseURI)).then((css) => clone.setAttribute("style", css)));
|
|
210
|
+
}
|
|
211
|
+
for (const element of Array.from(body.querySelectorAll("[data-camox-studio]"))) element.remove();
|
|
212
|
+
freezeAnimations(doc, clonesOf);
|
|
213
|
+
const background = [doc.documentElement, doc.body].map((element) => view.getComputedStyle(element).backgroundColor).find((color) => color && color !== "rgba(0, 0, 0, 0)");
|
|
214
|
+
if (blockOverlap !== void 0) html.style.setProperty("--camox-block-overlap", `${blockOverlap}px`);
|
|
215
|
+
html.style.setProperty("width", `${width}px`);
|
|
216
|
+
html.style.setProperty("height", `${height}px`);
|
|
217
|
+
html.style.setProperty("overflow", "hidden", "important");
|
|
218
|
+
const styles = await collectStyles(doc);
|
|
219
|
+
const rules = styles.rules.join("\n").replace(/:root\b/g, "html");
|
|
220
|
+
const [fonts, css] = await Promise.all([
|
|
221
|
+
embedFonts(doc, styles.fontFaces),
|
|
222
|
+
embedUrls(rules),
|
|
223
|
+
Promise.all(fixes)
|
|
224
|
+
]);
|
|
225
|
+
const style = doc.createElementNS(XHTML, "style");
|
|
226
|
+
style.textContent = `${fonts}\n${css}`;
|
|
227
|
+
const head = doc.createElementNS(XHTML, "head");
|
|
228
|
+
head.appendChild(style);
|
|
229
|
+
html.insertBefore(head, body);
|
|
230
|
+
const markup = new XMLSerializer().serializeToString(html);
|
|
231
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">` + (background ? `<rect width="100%" height="100%" fill="${background}"/>` : "") + `<foreignObject x="0" y="0" width="100%" height="100%">${markup}</foreignObject></svg>`;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Draws a same-origin frame, as its content is now, at `scale` times its CSS size. Its
|
|
235
|
+
* animations are frozen where they are.
|
|
236
|
+
*/
|
|
237
|
+
async function capture(frame, { scale, blockOverlap }) {
|
|
238
|
+
const doc = sameOriginDocument(frame);
|
|
239
|
+
const width = frame.offsetWidth;
|
|
240
|
+
let height = frame.offsetHeight;
|
|
241
|
+
if (!doc || !width || !height) throw new Error("The frame can't be captured.");
|
|
242
|
+
const overlap = blockOverlapOf(doc);
|
|
243
|
+
if (blockOverlap !== void 0 && blockOverlap !== overlap) height += doc.querySelectorAll(BLOCK_OVERLAP_SELECTOR).length * (overlap - blockOverlap);
|
|
244
|
+
const svg = await documentSvg(doc, width, height, scale, blockOverlap);
|
|
245
|
+
const image = new Image();
|
|
246
|
+
image.src = svgUrl(svg);
|
|
247
|
+
await image.decode();
|
|
248
|
+
const canvas = document.createElement("canvas");
|
|
249
|
+
canvas.width = Math.max(1, Math.round(width * scale));
|
|
250
|
+
canvas.height = Math.max(1, Math.round(height * scale));
|
|
251
|
+
const context = canvas.getContext("2d");
|
|
252
|
+
if (!context) throw new Error("The frame can't be drawn.");
|
|
253
|
+
context.imageSmoothingQuality = "high";
|
|
254
|
+
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
255
|
+
const blob = await new Promise((resolve) => canvas.toBlob(resolve, "image/webp", .92));
|
|
256
|
+
if (!blob) throw new Error("The frame's capture can't be encoded.");
|
|
257
|
+
return {
|
|
258
|
+
blob,
|
|
259
|
+
width,
|
|
260
|
+
height
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return { capture };
|
|
264
|
+
}
|
|
265
|
+
const noWrap = (css) => css;
|
|
266
|
+
function collectRules(rules, base, collected, wrap) {
|
|
267
|
+
for (const rule of Array.from(rules)) {
|
|
268
|
+
if ("styleSheet" in rule) {
|
|
269
|
+
const imported = rule;
|
|
270
|
+
if (!imported.styleSheet) continue;
|
|
271
|
+
let inner = wrap;
|
|
272
|
+
const media = imported.media.mediaText;
|
|
273
|
+
if (media) inner = wrapIn(inner, `@media ${media}`);
|
|
274
|
+
if (imported.layerName != null) inner = wrapIn(inner, `@layer ${imported.layerName}`);
|
|
275
|
+
try {
|
|
276
|
+
collectRules(imported.styleSheet.cssRules, imported.styleSheet.href ?? base, collected, inner);
|
|
277
|
+
} catch {}
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (rule.cssText.trimStart().startsWith("@font-face")) {
|
|
281
|
+
const family = unquote(rule.style.getPropertyValue("font-family"));
|
|
282
|
+
collected.fontFaces.push({
|
|
283
|
+
family,
|
|
284
|
+
css: wrap(absoluteUrls(rule.cssText, base))
|
|
285
|
+
});
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
collected.rules.push(wrap(absoluteUrls(rule.cssText, base)));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function wrapIn(wrap, at) {
|
|
292
|
+
return (css) => wrap(`${at} {${css}}`);
|
|
293
|
+
}
|
|
294
|
+
function absoluteUrls(css, base) {
|
|
295
|
+
return css.replace(URL_PATTERN, (match, _quote, url) => {
|
|
296
|
+
if (url.startsWith("data:") || url.startsWith("#")) return match;
|
|
297
|
+
try {
|
|
298
|
+
return `url("${new URL(url, base).href}")`;
|
|
299
|
+
} catch {
|
|
300
|
+
return match;
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
function unquote(value) {
|
|
305
|
+
return value.replace(/["']/g, "").trim();
|
|
306
|
+
}
|
|
307
|
+
function sameOriginDocument(frame) {
|
|
308
|
+
try {
|
|
309
|
+
return frame.contentDocument?.body ? frame.contentDocument : null;
|
|
310
|
+
} catch {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function replaceElement(clone, original, tag) {
|
|
315
|
+
const replacement = clone.ownerDocument.createElementNS(XHTML, tag);
|
|
316
|
+
for (const attribute of Array.from(original.attributes)) replacement.setAttribute(attribute.name, attribute.value);
|
|
317
|
+
clone.replaceWith(replacement);
|
|
318
|
+
return replacement;
|
|
319
|
+
}
|
|
320
|
+
/** CSS animations stop at their current time; scripted ones keep their current look. */
|
|
321
|
+
function freezeAnimations(doc, clonesOf) {
|
|
322
|
+
const view = doc.defaultView;
|
|
323
|
+
if (!view) return;
|
|
324
|
+
const byElement = /* @__PURE__ */ new Map();
|
|
325
|
+
for (const animation of doc.getAnimations()) {
|
|
326
|
+
const effect = animation.effect;
|
|
327
|
+
if (!effect?.target || effect.pseudoElement) continue;
|
|
328
|
+
byElement.set(effect.target, [...byElement.get(effect.target) ?? [], animation]);
|
|
329
|
+
}
|
|
330
|
+
for (const [element, animations] of byElement) {
|
|
331
|
+
const clone = clonesOf.get(element);
|
|
332
|
+
if (!clone) continue;
|
|
333
|
+
const computed = view.getComputedStyle(element);
|
|
334
|
+
if (animations.some((animation) => "animationName" in animation)) {
|
|
335
|
+
const delays = computed.animationName.split(",").map((name) => {
|
|
336
|
+
const animation = animations.find((candidate) => candidate.animationName === name.trim());
|
|
337
|
+
return `${-(Number(animation?.currentTime) || 0)}ms`;
|
|
338
|
+
});
|
|
339
|
+
clone.style.setProperty("animation-delay", delays.join(", "), "important");
|
|
340
|
+
clone.style.setProperty("animation-play-state", "paused", "important");
|
|
341
|
+
}
|
|
342
|
+
if (animations.some((animation) => !("animationName" in animation) && !("transitionProperty" in animation))) {
|
|
343
|
+
clone.style.setProperty("transform", computed.transform, "important");
|
|
344
|
+
clone.style.setProperty("opacity", computed.opacity, "important");
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function blockOverlapOf(doc) {
|
|
349
|
+
return parseFloat(doc.documentElement.style.getPropertyValue("--camox-block-overlap")) || 0;
|
|
350
|
+
}
|
|
351
|
+
function svgUrl(svg) {
|
|
352
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
|
353
|
+
}
|
|
354
|
+
function dataUrl(blob) {
|
|
355
|
+
return new Promise((resolve, reject) => {
|
|
356
|
+
const reader = new FileReader();
|
|
357
|
+
reader.onload = () => resolve(reader.result);
|
|
358
|
+
reader.onerror = () => reject(reader.error);
|
|
359
|
+
reader.readAsDataURL(blob);
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
//#endregion
|
|
364
|
+
export { createFrameCapturer };
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { PREVIEW_PAUSED_ATTRIBUTE } from "../../core/lib/previewPaused.js";
|
|
2
|
+
import { EMPTY_PREVIEW_DOCUMENT, isSiteStyle } from "../preview/components/previewStyles.js";
|
|
3
|
+
|
|
4
|
+
//#region src/features/canvas/canvasFrameDocument.ts
|
|
5
|
+
/** Freezes CSS animations, including ones that start while the frame is paused. */
|
|
6
|
+
const PAUSED_ANIMATIONS_STYLE = `html[${PREVIEW_PAUSED_ATTRIBUTE}] *, html[${PREVIEW_PAUSED_ATTRIBUTE}] *::before, html[${PREVIEW_PAUSED_ATTRIBUTE}] *::after { animation-play-state: paused !important; }`;
|
|
7
|
+
/** Set by the camera on elements that hold Canvas frames: see `canvasBlockOverlap`. */
|
|
8
|
+
const BLOCK_OVERLAP_ATTRIBUTE = "data-canvas-block-overlap";
|
|
9
|
+
const BLOCK = "[data-camox-viewport-block]";
|
|
10
|
+
/** Every block after the first, in its container or a later one, covers the end of the one before. */
|
|
11
|
+
const BLOCK_OVERLAP_SELECTOR = `:is(${BLOCK}, :has(${BLOCK})) ~ ${BLOCK}, :is(${BLOCK}, :has(${BLOCK})) ~ * ${BLOCK}`;
|
|
12
|
+
const BLOCK_OVERLAP_STYLE = `${BLOCK_OVERLAP_SELECTOR} { margin-top: calc(-1 * var(--camox-block-overlap, 0px)); }`;
|
|
13
|
+
/**
|
|
14
|
+
* How far, in CSS pixels, each block in a Canvas frame overlaps the one before it.
|
|
15
|
+
* Where two blocks meet partway through a screen pixel, each covers only part of
|
|
16
|
+
* it, and whatever is under both (the site's background, often unlike either
|
|
17
|
+
* block) shows through as a light seam. Zoomed out, an overlap of at least a
|
|
18
|
+
* device pixel covers it, at the cost of about as much of a block's bottom edge. From
|
|
19
|
+
* 100%, where seams are rare, borders stay exact. Powers of two change a page's
|
|
20
|
+
* height only at a few zooms.
|
|
21
|
+
*/
|
|
22
|
+
function canvasBlockOverlap(scale, pixelRatio) {
|
|
23
|
+
if (scale >= 1) return 0;
|
|
24
|
+
return 2 ** Math.max(0, Math.ceil(Math.log2(1 / (scale * pixelRatio))));
|
|
25
|
+
}
|
|
26
|
+
function canvasPageHref(input) {
|
|
27
|
+
return new URL(`${input.runtimeBasePath.replace(/\/$/, "")}/${input.pathname.replace(/^\//, "")}`, input.href).href;
|
|
28
|
+
}
|
|
29
|
+
/** Only whole viewport height declarations, not arbitrary values, calc(), or media queries. */
|
|
30
|
+
function logicalViewportValue(value) {
|
|
31
|
+
const trimmed = value.trim();
|
|
32
|
+
return /^100(?:s|l|d)?vh$/i.test(trimmed) ? `var(--camox-viewport-height, ${trimmed})` : null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The top-level rule count of each sheet once all of it has been adapted. Every
|
|
36
|
+
* measurement adapts, so a sheet is only walked again once rules are inserted
|
|
37
|
+
* into or removed from it, as CSS-in-JS libraries do. Replacing a style
|
|
38
|
+
* element's text creates a new sheet.
|
|
39
|
+
*/
|
|
40
|
+
const adaptedRuleCounts = /* @__PURE__ */ new WeakMap();
|
|
41
|
+
/** Mutate this iframe's CSSOM in place so layers, breakpoints and !important keep their order. */
|
|
42
|
+
function adaptCanvasStyleSheets(doc) {
|
|
43
|
+
const visited = /* @__PURE__ */ new Set();
|
|
44
|
+
/** Whether the sheet is settled: false while one of its imports is still loading. */
|
|
45
|
+
function visitSheet(sheet) {
|
|
46
|
+
if (visited.has(sheet)) return true;
|
|
47
|
+
visited.add(sheet);
|
|
48
|
+
try {
|
|
49
|
+
const rules = sheet.cssRules;
|
|
50
|
+
if (adaptedRuleCounts.get(sheet) === rules.length) return true;
|
|
51
|
+
const settled = visitRules(rules);
|
|
52
|
+
if (settled) adaptedRuleCounts.set(sheet, rules.length);
|
|
53
|
+
return settled;
|
|
54
|
+
} catch {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function visitRules(rules) {
|
|
59
|
+
let settled = true;
|
|
60
|
+
for (const rule of Array.from(rules)) {
|
|
61
|
+
if ("style" in rule) {
|
|
62
|
+
const style = rule.style;
|
|
63
|
+
for (const property of [
|
|
64
|
+
"height",
|
|
65
|
+
"min-height",
|
|
66
|
+
"max-height"
|
|
67
|
+
]) {
|
|
68
|
+
const replacement = logicalViewportValue(style.getPropertyValue(property));
|
|
69
|
+
if (replacement) style.setProperty(property, replacement, style.getPropertyPriority(property));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if ("cssRules" in rule && !visitRules(rule.cssRules)) settled = false;
|
|
73
|
+
if ("styleSheet" in rule) {
|
|
74
|
+
const imported = rule.styleSheet;
|
|
75
|
+
if (!imported || !visitSheet(imported)) settled = false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return settled;
|
|
79
|
+
}
|
|
80
|
+
for (const sheet of [...Array.from(doc.styleSheets), ...Array.from(doc.adoptedStyleSheets ?? [])]) visitSheet(sheet);
|
|
81
|
+
}
|
|
82
|
+
/** Build before loading so relative styles/images resolve against the page, never /_camox/canvas. */
|
|
83
|
+
function createCanvasDocument(previewDocument, href) {
|
|
84
|
+
const doc = new DOMParser().parseFromString(previewDocument ?? "<!doctype html><html><head></head><body><div id=\"root\" data-camox-preview-root></div></body></html>", "text/html");
|
|
85
|
+
if (!previewDocument) {
|
|
86
|
+
for (const style of document.head.querySelectorAll("style, link[rel=\"stylesheet\"]")) if (isSiteStyle(style)) doc.head.appendChild(style.cloneNode(true));
|
|
87
|
+
}
|
|
88
|
+
doc.querySelectorAll("base").forEach((base) => base.remove());
|
|
89
|
+
const base = doc.createElement("base");
|
|
90
|
+
base.href = href;
|
|
91
|
+
doc.head.insertBefore(base, doc.head.firstChild);
|
|
92
|
+
doc.querySelectorAll("script").forEach((script) => script.remove());
|
|
93
|
+
doc.querySelector("[data-camox-preview-root]")?.replaceChildren();
|
|
94
|
+
doc.documentElement.style.setProperty("overscroll-behavior", "none");
|
|
95
|
+
doc.body.style.setProperty("overscroll-behavior", "none");
|
|
96
|
+
const paused = doc.createElement("style");
|
|
97
|
+
paused.textContent = PAUSED_ANIMATIONS_STYLE;
|
|
98
|
+
doc.head.appendChild(paused);
|
|
99
|
+
return `<!doctype html>${doc.documentElement.outerHTML}`;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Give a loaded document what `createCanvasDocument` builds into a Canvas frame's,
|
|
103
|
+
* for as long as it shows in the Canvas: no swipe navigation, CSS animations that
|
|
104
|
+
* pause with the frame, and a page as tall as its content. Returns how to undo
|
|
105
|
+
* all of it, so the document can fill the Preview again.
|
|
106
|
+
*/
|
|
107
|
+
function adoptCanvasDocument(iframe, root, viewportHeight) {
|
|
108
|
+
const doc = root.ownerDocument;
|
|
109
|
+
const paused = doc.createElement("style");
|
|
110
|
+
paused.textContent = PAUSED_ANIMATIONS_STYLE;
|
|
111
|
+
doc.head.appendChild(paused);
|
|
112
|
+
const restored = [doc.documentElement, doc.body].map((element) => ({
|
|
113
|
+
element,
|
|
114
|
+
overscroll: element.style.getPropertyValue("overscroll-behavior"),
|
|
115
|
+
overflow: element.style.getPropertyValue("overflow"),
|
|
116
|
+
overflowPriority: element.style.getPropertyPriority("overflow")
|
|
117
|
+
}));
|
|
118
|
+
for (const { element } of restored) element.style.setProperty("overscroll-behavior", "none");
|
|
119
|
+
const stopObserving = observeCanvasDocument(iframe, root, viewportHeight);
|
|
120
|
+
return () => {
|
|
121
|
+
stopObserving();
|
|
122
|
+
paused.remove();
|
|
123
|
+
for (const { element, overscroll, overflow, overflowPriority } of restored) {
|
|
124
|
+
element.style.setProperty("overscroll-behavior", overscroll);
|
|
125
|
+
element.style.setProperty("overflow", overflow, overflowPriority);
|
|
126
|
+
}
|
|
127
|
+
doc.documentElement.style.removeProperty("--camox-viewport-height");
|
|
128
|
+
iframe.style.removeProperty("height");
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function isMedia(target) {
|
|
132
|
+
const name = target?.localName;
|
|
133
|
+
return name === "video" || name === "audio";
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Pause a frame's document: its animations, transitions and media, including
|
|
137
|
+
* media that starts playing while paused. JavaScript-driven animations are up
|
|
138
|
+
* to blocks, which can watch the document's paused attribute. Returns how to
|
|
139
|
+
* resume exactly what it paused.
|
|
140
|
+
*/
|
|
141
|
+
function pauseCanvasDocument(doc) {
|
|
142
|
+
doc.documentElement.setAttribute(PREVIEW_PAUSED_ATTRIBUTE, "");
|
|
143
|
+
const animations = [];
|
|
144
|
+
for (const animation of doc.getAnimations?.() ?? []) {
|
|
145
|
+
if ("animationName" in animation || animation.playState !== "running") continue;
|
|
146
|
+
animation.pause();
|
|
147
|
+
animations.push(animation);
|
|
148
|
+
}
|
|
149
|
+
const media = /* @__PURE__ */ new Set();
|
|
150
|
+
const pauseMedia = (element) => {
|
|
151
|
+
if (element.paused) return;
|
|
152
|
+
element.pause();
|
|
153
|
+
media.add(element);
|
|
154
|
+
};
|
|
155
|
+
doc.querySelectorAll("video, audio").forEach((element) => {
|
|
156
|
+
if (isMedia(element)) pauseMedia(element);
|
|
157
|
+
});
|
|
158
|
+
const onPlay = (event) => {
|
|
159
|
+
if (isMedia(event.target)) pauseMedia(event.target);
|
|
160
|
+
};
|
|
161
|
+
doc.addEventListener("play", onPlay, true);
|
|
162
|
+
return () => {
|
|
163
|
+
doc.removeEventListener("play", onPlay, true);
|
|
164
|
+
doc.documentElement.removeAttribute(PREVIEW_PAUSED_ATTRIBUTE);
|
|
165
|
+
for (const animation of animations) if (animation.playState === "paused") animation.play();
|
|
166
|
+
for (const element of media) if (element.isConnected && element.paused) element.play().catch(() => {});
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The stylesheets of a site document's head, which shape how any content
|
|
171
|
+
* renders in it. Pages of a site share them, while their titles, metadata and
|
|
172
|
+
* preloads differ.
|
|
173
|
+
*/
|
|
174
|
+
function canvasDocumentStyles(previewDocument) {
|
|
175
|
+
if (!previewDocument) return "";
|
|
176
|
+
const doc = new DOMParser().parseFromString(previewDocument, "text/html");
|
|
177
|
+
return Array.from(doc.head.querySelectorAll("style, link[rel=\"stylesheet\"]")).map((style) => style.outerHTML).join("\n");
|
|
178
|
+
}
|
|
179
|
+
function measureCanvasDocument(iframe, root, baseline, fit = "page") {
|
|
180
|
+
const doc = root.ownerDocument;
|
|
181
|
+
adaptCanvasStyleSheets(doc);
|
|
182
|
+
iframe.style.height = `${baseline}px`;
|
|
183
|
+
const bodyRect = doc.body.getBoundingClientRect();
|
|
184
|
+
const rootRect = root.getBoundingClientRect();
|
|
185
|
+
if (fit === "content") {
|
|
186
|
+
const height = exactBottom(rootRect.bottom, root.scrollHeight + rootRect.top);
|
|
187
|
+
iframe.style.height = `${Math.max(1, height)}px`;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const height = exactBottom(Math.max(rootRect.bottom, bodyRect.bottom), doc.body.scrollHeight + bodyRect.top, doc.documentElement.scrollHeight, root.scrollHeight + rootRect.top);
|
|
191
|
+
iframe.style.height = `${Math.max(baseline, height)}px`;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The bottom of a document's content, from its boxes' exact, often fractional,
|
|
195
|
+
* bottom. A frame any taller shows a sliver of the document's own background
|
|
196
|
+
* under the last block, which a scaled Canvas can draw as a bright line. Scroll
|
|
197
|
+
* heights are rounded to whole pixels, so they only count where content
|
|
198
|
+
* overflows past those boxes.
|
|
199
|
+
*/
|
|
200
|
+
function exactBottom(boxesBottom, ...scrollBottoms) {
|
|
201
|
+
const scrollBottom = Math.max(...scrollBottoms);
|
|
202
|
+
return scrollBottom > boxesBottom + 1 ? scrollBottom : boxesBottom;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Re-measure at a fixed baseline, not at the previous full-page height: document scrollHeight
|
|
206
|
+
* is at least viewport height and therefore cannot shrink an already expanded iframe.
|
|
207
|
+
*
|
|
208
|
+
* While `suspended` (a resting frame, whose content is hidden), the frame keeps its size.
|
|
209
|
+
* Once `resume` is called, it re-measures only if its content or size changed meanwhile.
|
|
210
|
+
*/
|
|
211
|
+
function observeCanvasDocument(iframe, root, viewportHeight, fit = "page", suspended = () => false) {
|
|
212
|
+
const doc = root.ownerDocument;
|
|
213
|
+
const baseline = Math.max(1, viewportHeight);
|
|
214
|
+
doc.documentElement.style.setProperty("--camox-viewport-height", `${baseline}px`);
|
|
215
|
+
doc.documentElement.style.setProperty("overflow", "hidden", "important");
|
|
216
|
+
doc.body.style.setProperty("overflow", "hidden", "important");
|
|
217
|
+
const overlapStyle = doc.createElement("style");
|
|
218
|
+
overlapStyle.textContent = BLOCK_OVERLAP_STYLE;
|
|
219
|
+
doc.head.appendChild(overlapStyle);
|
|
220
|
+
const followOverlap = () => {
|
|
221
|
+
const overlap = iframe.closest(`[${BLOCK_OVERLAP_ATTRIBUTE}]`);
|
|
222
|
+
const pixels = Number(overlap?.getAttribute(BLOCK_OVERLAP_ATTRIBUTE)) || 0;
|
|
223
|
+
doc.documentElement.style.setProperty("--camox-block-overlap", `${pixels}px`);
|
|
224
|
+
};
|
|
225
|
+
followOverlap();
|
|
226
|
+
const overlapChanges = new MutationObserver(followOverlap);
|
|
227
|
+
overlapChanges.observe(iframe.ownerDocument.documentElement, {
|
|
228
|
+
subtree: true,
|
|
229
|
+
attributes: true,
|
|
230
|
+
attributeFilter: [BLOCK_OVERLAP_ATTRIBUTE]
|
|
231
|
+
});
|
|
232
|
+
let frame = 0;
|
|
233
|
+
let disposed = false;
|
|
234
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
235
|
+
const size = (element) => {
|
|
236
|
+
const rect = element.getBoundingClientRect();
|
|
237
|
+
return `${rect.width}:${rect.height}`;
|
|
238
|
+
};
|
|
239
|
+
const resize = new ResizeObserver((entries) => {
|
|
240
|
+
if (entries.some(({ target }) => sizes.get(target) !== size(target))) schedule();
|
|
241
|
+
});
|
|
242
|
+
function measure() {
|
|
243
|
+
frame = 0;
|
|
244
|
+
if (suspended()) return;
|
|
245
|
+
measureCanvasDocument(iframe, root, baseline, fit);
|
|
246
|
+
const elements = /* @__PURE__ */ new Set([
|
|
247
|
+
doc.body,
|
|
248
|
+
root,
|
|
249
|
+
...root.querySelectorAll("*")
|
|
250
|
+
]);
|
|
251
|
+
for (const element of sizes.keys()) {
|
|
252
|
+
if (elements.has(element)) continue;
|
|
253
|
+
resize.unobserve(element);
|
|
254
|
+
sizes.delete(element);
|
|
255
|
+
}
|
|
256
|
+
for (const element of elements) {
|
|
257
|
+
if (!sizes.has(element)) resize.observe(element);
|
|
258
|
+
sizes.set(element, size(element));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
function schedule() {
|
|
262
|
+
if (disposed || frame) return;
|
|
263
|
+
frame = requestAnimationFrame(measure);
|
|
264
|
+
}
|
|
265
|
+
let changedWhileSuspended = false;
|
|
266
|
+
function contentChanged() {
|
|
267
|
+
if (suspended()) changedWhileSuspended = true;
|
|
268
|
+
schedule();
|
|
269
|
+
}
|
|
270
|
+
const isUnsizedChange = (record) => record.type === "attributes" && (root.contains(record.target) || record.attributeName === "data-camox-paused");
|
|
271
|
+
const mutations = new MutationObserver((records) => {
|
|
272
|
+
if (!records.every(isUnsizedChange)) contentChanged();
|
|
273
|
+
});
|
|
274
|
+
mutations.observe(root, {
|
|
275
|
+
subtree: true,
|
|
276
|
+
childList: true,
|
|
277
|
+
attributes: true,
|
|
278
|
+
characterData: true
|
|
279
|
+
});
|
|
280
|
+
mutations.observe(doc.head, {
|
|
281
|
+
subtree: true,
|
|
282
|
+
childList: true,
|
|
283
|
+
attributes: true,
|
|
284
|
+
characterData: true
|
|
285
|
+
});
|
|
286
|
+
mutations.observe(doc.documentElement, { attributes: true });
|
|
287
|
+
mutations.observe(doc.body, { attributes: true });
|
|
288
|
+
doc.addEventListener("load", contentChanged, true);
|
|
289
|
+
doc.addEventListener("error", contentChanged, true);
|
|
290
|
+
doc.fonts?.addEventListener("loadingdone", contentChanged);
|
|
291
|
+
doc.fonts?.ready.then(contentChanged);
|
|
292
|
+
measure();
|
|
293
|
+
const resume = () => {
|
|
294
|
+
if (disposed) return;
|
|
295
|
+
requestAnimationFrame(() => {
|
|
296
|
+
if (suspended()) return;
|
|
297
|
+
if (changedWhileSuspended || [doc.body, root].some((el) => sizes.get(el) !== size(el))) schedule();
|
|
298
|
+
changedWhileSuspended = false;
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
const stop = () => {
|
|
302
|
+
disposed = true;
|
|
303
|
+
cancelAnimationFrame(frame);
|
|
304
|
+
resize.disconnect();
|
|
305
|
+
mutations.disconnect();
|
|
306
|
+
overlapChanges.disconnect();
|
|
307
|
+
overlapStyle.remove();
|
|
308
|
+
doc.documentElement.style.removeProperty("--camox-block-overlap");
|
|
309
|
+
doc.removeEventListener("load", contentChanged, true);
|
|
310
|
+
doc.removeEventListener("error", contentChanged, true);
|
|
311
|
+
doc.fonts?.removeEventListener("loadingdone", contentChanged);
|
|
312
|
+
};
|
|
313
|
+
return Object.assign(stop, { resume });
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
//#endregion
|
|
317
|
+
export { BLOCK_OVERLAP_SELECTOR, adaptCanvasStyleSheets, adoptCanvasDocument, canvasBlockOverlap, canvasDocumentStyles, canvasPageHref, createCanvasDocument, logicalViewportValue, measureCanvasDocument, observeCanvasDocument, pauseCanvasDocument };
|