canopycms 0.0.68-int.98 → 0.1.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/CHANGELOG.md +2131 -0
- package/README.md +47 -63
- package/dist/ai/generate.d.ts +5 -0
- package/dist/ai/generate.js +244 -115
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/json-to-markdown.d.ts +21 -1
- package/dist/ai/json-to-markdown.js +120 -57
- package/dist/ai/references.d.ts +72 -0
- package/dist/ai/references.js +223 -0
- package/dist/ai/resolve-branch.js +9 -2
- package/dist/ai/to-plain-text.d.ts +17 -8
- package/dist/ai/to-plain-text.js +18 -15
- package/dist/ai/types.d.ts +84 -3
- package/dist/api/admin-branch-health.d.ts +24 -1
- package/dist/api/admin-branch-health.js +52 -20
- package/dist/api/admin.d.ts +23 -5
- package/dist/api/admin.js +36 -1
- package/dist/api/assets.js +88 -78
- package/dist/api/branch-create-window.d.ts +12 -0
- package/dist/api/branch-create-window.js +13 -0
- package/dist/api/branch-merge.d.ts +1 -1
- package/dist/api/branch-merge.js +1 -1
- package/dist/api/branch-review.d.ts +3 -3
- package/dist/api/branch-review.js +16 -7
- package/dist/api/branch-status.d.ts +16 -6
- package/dist/api/branch-status.js +57 -12
- package/dist/api/branch-withdraw.d.ts +2 -2
- package/dist/api/branch-withdraw.js +8 -3
- package/dist/api/branch.d.ts +25 -12
- package/dist/api/branch.js +164 -103
- package/dist/api/client.d.ts +78 -58
- package/dist/api/client.js +150 -117
- package/dist/api/comments.d.ts +12 -3
- package/dist/api/comments.js +118 -9
- package/dist/api/content.d.ts +9 -9
- package/dist/api/content.js +131 -22
- package/dist/api/entries-constants.d.ts +14 -3
- package/dist/api/entries-constants.js +14 -3
- package/dist/api/entries.d.ts +40 -2
- package/dist/api/entries.js +102 -6
- package/dist/api/github-sync.d.ts +19 -0
- package/dist/api/github-sync.js +50 -3
- package/dist/api/groups.d.ts +4 -4
- package/dist/api/groups.js +2 -1
- package/dist/api/guards.js +23 -3
- package/dist/api/index.d.ts +3 -1
- package/dist/api/index.js +1 -0
- package/dist/api/permissions.d.ts +21 -2
- package/dist/api/permissions.js +50 -3
- package/dist/api/request-deadline.d.ts +59 -0
- package/dist/api/request-deadline.js +73 -0
- package/dist/api/resolve-references.js +8 -7
- package/dist/api/route-builder.d.ts +14 -0
- package/dist/api/route-builder.js +14 -1
- package/dist/api/schema.d.ts +11 -9
- package/dist/api/schema.js +2 -0
- package/dist/api/settings-helpers.d.ts +5 -1
- package/dist/api/settings-helpers.js +10 -2
- package/dist/api/types.d.ts +18 -0
- package/dist/api/users-constants.d.ts +6 -0
- package/dist/api/users-constants.js +6 -0
- package/dist/assets/asset-url.d.ts +28 -16
- package/dist/assets/asset-url.js +51 -25
- package/dist/assets/finalize.js +3 -0
- package/dist/assets/index.d.ts +1 -1
- package/dist/assets/materialize.d.ts +153 -0
- package/dist/assets/materialize.js +402 -0
- package/dist/assets/store-local.d.ts +19 -5
- package/dist/assets/store-local.js +69 -10
- package/dist/assets/store-s3.d.ts +38 -7
- package/dist/assets/store-s3.js +128 -22
- package/dist/assets/transform-directives.d.ts +52 -13
- package/dist/assets/transform-directives.js +87 -27
- package/dist/assets/transform.d.ts +3 -3
- package/dist/assets/transform.js +13 -8
- package/dist/assets/types.d.ts +31 -4
- package/dist/auth/cache.d.ts +1 -1
- package/dist/auth/cache.js +1 -1
- package/dist/auth/caching-auth-plugin.d.ts +2 -0
- package/dist/auth/caching-auth-plugin.js +12 -2
- package/dist/auth/file-based-auth-cache.d.ts +8 -0
- package/dist/auth/file-based-auth-cache.js +18 -8
- package/dist/auth/person-name.d.ts +5 -0
- package/dist/auth/person-name.js +8 -0
- package/dist/auth/plugin.d.ts +10 -1
- package/dist/auth/types.d.ts +2 -0
- package/dist/auth/user-metadata-lookup.d.ts +21 -0
- package/dist/auth/user-metadata-lookup.js +116 -0
- package/dist/authorization/branch.d.ts +1 -1
- package/dist/authorization/branch.js +12 -13
- package/dist/authorization/groups/schema.d.ts +4 -4
- package/dist/authorization/helpers.d.ts +8 -0
- package/dist/authorization/helpers.js +8 -0
- package/dist/authorization/index.d.ts +1 -1
- package/dist/authorization/index.js +1 -1
- package/dist/authorization/protected-branch.d.ts +5 -4
- package/dist/authorization/protected-branch.js +7 -5
- package/dist/branch-health.d.ts +35 -8
- package/dist/branch-health.js +54 -18
- package/dist/branch-metadata-error.d.ts +27 -0
- package/dist/branch-metadata-error.js +23 -0
- package/dist/branch-metadata-file.d.ts +205 -24
- package/dist/branch-metadata-file.js +126 -25
- package/dist/branch-metadata.d.ts +45 -6
- package/dist/branch-metadata.js +142 -50
- package/dist/branch-provisioning.d.ts +167 -0
- package/dist/branch-provisioning.js +501 -0
- package/dist/branch-registry.d.ts +8 -0
- package/dist/branch-registry.js +61 -9
- package/dist/branch-schema-cache.d.ts +29 -12
- package/dist/branch-schema-cache.js +64 -21
- package/dist/branch-sparse.d.ts +23 -0
- package/dist/branch-sparse.js +118 -0
- package/dist/branch-workspace.d.ts +42 -3
- package/dist/branch-workspace.js +239 -100
- package/dist/build/asset-refs.d.ts +94 -0
- package/dist/build/asset-refs.js +267 -0
- package/dist/build/generate-ai-content.js +5 -0
- package/dist/build/inline-flight.d.ts +37 -0
- package/dist/build/inline-flight.js +142 -0
- package/dist/cli/asset-refs.d.ts +52 -0
- package/dist/cli/asset-refs.js +194 -0
- package/dist/cli/cli.d.ts +15 -1
- package/dist/cli/cli.js +8543 -3193
- package/dist/cli/configured-asset-store.d.ts +12 -0
- package/dist/cli/configured-asset-store.js +46 -0
- package/dist/cli/generate-ai-content.js +4244 -1072
- package/dist/cli/init.js +217 -126
- package/dist/cli/project-detect.d.ts +2 -0
- package/dist/cli/project-detect.js +2 -0
- package/dist/cli/template-files/Dockerfile.cms.template +13 -5
- package/dist/cli/template-files/cms-stack.ts.template +16 -13
- package/dist/client.d.ts +1 -2
- package/dist/client.js +1 -2
- package/dist/comment-store.d.ts +25 -0
- package/dist/comment-store.js +43 -8
- package/dist/config/flatten.js +2 -0
- package/dist/config/helpers.js +2 -2
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schemas/config.d.ts +12 -29
- package/dist/config/schemas/config.js +9 -12
- package/dist/config/schemas/field.d.ts +3 -0
- package/dist/config/schemas/field.js +58 -4
- package/dist/config/schemas/media.d.ts +4 -13
- package/dist/config/schemas/media.js +5 -8
- package/dist/config/schemas/url.d.ts +2 -20
- package/dist/config/schemas/url.js +2 -25
- package/dist/config/types.d.ts +116 -22
- package/dist/config/types.js +3 -9
- package/dist/config/validation.d.ts +25 -4
- package/dist/config/validation.js +116 -3
- package/dist/content-id-index.js +2 -2
- package/dist/content-index-generation.d.ts +1 -1
- package/dist/content-index-generation.js +1 -1
- package/dist/content-listing.d.ts +10 -1
- package/dist/content-listing.js +22 -5
- package/dist/content-reader.d.ts +9 -0
- package/dist/content-reader.js +16 -11
- package/dist/content-store.d.ts +75 -12
- package/dist/content-store.js +249 -86
- package/dist/content-tree.js +2 -2
- package/dist/context.js +5 -2
- package/dist/dangling-reference-log.d.ts +17 -0
- package/dist/dangling-reference-log.js +29 -0
- package/dist/dev-content-watcher.d.ts +1 -1
- package/dist/dev-content-watcher.js +1 -1
- package/dist/editor/BranchManager.d.ts +23 -7
- package/dist/editor/BranchManager.js +119 -54
- package/dist/editor/CanopyEditor.d.ts +1 -1
- package/dist/editor/CanopyEditor.js +1 -2
- package/dist/editor/CommentsPanel.d.ts +14 -0
- package/dist/editor/CommentsPanel.js +68 -43
- package/dist/editor/Editor.d.ts +11 -5
- package/dist/editor/Editor.js +891 -218
- package/dist/editor/EditorAuthGate.d.ts +7 -1
- package/dist/editor/EditorAuthGate.js +41 -9
- package/dist/editor/EntryNavigator.d.ts +22 -0
- package/dist/editor/EntryNavigator.js +353 -105
- package/dist/editor/FormHeader.d.ts +25 -0
- package/dist/editor/FormHeader.js +82 -0
- package/dist/editor/FormRenderer.d.ts +27 -3
- package/dist/editor/FormRenderer.js +171 -76
- package/dist/editor/PreviewFrame.d.ts +40 -0
- package/dist/editor/PreviewFrame.js +215 -0
- package/dist/editor/PreviewPane.d.ts +47 -0
- package/dist/editor/PreviewPane.js +147 -0
- package/dist/editor/PreviewToolbarTools.d.ts +22 -0
- package/dist/editor/PreviewToolbarTools.js +73 -0
- package/dist/editor/admin/SystemHealthPanel.d.ts +2 -1
- package/dist/editor/admin/SystemHealthPanel.js +163 -33
- package/dist/editor/admin/useSystemHealth.d.ts +18 -0
- package/dist/editor/admin/useSystemHealth.js +70 -1
- package/dist/editor/branch-status.d.ts +17 -0
- package/dist/editor/branch-status.js +29 -0
- package/dist/editor/canopy-path.d.ts +9 -6
- package/dist/editor/canopy-path.js +8 -11
- package/dist/editor/client-reference-resolver.d.ts +43 -16
- package/dist/editor/client-reference-resolver.js +155 -64
- package/dist/editor/comments/BranchComments.d.ts +1 -1
- package/dist/editor/comments/BranchComments.js +2 -2
- package/dist/editor/comments/CloseRefusedNotice.d.ts +4 -0
- package/dist/editor/comments/CloseRefusedNotice.js +7 -0
- package/dist/editor/comments/CommentThreadsPopover.d.ts +47 -0
- package/dist/editor/comments/CommentThreadsPopover.js +274 -0
- package/dist/editor/comments/CommentWriteAlert.d.ts +11 -0
- package/dist/editor/comments/CommentWriteAlert.js +13 -0
- package/dist/editor/comments/EntryComments.d.ts +10 -4
- package/dist/editor/comments/EntryComments.js +14 -17
- package/dist/editor/comments/FieldWrapper.d.ts +18 -4
- package/dist/editor/comments/FieldWrapper.js +62 -28
- package/dist/editor/comments/InlineCommentThread.d.ts +9 -2
- package/dist/editor/comments/InlineCommentThread.js +11 -47
- package/dist/editor/comments/ThreadCarousel.d.ts +1 -1
- package/dist/editor/comments/ThreadCarousel.js +18 -21
- package/dist/editor/comments/ThreadReplyControls.d.ts +28 -0
- package/dist/editor/comments/ThreadReplyControls.js +92 -0
- package/dist/editor/comments/field-comment-context.d.ts +14 -0
- package/dist/editor/comments/field-comment-context.js +14 -0
- package/dist/editor/comments/form-comments.d.ts +48 -0
- package/dist/editor/comments/form-comments.js +178 -0
- package/dist/editor/comments/item-anchor-copy.d.ts +6 -0
- package/dist/editor/comments/item-anchor-copy.js +6 -0
- package/dist/editor/comments/move-focus-if-unmoved.d.ts +6 -0
- package/dist/editor/comments/move-focus-if-unmoved.js +11 -0
- package/dist/editor/comments/popover-placement.d.ts +18 -0
- package/dist/editor/comments/popover-placement.js +58 -0
- package/dist/editor/comments/use-tracked-writes.d.ts +16 -0
- package/dist/editor/comments/use-tracked-writes.js +53 -0
- package/dist/editor/components/BranchAccessList.d.ts +15 -0
- package/dist/editor/components/BranchAccessList.js +52 -0
- package/dist/editor/components/BranchLockBanner.d.ts +22 -0
- package/dist/editor/components/BranchLockBanner.js +39 -0
- package/dist/editor/components/BranchPill.d.ts +32 -0
- package/dist/editor/components/BranchPill.js +33 -0
- package/dist/editor/components/BranchesDrawer.d.ts +11 -0
- package/dist/editor/components/BranchesDrawer.js +5 -0
- package/dist/editor/components/ConfirmDeleteModal.d.ts +6 -1
- package/dist/editor/components/ConfirmDeleteModal.js +3 -2
- package/dist/editor/components/DestructiveMenuItem.d.ts +7 -0
- package/dist/editor/components/DestructiveMenuItem.js +8 -0
- package/dist/editor/components/EditorCrashScreen.d.ts +17 -0
- package/dist/editor/components/EditorCrashScreen.js +22 -0
- package/dist/editor/components/EditorErrorBoundary.d.ts +38 -0
- package/dist/editor/components/EditorErrorBoundary.js +40 -0
- package/dist/editor/components/EditorHeader.d.ts +56 -106
- package/dist/editor/components/EditorHeader.js +130 -131
- package/dist/editor/components/EditorRail.d.ts +49 -0
- package/dist/editor/components/EditorRail.js +65 -0
- package/dist/editor/components/EntryBreadcrumb.d.ts +33 -0
- package/dist/editor/components/EntryBreadcrumb.js +56 -0
- package/dist/editor/components/EntryCreateModal.d.ts +12 -7
- package/dist/editor/components/EntryCreateModal.js +51 -57
- package/dist/editor/components/NoEditPermissionNotice.d.ts +6 -5
- package/dist/editor/components/NoEditPermissionNotice.js +5 -4
- package/dist/editor/components/QuickOpenPalette.d.ts +22 -0
- package/dist/editor/components/QuickOpenPalette.js +66 -0
- package/dist/editor/components/ReadOnlyDraftNotice.d.ts +11 -0
- package/dist/editor/components/ReadOnlyDraftNotice.js +12 -0
- package/dist/editor/components/ReasonedMenuItem.d.ts +15 -0
- package/dist/editor/components/ReasonedMenuItem.js +15 -0
- package/dist/editor/components/ReferencedByList.d.ts +11 -0
- package/dist/editor/components/ReferencedByList.js +28 -0
- package/dist/editor/components/RelativeTime.d.ts +10 -0
- package/dist/editor/components/RelativeTime.js +10 -0
- package/dist/editor/components/RenameEntryModal.d.ts +9 -1
- package/dist/editor/components/RenameEntryModal.js +19 -31
- package/dist/editor/components/RetryToastMessage.d.ts +9 -0
- package/dist/editor/components/RetryToastMessage.js +6 -0
- package/dist/editor/components/SaveAllResult.d.ts +25 -0
- package/dist/editor/components/SaveAllResult.js +86 -0
- package/dist/editor/components/SaveSplitButton.d.ts +30 -0
- package/dist/editor/components/SaveSplitButton.js +108 -0
- package/dist/editor/components/StagedChangesDrawer.d.ts +39 -0
- package/dist/editor/components/StagedChangesDrawer.js +92 -0
- package/dist/editor/components/UnavailableEntryNotice.d.ts +6 -0
- package/dist/editor/components/UnavailableEntryNotice.js +9 -0
- package/dist/editor/components/UnavailableTypeMessage.d.ts +6 -0
- package/dist/editor/components/UnavailableTypeMessage.js +9 -0
- package/dist/editor/components/UndoToastMessage.d.ts +18 -0
- package/dist/editor/components/UndoToastMessage.js +10 -0
- package/dist/editor/components/UserBadge.d.ts +2 -5
- package/dist/editor/components/UserBadge.js +62 -33
- package/dist/editor/components/ValidationSummary.d.ts +14 -0
- package/dist/editor/components/ValidationSummary.js +24 -0
- package/dist/editor/components/index.d.ts +2 -2
- package/dist/editor/components/index.js +2 -2
- package/dist/editor/context/ApiClientContext.d.ts +8 -2
- package/dist/editor/context/ApiClientContext.js +19 -8
- package/dist/editor/context/AssetContext.d.ts +13 -35
- package/dist/editor/context/AssetContext.js +12 -31
- package/dist/editor/context/EditorStateContext.d.ts +4 -4
- package/dist/editor/context/EditorStateContext.js +4 -4
- package/dist/editor/context/SWRProvider.d.ts +3 -3
- package/dist/editor/context/SiteMdxAllowContext.d.ts +4 -0
- package/dist/editor/context/SiteMdxAllowContext.js +7 -0
- package/dist/editor/context/index.d.ts +2 -1
- package/dist/editor/context/index.js +2 -1
- package/dist/editor/contrast.d.ts +19 -0
- package/dist/editor/contrast.js +41 -0
- package/dist/editor/copy.d.ts +18 -0
- package/dist/editor/copy.js +18 -0
- package/dist/editor/editor-config.js +16 -0
- package/dist/editor/editor-utils.d.ts +34 -8
- package/dist/editor/editor-utils.js +42 -20
- package/dist/editor/entry-search.d.ts +20 -0
- package/dist/editor/entry-search.js +34 -0
- package/dist/editor/field-breadcrumb.d.ts +12 -0
- package/dist/editor/field-breadcrumb.js +85 -0
- package/dist/editor/field-props.d.ts +86 -0
- package/dist/editor/field-props.js +27 -0
- package/dist/editor/field-target.d.ts +30 -0
- package/dist/editor/field-target.js +77 -0
- package/dist/editor/fields/BlockField.d.ts +12 -0
- package/dist/editor/fields/BlockField.js +89 -73
- package/dist/editor/fields/CodeField.d.ts +2 -0
- package/dist/editor/fields/CodeField.js +6 -2
- package/dist/editor/fields/DateTimeField.d.ts +4 -29
- package/dist/editor/fields/DateTimeField.js +79 -41
- package/dist/editor/fields/ExpandAllButton.d.ts +6 -0
- package/dist/editor/fields/ExpandAllButton.js +4 -0
- package/dist/editor/fields/FieldCrashFallback.d.ts +17 -0
- package/dist/editor/fields/FieldCrashFallback.js +30 -0
- package/dist/editor/fields/FieldLabel.d.ts +37 -0
- package/dist/editor/fields/FieldLabel.js +19 -0
- package/dist/editor/fields/ImageField.d.ts +4 -0
- package/dist/editor/fields/ImageField.js +29 -14
- package/dist/editor/fields/InlineGroupField.js +4 -3
- package/dist/editor/fields/ItemList.d.ts +42 -0
- package/dist/editor/fields/ItemList.js +130 -0
- package/dist/editor/fields/MarkdownField.d.ts +32 -0
- package/dist/editor/fields/MarkdownField.js +223 -46
- package/dist/editor/fields/MdxImageDialog.js +1 -1
- package/dist/editor/fields/NumberField.d.ts +2 -0
- package/dist/editor/fields/NumberField.js +8 -5
- package/dist/editor/fields/NumberListField.d.ts +2 -0
- package/dist/editor/fields/NumberListField.js +19 -16
- package/dist/editor/fields/ObjectField.d.ts +2 -7
- package/dist/editor/fields/ObjectField.js +15 -5
- package/dist/editor/fields/ObjectListField.d.ts +29 -0
- package/dist/editor/fields/ObjectListField.js +57 -0
- package/dist/editor/fields/ReferenceField.d.ts +2 -0
- package/dist/editor/fields/ReferenceField.js +34 -9
- package/dist/editor/fields/SelectField.d.ts +2 -0
- package/dist/editor/fields/SelectField.js +11 -2
- package/dist/editor/fields/StringListField.d.ts +4 -1
- package/dist/editor/fields/StringListField.js +22 -8
- package/dist/editor/fields/TextField.d.ts +2 -0
- package/dist/editor/fields/TextField.js +6 -2
- package/dist/editor/fields/ToggleField.d.ts +2 -0
- package/dist/editor/fields/ToggleField.js +8 -3
- package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +1 -1
- package/dist/editor/fields/entry-link/InsertEntryLink.js +1 -1
- package/dist/editor/fields/form-expansion.d.ts +9 -0
- package/dist/editor/fields/form-expansion.js +2 -0
- package/dist/editor/fields/item-expansion.d.ts +15 -0
- package/dist/editor/fields/item-expansion.js +60 -0
- package/dist/editor/fields/list-item-keys.d.ts +8 -0
- package/dist/editor/fields/list-item-keys.js +50 -0
- package/dist/editor/fields/list-item-title.d.ts +20 -0
- package/dist/editor/fields/list-item-title.js +85 -0
- package/dist/editor/fields/markdown-export-options.d.ts +2 -0
- package/dist/editor/fields/markdown-export-options.js +8 -0
- package/dist/editor/fields/markdown-fidelity-visitors.d.ts +11 -0
- package/dist/editor/fields/markdown-fidelity-visitors.js +160 -0
- package/dist/editor/fields/mdx-jsx-support.d.ts +5 -1
- package/dist/editor/fields/mdx-jsx-support.js +157 -69
- package/dist/editor/fields/rich-text-failures.d.ts +5 -0
- package/dist/editor/fields/rich-text-failures.js +26 -0
- package/dist/editor/fields/toolbar-wrap.d.ts +7 -0
- package/dist/editor/fields/toolbar-wrap.js +35 -0
- package/dist/editor/form-outline.d.ts +13 -0
- package/dist/editor/form-outline.js +70 -0
- package/dist/editor/group-manager/GroupCard.d.ts +4 -1
- package/dist/editor/group-manager/GroupCard.js +12 -3
- package/dist/editor/group-manager/GroupForm.js +1 -1
- package/dist/editor/group-manager/InternalGroupsTab.d.ts +2 -1
- package/dist/editor/group-manager/InternalGroupsTab.js +4 -4
- package/dist/editor/group-manager/MemberList.d.ts +2 -1
- package/dist/editor/group-manager/MemberList.js +15 -4
- package/dist/editor/group-manager/hooks/useGroupState.d.ts +5 -0
- package/dist/editor/group-manager/hooks/useGroupState.js +30 -7
- package/dist/editor/group-manager/index.js +34 -5
- package/dist/editor/group-manager/types.d.ts +5 -2
- package/dist/editor/hooks/create-branch-request.d.ts +20 -0
- package/dist/editor/hooks/create-branch-request.js +54 -0
- package/dist/editor/hooks/save-all.d.ts +68 -0
- package/dist/editor/hooks/save-all.js +61 -0
- package/dist/editor/hooks/useBranchActions.d.ts +11 -1
- package/dist/editor/hooks/useBranchActions.js +42 -19
- package/dist/editor/hooks/useBranchManager.d.ts +21 -0
- package/dist/editor/hooks/useBranchManager.js +321 -125
- package/dist/editor/hooks/useBranchesData.d.ts +8 -4
- package/dist/editor/hooks/useBranchesData.js +9 -5
- package/dist/editor/hooks/useCommentSystem.d.ts +14 -2
- package/dist/editor/hooks/useCommentSystem.js +79 -55
- package/dist/editor/hooks/useCommentsData.d.ts +11 -7
- package/dist/editor/hooks/useCommentsData.js +20 -19
- package/dist/editor/hooks/useDraftManager.d.ts +77 -3
- package/dist/editor/hooks/useDraftManager.js +887 -94
- package/dist/editor/hooks/useEditorLayout.d.ts +53 -7
- package/dist/editor/hooks/useEditorLayout.js +98 -20
- package/dist/editor/hooks/useEntriesData.d.ts +2 -1
- package/dist/editor/hooks/useEntriesData.js +18 -4
- package/dist/editor/hooks/useEntryManager.d.ts +41 -2
- package/dist/editor/hooks/useEntryManager.js +227 -41
- package/dist/editor/hooks/useGroupManager.js +13 -16
- package/dist/editor/hooks/usePermissionManager.js +14 -6
- package/dist/editor/hooks/usePreviewMarks.d.ts +21 -0
- package/dist/editor/hooks/usePreviewMarks.js +33 -0
- package/dist/editor/hooks/useReferenceResolution.d.ts +10 -3
- package/dist/editor/hooks/useReferenceResolution.js +34 -177
- package/dist/editor/hooks/useSchemaManager.d.ts +25 -6
- package/dist/editor/hooks/useSchemaManager.js +97 -39
- package/dist/editor/hooks/useUserMetadata.d.ts +14 -4
- package/dist/editor/hooks/useUserMetadata.js +76 -47
- package/dist/editor/hooks/user-metadata-batcher.d.ts +8 -0
- package/dist/editor/hooks/user-metadata-batcher.js +33 -0
- package/dist/editor/media/AssetCard.d.ts +6 -7
- package/dist/editor/media/AssetCard.js +24 -31
- package/dist/editor/media/AssetListRow.d.ts +16 -0
- package/dist/editor/media/AssetListRow.js +41 -0
- package/dist/editor/media/CropStep.js +6 -1
- package/dist/editor/media/MediaLibrary.d.ts +4 -3
- package/dist/editor/media/MediaLibrary.js +11 -4
- package/dist/editor/media/MediaLibraryBody.d.ts +3 -3
- package/dist/editor/media/MediaLibraryBody.js +195 -80
- package/dist/editor/media/MediaLibrarySkeleton.d.ts +6 -0
- package/dist/editor/media/MediaLibrarySkeleton.js +14 -0
- package/dist/editor/media/crop-math.d.ts +3 -4
- package/dist/editor/media/crop-math.js +11 -22
- package/dist/editor/media/editor-image-src.d.ts +8 -0
- package/dist/editor/media/editor-image-src.js +10 -0
- package/dist/editor/media/format-bytes.d.ts +2 -0
- package/dist/editor/media/format-bytes.js +23 -0
- package/dist/editor/media/media-library-prefs.d.ts +23 -0
- package/dist/editor/media/media-library-prefs.js +49 -0
- package/dist/editor/media/media-library-view.d.ts +34 -0
- package/dist/editor/media/media-library-view.js +40 -0
- package/dist/editor/media/useAssetLibrary.d.ts +30 -0
- package/dist/editor/media/useAssetLibrary.js +126 -0
- package/dist/editor/media/useStableAssetOrder.d.ts +11 -0
- package/dist/editor/media/useStableAssetOrder.js +37 -0
- package/dist/editor/media/useThumbnailFailure.d.ts +6 -0
- package/dist/editor/media/useThumbnailFailure.js +16 -0
- package/dist/editor/permission-manager/GroupSelector.js +2 -2
- package/dist/editor/permission-manager/PermissionEditor.d.ts +1 -1
- package/dist/editor/permission-manager/PermissionEditor.js +15 -4
- package/dist/editor/permission-manager/PermissionLevelBadge.js +1 -1
- package/dist/editor/permission-manager/PermissionTree.d.ts +1 -1
- package/dist/editor/permission-manager/UserSelector.d.ts +1 -1
- package/dist/editor/permission-manager/UserSelector.js +3 -3
- package/dist/editor/permission-manager/index.js +12 -3
- package/dist/editor/permission-manager/types.d.ts +5 -2
- package/dist/editor/platform.d.ts +3 -0
- package/dist/editor/platform.js +9 -0
- package/dist/editor/preview-asset-base.d.ts +9 -0
- package/dist/editor/preview-asset-base.js +17 -0
- package/dist/editor/preview-bridge.d.ts +51 -27
- package/dist/editor/preview-bridge.js +103 -144
- package/dist/editor/preview-marks.d.ts +15 -0
- package/dist/editor/preview-marks.js +74 -0
- package/dist/editor/preview-path.d.ts +12 -0
- package/dist/editor/preview-path.js +45 -1
- package/dist/editor/preview-scale.d.ts +27 -0
- package/dist/editor/preview-scale.js +34 -0
- package/dist/editor/raw-asset-base.d.ts +5 -0
- package/dist/editor/raw-asset-base.js +14 -0
- package/dist/editor/relative-time.d.ts +4 -4
- package/dist/editor/relative-time.js +16 -8
- package/dist/editor/schema-editor/CollectionEditor.d.ts +20 -3
- package/dist/editor/schema-editor/CollectionEditor.js +361 -62
- package/dist/editor/schema-editor/EntryTypeEditor.d.ts +5 -1
- package/dist/editor/schema-editor/EntryTypeEditor.js +11 -6
- package/dist/editor/schema-editor/collection-draft.d.ts +22 -0
- package/dist/editor/schema-editor/collection-draft.js +40 -0
- package/dist/editor/schema-editor/index.d.ts +1 -1
- package/dist/editor/schema-label.d.ts +10 -0
- package/dist/editor/schema-label.js +17 -0
- package/dist/editor/settings-conflict.d.ts +5 -0
- package/dist/editor/settings-conflict.js +8 -0
- package/dist/editor/shell/ActivityRail.d.ts +37 -0
- package/dist/editor/shell/ActivityRail.js +91 -0
- package/dist/editor/shell/EditorShell.d.ts +17 -0
- package/dist/editor/shell/EditorShell.js +31 -0
- package/dist/editor/shell/PaneLayout.d.ts +28 -0
- package/dist/editor/shell/PaneLayout.js +116 -0
- package/dist/editor/shell/SidePanelHost.d.ts +35 -0
- package/dist/editor/shell/SidePanelHost.js +184 -0
- package/dist/editor/shell/TopBar.d.ts +12 -0
- package/dist/editor/shell/TopBar.js +14 -0
- package/dist/editor/shell/host-chrome.d.ts +16 -0
- package/dist/editor/shell/host-chrome.js +69 -0
- package/dist/editor/slug.d.ts +16 -0
- package/dist/editor/slug.js +43 -0
- package/dist/editor/theme.d.ts +2 -1
- package/dist/editor/theme.js +314 -13
- package/dist/editor/unanswered.d.ts +17 -0
- package/dist/editor/unanswered.js +26 -0
- package/dist/editor/unavailable-entry-type.d.ts +20 -0
- package/dist/editor/unavailable-entry-type.js +28 -0
- package/dist/editor/undo-removal.d.ts +57 -0
- package/dist/editor/undo-removal.js +163 -0
- package/dist/editor/user-display.d.ts +4 -0
- package/dist/editor/user-display.js +5 -0
- package/dist/editor/utils/confirm-modal.d.ts +8 -0
- package/dist/editor/utils/confirm-modal.js +12 -0
- package/dist/editor/utils/editor-errors.d.ts +19 -0
- package/dist/editor/utils/editor-errors.js +32 -0
- package/dist/editor/utils/focus-return.d.ts +56 -0
- package/dist/editor/utils/focus-return.js +106 -0
- package/dist/entry-link-resolver.d.ts +11 -1
- package/dist/entry-link-resolver.js +65 -5
- package/dist/entry-schema-registry.d.ts +3 -3
- package/dist/entry-schema-registry.js +7 -4
- package/dist/entry-schema.d.ts +49 -3
- package/dist/entry-schema.js +18 -0
- package/dist/git-manager.d.ts +172 -10
- package/dist/git-manager.js +449 -107
- package/dist/github-service.d.ts +39 -2
- package/dist/github-service.js +101 -7
- package/dist/http/handler.js +74 -9
- package/dist/http/types.d.ts +2 -0
- package/dist/http/worker-not-ready.d.ts +9 -4
- package/dist/http/worker-not-ready.js +59 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/item-keys.d.ts +46 -0
- package/dist/item-keys.js +235 -0
- package/dist/operating-mode/editor-mode-check.d.ts +15 -0
- package/dist/operating-mode/editor-mode-check.js +24 -0
- package/dist/operating-mode/mode-env.d.ts +13 -14
- package/dist/operating-mode/mode-env.js +13 -14
- package/dist/operating-mode/types.d.ts +2 -2
- package/dist/paths/branch.d.ts +0 -1
- package/dist/paths/branch.js +1 -8
- package/dist/paths/index.d.ts +1 -1
- package/dist/paths/index.js +1 -1
- package/dist/paths/validation.js +0 -2
- package/dist/preview.d.ts +8 -0
- package/dist/preview.js +7 -0
- package/dist/reference-resolver.js +1 -0
- package/dist/resolve-canopy-user.d.ts +1 -1
- package/dist/resolve-canopy-user.js +1 -1
- package/dist/schema/meta-loader.d.ts +18 -8
- package/dist/schema/meta-loader.js +42 -9
- package/dist/schema/registry-fingerprint.d.ts +14 -0
- package/dist/schema/registry-fingerprint.js +63 -0
- package/dist/schema/resolver.d.ts +7 -4
- package/dist/schema/resolver.js +7 -6
- package/dist/schema/schema-store.d.ts +7 -10
- package/dist/schema/schema-store.js +13 -11
- package/dist/schema/schema-unavailable-error.d.ts +11 -0
- package/dist/schema/schema-unavailable-error.js +15 -0
- package/dist/schema/types.d.ts +22 -0
- package/dist/schema-registry-record.d.ts +35 -0
- package/dist/schema-registry-record.js +134 -0
- package/dist/server.d.ts +79 -9
- package/dist/server.js +62 -8
- package/dist/services.d.ts +33 -5
- package/dist/services.js +155 -37
- package/dist/settings-workspace.d.ts +23 -3
- package/dist/settings-workspace.js +159 -45
- package/dist/stamp-on-publish.d.ts +36 -0
- package/dist/stamp-on-publish.js +71 -0
- package/dist/static/index.d.ts +58 -5
- package/dist/static/index.js +133 -3
- package/dist/static/seo.d.ts +5 -5
- package/dist/static/seo.js +5 -5
- package/dist/submission-attribution.d.ts +8 -0
- package/dist/submission-attribution.js +22 -0
- package/dist/system-users.d.ts +12 -0
- package/dist/system-users.js +12 -0
- package/dist/task-queue/cms-task-queue.d.ts +1 -1
- package/dist/task-queue/cms-task-queue.js +1 -1
- package/dist/task-queue/index.d.ts +1 -1
- package/dist/task-queue/index.js +1 -1
- package/dist/task-queue/task-queue.d.ts +6 -0
- package/dist/task-queue/task-queue.js +29 -0
- package/dist/task-queue/worker-status.d.ts +35 -9
- package/dist/task-queue/worker-status.js +84 -17
- package/dist/types.d.ts +90 -9
- package/dist/url-collision.js +3 -3
- package/dist/url-exclusivity-fixtures.d.ts +8 -1
- package/dist/url-exclusivity-fixtures.js +11 -1
- package/dist/user.js +2 -1
- package/dist/utils/atomic-write.js +2 -1
- package/dist/utils/base-branch.d.ts +13 -0
- package/dist/utils/base-branch.js +23 -0
- package/dist/utils/content-serialize.d.ts +15 -7
- package/dist/utils/content-serialize.js +353 -51
- package/dist/utils/content-write-lock.d.ts +6 -1
- package/dist/utils/content-write-lock.js +21 -6
- package/dist/utils/debug.d.ts +1 -1
- package/dist/utils/debug.js +5 -3
- package/dist/utils/error.js +13 -2
- package/dist/utils/escape-text.d.ts +7 -0
- package/dist/utils/escape-text.js +27 -0
- package/dist/utils/fs.d.ts +12 -4
- package/dist/utils/fs.js +18 -5
- package/dist/utils/git.d.ts +21 -1
- package/dist/utils/git.js +63 -8
- package/dist/utils/json-source-splice.d.ts +14 -0
- package/dist/utils/json-source-splice.js +120 -0
- package/dist/utils/markdown-body-splice.d.ts +39 -0
- package/dist/utils/markdown-body-splice.js +421 -0
- package/dist/utils/occ-json-write.js +18 -12
- package/dist/utils/provision-log.d.ts +25 -0
- package/dist/utils/provision-log.js +49 -0
- package/dist/utils/provisioning-lock.d.ts +32 -1
- package/dist/utils/provisioning-lock.js +78 -32
- package/dist/utils/sanitize-href.d.ts +9 -23
- package/dist/utils/sanitize-href.js +14 -28
- package/dist/utils/title-field.d.ts +10 -0
- package/dist/utils/title-field.js +47 -0
- package/dist/utils/yaml-source-splice.d.ts +58 -0
- package/dist/utils/yaml-source-splice.js +515 -0
- package/dist/validation/block-structural-keys.d.ts +1 -1
- package/dist/validation/block-structural-keys.js +1 -1
- package/dist/validation/deletion-checker.d.ts +21 -29
- package/dist/validation/deletion-checker.js +41 -117
- package/dist/validation/entry-validator.d.ts +6 -10
- package/dist/validation/entry-validator.js +19 -26
- package/dist/validation/field-traversal.d.ts +22 -0
- package/dist/validation/field-traversal.js +39 -8
- package/dist/validation/markdown-safety.d.ts +68 -0
- package/dist/validation/markdown-safety.js +596 -0
- package/dist/validation/mdx-allowlist.d.ts +55 -0
- package/dist/validation/mdx-allowlist.js +183 -0
- package/dist/validation/reference-validator.d.ts +2 -0
- package/dist/validation/reference-validator.js +3 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/branch-policy.d.ts +124 -0
- package/dist/worker/branch-policy.js +243 -0
- package/dist/worker/bundle-header.d.ts +117 -0
- package/dist/worker/bundle-header.js +253 -0
- package/dist/worker/cms-worker.d.ts +236 -142
- package/dist/worker/cms-worker.js +772 -285
- package/dist/worker/gateway-egress.d.ts +43 -0
- package/dist/worker/gateway-egress.js +209 -0
- package/dist/worker/gateway-instance.d.ts +42 -0
- package/dist/worker/gateway-instance.js +129 -0
- package/dist/worker/git-process.d.ts +81 -0
- package/dist/worker/git-process.js +234 -0
- package/dist/worker/git-sync.d.ts +34 -17
- package/dist/worker/git-sync.js +394 -131
- package/dist/worker/github-auth.d.ts +9 -9
- package/dist/worker/github-auth.js +13 -15
- package/dist/worker/github-gateway-http.d.ts +62 -0
- package/dist/worker/github-gateway-http.js +977 -0
- package/dist/worker/github-gateway-wire.d.ts +590 -0
- package/dist/worker/github-gateway-wire.js +488 -0
- package/dist/worker/github-gateway.d.ts +238 -0
- package/dist/worker/github-gateway.js +726 -0
- package/dist/worker/github-mirror-upkeep.d.ts +75 -0
- package/dist/worker/github-mirror-upkeep.js +266 -0
- package/dist/worker/github-mirror.d.ts +364 -0
- package/dist/worker/github-mirror.js +1030 -0
- package/dist/worker/github-transfer.d.ts +56 -0
- package/dist/worker/github-transfer.js +444 -0
- package/dist/worker/history-rewrite.d.ts +4 -5
- package/dist/worker/history-rewrite.js +12 -17
- package/dist/worker/provisioned-workspace.d.ts +5 -5
- package/dist/worker/provisioned-workspace.js +5 -5
- package/dist/worker/rebase.d.ts +1 -1
- package/dist/worker/rebase.js +58 -54
- package/dist/worker/remote-git-maintenance.d.ts +3 -0
- package/dist/worker/remote-git-maintenance.js +12 -0
- package/dist/worker/schema-gate.d.ts +49 -0
- package/dist/worker/schema-gate.js +226 -0
- package/dist/worker/self-checks.d.ts +107 -0
- package/dist/worker/self-checks.js +358 -0
- package/dist/worker/shared-repo-git.d.ts +96 -0
- package/dist/worker/shared-repo-git.js +459 -0
- package/dist/worker/sparse-cone.d.ts +20 -0
- package/dist/worker/sparse-cone.js +136 -0
- package/dist/worker/stale-mirror.d.ts +82 -0
- package/dist/worker/stale-mirror.js +130 -0
- package/dist/worker/task-runner.d.ts +5 -3
- package/dist/worker/task-runner.js +287 -186
- package/dist/worker/worker-context.d.ts +36 -37
- package/package.json +35 -7
- package/dist/editor/EditorPanes.d.ts +0 -10
- package/dist/editor/EditorPanes.js +0 -98
- package/dist/editor/components/EditorFooter.d.ts +0 -11
- package/dist/editor/components/EditorFooter.js +0 -9
- package/dist/editor/components/EditorSidebar.d.ts +0 -67
- package/dist/editor/components/EditorSidebar.js +0 -22
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
/**
|
|
3
|
+
* The preview bridge's protocol and its host-page side: the hooks a framed page runs. Served to
|
|
4
|
+
* hosts through `canopycms/preview`, so everything it reaches stays free of CSS and editor UI
|
|
5
|
+
* (`pnpm lint:bundle`); the editor's side is `PreviewFrame.tsx`.
|
|
6
|
+
*/
|
|
7
|
+
import { useCallback, useEffect, useLayoutEffect, useState } from 'react';
|
|
8
|
+
import { createFieldProps } from './field-props.js';
|
|
9
|
+
import { setPreviewAssetBase } from './preview-asset-base.js';
|
|
5
10
|
import { isSamePreviewPath } from './preview-path.js';
|
|
6
|
-
|
|
11
|
+
import { readAssetBase } from './raw-asset-base.js';
|
|
7
12
|
export const CANOPY_PREVIEW_MESSAGE = 'canopycms:draft:update';
|
|
8
13
|
export const CANOPY_PREVIEW_FOCUS = 'canopycms:preview:focus';
|
|
9
14
|
export const CANOPY_PREVIEW_HIGHLIGHT = 'canopycms:preview:highlight';
|
|
10
15
|
export const CANOPY_PREVIEW_READY = 'canopycms:preview:ready';
|
|
11
16
|
export const CANOPY_PREVIEW_ERROR = 'canopycms:preview:error';
|
|
17
|
+
export const CANOPY_PREVIEW_MARKS = 'canopycms:preview:marks';
|
|
12
18
|
/**
|
|
13
19
|
* Resolve a (possibly relative) URL to an origin for postMessage targeting.
|
|
14
20
|
* Falls back to the page's own origin, so same-origin editor/preview setups
|
|
@@ -32,7 +38,7 @@ export const resolveMessageOrigin = (url) => {
|
|
|
32
38
|
* a sandboxed embed shares that string, so it identifies nothing, and postMessage
|
|
33
39
|
* throws on a literal 'null' targetOrigin. '' covers the SSR fallback.
|
|
34
40
|
*/
|
|
35
|
-
const isOpaqueOrigin = (origin) => origin === 'null' || origin === '';
|
|
41
|
+
export const isOpaqueOrigin = (origin) => origin === 'null' || origin === '';
|
|
36
42
|
/**
|
|
37
43
|
* True only for messages from the embedding editor window with the expected origin.
|
|
38
44
|
*
|
|
@@ -50,14 +56,8 @@ export const isTrustedEditorMessage = (event, editorOrigin) => {
|
|
|
50
56
|
return false;
|
|
51
57
|
return event.origin === expected && event.source === window.parent;
|
|
52
58
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return;
|
|
56
|
-
const target = targetOrigin ?? resolveMessageOrigin(iframe.src);
|
|
57
|
-
if (isOpaqueOrigin(target))
|
|
58
|
-
return;
|
|
59
|
-
iframe.contentWindow.postMessage(message, target);
|
|
60
|
-
};
|
|
59
|
+
/** The most distinct mark paths one report carries, and the longest path it carries. */
|
|
60
|
+
export const MARK_REPORT_LIMITS = { paths: 500, pathLength: 512 };
|
|
61
61
|
const resolvePreviewPath = (explicit) => {
|
|
62
62
|
if (explicit)
|
|
63
63
|
return explicit;
|
|
@@ -76,9 +76,7 @@ export const useCanopyPreview = (opts) => {
|
|
|
76
76
|
const { data, isLoading } = usePreviewData(resolvedPath, opts.initialData, bridgeOpts);
|
|
77
77
|
const highlightEnabled = usePreviewHighlight(bridgeOpts);
|
|
78
78
|
usePreviewFocusEmitter(resolvedPath, bridgeOpts);
|
|
79
|
-
const fieldProps = (
|
|
80
|
-
'data-canopy-path': Array.isArray(canopyPath) ? formatCanopyPath(canopyPath) : canopyPath,
|
|
81
|
-
});
|
|
79
|
+
const fieldProps = createFieldProps();
|
|
82
80
|
/**
|
|
83
81
|
* Report that the current draft fails to compile/render (the editor surfaces it
|
|
84
82
|
* next to the preview). Call with null once the draft renders cleanly again.
|
|
@@ -106,6 +104,7 @@ export const useCanopyPreview = (opts) => {
|
|
|
106
104
|
*/
|
|
107
105
|
export const usePreviewData = (path, initialData, opts) => {
|
|
108
106
|
const [data, setData] = useState(initialData);
|
|
107
|
+
// Every key of the loading state is optional, so an empty object is a valid one.
|
|
109
108
|
const [isLoading, setIsLoading] = useState({});
|
|
110
109
|
const editorOrigin = opts?.editorOrigin;
|
|
111
110
|
useEffect(() => {
|
|
@@ -121,6 +120,8 @@ export const usePreviewData = (path, initialData, opts) => {
|
|
|
121
120
|
return;
|
|
122
121
|
if (typeof msg.path !== 'string' || !isSamePreviewPath(msg.path, path))
|
|
123
122
|
return;
|
|
123
|
+
// Before setData, so the render the draft triggers already reads it.
|
|
124
|
+
setPreviewAssetBase(readAssetBase(msg.assetBase));
|
|
124
125
|
setData(msg.data);
|
|
125
126
|
if (msg.isLoading !== undefined) {
|
|
126
127
|
setIsLoading(msg.isLoading);
|
|
@@ -128,8 +129,8 @@ export const usePreviewData = (path, initialData, opts) => {
|
|
|
128
129
|
};
|
|
129
130
|
window.addEventListener('message', handler);
|
|
130
131
|
// Notify parent that this preview page is ready to receive draft updates.
|
|
131
|
-
// This is needed because onLoad in the parent
|
|
132
|
-
// so the first postMessage from the parent
|
|
132
|
+
// This is needed because onLoad in the parent can fire before React effects run,
|
|
133
|
+
// so the first postMessage from the parent may arrive before this listener is set up.
|
|
133
134
|
const target = resolveMessageOrigin(editorOrigin);
|
|
134
135
|
if (!isOpaqueOrigin(target)) {
|
|
135
136
|
window.parent.postMessage({ type: CANOPY_PREVIEW_READY, path }, target);
|
|
@@ -138,6 +139,26 @@ export const usePreviewData = (path, initialData, opts) => {
|
|
|
138
139
|
}, [path, editorOrigin]);
|
|
139
140
|
return { data, isLoading };
|
|
140
141
|
};
|
|
142
|
+
/** React 18 warns about a layout effect in a server render, where no effect runs. */
|
|
143
|
+
const useBrowserLayoutEffect = (effect, deps) => (typeof window === 'undefined' ? useEffect : useLayoutEffect)(effect, deps);
|
|
144
|
+
/**
|
|
145
|
+
* Holds a `createPreviewPage` view back until hydration commits, then stores `assetBase` and opens
|
|
146
|
+
* before paint: the server HTML has no public `/assets/t/` URL and nothing to mismatch. A draft's
|
|
147
|
+
* prefix still wins, since drafts follow the ready message `usePreviewData` posts from a passive
|
|
148
|
+
* effect. With no `assetBase` (a public page) it is open from the start.
|
|
149
|
+
* @internal `withCanopyPreview` is its only caller.
|
|
150
|
+
*/
|
|
151
|
+
export const usePreviewAssetBaseGate = (assetBase) => {
|
|
152
|
+
const held = assetBase !== undefined;
|
|
153
|
+
const [open, setOpen] = useState(!held);
|
|
154
|
+
useBrowserLayoutEffect(() => {
|
|
155
|
+
if (open)
|
|
156
|
+
return;
|
|
157
|
+
setPreviewAssetBase(readAssetBase(assetBase));
|
|
158
|
+
setOpen(true);
|
|
159
|
+
}, [open, assetBase]);
|
|
160
|
+
return open;
|
|
161
|
+
};
|
|
141
162
|
/**
|
|
142
163
|
* Hook for preview pages to listen for highlight mode and toggle an outline on clickable elements.
|
|
143
164
|
*/
|
|
@@ -175,6 +196,70 @@ export const usePreviewHighlight = (opts) => {
|
|
|
175
196
|
window.addEventListener('message', handler);
|
|
176
197
|
return () => window.removeEventListener('message', handler);
|
|
177
198
|
}, [editorOrigin]);
|
|
199
|
+
// Reported after the render that turned highlighting on, whenever the marks change (content
|
|
200
|
+
// rendered after hydration), and after every trusted draft even when they do not: the editor checks a
|
|
201
|
+
// report against its draft as it stood when the report arrived, so a block whose template
|
|
202
|
+
// changed under unchanged marks needs a fresh one.
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
if (!enabled || window.parent === window)
|
|
205
|
+
return;
|
|
206
|
+
const target = resolveMessageOrigin(editorOrigin);
|
|
207
|
+
if (isOpaqueOrigin(target))
|
|
208
|
+
return;
|
|
209
|
+
let reported;
|
|
210
|
+
const report = () => {
|
|
211
|
+
const marks = document.querySelectorAll('[data-canopy-path]');
|
|
212
|
+
const paths = new Set();
|
|
213
|
+
for (const mark of marks) {
|
|
214
|
+
if (paths.size === MARK_REPORT_LIMITS.paths)
|
|
215
|
+
break;
|
|
216
|
+
const path = mark.getAttribute('data-canopy-path') ?? '';
|
|
217
|
+
if (path.length <= MARK_REPORT_LIMITS.pathLength)
|
|
218
|
+
paths.add(path);
|
|
219
|
+
}
|
|
220
|
+
const msg = {
|
|
221
|
+
type: CANOPY_PREVIEW_MARKS,
|
|
222
|
+
count: marks.length,
|
|
223
|
+
paths: [...paths],
|
|
224
|
+
};
|
|
225
|
+
const key = JSON.stringify([msg.count, msg.paths]);
|
|
226
|
+
if (key === reported)
|
|
227
|
+
return;
|
|
228
|
+
reported = key;
|
|
229
|
+
window.parent.postMessage(msg, target);
|
|
230
|
+
};
|
|
231
|
+
report();
|
|
232
|
+
// A trailing throttle, so steady DOM churn cannot hold the report back, on a timer rather
|
|
233
|
+
// than requestAnimationFrame, which a hidden frame never runs. It also gives a draft time to
|
|
234
|
+
// render before the report it triggers.
|
|
235
|
+
let timer;
|
|
236
|
+
const schedule = () => {
|
|
237
|
+
timer ??= setTimeout(() => {
|
|
238
|
+
timer = undefined;
|
|
239
|
+
report();
|
|
240
|
+
}, 100);
|
|
241
|
+
};
|
|
242
|
+
const observer = new MutationObserver(schedule);
|
|
243
|
+
observer.observe(document.body, {
|
|
244
|
+
subtree: true,
|
|
245
|
+
childList: true,
|
|
246
|
+
attributeFilter: ['data-canopy-path'],
|
|
247
|
+
});
|
|
248
|
+
const onDraft = (event) => {
|
|
249
|
+
if (!isTrustedEditorMessage(event, editorOrigin))
|
|
250
|
+
return;
|
|
251
|
+
if (event.data?.type !== CANOPY_PREVIEW_MESSAGE)
|
|
252
|
+
return;
|
|
253
|
+
reported = undefined;
|
|
254
|
+
schedule();
|
|
255
|
+
};
|
|
256
|
+
window.addEventListener('message', onDraft);
|
|
257
|
+
return () => {
|
|
258
|
+
observer.disconnect();
|
|
259
|
+
window.removeEventListener('message', onDraft);
|
|
260
|
+
clearTimeout(timer);
|
|
261
|
+
};
|
|
262
|
+
}, [enabled, editorOrigin]);
|
|
178
263
|
return enabled;
|
|
179
264
|
};
|
|
180
265
|
/**
|
|
@@ -207,129 +292,3 @@ export const usePreviewFocusEmitter = (entryPath, opts) => {
|
|
|
207
292
|
return () => document.removeEventListener('click', handleClick);
|
|
208
293
|
}, [entryPath, editorOrigin]);
|
|
209
294
|
};
|
|
210
|
-
/**
|
|
211
|
-
* Lightweight iframe wrapper to keep the preview in sync with form state.
|
|
212
|
-
* It posts the latest draft data to the iframe after load and when data changes.
|
|
213
|
-
*/
|
|
214
|
-
export const PreviewFrame = ({ src, path, data, isLoading, className, style, highlightEnabled, onPreviewError, }) => {
|
|
215
|
-
const iframeRef = useRef(null);
|
|
216
|
-
// Pin the preview origin from the src prop: outbound messages target it (never '*'),
|
|
217
|
-
// and inbound messages must come from it. An iframe that navigates cross-origin
|
|
218
|
-
// silently stops participating in the bridge.
|
|
219
|
-
const previewOrigin = resolveMessageOrigin(src);
|
|
220
|
-
// Show progress bar while waiting for the preview's ready handshake.
|
|
221
|
-
const [syncPending, setSyncPending] = useState(data !== undefined);
|
|
222
|
-
// Reset when navigating to a different entry (src change = new iframe page load).
|
|
223
|
-
const [prevSrc, setPrevSrc] = useState(src);
|
|
224
|
-
if (src !== prevSrc) {
|
|
225
|
-
setPrevSrc(src);
|
|
226
|
-
setSyncPending(data !== undefined);
|
|
227
|
-
}
|
|
228
|
-
// Inject the progress bar keyframe animation once per page.
|
|
229
|
-
useEffect(() => {
|
|
230
|
-
const styleId = 'canopycms-preview-sync-style';
|
|
231
|
-
if (!document.getElementById(styleId)) {
|
|
232
|
-
const el = document.createElement('style');
|
|
233
|
-
el.id = styleId;
|
|
234
|
-
el.textContent = `@keyframes canopy-preview-sync { 0% { transform: translateX(-100%); } 100% { transform: translateX(250%); } }`;
|
|
235
|
-
document.head.appendChild(el);
|
|
236
|
-
}
|
|
237
|
-
}, []);
|
|
238
|
-
const post = () => {
|
|
239
|
-
if (data === undefined)
|
|
240
|
-
return;
|
|
241
|
-
sendDraftUpdate(iframeRef.current, {
|
|
242
|
-
type: CANOPY_PREVIEW_MESSAGE,
|
|
243
|
-
path,
|
|
244
|
-
data,
|
|
245
|
-
isLoading,
|
|
246
|
-
}, previewOrigin);
|
|
247
|
-
};
|
|
248
|
-
const postHighlight = () => {
|
|
249
|
-
if (!iframeRef.current?.contentWindow)
|
|
250
|
-
return;
|
|
251
|
-
if (isOpaqueOrigin(previewOrigin))
|
|
252
|
-
return;
|
|
253
|
-
const msg = {
|
|
254
|
-
type: CANOPY_PREVIEW_HIGHLIGHT,
|
|
255
|
-
enabled: Boolean(highlightEnabled),
|
|
256
|
-
};
|
|
257
|
-
iframeRef.current.contentWindow.postMessage(msg, previewOrigin);
|
|
258
|
-
};
|
|
259
|
-
// Keep refs pointing at the latest closures so the message handler below never goes stale.
|
|
260
|
-
const postRef = useRef(post);
|
|
261
|
-
const postHighlightRef = useRef(postHighlight);
|
|
262
|
-
const onPreviewErrorRef = useRef(onPreviewError);
|
|
263
|
-
useEffect(() => {
|
|
264
|
-
postRef.current = post;
|
|
265
|
-
postHighlightRef.current = postHighlight;
|
|
266
|
-
onPreviewErrorRef.current = onPreviewError;
|
|
267
|
-
});
|
|
268
|
-
useEffect(() => {
|
|
269
|
-
post();
|
|
270
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- post is tracked via ref
|
|
271
|
-
}, [data, isLoading]);
|
|
272
|
-
useEffect(() => {
|
|
273
|
-
postHighlight();
|
|
274
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- postHighlight is tracked via ref
|
|
275
|
-
}, [highlightEnabled]);
|
|
276
|
-
// When the preview page's React effects have run and its message listener is ready,
|
|
277
|
-
// it sends CANOPY_PREVIEW_READY. We respond with the current data so the preview
|
|
278
|
-
// receives the draft even if it wasn't ready when onLoad fired.
|
|
279
|
-
useEffect(() => {
|
|
280
|
-
const handleMessage = (event) => {
|
|
281
|
-
if (!iframeRef.current || event.source !== iframeRef.current.contentWindow)
|
|
282
|
-
return;
|
|
283
|
-
// Source check alone is insufficient if the iframe navigated cross-origin.
|
|
284
|
-
if (isOpaqueOrigin(previewOrigin) || event.origin !== previewOrigin)
|
|
285
|
-
return;
|
|
286
|
-
const type = event.data?.type;
|
|
287
|
-
if (type === CANOPY_PREVIEW_READY) {
|
|
288
|
-
postRef.current();
|
|
289
|
-
postHighlightRef.current();
|
|
290
|
-
setSyncPending(false);
|
|
291
|
-
}
|
|
292
|
-
else if (type === CANOPY_PREVIEW_ERROR) {
|
|
293
|
-
const msg = event.data;
|
|
294
|
-
// Shape-check the payload: an adopter passing an Error object (instead of
|
|
295
|
-
// err.message) must not crash the editor when rendered as a React child.
|
|
296
|
-
if (msg.message != null && typeof msg.message !== 'string')
|
|
297
|
-
return;
|
|
298
|
-
const fieldPath = typeof msg.fieldPath === 'string' ? msg.fieldPath : undefined;
|
|
299
|
-
onPreviewErrorRef.current?.(msg.message == null
|
|
300
|
-
? null
|
|
301
|
-
: { message: msg.message, ...(fieldPath ? { fieldPath } : {}) });
|
|
302
|
-
}
|
|
303
|
-
};
|
|
304
|
-
window.addEventListener('message', handleMessage);
|
|
305
|
-
return () => window.removeEventListener('message', handleMessage);
|
|
306
|
-
}, [previewOrigin]);
|
|
307
|
-
return (_jsxs("div", { className: className, style: { position: 'relative', overflow: 'hidden', ...style }, children: [syncPending && (_jsx("div", { style: {
|
|
308
|
-
position: 'absolute',
|
|
309
|
-
top: 0,
|
|
310
|
-
left: 0,
|
|
311
|
-
right: 0,
|
|
312
|
-
height: 3,
|
|
313
|
-
zIndex: 1,
|
|
314
|
-
overflow: 'hidden',
|
|
315
|
-
}, children: _jsx("div", { style: {
|
|
316
|
-
position: 'absolute',
|
|
317
|
-
top: 0,
|
|
318
|
-
left: 0,
|
|
319
|
-
height: '100%',
|
|
320
|
-
width: '40%',
|
|
321
|
-
background: 'var(--mantine-color-blue-filled, #228be6)',
|
|
322
|
-
borderRadius: '0 2px 2px 0',
|
|
323
|
-
animation: 'canopy-preview-sync 1.5s ease-in-out infinite',
|
|
324
|
-
} }) })), _jsx("iframe", { ref: iframeRef, src: src, style: {
|
|
325
|
-
display: 'block',
|
|
326
|
-
position: 'absolute',
|
|
327
|
-
inset: 0,
|
|
328
|
-
width: '100%',
|
|
329
|
-
height: '100%',
|
|
330
|
-
border: 'none',
|
|
331
|
-
}, onLoad: () => {
|
|
332
|
-
post();
|
|
333
|
-
postHighlight();
|
|
334
|
-
} })] }));
|
|
335
|
-
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FieldConfig } from '../config/index.js';
|
|
2
|
+
/** A preview mark whose path names no field of the entry, and the closest path that does. */
|
|
3
|
+
export interface InexactMark {
|
|
4
|
+
/** The mark's path, normalized. */
|
|
5
|
+
path: string;
|
|
6
|
+
/** Its longest prefix that names a field, absent when not even the first segment does. */
|
|
7
|
+
nearest?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The marks naming no field: a key the schema lacks, a segment below a reference, image or
|
|
11
|
+
* scalar, or another template's field. Walks the schema, not the form's DOM, which leaves out
|
|
12
|
+
* collapsed fields; the draft only picks a block item's template, and an item it lacks takes
|
|
13
|
+
* every path, since marks can trail a draft by one report.
|
|
14
|
+
*/
|
|
15
|
+
export declare const findInexactMarks: (fields: readonly FieldConfig[], data: unknown, paths: readonly string[]) => InexactMark[];
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { flattenGroupFields } from '../utils/flatten-group-fields.js';
|
|
2
|
+
import { resolveBlockItem } from '../validation/field-traversal.js';
|
|
3
|
+
import { formatCanopyPath, normalizeCanopyPath, parseCanopyPath } from './canopy-path.js';
|
|
4
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
5
|
+
const itemAt = (list, index) => Array.isArray(list) ? list[index] : undefined;
|
|
6
|
+
const enterField = (field, value) => {
|
|
7
|
+
if (field.type === 'block')
|
|
8
|
+
return { kind: 'blocks', field: field, data: value };
|
|
9
|
+
if ('list' in field && field.list)
|
|
10
|
+
return { kind: 'items', field, data: value };
|
|
11
|
+
if (field.type === 'object') {
|
|
12
|
+
return { kind: 'record', fields: field.fields, data: value };
|
|
13
|
+
}
|
|
14
|
+
return { kind: 'leaf' };
|
|
15
|
+
};
|
|
16
|
+
const step = (position, segment) => {
|
|
17
|
+
switch (position.kind) {
|
|
18
|
+
case 'unknown':
|
|
19
|
+
return position;
|
|
20
|
+
case 'leaf':
|
|
21
|
+
return undefined;
|
|
22
|
+
case 'record': {
|
|
23
|
+
if (typeof segment !== 'string')
|
|
24
|
+
return undefined;
|
|
25
|
+
const field = flattenGroupFields(position.fields).find((f) => f.name === segment);
|
|
26
|
+
if (!field)
|
|
27
|
+
return undefined;
|
|
28
|
+
return enterField(field, isRecord(position.data) ? position.data[segment] : undefined);
|
|
29
|
+
}
|
|
30
|
+
case 'items': {
|
|
31
|
+
if (typeof segment !== 'number')
|
|
32
|
+
return undefined;
|
|
33
|
+
if (position.field.type !== 'object')
|
|
34
|
+
return { kind: 'leaf' };
|
|
35
|
+
const fields = position.field.fields;
|
|
36
|
+
return { kind: 'record', fields, data: itemAt(position.data, segment) };
|
|
37
|
+
}
|
|
38
|
+
case 'blocks': {
|
|
39
|
+
if (typeof segment !== 'number')
|
|
40
|
+
return undefined;
|
|
41
|
+
const item = itemAt(position.data, segment);
|
|
42
|
+
const resolved = isRecord(item) ? resolveBlockItem(position.field, item) : undefined;
|
|
43
|
+
return resolved ? { kind: 'record', ...resolved } : { kind: 'unknown' };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The marks naming no field: a key the schema lacks, a segment below a reference, image or
|
|
49
|
+
* scalar, or another template's field. Walks the schema, not the form's DOM, which leaves out
|
|
50
|
+
* collapsed fields; the draft only picks a block item's template, and an item it lacks takes
|
|
51
|
+
* every path, since marks can trail a draft by one report.
|
|
52
|
+
*/
|
|
53
|
+
export const findInexactMarks = (fields, data, paths) => {
|
|
54
|
+
const inexact = new Map();
|
|
55
|
+
for (const raw of paths) {
|
|
56
|
+
const path = normalizeCanopyPath(raw);
|
|
57
|
+
const segments = parseCanopyPath(path);
|
|
58
|
+
let position = { kind: 'record', fields, data };
|
|
59
|
+
let matched = 0;
|
|
60
|
+
for (const segment of segments) {
|
|
61
|
+
position = step(position, segment);
|
|
62
|
+
if (!position)
|
|
63
|
+
break;
|
|
64
|
+
matched++;
|
|
65
|
+
}
|
|
66
|
+
if (position && segments.length > 0)
|
|
67
|
+
continue;
|
|
68
|
+
inexact.set(path, {
|
|
69
|
+
path,
|
|
70
|
+
...(matched > 0 ? { nearest: formatCanopyPath(segments.slice(0, matched)) } : {}),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return [...inexact.values()];
|
|
74
|
+
};
|
|
@@ -8,3 +8,15 @@
|
|
|
8
8
|
export declare const normalizePreviewPath: (url: string) => string;
|
|
9
9
|
/** Whether two preview URLs name the same page; see `normalizePreviewPath`. */
|
|
10
10
|
export declare const isSamePreviewPath: (a: string, b: string) => boolean;
|
|
11
|
+
/** Where `buildPreviewSrc` mounts the preview routes, and the `basePath` the site's pages keep. */
|
|
12
|
+
export interface PreviewMount {
|
|
13
|
+
basePath?: string;
|
|
14
|
+
previewPrefix?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The previewed page's path on the site, as the preview toolbar shows and copies it: without the
|
|
18
|
+
* `previewPrefix` mount, the `branch` query the top bar already names, or a fragment. A relative
|
|
19
|
+
* URL stays relative. An absolute one keeps its origin, since it may be another site, unless an
|
|
20
|
+
* absolute `previewPrefix` mount is what was taken out.
|
|
21
|
+
*/
|
|
22
|
+
export declare const displayPreviewPath: (src: string, mount?: PreviewMount) => string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { stripTrailingSlashes } from '../utils/url-prefix.js';
|
|
1
|
+
import { isAbsoluteUrl, joinUrlPrefix, stripTrailingSlashes } from '../utils/url-prefix.js';
|
|
2
2
|
/**
|
|
3
3
|
* Reduces a preview URL to what identifies the page, its path and query, with no origin,
|
|
4
4
|
* fragment or trailing slash. The editor names a page by the `src` it built and the framed page
|
|
@@ -18,3 +18,47 @@ export const normalizePreviewPath = (url) => {
|
|
|
18
18
|
};
|
|
19
19
|
/** Whether two preview URLs name the same page; see `normalizePreviewPath`. */
|
|
20
20
|
export const isSamePreviewPath = (a, b) => normalizePreviewPath(a) === normalizePreviewPath(b);
|
|
21
|
+
/** `url` with the `previewPrefix` mount taken out, so `/base/preview/posts/x` reads `/base/posts/x`. */
|
|
22
|
+
const stripPreviewPrefix = (url, { basePath, previewPrefix }) => {
|
|
23
|
+
if (!previewPrefix || !stripTrailingSlashes(previewPrefix))
|
|
24
|
+
return url;
|
|
25
|
+
const mount = stripTrailingSlashes(joinUrlPrefix(basePath, joinUrlPrefix(previewPrefix, '/')));
|
|
26
|
+
if (!url.startsWith(mount))
|
|
27
|
+
return url;
|
|
28
|
+
const rest = url.slice(mount.length);
|
|
29
|
+
if (rest !== '' && !/^[/?#]/.test(rest))
|
|
30
|
+
return url;
|
|
31
|
+
// An absolute prefix skips `basePath`, so the page under it has none either.
|
|
32
|
+
const base = isAbsoluteUrl(mount) ? '' : stripTrailingSlashes(joinUrlPrefix(basePath, '/'));
|
|
33
|
+
return rest.startsWith('/') ? `${base}${rest}` : `${base || '/'}${rest}`;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The previewed page's path on the site, as the preview toolbar shows and copies it: without the
|
|
37
|
+
* `previewPrefix` mount, the `branch` query the top bar already names, or a fragment. A relative
|
|
38
|
+
* URL stays relative. An absolute one keeps its origin, since it may be another site, unless an
|
|
39
|
+
* absolute `previewPrefix` mount is what was taken out.
|
|
40
|
+
*/
|
|
41
|
+
export const displayPreviewPath = (src, mount = {}) => {
|
|
42
|
+
const url = stripPreviewPrefix(src, mount);
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = new URL(url, 'http://preview.invalid');
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return url;
|
|
49
|
+
}
|
|
50
|
+
// Filtered as text: a URLSearchParams round trip would form-encode the query it keeps (`flag`
|
|
51
|
+
// becomes `flag=`, `%20` becomes `+`).
|
|
52
|
+
const query = parsed.search
|
|
53
|
+
.slice(1)
|
|
54
|
+
.split('&')
|
|
55
|
+
.filter((part) => part !== '' && part.split('=', 1)[0] !== 'branch')
|
|
56
|
+
.join('&');
|
|
57
|
+
const path = `${parsed.pathname}${query ? `?${query}` : ''}`;
|
|
58
|
+
if (!isAbsoluteUrl(url))
|
|
59
|
+
return path;
|
|
60
|
+
// `origin` is 'null' for a scheme the URL spec does not treat as special.
|
|
61
|
+
return url.startsWith('//')
|
|
62
|
+
? `//${parsed.host}${path}`
|
|
63
|
+
: `${parsed.protocol}//${parsed.host}${path}`;
|
|
64
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PreviewWidth } from './hooks/useEditorLayout.js';
|
|
2
|
+
/** The CSS width each fixed preview width renders the site at. */
|
|
3
|
+
export declare const PREVIEW_DEVICE_WIDTHS: {
|
|
4
|
+
readonly desktop: 1280;
|
|
5
|
+
readonly tablet: 768;
|
|
6
|
+
readonly mobile: 390;
|
|
7
|
+
};
|
|
8
|
+
/** Where and how large the preview iframe renders inside a pane of a given size. */
|
|
9
|
+
export interface PreviewViewport {
|
|
10
|
+
/** The iframe's CSS width: the width the site's breakpoints see. */
|
|
11
|
+
frameWidth: number;
|
|
12
|
+
/** The iframe's CSS height, before scaling; scaled, it fills the pane's height. */
|
|
13
|
+
frameHeight: number;
|
|
14
|
+
/** The factor the iframe is scaled by, at most 1. */
|
|
15
|
+
scale: number;
|
|
16
|
+
/** The left offset that centres the scaled iframe in the pane. */
|
|
17
|
+
offsetX: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Fits the preview into a pane. "fit" renders at the pane's width. A fixed width renders at
|
|
21
|
+
* exactly that CSS width and, when the pane is narrower, scales down so the whole width shows,
|
|
22
|
+
* stretching the CSS height by the same factor so the scaled frame still fills the pane's height.
|
|
23
|
+
* An unmeasured pane (zero or non-finite) renders unscaled.
|
|
24
|
+
*/
|
|
25
|
+
export declare function computePreviewViewport(previewWidth: PreviewWidth, paneWidth: number, paneHeight: number): PreviewViewport;
|
|
26
|
+
/** The readout beside the width selector: the CSS width, plus the scale when it rounds below 100%. */
|
|
27
|
+
export declare function formatPreviewWidthReadout({ frameWidth, scale }: PreviewViewport): string;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** The CSS width each fixed preview width renders the site at. */
|
|
2
|
+
export const PREVIEW_DEVICE_WIDTHS = {
|
|
3
|
+
desktop: 1280,
|
|
4
|
+
tablet: 768,
|
|
5
|
+
mobile: 390,
|
|
6
|
+
};
|
|
7
|
+
const positiveOr = (value, fallback) => Number.isFinite(value) && value > 0 ? value : fallback;
|
|
8
|
+
/**
|
|
9
|
+
* Fits the preview into a pane. "fit" renders at the pane's width. A fixed width renders at
|
|
10
|
+
* exactly that CSS width and, when the pane is narrower, scales down so the whole width shows,
|
|
11
|
+
* stretching the CSS height by the same factor so the scaled frame still fills the pane's height.
|
|
12
|
+
* An unmeasured pane (zero or non-finite) renders unscaled.
|
|
13
|
+
*/
|
|
14
|
+
export function computePreviewViewport(previewWidth, paneWidth, paneHeight) {
|
|
15
|
+
const width = positiveOr(paneWidth, 0);
|
|
16
|
+
const height = positiveOr(paneHeight, 0);
|
|
17
|
+
if (previewWidth === 'fit') {
|
|
18
|
+
return { frameWidth: width, frameHeight: height, scale: 1, offsetX: 0 };
|
|
19
|
+
}
|
|
20
|
+
const frameWidth = PREVIEW_DEVICE_WIDTHS[previewWidth];
|
|
21
|
+
const scale = width > 0 ? Math.min(1, width / frameWidth) : 1;
|
|
22
|
+
return {
|
|
23
|
+
frameWidth,
|
|
24
|
+
frameHeight: height / scale,
|
|
25
|
+
scale,
|
|
26
|
+
offsetX: width > 0 ? Math.max(0, (width - frameWidth * scale) / 2) : 0,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** The readout beside the width selector: the CSS width, plus the scale when it rounds below 100%. */
|
|
30
|
+
export function formatPreviewWidthReadout({ frameWidth, scale }) {
|
|
31
|
+
const px = `${Math.round(frameWidth)} px`;
|
|
32
|
+
const percent = Math.round(scale * 100);
|
|
33
|
+
return percent < 100 ? `${px} at ${percent}%` : px;
|
|
34
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** The signed-in asset prefix, React-free so `createPreviewPage` can build it on a server. */
|
|
2
|
+
/** The raw route under `basePath`: unlike `/assets`, it computes derivatives no build made. */
|
|
3
|
+
export declare function authenticatedAssetBase(basePath?: string): string;
|
|
4
|
+
/** `value` if it is a same-origin path: nothing else may steer a preview's `<img>`s. */
|
|
5
|
+
export declare function readAssetBase(value: unknown): string | undefined;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** The signed-in asset prefix, React-free so `createPreviewPage` can build it on a server. */
|
|
2
|
+
import { joinUrlPrefix, toSameOriginPath } from '../utils/url-prefix.js';
|
|
3
|
+
/** The raw asset route (`api/assets.ts`'s `assetRawRoute`) under the default API base. */
|
|
4
|
+
const RAW_ASSET_ROUTE = '/api/canopycms/assets/raw';
|
|
5
|
+
/** The raw route under `basePath`: unlike `/assets`, it computes derivatives no build made. */
|
|
6
|
+
export function authenticatedAssetBase(basePath) {
|
|
7
|
+
return joinUrlPrefix(basePath, RAW_ASSET_ROUTE);
|
|
8
|
+
}
|
|
9
|
+
/** `value` if it is a same-origin path: nothing else may steer a preview's `<img>`s. */
|
|
10
|
+
export function readAssetBase(value) {
|
|
11
|
+
return typeof value === 'string' && value.startsWith('/') && toSameOriginPath(value) === value
|
|
12
|
+
? value
|
|
13
|
+
: undefined;
|
|
14
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Formats an ISO timestamp as a short relative-time label ("just now",
|
|
3
|
-
* "
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Formats an ISO timestamp as a short relative-time label ("just now", "5m ago", "3h ago",
|
|
3
|
+
* "2d ago", "2w ago", "3mo ago", "1y ago"). Weeks run to 4, months to 11. A timestamp in the
|
|
4
|
+
* future reads "just now", and invalid input is returned unchanged so callers never render
|
|
5
|
+
* "Invalid Date" to the user.
|
|
6
6
|
*/
|
|
7
7
|
export declare function formatRelativeTime(iso: string): string;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
const MINUTE_MS = 60 * 1000;
|
|
2
|
+
const HOUR_MS = 60 * MINUTE_MS;
|
|
3
|
+
const DAY_MS = 24 * HOUR_MS;
|
|
1
4
|
/**
|
|
2
|
-
* Formats an ISO timestamp as a short relative-time label ("just now",
|
|
3
|
-
* "
|
|
4
|
-
*
|
|
5
|
-
*
|
|
5
|
+
* Formats an ISO timestamp as a short relative-time label ("just now", "5m ago", "3h ago",
|
|
6
|
+
* "2d ago", "2w ago", "3mo ago", "1y ago"). Weeks run to 4, months to 11. A timestamp in the
|
|
7
|
+
* future reads "just now", and invalid input is returned unchanged so callers never render
|
|
8
|
+
* "Invalid Date" to the user.
|
|
6
9
|
*/
|
|
7
10
|
export function formatRelativeTime(iso) {
|
|
8
11
|
const date = new Date(iso);
|
|
@@ -10,16 +13,21 @@ export function formatRelativeTime(iso) {
|
|
|
10
13
|
return iso;
|
|
11
14
|
}
|
|
12
15
|
const diffMs = Date.now() - date.getTime();
|
|
13
|
-
const minutes = Math.floor(diffMs /
|
|
16
|
+
const minutes = Math.floor(diffMs / MINUTE_MS);
|
|
14
17
|
if (minutes < 1)
|
|
15
18
|
return 'just now';
|
|
16
19
|
if (minutes < 60)
|
|
17
20
|
return `${minutes}m ago`;
|
|
18
|
-
const hours = Math.floor(diffMs /
|
|
21
|
+
const hours = Math.floor(diffMs / HOUR_MS);
|
|
19
22
|
if (hours < 24)
|
|
20
23
|
return `${hours}h ago`;
|
|
21
|
-
const days = Math.floor(diffMs /
|
|
24
|
+
const days = Math.floor(diffMs / DAY_MS);
|
|
22
25
|
if (days < 7)
|
|
23
26
|
return `${days}d ago`;
|
|
24
|
-
|
|
27
|
+
if (days < 35)
|
|
28
|
+
return `${Math.floor(days / 7)}w ago`;
|
|
29
|
+
const years = Math.floor(days / 365);
|
|
30
|
+
if (years >= 1)
|
|
31
|
+
return `${years}y ago`;
|
|
32
|
+
return `${Math.min(11, Math.max(1, Math.floor(days / 30)))}mo ago`;
|
|
25
33
|
}
|
|
@@ -39,10 +39,27 @@ export interface CollectionEditorProps {
|
|
|
39
39
|
onAddEntryType?: (collectionPath: LogicalPath, entryType: CreateEntryTypeInput) => Promise<SchemaOpResult>;
|
|
40
40
|
/** Called when an entry type is updated (edit mode only); resolves to the save result */
|
|
41
41
|
onUpdateEntryType?: (collectionPath: LogicalPath, entryTypeName: string, updates: Partial<CreateEntryTypeInput>) => Promise<SchemaOpResult>;
|
|
42
|
-
/** Called when an entry type is removed (edit mode only) */
|
|
43
|
-
onRemoveEntryType?: (collectionPath: LogicalPath, entryTypeName: string) => Promise<SchemaOpResult
|
|
42
|
+
/** Called when an entry type is removed (edit mode only); resolves to the save result */
|
|
43
|
+
onRemoveEntryType?: (collectionPath: LogicalPath, entryTypeName: string) => Promise<SchemaOpResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Re-reads the entry types from the branch (edit mode only), after an action the server never
|
|
46
|
+
* answered; resolves to null when the read fails.
|
|
47
|
+
*/
|
|
48
|
+
onReloadEntryTypes?: (collectionPath: LogicalPath) => Promise<ExistingEntryType[] | null>;
|
|
44
49
|
onClose: () => void;
|
|
45
50
|
isSaving?: boolean;
|
|
46
51
|
error?: string | null;
|
|
52
|
+
/**
|
|
53
|
+
* What was entered for a create or details save that got no answer and that the opener's fresh
|
|
54
|
+
* read did not show landed, filled in when the dialog opens (docs/ux-guidelines.md: a create closed after no answer
|
|
55
|
+
* prefills its dialog's next open).
|
|
56
|
+
*/
|
|
57
|
+
restored?: RestoredCollectionInput | null;
|
|
58
|
+
/** Drops `restored`, once the user discards it. */
|
|
59
|
+
onDiscardRestored?: () => void;
|
|
47
60
|
}
|
|
48
|
-
|
|
61
|
+
/** A create's input, or a details save's changed fields. */
|
|
62
|
+
export type RestoredCollectionInput = Partial<CollectionFormData> & {
|
|
63
|
+
slug?: string;
|
|
64
|
+
};
|
|
65
|
+
export declare function CollectionEditor({ isOpen, editingCollection, parentPath, availableSchemas, onSave, onAddEntryType, onUpdateEntryType, onRemoveEntryType, onReloadEntryTypes, onClose, isSaving, error, restored, onDiscardRestored, }: CollectionEditorProps): import("react/jsx-runtime").JSX.Element;
|