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,3 +1,9 @@
|
|
|
1
|
+
import { titleFieldPath } from '../utils/title-field.js';
|
|
2
|
+
/** The `titleField` an entry type contributes: absent for an unavailable type, which has no schema. */
|
|
3
|
+
const titleFieldFor = (schema) => {
|
|
4
|
+
const titleField = titleFieldPath(schema ?? []);
|
|
5
|
+
return titleField ? { titleField } : {};
|
|
6
|
+
};
|
|
1
7
|
/**
|
|
2
8
|
* Build hierarchical editor collections from the flattened schema.
|
|
3
9
|
* Uses logicalPath as IDs to match API responses.
|
|
@@ -18,6 +24,13 @@ export const buildEditorCollections = (flatSchema) => {
|
|
|
18
24
|
}
|
|
19
25
|
childrenByParent.get(key).push(item);
|
|
20
26
|
}
|
|
27
|
+
// A collection's own `entries` carry no schema on the wire; the entry-type items hold the
|
|
28
|
+
// hydrated one.
|
|
29
|
+
const schemaByEntryType = new Map();
|
|
30
|
+
for (const item of flat) {
|
|
31
|
+
if (item.type === 'entry-type')
|
|
32
|
+
schemaByEntryType.set(`${item.parentPath}\0${item.name}`, item.schema);
|
|
33
|
+
}
|
|
21
34
|
// Build tree recursively using the grouped map
|
|
22
35
|
const buildTree = (parentPath) => {
|
|
23
36
|
const children = childrenByParent.get(parentPath) || [];
|
|
@@ -32,6 +45,9 @@ export const buildEditorCollections = (flatSchema) => {
|
|
|
32
45
|
format: et.format,
|
|
33
46
|
default: et.default,
|
|
34
47
|
maxItems: et.maxItems,
|
|
48
|
+
...(et.unavailable
|
|
49
|
+
? { unavailable: et.unavailable }
|
|
50
|
+
: titleFieldFor(schemaByEntryType.get(`${item.logicalPath}\0${et.name}`) ?? et.schema)),
|
|
35
51
|
}));
|
|
36
52
|
results.push({
|
|
37
53
|
path: item.logicalPath,
|
|
@@ -17,6 +17,20 @@ type PreviewEntry = {
|
|
|
17
17
|
itemType?: string;
|
|
18
18
|
previewSrc?: string;
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* The entry's route on the host site, or `undefined` when it has no page. The first
|
|
22
|
+
* `previewBaseByCollection` key present decides: for a root entry its own path
|
|
23
|
+
* (`<contentRoot>/<slug>`, used as-is), then its collection path, then its collection name (both
|
|
24
|
+
* with the slug appended). The spelling `<parent>/<name>` names both an entry and a same-named
|
|
25
|
+
* sibling collection (a landing entry beside its folder), so below the root it is read only as a
|
|
26
|
+
* collection key, and at the root one key routes both.
|
|
27
|
+
* A `false` value means no page. With no key, the route is the entry's `urlPath`, by the rule
|
|
28
|
+
* `listEntries` publishes it, so a root entry previews at `/<slug>` and only a root index at `/`.
|
|
29
|
+
* Site-relative unless a matching value is absolute.
|
|
30
|
+
*/
|
|
31
|
+
export declare const buildPreviewRoute: (entry: PreviewEntry, { previewBaseByCollection, contentRoot }: PreviewContext & {
|
|
32
|
+
contentRoot?: string;
|
|
33
|
+
}) => string | undefined;
|
|
20
34
|
/**
|
|
21
35
|
* Builds the preview iframe `src` for an entry: its route under `previewPrefix`, under the
|
|
22
36
|
* deployment `basePath` (`CanopyClientConfig.basePath`, e.g. `/preview-123`), in the host's
|
|
@@ -24,8 +38,8 @@ type PreviewEntry = {
|
|
|
24
38
|
* (from `previewBaseByCollection`) skips all three and gets only `?branch=`. An entry's own
|
|
25
39
|
* `previewSrc` gets only the `basePath`.
|
|
26
40
|
*
|
|
27
|
-
* The result must equal the framed page's own URL: the `<iframe src>` (`PreviewFrame`
|
|
28
|
-
*
|
|
41
|
+
* The result must equal the framed page's own URL: the `<iframe src>` (`PreviewFrame.tsx`)
|
|
42
|
+
* 404s without the prefixes, and a URL the host redirects costs a round trip
|
|
29
43
|
* on every load. `trailingSlash` defaults to the value `withCanopy` inlines at build time.
|
|
30
44
|
*
|
|
31
45
|
* `undefined` means the entry has no page, so the editor frames nothing rather than another page.
|
|
@@ -49,13 +63,25 @@ interface BuildEntriesFromListParams {
|
|
|
49
63
|
export declare const buildEntriesFromListResponse: ({ response, resolvePreviewSrc, flatSchema, }: BuildEntriesFromListParams) => EditorEntry[];
|
|
50
64
|
export declare const buildCollectionLabels: (collections?: EditorCollection[]) => Map<string, string>;
|
|
51
65
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
66
|
+
* An entry's type as its collection labels it, else its name read as words; undefined when
|
|
67
|
+
* untyped.
|
|
68
|
+
*/
|
|
69
|
+
export declare const entryTypeLabel: (collections: EditorCollection[] | undefined, entry: Pick<EditorEntry, "collectionPath" | "entryType">) => string | undefined;
|
|
70
|
+
/** An entry's navigator badge: "Missing" when its file is gone, else its type's label or name. */
|
|
71
|
+
export declare const entryStatusLabel: (collections: EditorCollection[] | undefined, entry: Pick<EditorEntry, "status" | "collectionPath" | "entryType">) => string | undefined;
|
|
72
|
+
/** One crumb before the entry's own: "All content", a collection, or a folder of a nested slug. */
|
|
73
|
+
export interface BreadcrumbSegment {
|
|
74
|
+
label: string;
|
|
75
|
+
/** The collection the crumb opens in the content panel; absent on "All content" and slug folders. */
|
|
76
|
+
collectionPath?: string;
|
|
77
|
+
/** A slug folder, which is not a node of the content tree, so it navigates nowhere. */
|
|
78
|
+
folder?: true;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Builds breadcrumb segments for an entry based on its collection hierarchy, starting with
|
|
82
|
+
* "All content". The first path part is skipped as the content root, which the tree does not show.
|
|
57
83
|
*/
|
|
58
|
-
export declare const buildBreadcrumbSegments: (currentEntry: EditorEntry | undefined, collectionLabels: Map<string, string>) =>
|
|
84
|
+
export declare const buildBreadcrumbSegments: (currentEntry: EditorEntry | undefined, collectionLabels: Map<string, string>) => BreadcrumbSegment[];
|
|
59
85
|
/**
|
|
60
86
|
* Calculates which collection nodes need to be expanded to show the path to a specific entry.
|
|
61
87
|
* Recursively walks the tree to find the target entry and marks all ancestor collections as expanded.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Import directly from normalize to avoid pulling in server-only branch.ts
|
|
2
2
|
import { normalizeFilesystemPath } from '../paths/normalize.js';
|
|
3
|
+
import { humanizeName } from './schema-label.js';
|
|
3
4
|
import { computeEntryUrl, isIndexSlug } from '../utils/entry-url.js';
|
|
4
5
|
import { isDataOnlyFormat } from '../utils/format.js';
|
|
5
6
|
import { isAbsoluteUrl, joinUrlPrefix, matchTrailingSlash, readTrailingSlashEnv, stripTrailingSlashes, } from '../utils/url-prefix.js';
|
|
@@ -47,7 +48,7 @@ const previewBaseFor = (bases, key) => bases && key && Object.prototype.hasOwnPr
|
|
|
47
48
|
* `listEntries` publishes it, so a root entry previews at `/<slug>` and only a root index at `/`.
|
|
48
49
|
* Site-relative unless a matching value is absolute.
|
|
49
50
|
*/
|
|
50
|
-
const buildPreviewRoute = (entry, { previewBaseByCollection, contentRoot = 'content' }) => {
|
|
51
|
+
export const buildPreviewRoute = (entry, { previewBaseByCollection, contentRoot = 'content' }) => {
|
|
51
52
|
const collectionPath = normalizeFilesystemPath(entry.collectionPath ?? '');
|
|
52
53
|
const isRootEntry = collectionPath === normalizeFilesystemPath(contentRoot);
|
|
53
54
|
const entryRoute = previewBaseFor(previewBaseByCollection, isRootEntry && entry.slug ? `${collectionPath}/${entry.slug}` : undefined);
|
|
@@ -73,8 +74,8 @@ const buildPreviewRoute = (entry, { previewBaseByCollection, contentRoot = 'cont
|
|
|
73
74
|
* (from `previewBaseByCollection`) skips all three and gets only `?branch=`. An entry's own
|
|
74
75
|
* `previewSrc` gets only the `basePath`.
|
|
75
76
|
*
|
|
76
|
-
* The result must equal the framed page's own URL: the `<iframe src>` (`PreviewFrame`
|
|
77
|
-
*
|
|
77
|
+
* The result must equal the framed page's own URL: the `<iframe src>` (`PreviewFrame.tsx`)
|
|
78
|
+
* 404s without the prefixes, and a URL the host redirects costs a round trip
|
|
78
79
|
* on every load. `trailingSlash` defaults to the value `withCanopy` inlines at build time.
|
|
79
80
|
*
|
|
80
81
|
* `undefined` means the entry has no page, so the editor frames nothing rather than another page.
|
|
@@ -132,12 +133,14 @@ export const buildEntriesFromListResponse = ({ response, resolvePreviewSrc, flat
|
|
|
132
133
|
return response.entries.map((entry) => {
|
|
133
134
|
// Resolve schema from flatSchema using parentPath + name
|
|
134
135
|
let schema = [];
|
|
136
|
+
let unavailable;
|
|
135
137
|
if (entry.collectionPath && entry.entryType) {
|
|
136
138
|
const entryTypeItem = flatSchema.find((item) => item.type === 'entry-type' &&
|
|
137
139
|
item.parentPath === entry.collectionPath &&
|
|
138
140
|
item.name === entry.entryType);
|
|
139
141
|
if (entryTypeItem && entryTypeItem.type === 'entry-type') {
|
|
140
142
|
schema = entryTypeItem.schema;
|
|
143
|
+
unavailable = entryTypeItem.unavailable;
|
|
141
144
|
}
|
|
142
145
|
}
|
|
143
146
|
return {
|
|
@@ -154,6 +157,7 @@ export const buildEntriesFromListResponse = ({ response, resolvePreviewSrc, flat
|
|
|
154
157
|
entryType: entry.entryType,
|
|
155
158
|
type: 'entry',
|
|
156
159
|
canEdit: entry.canEdit,
|
|
160
|
+
...(unavailable && { unavailable }),
|
|
157
161
|
};
|
|
158
162
|
});
|
|
159
163
|
};
|
|
@@ -173,34 +177,52 @@ export const buildCollectionLabels = (collections) => {
|
|
|
173
177
|
return map;
|
|
174
178
|
};
|
|
175
179
|
/**
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
* An entry's type as its collection labels it, else its name read as words; undefined when
|
|
181
|
+
* untyped.
|
|
182
|
+
*/
|
|
183
|
+
export const entryTypeLabel = (collections, entry) => {
|
|
184
|
+
if (!entry.entryType)
|
|
185
|
+
return undefined;
|
|
186
|
+
const find = (nodes) => {
|
|
187
|
+
for (const c of nodes) {
|
|
188
|
+
if (c.path === entry.collectionPath)
|
|
189
|
+
return c;
|
|
190
|
+
const inner = c.children ? find(c.children) : undefined;
|
|
191
|
+
if (inner)
|
|
192
|
+
return inner;
|
|
193
|
+
}
|
|
194
|
+
return undefined;
|
|
195
|
+
};
|
|
196
|
+
const collection = collections ? find(collections) : undefined;
|
|
197
|
+
const type = collection?.entryTypes?.find((t) => t.name === entry.entryType);
|
|
198
|
+
return type?.label ?? humanizeName(entry.entryType);
|
|
199
|
+
};
|
|
200
|
+
/** An entry's navigator badge: "Missing" when its file is gone, else its type's label or name. */
|
|
201
|
+
export const entryStatusLabel = (collections, entry) => {
|
|
202
|
+
if (entry.status === 'missing')
|
|
203
|
+
return 'Missing';
|
|
204
|
+
return entryTypeLabel(collections, entry) ?? (entry.status && humanizeName(entry.status));
|
|
205
|
+
};
|
|
206
|
+
/**
|
|
207
|
+
* Builds breadcrumb segments for an entry based on its collection hierarchy, starting with
|
|
208
|
+
* "All content". The first path part is skipped as the content root, which the tree does not show.
|
|
181
209
|
*/
|
|
182
210
|
export const buildBreadcrumbSegments = (currentEntry, collectionLabels) => {
|
|
211
|
+
const segments = [{ label: 'All content' }];
|
|
183
212
|
if (!currentEntry)
|
|
184
|
-
return
|
|
185
|
-
const segments = ['All Files'];
|
|
186
|
-
// Show collection hierarchy for entries that belong to a collection
|
|
213
|
+
return segments;
|
|
187
214
|
if (currentEntry.collectionPath) {
|
|
188
|
-
// Split the collectionPath into path parts and build cumulative paths
|
|
189
|
-
// e.g., "content/documentation/guides" -> ["content/documentation", "content/documentation/guides"]
|
|
190
215
|
const parts = currentEntry.collectionPath.split('/').filter(Boolean);
|
|
191
216
|
for (let i = 1; i < parts.length; i++) {
|
|
192
217
|
const pathUpToHere = parts.slice(0, i + 1).join('/');
|
|
193
218
|
const label = collectionLabels.get(pathUpToHere);
|
|
194
|
-
if (label)
|
|
195
|
-
segments.push(label);
|
|
196
|
-
}
|
|
219
|
+
if (label)
|
|
220
|
+
segments.push({ label, collectionPath: pathUpToHere });
|
|
197
221
|
}
|
|
198
222
|
}
|
|
199
|
-
// Add slug path segments (for nested slugs like "folder/file")
|
|
200
223
|
const slugSegments = (currentEntry.slug ?? '').split('/').filter(Boolean);
|
|
201
|
-
|
|
202
|
-
segments.push(
|
|
203
|
-
}
|
|
224
|
+
for (const folder of slugSegments.slice(0, -1))
|
|
225
|
+
segments.push({ label: folder, folder: true });
|
|
204
226
|
return segments;
|
|
205
227
|
};
|
|
206
228
|
/**
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Entry search shared by the navigator filter and the quick-open palette. */
|
|
2
|
+
export interface SearchableEntry {
|
|
3
|
+
label: string;
|
|
4
|
+
slug?: string;
|
|
5
|
+
collectionPath?: string;
|
|
6
|
+
collectionName?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Case-insensitive substring match over label and slug; an empty query matches everything. */
|
|
9
|
+
export declare function matchesEntryQuery(entry: Pick<SearchableEntry, 'label' | 'slug'>, query: string): boolean;
|
|
10
|
+
export declare function filterEntries<T extends SearchableEntry>(entries: readonly T[], query: string): T[];
|
|
11
|
+
export interface EntryGroup<T> {
|
|
12
|
+
heading: string;
|
|
13
|
+
entries: T[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Groups entries by collection, in first-seen order, each under its display heading:
|
|
17
|
+
* `labelFor(collectionPath)`, else the collection name or path, else `Root`. Two collections
|
|
18
|
+
* that share a heading stay separate groups.
|
|
19
|
+
*/
|
|
20
|
+
export declare function groupEntriesByCollection<T extends SearchableEntry>(entries: readonly T[], labelFor?: (collectionPath: string) => string | undefined): EntryGroup<T>[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Entry search shared by the navigator filter and the quick-open palette. */
|
|
2
|
+
/** Case-insensitive substring match over label and slug; an empty query matches everything. */
|
|
3
|
+
export function matchesEntryQuery(entry, query) {
|
|
4
|
+
const needle = query.trim().toLowerCase();
|
|
5
|
+
if (!needle)
|
|
6
|
+
return true;
|
|
7
|
+
return (entry.label.toLowerCase().includes(needle) ||
|
|
8
|
+
(entry.slug?.toLowerCase().includes(needle) ?? false));
|
|
9
|
+
}
|
|
10
|
+
export function filterEntries(entries, query) {
|
|
11
|
+
return entries.filter((entry) => matchesEntryQuery(entry, query));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Groups entries by collection, in first-seen order, each under its display heading:
|
|
15
|
+
* `labelFor(collectionPath)`, else the collection name or path, else `Root`. Two collections
|
|
16
|
+
* that share a heading stay separate groups.
|
|
17
|
+
*/
|
|
18
|
+
export function groupEntriesByCollection(entries, labelFor) {
|
|
19
|
+
const groups = new Map();
|
|
20
|
+
for (const entry of entries) {
|
|
21
|
+
const key = entry.collectionPath ?? entry.collectionName ?? '';
|
|
22
|
+
const group = groups.get(key);
|
|
23
|
+
if (group) {
|
|
24
|
+
group.entries.push(entry);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const heading = (entry.collectionPath ? labelFor?.(entry.collectionPath) : undefined) ??
|
|
28
|
+
entry.collectionName ??
|
|
29
|
+
entry.collectionPath ??
|
|
30
|
+
'Root';
|
|
31
|
+
groups.set(key, { heading, entries: [entry] });
|
|
32
|
+
}
|
|
33
|
+
return [...groups.values()];
|
|
34
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FieldConfig } from '../config/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* The human labels along a canopy path, for messages: `blocks[0].title` reads
|
|
4
|
+
* `['Page sections', 'Hero', 'Heading']`. A block is named by its template's label and an
|
|
5
|
+
* object-list item by its title (`listItemHeading`); a segment the schema doesn't know keeps its
|
|
6
|
+
* raw name, so a stale path still reads. A block whose template isn't known (no `value`, or an
|
|
7
|
+
* item that is gone) has its fields looked up in every template. `items: false` leaves the item
|
|
8
|
+
* crumbs out, for a path whose indexes no longer say which item it was.
|
|
9
|
+
*/
|
|
10
|
+
export declare const fieldBreadcrumb: (fields: readonly FieldConfig[], value: unknown, path: string, { items }?: {
|
|
11
|
+
items?: boolean;
|
|
12
|
+
}) => string[];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { isPlainRecord } from '../validation/field-traversal.js';
|
|
2
|
+
import { parseCanopyPath } from './canopy-path.js';
|
|
3
|
+
import { listItemHeading } from './fields/list-item-title.js';
|
|
4
|
+
import { schemaLabel } from './schema-label.js';
|
|
5
|
+
/** Labels for an image field's parts, which `ImageField` reports errors on. */
|
|
6
|
+
const IMAGE_PART_LABELS = new Map([
|
|
7
|
+
['src', 'File'],
|
|
8
|
+
['alt', 'Alt text'],
|
|
9
|
+
['crop', 'Crop'],
|
|
10
|
+
['width', 'Width'],
|
|
11
|
+
['height', 'Height'],
|
|
12
|
+
]);
|
|
13
|
+
/** The field named `name` in `fields`, looking inside inline groups, which add no path segment. */
|
|
14
|
+
const findField = (fields, name) => {
|
|
15
|
+
for (const field of fields) {
|
|
16
|
+
if (field.type === 'group' && 'fields' in field) {
|
|
17
|
+
const inner = findField(field.fields, name);
|
|
18
|
+
if (inner)
|
|
19
|
+
return inner;
|
|
20
|
+
}
|
|
21
|
+
else if (field.name === name) {
|
|
22
|
+
return field;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The human labels along a canopy path, for messages: `blocks[0].title` reads
|
|
29
|
+
* `['Page sections', 'Hero', 'Heading']`. A block is named by its template's label and an
|
|
30
|
+
* object-list item by its title (`listItemHeading`); a segment the schema doesn't know keeps its
|
|
31
|
+
* raw name, so a stale path still reads. A block whose template isn't known (no `value`, or an
|
|
32
|
+
* item that is gone) has its fields looked up in every template. `items: false` leaves the item
|
|
33
|
+
* crumbs out, for a path whose indexes no longer say which item it was.
|
|
34
|
+
*/
|
|
35
|
+
export const fieldBreadcrumb = (fields, value, path, { items = true } = {}) => {
|
|
36
|
+
const crumbs = [];
|
|
37
|
+
let scope = fields;
|
|
38
|
+
let current = value;
|
|
39
|
+
let field;
|
|
40
|
+
for (const segment of parseCanopyPath(path)) {
|
|
41
|
+
if (typeof segment === 'number') {
|
|
42
|
+
const item = Array.isArray(current) ? current[segment] : undefined;
|
|
43
|
+
if (field?.type === 'block') {
|
|
44
|
+
const block = isPlainRecord(item) ? item : {};
|
|
45
|
+
const templateName = typeof block.template === 'string' ? block.template : undefined;
|
|
46
|
+
const templates = field.templates;
|
|
47
|
+
const template = templates.find((t) => t.name === templateName);
|
|
48
|
+
if (items)
|
|
49
|
+
crumbs.push(template ? schemaLabel(template) : (templateName ?? `Item ${segment + 1}`));
|
|
50
|
+
scope = template ? template.fields : templates.flatMap((t) => t.fields);
|
|
51
|
+
current = block.value;
|
|
52
|
+
}
|
|
53
|
+
else if (field?.type === 'object') {
|
|
54
|
+
const objectField = field;
|
|
55
|
+
if (items)
|
|
56
|
+
crumbs.push(listItemHeading(item, objectField, segment));
|
|
57
|
+
scope = objectField.fields;
|
|
58
|
+
current = item;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
if (items)
|
|
62
|
+
crumbs.push(`Item ${segment + 1}`);
|
|
63
|
+
scope = undefined;
|
|
64
|
+
current = item;
|
|
65
|
+
}
|
|
66
|
+
field = undefined;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const imagePart = field?.type === 'image' ? IMAGE_PART_LABELS.get(segment) : undefined;
|
|
70
|
+
if (imagePart) {
|
|
71
|
+
crumbs.push(imagePart);
|
|
72
|
+
field = undefined;
|
|
73
|
+
scope = undefined;
|
|
74
|
+
current = undefined;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const found = scope ? findField(scope, segment) : undefined;
|
|
78
|
+
crumbs.push(found ? schemaLabel(found) : segment);
|
|
79
|
+
current = isPlainRecord(current) ? current[segment] : undefined;
|
|
80
|
+
field = found;
|
|
81
|
+
scope =
|
|
82
|
+
found?.type === 'object' && !found.list ? found.fields : undefined;
|
|
83
|
+
}
|
|
84
|
+
return crumbs;
|
|
85
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preview field marks, typed against the view's content. Exported from the root `canopycms`,
|
|
3
|
+
* not `canopycms/preview`: that module is `'use client'`, and a server component calling its
|
|
4
|
+
* functions gets a client reference that throws. A public page passes `fieldProps` as
|
|
5
|
+
* `undefined`, never a no-op function, which Next refuses to pass from server to client.
|
|
6
|
+
*/
|
|
7
|
+
import type { ResolvedReferenceMeta } from '../entry-schema.js';
|
|
8
|
+
import { type CanopyPathSegment } from './canopy-path.js';
|
|
9
|
+
export type FieldAttrs = {
|
|
10
|
+
'data-canopy-path'?: string;
|
|
11
|
+
};
|
|
12
|
+
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
|
|
13
|
+
type IsDigits<S extends string> = S extends `${Digit}${infer Rest}` ? Rest extends '' ? true : IsDigits<Rest> : false;
|
|
14
|
+
/** A template literal's `${number}` hole: a computed path, so untyped. */
|
|
15
|
+
type TemplateHole = {
|
|
16
|
+
readonly templateHole: true;
|
|
17
|
+
};
|
|
18
|
+
type ParseSegment<S extends string> = IsDigits<S> extends true ? number : `${number}` extends S ? TemplateHole : S;
|
|
19
|
+
type ParseBrackets<S extends string> = S extends `${infer Key}[${infer Index}]${infer Rest}` ? [...(Key extends '' ? [] : [ParseSegment<Key>]), ParseSegment<Index>, ...ParseBrackets<Rest>] : S extends '' ? [] : [ParseSegment<S>];
|
|
20
|
+
/** As `parseCanopyPath`: `'blocks.0.title'` → `['blocks', number, 'title']`. */
|
|
21
|
+
type ParsePath<S extends string> = S extends `${infer Head}.${infer Rest}` ? [...ParseBrackets<Head>, ...ParsePath<Rest>] : ParseBrackets<S>;
|
|
22
|
+
type ImageKeys = 'src' | 'alt' | 'width' | 'height' | 'crop';
|
|
23
|
+
/** A value the form edits as one field: a scalar, a reference in any state, an image. */
|
|
24
|
+
type IsLeaf<V> = V extends string | number | boolean | bigint | symbol ? true : V extends {
|
|
25
|
+
unavailable: true;
|
|
26
|
+
} | ResolvedReferenceMeta ? true : V extends {
|
|
27
|
+
src: string;
|
|
28
|
+
alt: string;
|
|
29
|
+
} ? [Exclude<keyof V, ImageKeys>] extends [never] ? true : false : false;
|
|
30
|
+
/**
|
|
31
|
+
* A block item's fields are its template's, under `value`; the form's paths skip `value`. Its
|
|
32
|
+
* `_key` (`ITEM_KEY`, item-keys.ts) is structure, not a field.
|
|
33
|
+
*/
|
|
34
|
+
type ListItem<E> = E extends {
|
|
35
|
+
template: string;
|
|
36
|
+
value: infer W;
|
|
37
|
+
} ? [Exclude<keyof E, 'template' | 'value' | '_key'>] extends [never] ? W : E : E;
|
|
38
|
+
type NextSegment<V> = unknown extends V ? CanopyPathSegment : V extends unknown ? IsLeaf<V> extends true ? never : V extends readonly unknown[] ? number : V extends object ? Exclude<keyof V & string, '_key'> : never : never;
|
|
39
|
+
type Child<V, S> = unknown extends V ? unknown : V extends unknown ? V extends readonly (infer E)[] ? S extends number ? Defined<ListItem<Defined<E>>> : never : IsLeaf<V> extends true ? never : S extends keyof V ? Defined<V[S]> : never : never;
|
|
40
|
+
type ValueAt<V, P> = P extends readonly [infer Head, ...infer Rest] ? ValueAt<Child<V, Head>, Rest> : V;
|
|
41
|
+
type IsFieldPath<V, P> = P extends readonly [infer Head, ...infer Rest] ? Head extends NextSegment<V> ? IsFieldPath<Child<V, Head>, Rest> : false : true;
|
|
42
|
+
/** The path with the allowed segments at its first wrong one, for the compile error. */
|
|
43
|
+
type ExpectedSegments<V, P> = P extends readonly [infer Head, ...infer Rest] ? Head extends NextSegment<V> ? readonly [Head, ...ExpectedSegments<Child<V, Head>, Rest>] : readonly [NextSegment<V>, ...CanopyPathSegment[]] : readonly [];
|
|
44
|
+
type SegmentsArg<V, P extends readonly unknown[]> = unknown extends V ? P : number extends P['length'] ? ComputedPathError : IsFieldPath<V, P> extends true ? P : ExpectedSegments<V, P>;
|
|
45
|
+
type Spell<P> = P extends readonly [infer Head, ...infer Rest] ? `${Head extends number ? `[${Head}]` : `.${Head & string}`}${Spell<Rest>}` : '';
|
|
46
|
+
type TrimDot<S extends string> = S extends `.${infer Rest}` ? Rest : S;
|
|
47
|
+
/** The paths one step past `P`'s last valid prefix, for the compile error. */
|
|
48
|
+
type Completions<V, P, Done extends readonly CanopyPathSegment[] = []> = P extends readonly [
|
|
49
|
+
infer Head,
|
|
50
|
+
...infer Rest
|
|
51
|
+
] ? Head extends NextSegment<V> ? Completions<Child<V, Head>, Rest, [...Done, Head]> : [NextSegment<V>] extends [never] ? TrimDot<Spell<Done>> : TrimDot<Spell<[...Done, NextSegment<V>]>> : never;
|
|
52
|
+
type ComputedPathError = 'a computed path is untyped: pass segments as a literal, e.g. ["list", i, "field"]';
|
|
53
|
+
type StringArg<V, S extends string> = unknown extends V ? S : string extends S ? ComputedPathError : TemplateHole extends ParsePath<S>[number] ? ComputedPathError : IsFieldPath<V, ParsePath<S>> extends true ? S : Completions<V, ParsePath<S>>;
|
|
54
|
+
/** `NonNullable<unknown>` is `{}`, which would make untyped content typed. */
|
|
55
|
+
type Defined<T> = unknown extends T ? unknown : NonNullable<T>;
|
|
56
|
+
declare const fieldPropsContent: unique symbol;
|
|
57
|
+
/**
|
|
58
|
+
* Marks the element rendering a field: `<h1 {...fieldProps('title')}>`. A path is a string
|
|
59
|
+
* literal or literal segments (`['blocks', i, 'title']`, for anything computed), checked against `T`
|
|
60
|
+
* the way the form names fields: a block's fields follow its index (no `.value`), and nothing
|
|
61
|
+
* is below a reference or image. Any template's fields compile after a block's index; the
|
|
62
|
+
* editor warns about the rest. `FieldProps` alone (`T = unknown`) takes any path.
|
|
63
|
+
*/
|
|
64
|
+
export type FieldProps<T = unknown> = FieldPropsCall<Defined<T>> & {
|
|
65
|
+
/** Bivariant, so any typed `FieldProps` still assigns to plain `FieldProps`. */
|
|
66
|
+
readonly [fieldPropsContent]?: {
|
|
67
|
+
bivariant(content: T): void;
|
|
68
|
+
}['bivariant'];
|
|
69
|
+
};
|
|
70
|
+
interface FieldPropsCall<V> {
|
|
71
|
+
<const S extends string>(path: StringArg<V, S>): FieldAttrs;
|
|
72
|
+
<const P extends readonly CanopyPathSegment[]>(path: SegmentsArg<V, P>): FieldAttrs;
|
|
73
|
+
}
|
|
74
|
+
/** An empty path marks nothing. @internal `useCanopyPreview` is its only caller. */
|
|
75
|
+
export declare const createFieldProps: <T>() => FieldProps<T>;
|
|
76
|
+
/** `fieldProps(path)`, or nothing on a public page; `[]` marks the field a caller scoped to. */
|
|
77
|
+
export declare function fieldAttrs<T, const S extends string>(fieldProps: FieldProps<T> | undefined, path: StringArg<Defined<T>, S>): FieldAttrs;
|
|
78
|
+
export declare function fieldAttrs<T, const P extends readonly CanopyPathSegment[]>(fieldProps: FieldProps<T> | undefined, path: SegmentsArg<Defined<T>, P>): FieldAttrs;
|
|
79
|
+
/**
|
|
80
|
+
* `fieldProps` for a component rendering part of the entry, which marks paths relative to it
|
|
81
|
+
* while its caller names the part: `scopeFieldProps(fieldProps, ['sections', i])`. `undefined`
|
|
82
|
+
* in, `undefined` out, so a public page never builds a function.
|
|
83
|
+
*/
|
|
84
|
+
export declare function scopeFieldProps<T, const S extends string>(fieldProps: FieldProps<T> | undefined, prefix: StringArg<Defined<T>, S>): FieldProps<ValueAt<Defined<T>, ParsePath<S>>> | undefined;
|
|
85
|
+
export declare function scopeFieldProps<T, const P extends readonly CanopyPathSegment[]>(fieldProps: FieldProps<T> | undefined, prefix: SegmentsArg<Defined<T>, P>): FieldProps<ValueAt<Defined<T>, P>> | undefined;
|
|
86
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preview field marks, typed against the view's content. Exported from the root `canopycms`,
|
|
3
|
+
* not `canopycms/preview`: that module is `'use client'`, and a server component calling its
|
|
4
|
+
* functions gets a client reference that throws. A public page passes `fieldProps` as
|
|
5
|
+
* `undefined`, never a no-op function, which Next refuses to pass from server to client.
|
|
6
|
+
*/
|
|
7
|
+
import { normalizeCanopyPath } from './canopy-path.js';
|
|
8
|
+
const markField = (path) => {
|
|
9
|
+
const normalized = normalizeCanopyPath(path);
|
|
10
|
+
return normalized ? { 'data-canopy-path': normalized } : {};
|
|
11
|
+
};
|
|
12
|
+
/** An empty path marks nothing. @internal `useCanopyPreview` is its only caller. */
|
|
13
|
+
export const createFieldProps = () => markField;
|
|
14
|
+
export function fieldAttrs(fieldProps, path) {
|
|
15
|
+
return fieldProps ? fieldProps(path) : {};
|
|
16
|
+
}
|
|
17
|
+
export function scopeFieldProps(fieldProps, prefix) {
|
|
18
|
+
if (!fieldProps)
|
|
19
|
+
return undefined;
|
|
20
|
+
const base = normalizeCanopyPath(prefix);
|
|
21
|
+
return (path) => {
|
|
22
|
+
const relative = normalizeCanopyPath(path);
|
|
23
|
+
if (!relative)
|
|
24
|
+
return fieldProps(base);
|
|
25
|
+
return fieldProps(relative.startsWith('[') ? `${base}${relative}` : `${base}.${relative}`);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The form element for a path, as given or as `canopy-path.ts` spells it (`blocks.2.title` is
|
|
3
|
+
* `blocks[2].title`), else for its nearest ancestor that has one: a list item (`tags[1]`) has no
|
|
4
|
+
* field of its own. Compares values, not a selector: the path is untrusted.
|
|
5
|
+
*/
|
|
6
|
+
export declare const findFieldTarget: (path: string, root?: ParentNode) => {
|
|
7
|
+
element: HTMLElement;
|
|
8
|
+
path: string;
|
|
9
|
+
} | undefined;
|
|
10
|
+
/** Dispatched from a form element to open every collapsed list row holding it (`ItemList`). */
|
|
11
|
+
export declare const REVEAL_FIELD_EVENT = "canopy:reveal-field";
|
|
12
|
+
/**
|
|
13
|
+
* Asks every collapsed row holding `element` to open. True when one was closed: its content shows
|
|
14
|
+
* once React re-renders, so act on the element through `whenRevealed`. @internal Exported for
|
|
15
|
+
* tests; callers use `whenRevealed`.
|
|
16
|
+
*/
|
|
17
|
+
export declare const revealField: (element: HTMLElement) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Runs `then` once `element` is shown: at once, or a few frames on when a row had to open. It asks
|
|
20
|
+
* again each frame, since a row mounted in the same commit as the element may not listen yet.
|
|
21
|
+
*/
|
|
22
|
+
export declare const whenRevealed: (element: HTMLElement, then: () => void) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Scrolls a field's element into view and focuses what a person would type into: the element
|
|
25
|
+
* itself when it is an input, else its first focusable input, else its first other focusable
|
|
26
|
+
* control that is not a comment control, else the element when it takes programmatic focus.
|
|
27
|
+
* An input out of the tab order or not rendered, such as a dropzone's file input, is never chosen.
|
|
28
|
+
* Collapsed list rows holding the element open first.
|
|
29
|
+
*/
|
|
30
|
+
export declare const focusFieldTarget: (element: HTMLElement) => void;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { formatCanopyPath, parseCanopyPath } from './canopy-path.js';
|
|
2
|
+
/**
|
|
3
|
+
* The form element for a path, as given or as `canopy-path.ts` spells it (`blocks.2.title` is
|
|
4
|
+
* `blocks[2].title`), else for its nearest ancestor that has one: a list item (`tags[1]`) has no
|
|
5
|
+
* field of its own. Compares values, not a selector: the path is untrusted.
|
|
6
|
+
*/
|
|
7
|
+
export const findFieldTarget = (path, root = document) => {
|
|
8
|
+
const byField = new Map();
|
|
9
|
+
for (const element of root.querySelectorAll('[data-canopy-field]')) {
|
|
10
|
+
const field = element.getAttribute('data-canopy-field');
|
|
11
|
+
if (field && !byField.has(field))
|
|
12
|
+
byField.set(field, element);
|
|
13
|
+
}
|
|
14
|
+
const exact = byField.get(path);
|
|
15
|
+
if (exact)
|
|
16
|
+
return { element: exact, path };
|
|
17
|
+
const segments = parseCanopyPath(path);
|
|
18
|
+
for (let length = segments.length; length > 0; length--) {
|
|
19
|
+
const candidate = formatCanopyPath(segments.slice(0, length));
|
|
20
|
+
const element = byField.get(candidate);
|
|
21
|
+
if (element)
|
|
22
|
+
return { element, path: candidate };
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
};
|
|
26
|
+
/** Dispatched from a form element to open every collapsed list row holding it (`ItemList`). */
|
|
27
|
+
export const REVEAL_FIELD_EVENT = 'canopy:reveal-field';
|
|
28
|
+
/**
|
|
29
|
+
* Asks every collapsed row holding `element` to open. True when one was closed: its content shows
|
|
30
|
+
* once React re-renders, so act on the element through `whenRevealed`. @internal Exported for
|
|
31
|
+
* tests; callers use `whenRevealed`.
|
|
32
|
+
*/
|
|
33
|
+
export const revealField = (element) => !element.dispatchEvent(new CustomEvent(REVEAL_FIELD_EVENT, { bubbles: true, cancelable: true }));
|
|
34
|
+
const MAX_REVEAL_FRAMES = 10;
|
|
35
|
+
/**
|
|
36
|
+
* Runs `then` once `element` is shown: at once, or a few frames on when a row had to open. It asks
|
|
37
|
+
* again each frame, since a row mounted in the same commit as the element may not listen yet.
|
|
38
|
+
*/
|
|
39
|
+
export const whenRevealed = (element, then) => {
|
|
40
|
+
const shown = () => typeof element.checkVisibility !== 'function' || element.checkVisibility();
|
|
41
|
+
if (!revealField(element) && shown()) {
|
|
42
|
+
then();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
let frames = 0;
|
|
46
|
+
const check = () => {
|
|
47
|
+
frames += 1;
|
|
48
|
+
if (shown() || frames >= MAX_REVEAL_FRAMES) {
|
|
49
|
+
then();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
revealField(element);
|
|
53
|
+
requestAnimationFrame(check);
|
|
54
|
+
};
|
|
55
|
+
requestAnimationFrame(check);
|
|
56
|
+
};
|
|
57
|
+
const EDITABLE = 'input:not([type="hidden"]), textarea, select, [contenteditable="true"]';
|
|
58
|
+
const FOCUSABLE = 'button, [href], [tabindex]';
|
|
59
|
+
/** Takes keyboard focus as it stands: enabled, in the tab order, and rendered. */
|
|
60
|
+
const canTakeFocus = (el) => !el.matches(':disabled, [tabindex="-1"]') &&
|
|
61
|
+
(typeof el.checkVisibility !== 'function' || el.checkVisibility({ visibilityProperty: true }));
|
|
62
|
+
/**
|
|
63
|
+
* Scrolls a field's element into view and focuses what a person would type into: the element
|
|
64
|
+
* itself when it is an input, else its first focusable input, else its first other focusable
|
|
65
|
+
* control that is not a comment control, else the element when it takes programmatic focus.
|
|
66
|
+
* An input out of the tab order or not rendered, such as a dropzone's file input, is never chosen.
|
|
67
|
+
* Collapsed list rows holding the element open first.
|
|
68
|
+
*/
|
|
69
|
+
export const focusFieldTarget = (element) => whenRevealed(element, () => focusShownField(element));
|
|
70
|
+
const focusShownField = (element) => {
|
|
71
|
+
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
72
|
+
const inputs = [element, ...element.querySelectorAll(EDITABLE)].filter((el) => el.matches(EDITABLE) && canTakeFocus(el));
|
|
73
|
+
// The element itself counts: a date field's target is its picker's trigger button.
|
|
74
|
+
const controls = [element, ...element.querySelectorAll(FOCUSABLE)].filter((el) => el.matches(FOCUSABLE) && canTakeFocus(el) && !el.closest('[data-comment-control]'));
|
|
75
|
+
const focusable = inputs[0] ?? controls[0] ?? (element.hasAttribute('tabindex') ? element : undefined);
|
|
76
|
+
focusable?.focus({ preventScroll: true });
|
|
77
|
+
};
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { BlockConfig, FieldConfig } from '../../config/index.js';
|
|
3
|
+
import { ITEM_KEY } from '../../item-keys.js';
|
|
3
4
|
export interface BlockInstance {
|
|
4
5
|
template: string;
|
|
5
6
|
value: Record<string, unknown>;
|
|
7
|
+
/** The block's key (`item-keys.ts`). */
|
|
8
|
+
[ITEM_KEY]?: string;
|
|
6
9
|
}
|
|
7
10
|
type RenderField = (field: FieldConfig, value: unknown, onChange: (v: unknown) => void, path: Array<string | number>) => React.ReactNode;
|
|
8
11
|
export interface BlockFieldProps {
|
|
9
12
|
label?: string;
|
|
13
|
+
required?: boolean;
|
|
10
14
|
description?: string;
|
|
11
15
|
templates: BlockConfig[];
|
|
12
16
|
value: BlockInstance[];
|
|
@@ -14,6 +18,14 @@ export interface BlockFieldProps {
|
|
|
14
18
|
renderField: RenderField;
|
|
15
19
|
path: Array<string | number>;
|
|
16
20
|
dataCanopyField?: string;
|
|
21
|
+
/** No add, remove, reorder or drag; rows still collapse. */
|
|
22
|
+
readOnly?: boolean;
|
|
23
|
+
/** Names the entry being edited; another entry starts with the default rows open. */
|
|
24
|
+
resetKey?: string;
|
|
25
|
+
/** True when a field inside block `index` has a validation error. */
|
|
26
|
+
hasErrors?: (index: number) => boolean;
|
|
27
|
+
/** Told of each block Remove takes out, so the caller can offer Undo. */
|
|
28
|
+
onRemoved?: (index: number, block: BlockInstance) => void;
|
|
17
29
|
}
|
|
18
30
|
export declare const BlockField: React.FC<BlockFieldProps>;
|
|
19
31
|
export {};
|