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
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AssetRecord } from '../../api/index.js';
|
|
2
|
+
interface AssetLibraryError {
|
|
3
|
+
message: string;
|
|
4
|
+
/** True when page 1 failed: nothing is loaded, and retrying starts over. */
|
|
5
|
+
firstPage: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface UseAssetLibraryResult {
|
|
8
|
+
assets: AssetRecord[];
|
|
9
|
+
/** Page 1 is in; the library is renderable even while later pages load. */
|
|
10
|
+
firstPageLoaded: boolean;
|
|
11
|
+
/** Every page is in. */
|
|
12
|
+
complete: boolean;
|
|
13
|
+
error: AssetLibraryError | null;
|
|
14
|
+
/** Resumes from the page that failed; starts over when page 1 failed or the listing moved. */
|
|
15
|
+
retry: () => void;
|
|
16
|
+
/** Puts an uploaded asset in the list; the one already loaded under its hash is replaced. */
|
|
17
|
+
prepend: (asset: AssetRecord) => void;
|
|
18
|
+
/** Drops a deleted asset, including from a page still on its way. */
|
|
19
|
+
remove: (hash32: string) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Loads the whole library in the background while `opened`: page 1 first, then each following
|
|
23
|
+
* page in turn, so search and sort see every asset. Closing (or unmounting) stops the walk, and
|
|
24
|
+
* reopening starts again from page 1.
|
|
25
|
+
*
|
|
26
|
+
* Finalize dedups content, so a re-uploaded file comes back as an existing record that may also
|
|
27
|
+
* arrive in a later page; assets are merged by `hash32`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function useAssetLibrary(opened: boolean): UseAssetLibraryResult;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { getErrorMessage } from '../../utils/error.js';
|
|
4
|
+
import { useApiClient } from '../context/index.js';
|
|
5
|
+
const PAGE_SIZE = 40;
|
|
6
|
+
/** Appends the incoming assets not already present; the first copy of a hash wins. */
|
|
7
|
+
function mergeAssets(existing, incoming, removed) {
|
|
8
|
+
const seen = new Set(existing.map((asset) => asset.hash32));
|
|
9
|
+
const added = [];
|
|
10
|
+
for (const asset of incoming) {
|
|
11
|
+
if (seen.has(asset.hash32) || removed.has(asset.hash32))
|
|
12
|
+
continue;
|
|
13
|
+
seen.add(asset.hash32);
|
|
14
|
+
added.push(asset);
|
|
15
|
+
}
|
|
16
|
+
return added.length === 0 ? [...existing] : [...existing, ...added];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Loads the whole library in the background while `opened`: page 1 first, then each following
|
|
20
|
+
* page in turn, so search and sort see every asset. Closing (or unmounting) stops the walk, and
|
|
21
|
+
* reopening starts again from page 1.
|
|
22
|
+
*
|
|
23
|
+
* Finalize dedups content, so a re-uploaded file comes back as an existing record that may also
|
|
24
|
+
* arrive in a later page; assets are merged by `hash32`.
|
|
25
|
+
*/
|
|
26
|
+
export function useAssetLibrary(opened) {
|
|
27
|
+
const client = useApiClient();
|
|
28
|
+
const [assets, setAssets] = useState([]);
|
|
29
|
+
const [firstPageLoaded, setFirstPageLoaded] = useState(false);
|
|
30
|
+
const [complete, setComplete] = useState(false);
|
|
31
|
+
const [error, setError] = useState(null);
|
|
32
|
+
// A response whose runId is no longer current belongs to a closed opening or a superseded
|
|
33
|
+
// walk, and is dropped.
|
|
34
|
+
const generation = useRef(0);
|
|
35
|
+
// Cursors already requested; a server that returns one twice would otherwise loop forever.
|
|
36
|
+
const requested = useRef(new Set());
|
|
37
|
+
const removed = useRef(new Set());
|
|
38
|
+
const failedCursor = useRef(undefined);
|
|
39
|
+
const walk = useCallback(async (startCursor, runId) => {
|
|
40
|
+
let cursor = startCursor;
|
|
41
|
+
for (;;) {
|
|
42
|
+
if (cursor !== undefined)
|
|
43
|
+
requested.current.add(cursor);
|
|
44
|
+
let page;
|
|
45
|
+
let message = '';
|
|
46
|
+
try {
|
|
47
|
+
const result = await client.assets.list(cursor === undefined
|
|
48
|
+
? { limit: String(PAGE_SIZE) }
|
|
49
|
+
: { cursor, limit: String(PAGE_SIZE) });
|
|
50
|
+
if (result.ok && result.data)
|
|
51
|
+
page = result.data;
|
|
52
|
+
else
|
|
53
|
+
message = result.error || 'The server did not return the list.';
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
message = getErrorMessage(err);
|
|
57
|
+
}
|
|
58
|
+
if (runId !== generation.current)
|
|
59
|
+
return;
|
|
60
|
+
if (!page) {
|
|
61
|
+
failedCursor.current = cursor;
|
|
62
|
+
if (cursor !== undefined)
|
|
63
|
+
requested.current.delete(cursor);
|
|
64
|
+
setError({ message, firstPage: cursor === undefined });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const incoming = page.assets;
|
|
68
|
+
setAssets((prev) => mergeAssets(prev, incoming, removed.current));
|
|
69
|
+
if (cursor === undefined)
|
|
70
|
+
setFirstPageLoaded(true);
|
|
71
|
+
const next = page.nextCursor;
|
|
72
|
+
if (!next) {
|
|
73
|
+
setComplete(true);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// A cursor seen before means the listing moved under the walk (the local store restarts
|
|
77
|
+
// from page 1 when its anchor asset is deleted), so pages may be missing: never report
|
|
78
|
+
// that as complete. Retry starts over.
|
|
79
|
+
if (requested.current.has(next)) {
|
|
80
|
+
failedCursor.current = undefined;
|
|
81
|
+
setError({ message: 'The library changed while it was loading.', firstPage: false });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
cursor = next;
|
|
85
|
+
}
|
|
86
|
+
}, [client]);
|
|
87
|
+
const start = useCallback(() => {
|
|
88
|
+
generation.current += 1;
|
|
89
|
+
requested.current = new Set();
|
|
90
|
+
removed.current = new Set();
|
|
91
|
+
failedCursor.current = undefined;
|
|
92
|
+
setAssets([]);
|
|
93
|
+
setFirstPageLoaded(false);
|
|
94
|
+
setComplete(false);
|
|
95
|
+
setError(null);
|
|
96
|
+
void walk(undefined, generation.current);
|
|
97
|
+
}, [walk]);
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (!opened)
|
|
100
|
+
return;
|
|
101
|
+
start();
|
|
102
|
+
return () => {
|
|
103
|
+
generation.current += 1;
|
|
104
|
+
};
|
|
105
|
+
// Restart only when the drawer/modal (re)opens, not when `start`'s identity changes.
|
|
106
|
+
}, [opened]);
|
|
107
|
+
const retry = useCallback(() => {
|
|
108
|
+
const cursor = failedCursor.current;
|
|
109
|
+
if (cursor === undefined) {
|
|
110
|
+
start();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
generation.current += 1;
|
|
114
|
+
setError(null);
|
|
115
|
+
void walk(cursor, generation.current);
|
|
116
|
+
}, [start, walk]);
|
|
117
|
+
const prepend = useCallback((asset) => {
|
|
118
|
+
removed.current.delete(asset.hash32);
|
|
119
|
+
setAssets((prev) => [asset, ...prev.filter((existing) => existing.hash32 !== asset.hash32)]);
|
|
120
|
+
}, []);
|
|
121
|
+
const remove = useCallback((hash32) => {
|
|
122
|
+
removed.current.add(hash32);
|
|
123
|
+
setAssets((prev) => prev.filter((existing) => existing.hash32 !== hash32));
|
|
124
|
+
}, []);
|
|
125
|
+
return { assets, firstPageLoaded, complete, error, retry, prepend, remove };
|
|
126
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AssetRecord } from '../../api/index.js';
|
|
2
|
+
import { type MediaSort } from './media-library-view.js';
|
|
3
|
+
/**
|
|
4
|
+
* The assets in display order, which holds still while pages arrive: each new page is sorted and
|
|
5
|
+
* appended after what is already shown, and the whole list is re-sorted only when `sort` changes
|
|
6
|
+
* or loading completes. The list API pages in storage order (hash order on S3), so a live re-sort
|
|
7
|
+
* would move cards under the pointer and a picker click could choose the wrong asset.
|
|
8
|
+
*
|
|
9
|
+
* `pinned` hashes (this opening's uploads, newest first) lead the list whatever the sort.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useStableAssetOrder(assets: readonly AssetRecord[], sort: MediaSort, complete: boolean, pinned: readonly string[]): AssetRecord[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { sortAssets } from './media-library-view.js';
|
|
4
|
+
/**
|
|
5
|
+
* The assets in display order, which holds still while pages arrive: each new page is sorted and
|
|
6
|
+
* appended after what is already shown, and the whole list is re-sorted only when `sort` changes
|
|
7
|
+
* or loading completes. The list API pages in storage order (hash order on S3), so a live re-sort
|
|
8
|
+
* would move cards under the pointer and a picker click could choose the wrong asset.
|
|
9
|
+
*
|
|
10
|
+
* `pinned` hashes (this opening's uploads, newest first) lead the list whatever the sort.
|
|
11
|
+
*/
|
|
12
|
+
export function useStableAssetOrder(assets, sort, complete, pinned) {
|
|
13
|
+
const key = `${sort}|${complete}`;
|
|
14
|
+
const [state, setState] = useState(() => ({
|
|
15
|
+
key,
|
|
16
|
+
order: sortAssets(assets, sort).map((asset) => asset.hash32),
|
|
17
|
+
}));
|
|
18
|
+
let order = state.order;
|
|
19
|
+
if (state.key !== key) {
|
|
20
|
+
order = sortAssets(assets, sort).map((asset) => asset.hash32);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const known = new Set(order);
|
|
24
|
+
const fresh = assets.filter((asset) => !known.has(asset.hash32));
|
|
25
|
+
if (fresh.length > 0)
|
|
26
|
+
order = [...order, ...sortAssets(fresh, sort).map((a) => a.hash32)];
|
|
27
|
+
}
|
|
28
|
+
// Adjusting state during render rather than in an effect avoids a frame in the old order:
|
|
29
|
+
// https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes
|
|
30
|
+
if (order !== state.order)
|
|
31
|
+
setState({ key, order });
|
|
32
|
+
const byHash = new Map(assets.map((asset) => [asset.hash32, asset]));
|
|
33
|
+
const pinnedSet = new Set(pinned);
|
|
34
|
+
const lead = pinned.flatMap((hash) => byHash.get(hash) ?? []);
|
|
35
|
+
const rest = order.flatMap((hash) => (pinnedSet.has(hash) ? [] : (byHash.get(hash) ?? [])));
|
|
36
|
+
return [...lead, ...rest];
|
|
37
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the thumbnail at `src` failed to load. A new `src` clears the failure, so a thumbnail
|
|
3
|
+
* that failed once gets another chance. Adjusting state during render, not in an effect, avoids
|
|
4
|
+
* an extra pass: https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes
|
|
5
|
+
*/
|
|
6
|
+
export declare function useThumbnailFailure(src: string): [boolean, () => void];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Whether the thumbnail at `src` failed to load. A new `src` clears the failure, so a thumbnail
|
|
5
|
+
* that failed once gets another chance. Adjusting state during render, not in an effect, avoids
|
|
6
|
+
* an extra pass: https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes
|
|
7
|
+
*/
|
|
8
|
+
export function useThumbnailFailure(src) {
|
|
9
|
+
const [failed, setFailed] = useState(false);
|
|
10
|
+
const [prevSrc, setPrevSrc] = useState(src);
|
|
11
|
+
if (src !== prevSrc) {
|
|
12
|
+
setPrevSrc(src);
|
|
13
|
+
setFailed(false);
|
|
14
|
+
}
|
|
15
|
+
return [failed, () => setFailed(true)];
|
|
16
|
+
}
|
|
@@ -10,7 +10,7 @@ export const GroupSelector = ({ searchQuery, onSearchChange, filteredGroups, onS
|
|
|
10
10
|
return (_jsx(Paper, { withBorder: true, p: "xs", mt: "xs", style: { maxWidth: 300 }, children: _jsxs(Stack, { gap: "xs", children: [_jsx("input", { type: "text", placeholder: "Search groups...", value: searchQuery, onChange: (e) => onSearchChange(e.target.value), "aria-label": "Search groups", style: {
|
|
11
11
|
width: '100%',
|
|
12
12
|
padding: '4px 6px',
|
|
13
|
-
border: '1px solid var(--
|
|
13
|
+
border: '1px solid var(--canopy-input-border)',
|
|
14
14
|
borderRadius: 'var(--mantine-radius-sm)',
|
|
15
15
|
fontSize: '12px',
|
|
16
16
|
} }), filteredGroups.length > 0 && (_jsx("div", { style: { maxHeight: 200, overflowY: 'auto' }, children: filteredGroups.map((group) => (_jsx("div", { style: {
|
|
@@ -19,7 +19,7 @@ export const GroupSelector = ({ searchQuery, onSearchChange, filteredGroups, onS
|
|
|
19
19
|
borderRadius: 'var(--mantine-radius-sm)',
|
|
20
20
|
fontSize: '12px',
|
|
21
21
|
}, onMouseEnter: (e) => {
|
|
22
|
-
e.currentTarget.style.backgroundColor = 'var(--mantine-color-
|
|
22
|
+
e.currentTarget.style.backgroundColor = 'var(--mantine-color-default-hover)';
|
|
23
23
|
}, onMouseLeave: (e) => {
|
|
24
24
|
e.currentTarget.style.backgroundColor = 'transparent';
|
|
25
25
|
}, onClick: () => onSelectGroup(group.value), children: _jsxs(Group, { gap: "xs", justify: "space-between", wrap: "nowrap", children: [_jsx("span", { children: group.label }), _jsx(Text, { size: "10px", c: "dimmed", style: { flexShrink: 0 }, children: GROUP_SOURCE_LABELS[group.source] })] }) }, group.value))) })), searchQuery.trim() && filteredGroups.length === 0 && (_jsx(Text, { size: "xs", c: "dimmed", children: "No groups found" }))] }) }));
|
|
@@ -14,7 +14,7 @@ export interface PermissionEditorProps {
|
|
|
14
14
|
userSearchQuery: string;
|
|
15
15
|
userSearchError: string | null;
|
|
16
16
|
onSearchUsers: (query: string) => void;
|
|
17
|
-
onGetUserMetadata
|
|
17
|
+
onGetUserMetadata: (userId: string) => Promise<UserSearchResult | null>;
|
|
18
18
|
onToggleUserSearch: (show: boolean) => void;
|
|
19
19
|
onAddUser: (path: string, level: PermissionLevel, userId: string) => void;
|
|
20
20
|
onRemoveUser: (path: string, level: PermissionLevel, userId: string) => void;
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useRef } from 'react';
|
|
3
4
|
import { ActionIcon, Badge, Button, Group, Paper, Stack, Tabs, Text, Tooltip } from '@mantine/core';
|
|
4
5
|
import { IconSearch, IconX, IconUserOff } from '@tabler/icons-react';
|
|
5
6
|
import { PERMISSION_LEVELS, LEVEL_CONFIG } from './constants.js';
|
|
6
7
|
import { GroupSelector } from './GroupSelector.js';
|
|
7
8
|
import { UserSelector } from './UserSelector.js';
|
|
8
9
|
import { UserBadge } from '../components/UserBadge.js';
|
|
10
|
+
import { useFocusAfterRemove } from '../utils/focus-return.js';
|
|
9
11
|
export const PermissionEditor = ({ node, activeLevel, onSetActiveLevel, canEdit, groups, isSelected, userSearchResults, isSearchingUsers, showUserSearch, userSearchQuery, userSearchError, onSearchUsers, onGetUserMetadata, onToggleUserSearch, onAddUser, onRemoveUser, showGroupSearch, groupSearchQuery, filteredGroups, onSearchGroups, onToggleGroupSearch, onAddGroup, onRemoveGroup, }) => {
|
|
12
|
+
const { listRef, focusAfterRemove } = useFocusAfterRemove();
|
|
13
|
+
const addGroupsRef = useRef(null);
|
|
14
|
+
const addUserRef = useRef(null);
|
|
10
15
|
const directPerm = node.directPermission;
|
|
11
16
|
const inheritedPerm = node.inheritedPermission;
|
|
12
17
|
const getTargetForLevel = (level, source) => {
|
|
@@ -19,10 +24,16 @@ export const PermissionEditor = ({ node, activeLevel, onSetActiveLevel, canEdit,
|
|
|
19
24
|
return (_jsxs(Tabs.Panel, { value: level, pt: "sm", children: [inheritedTarget && (_jsxs("div", { style: { marginBottom: 'var(--mantine-spacing-sm)' }, children: [_jsx(Text, { size: "xs", fw: 500, mb: 4, c: "dimmed", children: "Inherited from parent" }), _jsxs(Group, { gap: "xs", children: [inheritedTarget.allowedGroups?.map((groupId) => {
|
|
20
25
|
const groupInfo = groups.find((g) => g.value === groupId);
|
|
21
26
|
return (_jsx(Badge, { variant: "outline", color: "gray", size: "xs", children: groupInfo?.label ?? groupId }, `inherited-group-${groupId}`));
|
|
22
|
-
}), inheritedTarget.allowedUsers?.map((userId) =>
|
|
27
|
+
}), inheritedTarget.allowedUsers?.map((userId) => (_jsx(UserBadge, { userId: userId, getUserMetadata: onGetUserMetadata, variant: "avatar-name", size: "xs", badgeVariant: "outline", color: "gray", showEmailTooltip: true, showBadge: true, revealIdOnFallback: true }, `inherited-user-${userId}`)))] })] })), canEdit ? (_jsxs("div", { children: [_jsxs(Text, { size: "xs", fw: 500, mb: 4, children: [LEVEL_CONFIG[level].label, " access"] }), _jsxs(Group, { gap: "xs", mb: "xs", ref: listRef, children: [(directTarget?.allowedGroups ?? []).map((groupId, groupIndex) => {
|
|
23
28
|
const groupInfo = groups.find((g) => g.value === groupId);
|
|
24
|
-
return (_jsx(Badge, { variant: "
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
return (_jsx(Badge, { variant: "light", color: LEVEL_CONFIG[level].color, pr: 3, rightSection: _jsx(Tooltip, { label: `Remove ${groupInfo?.label ?? groupId}`, children: _jsx(ActionIcon, { size: "xs", color: LEVEL_CONFIG[level].color, radius: "xl", variant: "transparent", "aria-label": `Remove ${groupInfo?.label ?? groupId}`, onClick: () => {
|
|
30
|
+
onRemoveGroup(node.path, level, groupId);
|
|
31
|
+
focusAfterRemove(groupIndex, addGroupsRef);
|
|
32
|
+
}, children: _jsx(IconX, { size: 12, stroke: 2.5, style: { color: 'var(--mantine-color-text)' } }) }) }), children: groupInfo?.label ?? groupId }, `group-${groupId}`));
|
|
33
|
+
}), (directTarget?.allowedUsers ?? []).map((userId, userIndex) => (_jsx(UserBadge, { userId: userId, getUserMetadata: onGetUserMetadata, variant: "avatar-name", size: "xs", badgeVariant: "light", color: userId === 'anonymous' ? 'orange' : LEVEL_CONFIG[level].color, onRemove: () => {
|
|
34
|
+
onRemoveUser(node.path, level, userId);
|
|
35
|
+
focusAfterRemove((directTarget?.allowedGroups ?? []).length + userIndex, addUserRef);
|
|
36
|
+
}, showEmailTooltip: true, revealIdOnFallback: true }, `user-${userId}`))), (!directTarget?.allowedGroups || directTarget.allowedGroups.length === 0) &&
|
|
37
|
+
(!directTarget?.allowedUsers || directTarget.allowedUsers.length === 0) && (_jsxs(Text, { size: "xs", c: "dimmed", children: ["No groups or users assigned for ", level] }))] }), _jsxs(Group, { gap: "xs", children: [_jsx(Button, { ref: addGroupsRef, variant: "subtle", leftSection: _jsx(IconSearch, { size: 14 }), onClick: () => onToggleGroupSearch(!showGroupSearch), children: showGroupSearch ? 'Cancel' : 'Add groups' }), _jsx(Button, { ref: addUserRef, variant: "subtle", leftSection: _jsx(IconSearch, { size: 14 }), onClick: () => onToggleUserSearch(!showUserSearch), children: showUserSearch ? 'Cancel' : 'Add user' }), !(directTarget?.allowedUsers ?? []).includes('anonymous') && (_jsx(Tooltip, { label: "Allow unauthenticated/public access", children: _jsx(Button, { variant: "subtle", color: "orange", leftSection: _jsx(IconUserOff, { size: 14 }), onClick: () => onAddUser(node.path, level, 'anonymous'), children: "Add anonymous" }) }))] }), showGroupSearch && isSelected && activeLevel === level && (_jsx(GroupSelector, { searchQuery: groupSearchQuery, onSearchChange: onSearchGroups, filteredGroups: filteredGroups, onSelectGroup: (groupId) => onAddGroup(node.path, level, groupId) })), showUserSearch && isSelected && activeLevel === level && (_jsx(UserSelector, { searchQuery: userSearchQuery, onSearchChange: onSearchUsers, searchResults: userSearchResults, isSearching: isSearchingUsers, searchError: userSearchError, onSelectUser: (userId) => onAddUser(node.path, level, userId), onGetUserMetadata: onGetUserMetadata }))] })) : (_jsx(Text, { size: "xs", c: "dimmed", children: directTarget ? 'Read-only view' : 'No direct permissions set' }))] }, level));
|
|
27
38
|
})] })] }) }));
|
|
28
39
|
};
|
|
@@ -8,7 +8,7 @@ export const PermissionLevelBadge = ({ level, target, inherited, }) => {
|
|
|
8
8
|
inherited &&
|
|
9
9
|
((inherited.allowedUsers?.length ?? 0) > 0 || (inherited.allowedGroups?.length ?? 0) > 0);
|
|
10
10
|
if (hasPerms) {
|
|
11
|
-
return (_jsx(Badge, { size: "xs", variant: "
|
|
11
|
+
return (_jsx(Badge, { size: "xs", variant: "light", color: LEVEL_CONFIG[level].color, children: LEVEL_CONFIG[level].label }));
|
|
12
12
|
}
|
|
13
13
|
if (hasInherited) {
|
|
14
14
|
return (_jsx(Tooltip, { label: `${LEVEL_CONFIG[level].label} inherited from parent`, children: _jsx(Badge, { size: "xs", variant: "outline", color: "gray", children: LEVEL_CONFIG[level].label }) }));
|
|
@@ -20,7 +20,7 @@ export interface PermissionTreeProps {
|
|
|
20
20
|
onSetActiveLevel: (level: PermissionLevel) => void;
|
|
21
21
|
onUpdatePermission: (path: string, level: PermissionLevel, updates: Partial<PermissionTarget>) => void;
|
|
22
22
|
onSearchUsers: (query: string) => void;
|
|
23
|
-
onGetUserMetadata
|
|
23
|
+
onGetUserMetadata: (userId: string) => Promise<UserSearchResult | null>;
|
|
24
24
|
onToggleUserSearch: (show: boolean) => void;
|
|
25
25
|
onAddUser: (path: string, level: PermissionLevel, userId: string) => void;
|
|
26
26
|
onRemoveUser: (path: string, level: PermissionLevel, userId: string) => void;
|
|
@@ -7,6 +7,6 @@ export interface UserSelectorProps {
|
|
|
7
7
|
isSearching: boolean;
|
|
8
8
|
searchError: string | null;
|
|
9
9
|
onSelectUser: (userId: string) => void;
|
|
10
|
-
onGetUserMetadata
|
|
10
|
+
onGetUserMetadata: (userId: string) => Promise<UserSearchResult | null>;
|
|
11
11
|
}
|
|
12
12
|
export declare const UserSelector: React.FC<UserSelectorProps>;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Paper, Stack, Text, Loader } from '@mantine/core';
|
|
4
4
|
import { UserBadge } from '../components/UserBadge.js';
|
|
5
5
|
export const UserSelector = ({ searchQuery, onSearchChange, searchResults, isSearching, searchError, onSelectUser, onGetUserMetadata, }) => {
|
|
6
6
|
return (_jsx(Paper, { withBorder: true, p: "sm", mt: "xs", children: _jsxs(Stack, { gap: "xs", children: [_jsx("input", { type: "text", placeholder: "Search users by name or email...", value: searchQuery, onChange: (e) => onSearchChange(e.target.value), "aria-label": "Search users by name or email", style: {
|
|
7
7
|
width: '100%',
|
|
8
8
|
padding: '6px 8px',
|
|
9
|
-
border: '1px solid var(--
|
|
9
|
+
border: '1px solid var(--canopy-input-border)',
|
|
10
10
|
borderRadius: 'var(--mantine-radius-sm)',
|
|
11
11
|
fontSize: '14px',
|
|
12
|
-
} }), isSearching && _jsx(Loader, { size: "xs" }), searchError && (_jsx(Text, { size: "xs", c: "
|
|
12
|
+
} }), isSearching && _jsx(Loader, { size: "xs" }), searchError && (_jsx(Text, { size: "xs", c: "var(--canopy-danger-text)", children: searchError })), searchResults.length > 0 && (_jsx(Stack, { gap: 4, children: searchResults.map((user) => (_jsx(Paper, { p: "xs", withBorder: true, style: { cursor: 'pointer' }, onClick: () => onSelectUser(user.id), children: _jsx(UserBadge, { userId: user.id, getUserMetadata: onGetUserMetadata, variant: "full", size: "sm", cachedUser: user, showEmailTooltip: false }) }, user.id))) })), searchQuery.trim() && !isSearching && searchResults.length === 0 && (_jsx(Text, { size: "xs", c: "dimmed", children: "No users found" }))] }) }));
|
|
13
13
|
};
|
|
@@ -7,9 +7,16 @@ import { usePermissionTree } from './hooks/usePermissionTree.js';
|
|
|
7
7
|
import { useGroupsAndUsers } from './hooks/useGroupsAndUsers.js';
|
|
8
8
|
import { PermissionTree } from './PermissionTree.js';
|
|
9
9
|
import { findTreeNode } from './utils.js';
|
|
10
|
-
|
|
10
|
+
import { SettingsChangedError } from '../settings-conflict.js';
|
|
11
|
+
import { StagedChangesDrawer, useOpeningKey } from '../components/StagedChangesDrawer.js';
|
|
12
|
+
export const PermissionManager = (props) => {
|
|
13
|
+
const { key, onExited } = useOpeningKey();
|
|
14
|
+
return _jsx(PermissionManagerDrawer, { ...props, onExited: onExited }, key);
|
|
15
|
+
};
|
|
16
|
+
const PermissionManagerDrawer = ({ opened, onClose, onExited, returnFocusTo, collections, contentRoot = 'content', permissions, canEdit, onSave, onSearchUsers, onGetUserMetadata, onListGroups, loading = false, contentTree, }) => {
|
|
11
17
|
const [isSaving, setIsSaving] = useState(false);
|
|
12
18
|
const [error, setError] = useState(null);
|
|
19
|
+
const [errorIsConflict, setErrorIsConflict] = useState(false);
|
|
13
20
|
const [activeLevel, setActiveLevel] = useState('read');
|
|
14
21
|
const { annotatedTree, expandedNodes, selectedNode, localPermissions, isDirty, toggleNode, expandAll, collapseAll, selectNode, updateNodePermission, resetPermissions, setIsDirty, } = usePermissionTree({
|
|
15
22
|
collections,
|
|
@@ -18,7 +25,8 @@ export const PermissionManager = ({ collections, contentRoot = 'content', permis
|
|
|
18
25
|
contentTree,
|
|
19
26
|
});
|
|
20
27
|
const { groupSelectData, filteredGroups, groupLoadError, groupSearchQuery, showGroupSearch, setGroupSearchQuery, setShowGroupSearch, clearGroupLoadError, userSearchResults, isSearchingUsers, userSearchQuery, showUserSearch, userSearchError, setUserSearchQuery, toggleUserSearch, } = useGroupsAndUsers({
|
|
21
|
-
|
|
28
|
+
// This panel stays mounted while its drawer is closed; groups load on each opening.
|
|
29
|
+
onListGroups: opened ? onListGroups : undefined,
|
|
22
30
|
onSearchUsers,
|
|
23
31
|
canEdit,
|
|
24
32
|
});
|
|
@@ -33,6 +41,7 @@ export const PermissionManager = ({ collections, contentRoot = 'content', permis
|
|
|
33
41
|
}
|
|
34
42
|
catch (err) {
|
|
35
43
|
setError(err instanceof Error ? err.message : 'Failed to save permissions');
|
|
44
|
+
setErrorIsConflict(err instanceof SettingsChangedError);
|
|
36
45
|
}
|
|
37
46
|
finally {
|
|
38
47
|
setIsSaving(false);
|
|
@@ -102,7 +111,7 @@ export const PermissionManager = ({ collections, contentRoot = 'content', permis
|
|
|
102
111
|
allowedUsers: currentTarget.allowedUsers,
|
|
103
112
|
});
|
|
104
113
|
}, [annotatedTree, localPermissions, updateNodePermission]);
|
|
105
|
-
return (_jsxs(Stack, { h: "100%", style: { display: 'flex', flexDirection: 'column' }, gap: 0, children: [!canEdit && (_jsx(Alert, { icon: _jsx(IconAlertCircle, { size: 16 }), color: "yellow", mb: "sm", title: "Read-only", children: "You need admin access to edit permissions" })), error && (_jsx(Alert, { icon: _jsx(IconAlertCircle, { size: 16 }), color:
|
|
114
|
+
return (_jsx(StagedChangesDrawer, { opened: opened, onClose: onClose, title: "Permissions", description: "Manage content access by path (read, edit, review)", size: 700, isDirty: isDirty, isSaving: isSaving, canEdit: canEdit, saveLabel: "Save permissions", onSave: handleSave, onDiscard: handleDiscard, onExited: onExited, returnFocusTo: returnFocusTo, children: _jsxs(Stack, { h: "100%", style: { display: 'flex', flexDirection: 'column' }, gap: 0, children: [!canEdit && (_jsx(Alert, { icon: _jsx(IconAlertCircle, { size: 16 }), color: "yellow", mb: "sm", title: "Read-only", children: "You need admin access to edit permissions" })), error && (_jsx(Alert, { icon: _jsx(IconAlertCircle, { size: 16 }), color: errorIsConflict ? 'yellow' : 'red', mb: "sm", title: errorIsConflict ? 'Not saved' : 'Error', withCloseButton: true, onClose: () => setError(null), children: error })), groupLoadError && (_jsx(Alert, { icon: _jsx(IconAlertCircle, { size: 16 }), color: "orange", mb: "sm", title: "Warning", withCloseButton: true, onClose: clearGroupLoadError, children: groupLoadError })), _jsxs(Group, { gap: "xs", pb: "sm", children: [_jsx(Button, { variant: "subtle", onClick: expandAll, children: "Expand all" }), _jsx(Button, { variant: "subtle", onClick: collapseAll, children: "Collapse all" })] }), _jsx(ScrollArea, { style: { flex: 1 }, pb: "md", children: loading ? (_jsxs(Group, { justify: "center", py: "xl", children: [_jsx(Loader, { size: "md" }), _jsx(Text, { size: "sm", c: "dimmed", children: "Loading permissions..." })] })) : (_jsx(PermissionTree, { node: annotatedTree, expandedNodes: expandedNodes, selectedNode: selectedNode, canEdit: canEdit, groups: groupSelectData, activeLevel: activeLevel, userSearchResults: userSearchResults, isSearchingUsers: isSearchingUsers, showUserSearch: showUserSearch, userSearchQuery: userSearchQuery, userSearchError: userSearchError, showGroupSearch: showGroupSearch, groupSearchQuery: groupSearchQuery, filteredGroups: filteredGroups, onToggle: toggleNode, onSelect: selectNode, onSetActiveLevel: setActiveLevel, onUpdatePermission: updateNodePermission, onSearchUsers: setUserSearchQuery, onGetUserMetadata: onGetUserMetadata, onToggleUserSearch: toggleUserSearch, onAddUser: handleAddUser, onRemoveUser: handleRemoveUser, onSearchGroups: setGroupSearchQuery, onToggleGroupSearch: setShowGroupSearch, onAddGroup: handleAddGroup, onRemoveGroup: handleRemoveGroup })) })] }) }));
|
|
106
115
|
};
|
|
107
116
|
export { usePermissionTree } from './hooks/usePermissionTree.js';
|
|
108
117
|
export { useGroupsAndUsers } from './hooks/useGroupsAndUsers.js';
|
|
@@ -2,6 +2,10 @@ import type { PathPermission, PermissionLevel, PermissionTarget } from '../../co
|
|
|
2
2
|
import type { UserSearchResult, GroupSource, PermissionGroupOption } from '../../auth/types.js';
|
|
3
3
|
import type { EditorCollection } from '../Editor.js';
|
|
4
4
|
export interface PermissionManagerProps {
|
|
5
|
+
opened: boolean;
|
|
6
|
+
/** Closes the drawer; while there are unsaved changes it runs only after the user confirms discarding them. */
|
|
7
|
+
onClose: () => void;
|
|
8
|
+
returnFocusTo?: () => HTMLElement | null;
|
|
5
9
|
/** Collections from API */
|
|
6
10
|
collections?: EditorCollection[];
|
|
7
11
|
/** Content root path (default: 'content') */
|
|
@@ -11,10 +15,9 @@ export interface PermissionManagerProps {
|
|
|
11
15
|
canEdit: boolean;
|
|
12
16
|
onSave?: (permissions: PathPermission[]) => Promise<void>;
|
|
13
17
|
onSearchUsers?: (query: string, limit?: number) => Promise<UserSearchResult[]>;
|
|
14
|
-
onGetUserMetadata
|
|
18
|
+
onGetUserMetadata: (userId: string) => Promise<UserSearchResult | null>;
|
|
15
19
|
/** Handler to list groups (internal + external, tagged by `source`) */
|
|
16
20
|
onListGroups?: () => Promise<PermissionGroupOption[]>;
|
|
17
|
-
onClose?: () => void;
|
|
18
21
|
loading?: boolean;
|
|
19
22
|
/** Optional: actual filesystem content tree (for entries not in schema) */
|
|
20
23
|
contentTree?: ContentNode;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Platform detection for shortcut labels. */
|
|
2
|
+
/** The label for Mantine's `mod` key: ⌘ on Apple platforms, Ctrl elsewhere (and during SSR). */
|
|
3
|
+
export function modKeyLabel() {
|
|
4
|
+
if (typeof navigator === 'undefined')
|
|
5
|
+
return 'Ctrl';
|
|
6
|
+
const nav = navigator;
|
|
7
|
+
const platform = nav.userAgentData?.platform || nav.platform || '';
|
|
8
|
+
return /mac|iphone|ipad/i.test(platform) ? '⌘' : 'Ctrl';
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The prefix a live preview puts `/assets/t/` srcs behind, for `assets/asset-url.ts`; imports
|
|
3
|
+
* nothing. A browser stores it from drafts and `usePreviewAssetBaseGate`. A server reads the
|
|
4
|
+
* getter `canopycms-next`'s preview page registers, which is request-scoped, so a render outside a
|
|
5
|
+
* preview request, including every static build, sees `undefined`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function setPreviewAssetBase(base: string | undefined): void;
|
|
8
|
+
export declare function setServerPreviewAssetBaseGetter(getter: (() => string | undefined) | undefined): void;
|
|
9
|
+
export declare function getPreviewAssetBase(): string | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The prefix a live preview puts `/assets/t/` srcs behind, for `assets/asset-url.ts`; imports
|
|
3
|
+
* nothing. A browser stores it from drafts and `usePreviewAssetBaseGate`. A server reads the
|
|
4
|
+
* getter `canopycms-next`'s preview page registers, which is request-scoped, so a render outside a
|
|
5
|
+
* preview request, including every static build, sees `undefined`.
|
|
6
|
+
*/
|
|
7
|
+
let previewAssetBase;
|
|
8
|
+
let serverPreviewAssetBase;
|
|
9
|
+
export function setPreviewAssetBase(base) {
|
|
10
|
+
previewAssetBase = base;
|
|
11
|
+
}
|
|
12
|
+
export function setServerPreviewAssetBaseGetter(getter) {
|
|
13
|
+
serverPreviewAssetBase = getter;
|
|
14
|
+
}
|
|
15
|
+
export function getPreviewAssetBase() {
|
|
16
|
+
return typeof window === 'undefined' ? serverPreviewAssetBase?.() : previewAssetBase;
|
|
17
|
+
}
|
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { type CanopyPathSegment } from './canopy-path.js';
|
|
3
|
-
export declare const __CANOPY_PREVIEW_CLIENT__ = true;
|
|
1
|
+
import type { ResolvedReferenceMeta } from '../entry-schema.js';
|
|
4
2
|
export declare const CANOPY_PREVIEW_MESSAGE = "canopycms:draft:update";
|
|
5
3
|
export declare const CANOPY_PREVIEW_FOCUS = "canopycms:preview:focus";
|
|
6
4
|
export declare const CANOPY_PREVIEW_HIGHLIGHT = "canopycms:preview:highlight";
|
|
7
5
|
export declare const CANOPY_PREVIEW_READY = "canopycms:preview:ready";
|
|
8
6
|
export declare const CANOPY_PREVIEW_ERROR = "canopycms:preview:error";
|
|
7
|
+
export declare const CANOPY_PREVIEW_MARKS = "canopycms:preview:marks";
|
|
8
|
+
/**
|
|
9
|
+
* The shape of the preview's `isLoading` for data of type `T`: a `boolean` at each reference
|
|
10
|
+
* position, `true` while the editor is still resolving that reference, under the same keys and
|
|
11
|
+
* indexes as the data. A reference typed by `resolvedSchema` is recognized by its resolved or
|
|
12
|
+
* `unavailable` shape; any other non-object leaf may be an untyped reference, so it is a
|
|
13
|
+
* `boolean` too. Every key is optional because only reference positions are present, and nothing
|
|
14
|
+
* is before the first draft arrives.
|
|
15
|
+
*/
|
|
16
|
+
export type PreviewLoadingState<T> = T extends readonly (infer U)[] ? PreviewLoadingState<NonNullable<U>>[] : T extends ResolvedReferenceMeta | {
|
|
17
|
+
unavailable: true;
|
|
18
|
+
} ? boolean : T extends object ? {
|
|
19
|
+
[K in keyof T]?: PreviewLoadingState<NonNullable<T[K]>>;
|
|
20
|
+
} : boolean;
|
|
9
21
|
export interface DraftUpdateMessage {
|
|
10
22
|
type: typeof CANOPY_PREVIEW_MESSAGE;
|
|
11
23
|
path: string;
|
|
12
24
|
data?: unknown;
|
|
13
25
|
isLoading?: unknown;
|
|
26
|
+
/** The editor's authenticated asset route; see `PreviewFrame`'s `assetBase`. */
|
|
27
|
+
assetBase?: unknown;
|
|
14
28
|
}
|
|
15
29
|
/**
|
|
16
30
|
* Resolve a (possibly relative) URL to an origin for postMessage targeting.
|
|
@@ -18,6 +32,13 @@ export interface DraftUpdateMessage {
|
|
|
18
32
|
* need no configuration.
|
|
19
33
|
*/
|
|
20
34
|
export declare const resolveMessageOrigin: (url?: string) => string;
|
|
35
|
+
/**
|
|
36
|
+
* Opaque origins (sandboxed iframes, data: URLs) serialize to the string 'null':
|
|
37
|
+
* never trust them for inbound messages, and never post to them — every window in
|
|
38
|
+
* a sandboxed embed shares that string, so it identifies nothing, and postMessage
|
|
39
|
+
* throws on a literal 'null' targetOrigin. '' covers the SSR fallback.
|
|
40
|
+
*/
|
|
41
|
+
export declare const isOpaqueOrigin: (origin: string) => boolean;
|
|
21
42
|
/**
|
|
22
43
|
* True only for messages from the embedding editor window with the expected origin.
|
|
23
44
|
*
|
|
@@ -28,7 +49,6 @@ export declare const resolveMessageOrigin: (url?: string) => string;
|
|
|
28
49
|
* expected editor origin (same-origin unless `editorOrigin` is configured).
|
|
29
50
|
*/
|
|
30
51
|
export declare const isTrustedEditorMessage: (event: MessageEvent, editorOrigin?: string) => boolean;
|
|
31
|
-
export declare const sendDraftUpdate: (iframe: HTMLIFrameElement | null, message: DraftUpdateMessage, targetOrigin?: string) => void;
|
|
32
52
|
export interface PreviewFocusMessage {
|
|
33
53
|
type: typeof CANOPY_PREVIEW_FOCUS;
|
|
34
54
|
entryPath: string;
|
|
@@ -38,6 +58,22 @@ export interface HighlightMessage {
|
|
|
38
58
|
type: typeof CANOPY_PREVIEW_HIGHLIGHT;
|
|
39
59
|
enabled: boolean;
|
|
40
60
|
}
|
|
61
|
+
/** The most distinct mark paths one report carries, and the longest path it carries. */
|
|
62
|
+
export declare const MARK_REPORT_LIMITS: {
|
|
63
|
+
readonly paths: 500;
|
|
64
|
+
readonly pathLength: 512;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Preview → editor, while highlighting is on: how many `data-canopy-path` elements the page has,
|
|
68
|
+
* so the editor can say when there is nothing to outline, and their distinct paths as the page
|
|
69
|
+
* spells them, within `MARK_REPORT_LIMITS`, so it can say which name no field. An older bridge
|
|
70
|
+
* sends no report, and one before `paths` sends only the count.
|
|
71
|
+
*/
|
|
72
|
+
export interface PreviewMarksMessage {
|
|
73
|
+
type: typeof CANOPY_PREVIEW_MARKS;
|
|
74
|
+
count: number;
|
|
75
|
+
paths?: string[];
|
|
76
|
+
}
|
|
41
77
|
/**
|
|
42
78
|
* Preview → editor report that the current draft fails to compile/render
|
|
43
79
|
* (e.g. malformed MDX). `message: null` clears an earlier error.
|
|
@@ -59,11 +95,9 @@ export declare const useCanopyPreview: <T>(opts: {
|
|
|
59
95
|
editorOrigin?: string;
|
|
60
96
|
}) => {
|
|
61
97
|
data: T;
|
|
62
|
-
isLoading:
|
|
98
|
+
isLoading: PreviewLoadingState<T>;
|
|
63
99
|
highlightEnabled: boolean;
|
|
64
|
-
fieldProps: (
|
|
65
|
-
'data-canopy-path': string;
|
|
66
|
-
};
|
|
100
|
+
fieldProps: import("./field-props.js").FieldProps<T>;
|
|
67
101
|
reportError: (message: string | null, fieldPath?: string) => void;
|
|
68
102
|
};
|
|
69
103
|
/**
|
|
@@ -74,8 +108,16 @@ export declare const usePreviewData: <T>(path: string, initialData: T, opts?: {
|
|
|
74
108
|
editorOrigin?: string;
|
|
75
109
|
}) => {
|
|
76
110
|
data: T;
|
|
77
|
-
isLoading:
|
|
111
|
+
isLoading: PreviewLoadingState<T>;
|
|
78
112
|
};
|
|
113
|
+
/**
|
|
114
|
+
* Holds a `createPreviewPage` view back until hydration commits, then stores `assetBase` and opens
|
|
115
|
+
* before paint: the server HTML has no public `/assets/t/` URL and nothing to mismatch. A draft's
|
|
116
|
+
* prefix still wins, since drafts follow the ready message `usePreviewData` posts from a passive
|
|
117
|
+
* effect. With no `assetBase` (a public page) it is open from the start.
|
|
118
|
+
* @internal `withCanopyPreview` is its only caller.
|
|
119
|
+
*/
|
|
120
|
+
export declare const usePreviewAssetBaseGate: (assetBase: unknown) => boolean;
|
|
79
121
|
/**
|
|
80
122
|
* Hook for preview pages to listen for highlight mode and toggle an outline on clickable elements.
|
|
81
123
|
*/
|
|
@@ -88,21 +130,3 @@ export declare const usePreviewHighlight: (opts?: {
|
|
|
88
130
|
export declare const usePreviewFocusEmitter: (entryPath: string, opts?: {
|
|
89
131
|
editorOrigin?: string;
|
|
90
132
|
}) => void;
|
|
91
|
-
/**
|
|
92
|
-
* Lightweight iframe wrapper to keep the preview in sync with form state.
|
|
93
|
-
* It posts the latest draft data to the iframe after load and when data changes.
|
|
94
|
-
*/
|
|
95
|
-
export declare const PreviewFrame: ({ src, path, data, isLoading, className, style, highlightEnabled, onPreviewError, }: {
|
|
96
|
-
src: string;
|
|
97
|
-
path: string;
|
|
98
|
-
data?: unknown;
|
|
99
|
-
isLoading?: unknown;
|
|
100
|
-
className?: string;
|
|
101
|
-
style?: CSSProperties;
|
|
102
|
-
highlightEnabled?: boolean;
|
|
103
|
-
/** Called when the preview reports a draft compile/render error; null clears it. */
|
|
104
|
-
onPreviewError?: (error: {
|
|
105
|
-
message: string;
|
|
106
|
-
fieldPath?: string;
|
|
107
|
-
} | null) => void;
|
|
108
|
-
}) => import("react/jsx-runtime").JSX.Element;
|