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,40 @@
|
|
|
1
|
+
/** The directory name in a logical path, without its embedded id ("posts.abc123" -> "posts"). */
|
|
2
|
+
export function slugOf(logicalPath) {
|
|
3
|
+
const pathParts = logicalPath.split('/');
|
|
4
|
+
const lastPart = pathParts[pathParts.length - 1];
|
|
5
|
+
return lastPart?.split('.')[0] || '';
|
|
6
|
+
}
|
|
7
|
+
function findByPath(collections, path) {
|
|
8
|
+
for (const collection of collections) {
|
|
9
|
+
if (collection.path === path)
|
|
10
|
+
return collection;
|
|
11
|
+
const nested = collection.children && findByPath(collection.children, path);
|
|
12
|
+
if (nested)
|
|
13
|
+
return nested;
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
/** The collection named `name` directly under `parentPath`, or at the top level without one. */
|
|
18
|
+
function findCollectionByName(collections, parentPath, name) {
|
|
19
|
+
const siblings = parentPath ? (findByPath(collections, parentPath)?.children ?? []) : collections;
|
|
20
|
+
return siblings.find((c) => c.type === 'collection' && c.name === name);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Whether the tree shows a details save: its collection holds every value it sent, or, for a save
|
|
24
|
+
* that changed the slug, is gone from its old path.
|
|
25
|
+
*/
|
|
26
|
+
function detailsLanded(collections, draft) {
|
|
27
|
+
const { updates } = draft;
|
|
28
|
+
const collection = findByPath(collections, draft.collectionPath);
|
|
29
|
+
if (!collection)
|
|
30
|
+
return updates.slug !== undefined && collections.length > 0;
|
|
31
|
+
return ((updates.name === undefined || collection.name === updates.name) &&
|
|
32
|
+
(updates.label === undefined || (collection.label ?? '') === updates.label) &&
|
|
33
|
+
(updates.slug === undefined || slugOf(collection.path) === updates.slug));
|
|
34
|
+
}
|
|
35
|
+
/** Whether `collections`, read from the draft's branch, show the draft's write. */
|
|
36
|
+
export function collectionDraftLanded(collections, draft) {
|
|
37
|
+
return draft.kind === 'create'
|
|
38
|
+
? !!findCollectionByName(collections, draft.input.parentPath, draft.input.name)
|
|
39
|
+
: detailsLanded(collections, draft);
|
|
40
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { CollectionEditor, type CollectionEditorProps, type ExistingCollection, type ExistingEntryType, } from './CollectionEditor.js';
|
|
1
|
+
export { CollectionEditor, type CollectionEditorProps, type ExistingCollection, type ExistingEntryType, type RestoredCollectionInput, } from './CollectionEditor.js';
|
|
2
2
|
export { EntryTypeEditor, type EntryTypeEditorProps, type EntryTypeFormData, } from './EntryTypeEditor.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** A schema name read as words in sentence case: `blogPost` is "Blog post", `HTMLPage` "HTML page". */
|
|
2
|
+
export declare const humanizeName: (name: string) => string;
|
|
3
|
+
/**
|
|
4
|
+
* What the editor calls a field, group or block template: its label, else its name humanised, so
|
|
5
|
+
* an unlabelled schema name never shows raw (docs/ux-guidelines.md, Copy).
|
|
6
|
+
*/
|
|
7
|
+
export declare const schemaLabel: (item: {
|
|
8
|
+
label?: string;
|
|
9
|
+
name: string;
|
|
10
|
+
}) => string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** A schema name read as words in sentence case: `blogPost` is "Blog post", `HTMLPage` "HTML page". */
|
|
2
|
+
export const humanizeName = (name) => {
|
|
3
|
+
const words = name
|
|
4
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
5
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
|
|
6
|
+
.split(/[-_\s]+/)
|
|
7
|
+
.filter(Boolean)
|
|
8
|
+
// An acronym ("HTML") keeps its capitals.
|
|
9
|
+
.map((word) => (/^[A-Z0-9]{2,}$/.test(word) ? word : word.toLowerCase()))
|
|
10
|
+
.join(' ');
|
|
11
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* What the editor calls a field, group or block template: its label, else its name humanised, so
|
|
15
|
+
* an unlabelled schema name never shows raw (docs/ux-guidelines.md, Copy).
|
|
16
|
+
*/
|
|
17
|
+
export const schemaLabel = (item) => item.label ?? humanizeName(item.name);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const SETTINGS_CHANGED_CODE = 'SETTINGS_CHANGED';
|
|
2
|
+
/** A groups or permissions save refused as the file changed since it loaded: shown yellow. */
|
|
3
|
+
export class SettingsChangedError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = 'SettingsChangedError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React, { type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
2
|
+
export interface ActivityRailProps {
|
|
3
|
+
/** Rail buttons from the top: the monogram, then the activities. */
|
|
4
|
+
top: ReactNode;
|
|
5
|
+
/** Rail buttons pinned to the bottom, such as Settings. */
|
|
6
|
+
bottom?: ReactNode;
|
|
7
|
+
/** The account control, a tab stop of its own: an adopter's component manages its own focus. */
|
|
8
|
+
account?: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The left activity rail: one vertical toolbar whose `RailButton`s share a single Tab stop and move
|
|
12
|
+
* with the arrow keys, Home and End, then the account control. Layout only; the buttons come in as
|
|
13
|
+
* children.
|
|
14
|
+
*/
|
|
15
|
+
export declare function ActivityRail({ top, bottom, account }: ActivityRailProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export interface RailButtonProps extends Omit<ComponentPropsWithoutRef<'button'>, 'children'> {
|
|
17
|
+
/** Unique within the rail; the roving Tab stop is tracked by it. Not `id`: Mantine's
|
|
18
|
+
* `Menu.Target` sets that on its child. */
|
|
19
|
+
railId: string;
|
|
20
|
+
/** The accessible name and the tooltip's text. */
|
|
21
|
+
label: string;
|
|
22
|
+
icon: ReactNode;
|
|
23
|
+
/** Highlights the button while its panel or drawer is open. */
|
|
24
|
+
active?: boolean;
|
|
25
|
+
/** Tinted at rest, as the site monogram is. */
|
|
26
|
+
tinted?: boolean;
|
|
27
|
+
/** Shown after the label in the tooltip, as the platform writes it ("⌘P"). */
|
|
28
|
+
shortcut?: string;
|
|
29
|
+
/** A count badge, hidden at zero; the label should say it too. */
|
|
30
|
+
count?: number;
|
|
31
|
+
countTestId?: string;
|
|
32
|
+
tooltipDisabled?: boolean;
|
|
33
|
+
/** Disables the button, which stays focusable, and its tooltip gives this reason instead. */
|
|
34
|
+
disabledReason?: string;
|
|
35
|
+
}
|
|
36
|
+
/** A rail button: icon only, named by `label`, which its tooltip repeats on hover and focus. */
|
|
37
|
+
export declare const RailButton: React.ForwardRefExoticComponent<RailButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef, useId, useLayoutEffect, useRef, useState, } from 'react';
|
|
4
|
+
import { ActionIcon, Indicator, Stack, Tooltip, VisuallyHidden } from '@mantine/core';
|
|
5
|
+
import { useElementSize, useMergedRef } from '@mantine/hooks';
|
|
6
|
+
const ACTIVITY_RAIL_WIDTH = 44;
|
|
7
|
+
const RAIL_ITEM_ATTR = 'data-rail-item';
|
|
8
|
+
/**
|
|
9
|
+
* The left activity rail: one vertical toolbar whose `RailButton`s share a single Tab stop and move
|
|
10
|
+
* with the arrow keys, Home and End, then the account control. Layout only; the buttons come in as
|
|
11
|
+
* children.
|
|
12
|
+
*/
|
|
13
|
+
export function ActivityRail({ top, bottom, account }) {
|
|
14
|
+
const toolbarRef = useRef(null);
|
|
15
|
+
const currentRef = useRef(null);
|
|
16
|
+
// Re-renders on a resize, so the check below runs again.
|
|
17
|
+
const { ref: sizeRef } = useElementSize();
|
|
18
|
+
const mergedToolbarRef = useMergedRef(toolbarRef, sizeRef);
|
|
19
|
+
const [overflowing, setOverflowing] = useState(false);
|
|
20
|
+
const items = () => Array.from(toolbarRef.current?.querySelectorAll(`[${RAIL_ITEM_ATTR}]`) ?? []);
|
|
21
|
+
// Set on the DOM after every render, so a button that mounts or goes takes or hands on the Tab
|
|
22
|
+
// stop in the same commit; it falls back to the first button.
|
|
23
|
+
const placeTabStop = () => {
|
|
24
|
+
const all = items();
|
|
25
|
+
const stop = all.find((item) => item.getAttribute(RAIL_ITEM_ATTR) === currentRef.current);
|
|
26
|
+
for (const item of all)
|
|
27
|
+
item.tabIndex = item === (stop ?? all[0]) ? 0 : -1;
|
|
28
|
+
};
|
|
29
|
+
useLayoutEffect(placeTabStop);
|
|
30
|
+
// Only a rail too short for its buttons scrolls: a scroll container clips sideways too, and at
|
|
31
|
+
// full height nothing, such as a two-digit count badge, should be cut off.
|
|
32
|
+
useLayoutEffect(() => {
|
|
33
|
+
const toolbar = toolbarRef.current;
|
|
34
|
+
if (toolbar)
|
|
35
|
+
setOverflowing(toolbar.scrollHeight > toolbar.clientHeight);
|
|
36
|
+
});
|
|
37
|
+
const onKeyDown = (event) => {
|
|
38
|
+
const all = items();
|
|
39
|
+
// A menu portalled from a button still bubbles here through React; only the buttons rove.
|
|
40
|
+
const index = all.indexOf(event.target);
|
|
41
|
+
if (index < 0)
|
|
42
|
+
return;
|
|
43
|
+
const next = event.key === 'ArrowDown'
|
|
44
|
+
? all[(index + 1) % all.length]
|
|
45
|
+
: event.key === 'ArrowUp'
|
|
46
|
+
? all[(index - 1 + all.length) % all.length]
|
|
47
|
+
: event.key === 'Home'
|
|
48
|
+
? all[0]
|
|
49
|
+
: event.key === 'End'
|
|
50
|
+
? all[all.length - 1]
|
|
51
|
+
: undefined;
|
|
52
|
+
if (!next)
|
|
53
|
+
return;
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
next.focus();
|
|
56
|
+
};
|
|
57
|
+
return (_jsxs(Stack, { gap: 0, h: "100%", w: ACTIVITY_RAIL_WIDTH, align: "center", style: {
|
|
58
|
+
borderInlineEnd: '1px solid var(--canopy-input-border)',
|
|
59
|
+
background: 'var(--mantine-color-body)',
|
|
60
|
+
}, children: [_jsxs(Stack, { ref: mergedToolbarRef, role: "toolbar", "aria-orientation": "vertical", "aria-label": "Activities", gap: 6, py: 8, align: "center", style: {
|
|
61
|
+
flex: 1,
|
|
62
|
+
minHeight: 0,
|
|
63
|
+
...(overflowing && {
|
|
64
|
+
alignSelf: 'stretch',
|
|
65
|
+
overflowY: 'auto',
|
|
66
|
+
overflowX: 'hidden',
|
|
67
|
+
scrollbarWidth: 'none',
|
|
68
|
+
}),
|
|
69
|
+
}, onKeyDown: onKeyDown, onFocus: (event) => {
|
|
70
|
+
const id = event.target.getAttribute(RAIL_ITEM_ATTR);
|
|
71
|
+
if (id) {
|
|
72
|
+
currentRef.current = id;
|
|
73
|
+
placeTabStop();
|
|
74
|
+
}
|
|
75
|
+
}, children: [top, _jsx("div", { style: { flex: 1 } }), bottom] }), account && _jsx(Stack, { pb: 8, children: account })] }));
|
|
76
|
+
}
|
|
77
|
+
/** A rail button: icon only, named by `label`, which its tooltip repeats on hover and focus. */
|
|
78
|
+
export const RailButton = forwardRef(function RailButton({ railId, label, icon, active = false, tinted = false, shortcut, count = 0, countTestId, tooltipDisabled, disabledReason, onClick, ...rest }, ref) {
|
|
79
|
+
const reasonId = useId();
|
|
80
|
+
const button = (_jsxs(ActionIcon, { ref: ref,
|
|
81
|
+
// Filled, not tinted, when active: a tint alone would show the state by colour, under 3:1.
|
|
82
|
+
variant: active ? 'filled' : tinted ? 'light' : 'subtle', color: active || tinted ? undefined : 'gray', size: "lg", radius: "md", "aria-label": label, ...rest, "aria-disabled": disabledReason ? true : rest['aria-disabled'], "aria-describedby": disabledReason ? reasonId : rest['aria-describedby'], "data-disabled": disabledReason ? true : undefined, onClick: disabledReason ? undefined : onClick, [RAIL_ITEM_ATTR]: railId, children: [icon, disabledReason && (_jsx(VisuallyHidden, { component: "span", id: reasonId, children: disabledReason }))] }));
|
|
83
|
+
return (_jsx(Tooltip, { label: disabledReason ?? (shortcut ? `${label} (${shortcut})` : label), position: "right", disabled: tooltipDisabled, children: _jsx(Indicator, { label: _jsx("span", { "aria-hidden": true, "data-testid": countTestId, children: count }), size: 16, offset: 4, withBorder: true, disabled: count === 0, styles: {
|
|
84
|
+
indicator: {
|
|
85
|
+
pointerEvents: 'none',
|
|
86
|
+
// Mantine paints the pill again in a ::before, from this variable.
|
|
87
|
+
'--indicator-color': 'var(--canopy-count-bg)',
|
|
88
|
+
'--indicator-text-color': 'var(--mantine-color-text)',
|
|
89
|
+
},
|
|
90
|
+
}, children: button }) }));
|
|
91
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface EditorShellProps {
|
|
3
|
+
topBar?: ReactNode;
|
|
4
|
+
banner?: ReactNode;
|
|
5
|
+
rail?: ReactNode;
|
|
6
|
+
panel?: ReactNode;
|
|
7
|
+
main: ReactNode;
|
|
8
|
+
/** Drawers, modals and palettes, each portalling itself into `<body>`. */
|
|
9
|
+
overlays?: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The editor's window: a full-viewport grid of regions (top bar, banner, rail, panel, main,
|
|
13
|
+
* overlays), portalled into `<body>` above the host page, whose chrome it hides and makes inert
|
|
14
|
+
* and whose scrolling it stops. Layout only: features plug into the regions. The rail sits at the
|
|
15
|
+
* inline start, opposite the toasts (`theme.tsx`), and comes first after the banner in focus order.
|
|
16
|
+
*/
|
|
17
|
+
export declare function EditorShell({ topBar, banner, rail, panel, main, overlays }: EditorShellProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useRef } from 'react';
|
|
4
|
+
import { Portal, getDefaultZIndex } from '@mantine/core';
|
|
5
|
+
import { RemoveScroll } from 'react-remove-scroll';
|
|
6
|
+
import { useHideHostChrome } from './host-chrome.js';
|
|
7
|
+
const gridStyle = {
|
|
8
|
+
position: 'fixed',
|
|
9
|
+
inset: 0,
|
|
10
|
+
// Widgets a host adds outside its React tree bring their own z-index, so the shell sits just
|
|
11
|
+
// under Mantine's drawers and modals, leaving the layer between for MDXEditor's popups.
|
|
12
|
+
zIndex: getDefaultZIndex('modal') - 2,
|
|
13
|
+
display: 'grid',
|
|
14
|
+
gridTemplateAreas: '"topbar topbar topbar" "banner banner banner" "rail panel main"',
|
|
15
|
+
gridTemplateRows: 'auto auto minmax(0, 1fr)',
|
|
16
|
+
gridTemplateColumns: 'auto auto minmax(0, 1fr)',
|
|
17
|
+
background: 'var(--canopy-surface-subtle)',
|
|
18
|
+
color: 'var(--mantine-color-text)',
|
|
19
|
+
};
|
|
20
|
+
const region = (area) => ({ gridArea: area, minWidth: 0, minHeight: 0 });
|
|
21
|
+
/**
|
|
22
|
+
* The editor's window: a full-viewport grid of regions (top bar, banner, rail, panel, main,
|
|
23
|
+
* overlays), portalled into `<body>` above the host page, whose chrome it hides and makes inert
|
|
24
|
+
* and whose scrolling it stops. Layout only: features plug into the regions. The rail sits at the
|
|
25
|
+
* inline start, opposite the toasts (`theme.tsx`), and comes first after the banner in focus order.
|
|
26
|
+
*/
|
|
27
|
+
export function EditorShell({ topBar, banner, rail, panel, main, overlays }) {
|
|
28
|
+
const anchorRef = useRef(null);
|
|
29
|
+
useHideHostChrome(anchorRef);
|
|
30
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { ref: anchorRef, hidden: true, "data-canopy-shell-anchor": true }), _jsx(Portal, { children: _jsxs(RemoveScroll, { noIsolation: true, removeScrollBar: true, style: gridStyle, "data-canopy-shell": "", children: [topBar && (_jsx("header", { style: region('topbar'), "data-canopy-region": "topbar", children: topBar })), _jsx("div", { style: region('banner'), "data-canopy-region": "banner", children: banner }), rail && (_jsx("aside", { style: region('rail'), "data-canopy-region": "rail", "aria-label": "Editor tools", children: rail })), _jsx("div", { style: region('panel'), "data-canopy-region": "panel", children: panel }), _jsx("main", { style: region('main'), "data-canopy-region": "main", children: main }), _jsx("div", { style: { position: 'absolute' }, "data-canopy-region": "overlays", children: overlays })] }) })] }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/** Preview beside the form, or above it. */
|
|
3
|
+
export type PaneArrangement = 'side' | 'stacked';
|
|
4
|
+
export declare const SPLIT_PERCENT_MIN = 15;
|
|
5
|
+
export declare const SPLIT_PERCENT_MAX = 85;
|
|
6
|
+
export declare const DEFAULT_SIDE_SPLIT_PERCENT = 52;
|
|
7
|
+
export declare const DEFAULT_STACKED_SPLIT_PERCENT = 58;
|
|
8
|
+
export interface PaneLayoutProps {
|
|
9
|
+
arrangement: PaneArrangement;
|
|
10
|
+
/** The preview's share (percent) beside the form; follows this prop when it changes. */
|
|
11
|
+
sideSplitPercent?: number;
|
|
12
|
+
/** The preview's share (percent) above the form; follows this prop when it changes. */
|
|
13
|
+
stackedSplitPercent?: number;
|
|
14
|
+
/** Called when a drag or a keyboard resize changes the split, with the preview's percent. */
|
|
15
|
+
onSplitPercentChange?: (arrangement: PaneArrangement, percent: number) => void;
|
|
16
|
+
preview?: React.ReactNode;
|
|
17
|
+
form?: React.ReactNode;
|
|
18
|
+
/** Laid over the form pane, outside its scrolling: the side panel in overlay mode. */
|
|
19
|
+
formOverlay?: React.ReactNode;
|
|
20
|
+
/** Takes the form out of focus and the pointer's reach while an open `formOverlay` hides it. */
|
|
21
|
+
formInert?: boolean;
|
|
22
|
+
/** Lays the preview over the whole layout, form and separator inert, without remounting it. */
|
|
23
|
+
previewMaximized?: boolean;
|
|
24
|
+
/** Called as a held separator first moves the split, and as it is let go after that. */
|
|
25
|
+
onSeparatorDrag?: (dragging: boolean) => void;
|
|
26
|
+
}
|
|
27
|
+
/** The resizable preview/form split that fills the shell's main region. */
|
|
28
|
+
export declare function PaneLayout({ arrangement, sideSplitPercent, stackedSplitPercent, onSplitPercentChange, preview, form, formOverlay, formInert, previewMaximized, onSeparatorDrag, }: PaneLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useLayoutEffect, useRef } from 'react';
|
|
4
|
+
import { Box } from '@mantine/core';
|
|
5
|
+
import { Group, Panel, Separator, useGroupRef } from 'react-resizable-panels';
|
|
6
|
+
export const SPLIT_PERCENT_MIN = 15;
|
|
7
|
+
export const SPLIT_PERCENT_MAX = 85;
|
|
8
|
+
export const DEFAULT_SIDE_SPLIT_PERCENT = 52;
|
|
9
|
+
export const DEFAULT_STACKED_SPLIT_PERCENT = 58;
|
|
10
|
+
const PREVIEW = 'canopy-preview';
|
|
11
|
+
const FORM = 'canopy-form';
|
|
12
|
+
// The preview's limits are the stored split's, so a drag never ends somewhere storage would move;
|
|
13
|
+
// the form also keeps a usable minimum in a narrow window.
|
|
14
|
+
const PREVIEW_LIMITS = { minSize: `${SPLIT_PERCENT_MIN}%`, maxSize: `${SPLIT_PERCENT_MAX}%` };
|
|
15
|
+
const FORM_MIN_PX = 160;
|
|
16
|
+
const SEPARATOR_STYLES = `
|
|
17
|
+
.canopy-pane-separator {
|
|
18
|
+
min-width: 8px;
|
|
19
|
+
min-height: 8px;
|
|
20
|
+
background: var(--canopy-surface-muted);
|
|
21
|
+
box-shadow: inset 0 0 0 1px var(--canopy-input-border);
|
|
22
|
+
}
|
|
23
|
+
.canopy-pane-separator:is([data-separator='hover'], [data-separator='active']) {
|
|
24
|
+
background: var(--canopy-focus-ring);
|
|
25
|
+
}
|
|
26
|
+
.canopy-pane-separator:focus-visible {
|
|
27
|
+
outline: 2px solid var(--canopy-focus-ring);
|
|
28
|
+
outline-offset: -2px;
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
const splitLayout = (percent) => ({ [PREVIEW]: percent, [FORM]: 100 - percent });
|
|
32
|
+
/** The resizable preview/form split that fills the shell's main region. */
|
|
33
|
+
export function PaneLayout({ arrangement, sideSplitPercent = DEFAULT_SIDE_SPLIT_PERCENT, stackedSplitPercent = DEFAULT_STACKED_SPLIT_PERCENT, onSplitPercentChange, preview, form, formOverlay, formInert = false, previewMaximized = false, onSeparatorDrag, }) {
|
|
34
|
+
const groupRef = useGroupRef();
|
|
35
|
+
const separatorRef = useRef(null);
|
|
36
|
+
const previewRef = useRef(null);
|
|
37
|
+
const formRef = useRef(null);
|
|
38
|
+
const formPanelRef = useRef(null);
|
|
39
|
+
const percent = arrangement === 'side' ? sideSplitPercent : stackedSplitPercent;
|
|
40
|
+
// Stored sizes load after mount, and the group must never remount: that would reload the preview.
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const group = groupRef.current;
|
|
43
|
+
if (!group)
|
|
44
|
+
return;
|
|
45
|
+
if (Math.abs((group.getLayout()[PREVIEW] ?? percent) - percent) > 0.1) {
|
|
46
|
+
group.setLayout(splitLayout(percent));
|
|
47
|
+
}
|
|
48
|
+
}, [groupRef, percent, arrangement]);
|
|
49
|
+
// React 18 has no `inert` prop, so it is set on the DOM. A layout effect, so it changes before
|
|
50
|
+
// any passive effect of the same commit tries to focus into the form or out of the overlay.
|
|
51
|
+
useLayoutEffect(() => {
|
|
52
|
+
formRef.current?.toggleAttribute('inert', formInert);
|
|
53
|
+
formPanelRef.current?.toggleAttribute('inert', previewMaximized);
|
|
54
|
+
separatorRef.current?.toggleAttribute('inert', previewMaximized);
|
|
55
|
+
}, [formInert, previewMaximized]);
|
|
56
|
+
const onSeparatorDragRef = useRef(onSeparatorDrag);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
onSeparatorDragRef.current = onSeparatorDrag;
|
|
59
|
+
}, [onSeparatorDrag]);
|
|
60
|
+
// A press alone is no drag: one that only focuses the separator must not report one.
|
|
61
|
+
const hold = useRef(null);
|
|
62
|
+
// An iframe takes the pointer moves a drag needs, so the panes ignore the pointer while the
|
|
63
|
+
// separator is held. Set on the DOM as the attribute flips, before the first move arrives.
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
const separator = separatorRef.current;
|
|
66
|
+
if (!separator)
|
|
67
|
+
return;
|
|
68
|
+
const observer = new MutationObserver(() => {
|
|
69
|
+
const held = separator.getAttribute('data-separator') === 'active';
|
|
70
|
+
for (const pane of [previewRef.current, formRef.current]) {
|
|
71
|
+
if (pane)
|
|
72
|
+
pane.style.pointerEvents = held ? 'none' : '';
|
|
73
|
+
}
|
|
74
|
+
if (held && !hold.current) {
|
|
75
|
+
hold.current = { start: groupRef.current?.getLayout()[PREVIEW] ?? 0, moved: false };
|
|
76
|
+
}
|
|
77
|
+
else if (!held && hold.current) {
|
|
78
|
+
if (hold.current.moved)
|
|
79
|
+
onSeparatorDragRef.current?.(false);
|
|
80
|
+
hold.current = null;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
observer.observe(separator, { attributes: true, attributeFilter: ['data-separator'] });
|
|
84
|
+
return () => observer.disconnect();
|
|
85
|
+
}, [groupRef]);
|
|
86
|
+
const side = arrangement === 'side';
|
|
87
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { children: SEPARATOR_STYLES }), _jsxs(Group, { groupRef: groupRef, orientation: side ? 'horizontal' : 'vertical', defaultLayout: splitLayout(percent),
|
|
88
|
+
// The hidden separator would otherwise still take drags over a maximized preview: the
|
|
89
|
+
// library hit-tests separators by their rectangles, which `inert` leaves in place.
|
|
90
|
+
disabled: previewMaximized, onLayoutChange: (layout) => {
|
|
91
|
+
const next = layout[PREVIEW];
|
|
92
|
+
const held = hold.current;
|
|
93
|
+
if (held && !held.moved && next !== undefined && Math.abs(next - held.start) > 0.01) {
|
|
94
|
+
held.moved = true;
|
|
95
|
+
onSeparatorDragRef.current?.(true);
|
|
96
|
+
}
|
|
97
|
+
}, onLayoutChanged: (layout, meta) => {
|
|
98
|
+
const next = layout[PREVIEW];
|
|
99
|
+
if (meta.isUserInteraction && next !== undefined && Math.abs(next - percent) > 0.01)
|
|
100
|
+
onSplitPercentChange?.(arrangement, next);
|
|
101
|
+
},
|
|
102
|
+
// The maximized preview's containing block: the panels themselves are unpositioned.
|
|
103
|
+
style: { height: '100%', width: '100%', position: 'relative' }, children: [_jsx(Panel, { id: PREVIEW, ...PREVIEW_LIMITS, children: _jsx(Box, { ref: previewRef, "data-testid": "preview-pane", "data-maximized": previewMaximized || undefined, h: "100%", w: "100%", style: previewMaximized
|
|
104
|
+
? { overflow: 'auto', position: 'absolute', inset: 0, zIndex: 1 }
|
|
105
|
+
: { overflow: 'auto' }, children: preview }) }), _jsx(Separator, { elementRef: separatorRef, "aria-label": "Resize preview and form", className: "canopy-pane-separator" }), _jsx(Panel, { id: FORM, minSize: FORM_MIN_PX, children: _jsxs(Box, { ref: formPanelRef, pos: "relative", h: "100%", w: "100%",
|
|
106
|
+
// `formOverlay` stacks within this pane, so a maximized preview always covers it.
|
|
107
|
+
style: { isolation: 'isolate' }, children: [_jsx(Box, { ref: formRef, "data-testid": "form-pane", h: "100%", w: "100%", style: {
|
|
108
|
+
// FormHeader reads this to span the padding.
|
|
109
|
+
'--canopy-form-pane-padding': '20px',
|
|
110
|
+
padding: 'var(--canopy-form-pane-padding)',
|
|
111
|
+
overflow: 'auto',
|
|
112
|
+
backgroundColor: 'var(--canopy-surface-muted)',
|
|
113
|
+
// Keeps the form's sticky header under `formOverlay`.
|
|
114
|
+
isolation: 'isolate',
|
|
115
|
+
}, children: form }), formOverlay] }) })] })] }));
|
|
116
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/** Docked beside the main region, or laid over the form pane only. */
|
|
3
|
+
type SidePanelMode = 'docked' | 'overlay';
|
|
4
|
+
export interface SidePanelHostProps {
|
|
5
|
+
title: string;
|
|
6
|
+
mode: SidePanelMode;
|
|
7
|
+
opened: boolean;
|
|
8
|
+
/** The stored width; a drag shows its own until it ends. */
|
|
9
|
+
width: number;
|
|
10
|
+
minWidth: number;
|
|
11
|
+
maxWidth: number;
|
|
12
|
+
/** Called when a drag ends and on each resize key. */
|
|
13
|
+
onWidthChange: (width: number) => void;
|
|
14
|
+
onClose: () => void;
|
|
15
|
+
/**
|
|
16
|
+
* Bumped by the caller each time the user asks for the panel, open or not: each new value moves
|
|
17
|
+
* focus in. Opening alone never does, as a panel also opens when stored state loads.
|
|
18
|
+
*/
|
|
19
|
+
focusRequest?: number;
|
|
20
|
+
/** Where focus goes when the panel closes with focus inside it. */
|
|
21
|
+
returnFocusTo?: () => HTMLElement | null;
|
|
22
|
+
/** Controls beside the title, before the close button. */
|
|
23
|
+
actions?: ReactNode;
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
id?: string;
|
|
26
|
+
'data-testid'?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A titled side panel, resizable from its inline-end edge. Docked, it is a column of the shell;
|
|
30
|
+
* as an overlay it covers the form pane only, so the preview stays in view, and Escape closes it.
|
|
31
|
+
* A `focusRequest` moves focus to its `data-autofocus` element, else to the panel; closing with
|
|
32
|
+
* focus inside returns focus to `returnFocusTo`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function SidePanelHost({ title, mode, opened, width, minWidth, maxWidth, onWidthChange, onClose, focusRequest, returnFocusTo, actions, children, id, 'data-testid': testId, }: SidePanelHostProps): import("react/jsx-runtime").JSX.Element | null;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useId, useRef, useState, } from 'react';
|
|
4
|
+
import { CloseButton, Group, Text, Tooltip } from '@mantine/core';
|
|
5
|
+
import { useWindowEvent } from '@mantine/hooks';
|
|
6
|
+
import { focusIfIdle } from '../utils/focus-return.js';
|
|
7
|
+
const KEY_STEP_PX = 16;
|
|
8
|
+
const RESIZING_ATTR = 'data-canopy-panel-resizing';
|
|
9
|
+
const PANEL_STYLES = `
|
|
10
|
+
body[${RESIZING_ATTR}] { cursor: col-resize; user-select: none; }
|
|
11
|
+
body[${RESIZING_ATTR}] iframe { pointer-events: none; }
|
|
12
|
+
.canopy-side-panel-separator {
|
|
13
|
+
position: absolute;
|
|
14
|
+
inset-block: 0;
|
|
15
|
+
inset-inline-end: -3px;
|
|
16
|
+
width: 6px;
|
|
17
|
+
cursor: col-resize;
|
|
18
|
+
touch-action: none;
|
|
19
|
+
z-index: 1;
|
|
20
|
+
}
|
|
21
|
+
.canopy-side-panel-separator:hover,
|
|
22
|
+
.canopy-side-panel-separator[data-dragging] {
|
|
23
|
+
background: var(--canopy-focus-ring);
|
|
24
|
+
}
|
|
25
|
+
.canopy-side-panel-separator:focus-visible {
|
|
26
|
+
outline: 2px solid var(--canopy-focus-ring);
|
|
27
|
+
outline-offset: -2px;
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
31
|
+
/**
|
|
32
|
+
* A titled side panel, resizable from its inline-end edge. Docked, it is a column of the shell;
|
|
33
|
+
* as an overlay it covers the form pane only, so the preview stays in view, and Escape closes it.
|
|
34
|
+
* A `focusRequest` moves focus to its `data-autofocus` element, else to the panel; closing with
|
|
35
|
+
* focus inside returns focus to `returnFocusTo`.
|
|
36
|
+
*/
|
|
37
|
+
export function SidePanelHost({ title, mode, opened, width, minWidth, maxWidth, onWidthChange, onClose, focusRequest = 0, returnFocusTo, actions, children, id, 'data-testid': testId, }) {
|
|
38
|
+
const fallbackId = useId();
|
|
39
|
+
const panelId = id ?? fallbackId;
|
|
40
|
+
const titleId = `${panelId}-title`;
|
|
41
|
+
const panelRef = useRef(null);
|
|
42
|
+
const focusInside = useRef(false);
|
|
43
|
+
const wasOpened = useRef(opened);
|
|
44
|
+
const servedFocusRequest = useRef(focusRequest);
|
|
45
|
+
const drag = useRef(null);
|
|
46
|
+
const [dragWidth, setDragWidth] = useState(null);
|
|
47
|
+
if (!opened && dragWidth !== null)
|
|
48
|
+
setDragWidth(null);
|
|
49
|
+
const shownWidth = clamp(dragWidth ?? width, minWidth, maxWidth);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
const panel = panelRef.current;
|
|
52
|
+
const asked = opened && focusRequest !== servedFocusRequest.current;
|
|
53
|
+
servedFocusRequest.current = focusRequest;
|
|
54
|
+
if (!asked || !panel)
|
|
55
|
+
return;
|
|
56
|
+
const autofocus = () => panel.querySelector('[data-autofocus]');
|
|
57
|
+
const field = autofocus();
|
|
58
|
+
if (field) {
|
|
59
|
+
field.focus({ preventScroll: true });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// The field may render with content still loading: it takes focus when it arrives, unless
|
|
63
|
+
// focus has moved off the panel by then.
|
|
64
|
+
panel.focus({ preventScroll: true });
|
|
65
|
+
const observer = new MutationObserver(() => {
|
|
66
|
+
if (document.activeElement !== panel)
|
|
67
|
+
return observer.disconnect();
|
|
68
|
+
const arrived = autofocus();
|
|
69
|
+
if (!arrived)
|
|
70
|
+
return;
|
|
71
|
+
observer.disconnect();
|
|
72
|
+
arrived.focus({ preventScroll: true });
|
|
73
|
+
});
|
|
74
|
+
observer.observe(panel, { childList: true, subtree: true });
|
|
75
|
+
return () => observer.disconnect();
|
|
76
|
+
}, [opened, focusRequest]);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (!opened && wasOpened.current) {
|
|
79
|
+
// Its separator is gone, and with it the pointer capture that would have ended a drag.
|
|
80
|
+
drag.current = null;
|
|
81
|
+
document.body.removeAttribute(RESIZING_ATTR);
|
|
82
|
+
if (focusInside.current) {
|
|
83
|
+
focusInside.current = false;
|
|
84
|
+
focusIfIdle(returnFocusTo?.() ?? null);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
wasOpened.current = opened;
|
|
88
|
+
}, [opened, returnFocusTo]);
|
|
89
|
+
// On the window, not the panel: a dialog portalled from the panel would bubble its Escape here
|
|
90
|
+
// through React, so only a key from inside the panel's own DOM closes it.
|
|
91
|
+
useWindowEvent('keydown', (event) => {
|
|
92
|
+
if (opened &&
|
|
93
|
+
mode === 'overlay' &&
|
|
94
|
+
event.key === 'Escape' &&
|
|
95
|
+
!event.defaultPrevented &&
|
|
96
|
+
event.target instanceof Node &&
|
|
97
|
+
panelRef.current?.contains(event.target)) {
|
|
98
|
+
event.preventDefault();
|
|
99
|
+
onClose();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
// A drag cut short by unmounting must not leave the page's iframes deaf to the pointer.
|
|
103
|
+
useEffect(() => () => document.body.removeAttribute(RESIZING_ATTR), []);
|
|
104
|
+
if (!opened)
|
|
105
|
+
return null;
|
|
106
|
+
const endDrag = () => {
|
|
107
|
+
const state = drag.current;
|
|
108
|
+
if (!state)
|
|
109
|
+
return;
|
|
110
|
+
drag.current = null;
|
|
111
|
+
document.body.removeAttribute(RESIZING_ATTR);
|
|
112
|
+
setDragWidth(null);
|
|
113
|
+
onWidthChange(state.width);
|
|
114
|
+
};
|
|
115
|
+
const separatorHandlers = {
|
|
116
|
+
onPointerDown: (event) => {
|
|
117
|
+
if (event.button !== 0)
|
|
118
|
+
return;
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
121
|
+
const rtl = getComputedStyle(event.currentTarget).direction === 'rtl';
|
|
122
|
+
drag.current = {
|
|
123
|
+
startX: event.clientX,
|
|
124
|
+
startWidth: shownWidth,
|
|
125
|
+
width: shownWidth,
|
|
126
|
+
sign: rtl ? -1 : 1,
|
|
127
|
+
};
|
|
128
|
+
document.body.setAttribute(RESIZING_ATTR, '');
|
|
129
|
+
setDragWidth(shownWidth);
|
|
130
|
+
},
|
|
131
|
+
onPointerMove: (event) => {
|
|
132
|
+
const state = drag.current;
|
|
133
|
+
if (!state)
|
|
134
|
+
return;
|
|
135
|
+
state.width = clamp(state.startWidth + state.sign * (event.clientX - state.startX), minWidth, maxWidth);
|
|
136
|
+
setDragWidth(state.width);
|
|
137
|
+
},
|
|
138
|
+
onPointerUp: endDrag,
|
|
139
|
+
onPointerCancel: endDrag,
|
|
140
|
+
onLostPointerCapture: endDrag,
|
|
141
|
+
onKeyDown: (event) => {
|
|
142
|
+
const rtl = getComputedStyle(event.currentTarget).direction === 'rtl';
|
|
143
|
+
const grow = rtl ? 'ArrowLeft' : 'ArrowRight';
|
|
144
|
+
const shrink = rtl ? 'ArrowRight' : 'ArrowLeft';
|
|
145
|
+
const next = event.key === grow
|
|
146
|
+
? shownWidth + KEY_STEP_PX
|
|
147
|
+
: event.key === shrink
|
|
148
|
+
? shownWidth - KEY_STEP_PX
|
|
149
|
+
: event.key === 'Home'
|
|
150
|
+
? minWidth
|
|
151
|
+
: event.key === 'End'
|
|
152
|
+
? maxWidth
|
|
153
|
+
: undefined;
|
|
154
|
+
if (next === undefined)
|
|
155
|
+
return;
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
onWidthChange(clamp(next, minWidth, maxWidth));
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
const overlay = mode === 'overlay';
|
|
161
|
+
const style = {
|
|
162
|
+
position: overlay ? 'absolute' : 'relative',
|
|
163
|
+
insetBlock: overlay ? 0 : undefined,
|
|
164
|
+
insetInlineStart: overlay ? 0 : undefined,
|
|
165
|
+
zIndex: overlay ? 2 : undefined,
|
|
166
|
+
boxShadow: overlay ? 'var(--mantine-shadow-md)' : undefined,
|
|
167
|
+
width: shownWidth,
|
|
168
|
+
maxWidth: '100%',
|
|
169
|
+
height: '100%',
|
|
170
|
+
display: 'flex',
|
|
171
|
+
flexDirection: 'column',
|
|
172
|
+
background: 'var(--mantine-color-body)',
|
|
173
|
+
borderInlineEnd: '1px solid var(--canopy-input-border)',
|
|
174
|
+
};
|
|
175
|
+
return (_jsxs("section", { ref: panelRef, id: panelId, "aria-labelledby": titleId, tabIndex: -1, "data-testid": testId, "data-panel-mode": mode, style: style, onFocus: () => {
|
|
176
|
+
focusInside.current = true;
|
|
177
|
+
}, onBlur: (event) => {
|
|
178
|
+
if (!event.currentTarget.contains(event.relatedTarget))
|
|
179
|
+
focusInside.current = false;
|
|
180
|
+
}, children: [_jsx("style", { children: PANEL_STYLES }), _jsxs(Group, { justify: "space-between", wrap: "nowrap", gap: "xs", px: "md", py: "xs", style: { borderBlockEnd: '1px solid var(--canopy-input-border)' }, children: [_jsx(Text, { id: titleId, component: "h2", fw: 600, size: "md", m: 0, children: title }), _jsxs(Group, { gap: "xs", wrap: "nowrap", children: [actions, _jsx(Tooltip, { label: `Close ${title.toLowerCase()} panel`, children: _jsx(CloseButton, { "aria-label": `Close ${title.toLowerCase()} panel`, onClick: onClose }) })] })] }), _jsx("div", { style: { flex: 1, minHeight: 0, overflow: 'auto' }, children: children }), _jsx("div", { role: "separator", "aria-orientation": "vertical", "aria-label": `Resize ${title.toLowerCase()} panel`, "aria-controls": panelId, "aria-valuenow": Math.round(shownWidth), "aria-valuemin": minWidth, "aria-valuemax": maxWidth,
|
|
181
|
+
// A focusable separator is a window splitter, a widget; jsx-a11y counts every one static.
|
|
182
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
183
|
+
tabIndex: 0, className: "canopy-side-panel-separator", "data-dragging": dragWidth !== null || undefined, ...separatorHandlers })] }));
|
|
184
|
+
}
|