canopycms 0.0.68-int.98 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2131 -0
- package/README.md +47 -63
- package/dist/ai/generate.d.ts +5 -0
- package/dist/ai/generate.js +244 -115
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/json-to-markdown.d.ts +21 -1
- package/dist/ai/json-to-markdown.js +120 -57
- package/dist/ai/references.d.ts +72 -0
- package/dist/ai/references.js +223 -0
- package/dist/ai/resolve-branch.js +9 -2
- package/dist/ai/to-plain-text.d.ts +17 -8
- package/dist/ai/to-plain-text.js +18 -15
- package/dist/ai/types.d.ts +84 -3
- package/dist/api/admin-branch-health.d.ts +24 -1
- package/dist/api/admin-branch-health.js +52 -20
- package/dist/api/admin.d.ts +23 -5
- package/dist/api/admin.js +36 -1
- package/dist/api/assets.js +88 -78
- package/dist/api/branch-create-window.d.ts +12 -0
- package/dist/api/branch-create-window.js +13 -0
- package/dist/api/branch-merge.d.ts +1 -1
- package/dist/api/branch-merge.js +1 -1
- package/dist/api/branch-review.d.ts +3 -3
- package/dist/api/branch-review.js +16 -7
- package/dist/api/branch-status.d.ts +16 -6
- package/dist/api/branch-status.js +57 -12
- package/dist/api/branch-withdraw.d.ts +2 -2
- package/dist/api/branch-withdraw.js +8 -3
- package/dist/api/branch.d.ts +25 -12
- package/dist/api/branch.js +164 -103
- package/dist/api/client.d.ts +78 -58
- package/dist/api/client.js +150 -117
- package/dist/api/comments.d.ts +12 -3
- package/dist/api/comments.js +118 -9
- package/dist/api/content.d.ts +9 -9
- package/dist/api/content.js +131 -22
- package/dist/api/entries-constants.d.ts +14 -3
- package/dist/api/entries-constants.js +14 -3
- package/dist/api/entries.d.ts +40 -2
- package/dist/api/entries.js +102 -6
- package/dist/api/github-sync.d.ts +19 -0
- package/dist/api/github-sync.js +50 -3
- package/dist/api/groups.d.ts +4 -4
- package/dist/api/groups.js +2 -1
- package/dist/api/guards.js +23 -3
- package/dist/api/index.d.ts +3 -1
- package/dist/api/index.js +1 -0
- package/dist/api/permissions.d.ts +21 -2
- package/dist/api/permissions.js +50 -3
- package/dist/api/request-deadline.d.ts +59 -0
- package/dist/api/request-deadline.js +73 -0
- package/dist/api/resolve-references.js +8 -7
- package/dist/api/route-builder.d.ts +14 -0
- package/dist/api/route-builder.js +14 -1
- package/dist/api/schema.d.ts +11 -9
- package/dist/api/schema.js +2 -0
- package/dist/api/settings-helpers.d.ts +5 -1
- package/dist/api/settings-helpers.js +10 -2
- package/dist/api/types.d.ts +18 -0
- package/dist/api/users-constants.d.ts +6 -0
- package/dist/api/users-constants.js +6 -0
- package/dist/assets/asset-url.d.ts +28 -16
- package/dist/assets/asset-url.js +51 -25
- package/dist/assets/finalize.js +3 -0
- package/dist/assets/index.d.ts +1 -1
- package/dist/assets/materialize.d.ts +153 -0
- package/dist/assets/materialize.js +402 -0
- package/dist/assets/store-local.d.ts +19 -5
- package/dist/assets/store-local.js +69 -10
- package/dist/assets/store-s3.d.ts +38 -7
- package/dist/assets/store-s3.js +128 -22
- package/dist/assets/transform-directives.d.ts +52 -13
- package/dist/assets/transform-directives.js +87 -27
- package/dist/assets/transform.d.ts +3 -3
- package/dist/assets/transform.js +13 -8
- package/dist/assets/types.d.ts +31 -4
- package/dist/auth/cache.d.ts +1 -1
- package/dist/auth/cache.js +1 -1
- package/dist/auth/caching-auth-plugin.d.ts +2 -0
- package/dist/auth/caching-auth-plugin.js +12 -2
- package/dist/auth/file-based-auth-cache.d.ts +8 -0
- package/dist/auth/file-based-auth-cache.js +18 -8
- package/dist/auth/person-name.d.ts +5 -0
- package/dist/auth/person-name.js +8 -0
- package/dist/auth/plugin.d.ts +10 -1
- package/dist/auth/types.d.ts +2 -0
- package/dist/auth/user-metadata-lookup.d.ts +21 -0
- package/dist/auth/user-metadata-lookup.js +116 -0
- package/dist/authorization/branch.d.ts +1 -1
- package/dist/authorization/branch.js +12 -13
- package/dist/authorization/groups/schema.d.ts +4 -4
- package/dist/authorization/helpers.d.ts +8 -0
- package/dist/authorization/helpers.js +8 -0
- package/dist/authorization/index.d.ts +1 -1
- package/dist/authorization/index.js +1 -1
- package/dist/authorization/protected-branch.d.ts +5 -4
- package/dist/authorization/protected-branch.js +7 -5
- package/dist/branch-health.d.ts +35 -8
- package/dist/branch-health.js +54 -18
- package/dist/branch-metadata-error.d.ts +27 -0
- package/dist/branch-metadata-error.js +23 -0
- package/dist/branch-metadata-file.d.ts +205 -24
- package/dist/branch-metadata-file.js +126 -25
- package/dist/branch-metadata.d.ts +45 -6
- package/dist/branch-metadata.js +142 -50
- package/dist/branch-provisioning.d.ts +167 -0
- package/dist/branch-provisioning.js +501 -0
- package/dist/branch-registry.d.ts +8 -0
- package/dist/branch-registry.js +61 -9
- package/dist/branch-schema-cache.d.ts +29 -12
- package/dist/branch-schema-cache.js +64 -21
- package/dist/branch-sparse.d.ts +23 -0
- package/dist/branch-sparse.js +118 -0
- package/dist/branch-workspace.d.ts +42 -3
- package/dist/branch-workspace.js +239 -100
- package/dist/build/asset-refs.d.ts +94 -0
- package/dist/build/asset-refs.js +267 -0
- package/dist/build/generate-ai-content.js +5 -0
- package/dist/build/inline-flight.d.ts +37 -0
- package/dist/build/inline-flight.js +142 -0
- package/dist/cli/asset-refs.d.ts +52 -0
- package/dist/cli/asset-refs.js +194 -0
- package/dist/cli/cli.d.ts +15 -1
- package/dist/cli/cli.js +8543 -3193
- package/dist/cli/configured-asset-store.d.ts +12 -0
- package/dist/cli/configured-asset-store.js +46 -0
- package/dist/cli/generate-ai-content.js +4244 -1072
- package/dist/cli/init.js +217 -126
- package/dist/cli/project-detect.d.ts +2 -0
- package/dist/cli/project-detect.js +2 -0
- package/dist/cli/template-files/Dockerfile.cms.template +13 -5
- package/dist/cli/template-files/cms-stack.ts.template +16 -13
- package/dist/client.d.ts +1 -2
- package/dist/client.js +1 -2
- package/dist/comment-store.d.ts +25 -0
- package/dist/comment-store.js +43 -8
- package/dist/config/flatten.js +2 -0
- package/dist/config/helpers.js +2 -2
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schemas/config.d.ts +12 -29
- package/dist/config/schemas/config.js +9 -12
- package/dist/config/schemas/field.d.ts +3 -0
- package/dist/config/schemas/field.js +58 -4
- package/dist/config/schemas/media.d.ts +4 -13
- package/dist/config/schemas/media.js +5 -8
- package/dist/config/schemas/url.d.ts +2 -20
- package/dist/config/schemas/url.js +2 -25
- package/dist/config/types.d.ts +116 -22
- package/dist/config/types.js +3 -9
- package/dist/config/validation.d.ts +25 -4
- package/dist/config/validation.js +116 -3
- package/dist/content-id-index.js +2 -2
- package/dist/content-index-generation.d.ts +1 -1
- package/dist/content-index-generation.js +1 -1
- package/dist/content-listing.d.ts +10 -1
- package/dist/content-listing.js +22 -5
- package/dist/content-reader.d.ts +9 -0
- package/dist/content-reader.js +16 -11
- package/dist/content-store.d.ts +75 -12
- package/dist/content-store.js +249 -86
- package/dist/content-tree.js +2 -2
- package/dist/context.js +5 -2
- package/dist/dangling-reference-log.d.ts +17 -0
- package/dist/dangling-reference-log.js +29 -0
- package/dist/dev-content-watcher.d.ts +1 -1
- package/dist/dev-content-watcher.js +1 -1
- package/dist/editor/BranchManager.d.ts +23 -7
- package/dist/editor/BranchManager.js +119 -54
- package/dist/editor/CanopyEditor.d.ts +1 -1
- package/dist/editor/CanopyEditor.js +1 -2
- package/dist/editor/CommentsPanel.d.ts +14 -0
- package/dist/editor/CommentsPanel.js +68 -43
- package/dist/editor/Editor.d.ts +11 -5
- package/dist/editor/Editor.js +891 -218
- package/dist/editor/EditorAuthGate.d.ts +7 -1
- package/dist/editor/EditorAuthGate.js +41 -9
- package/dist/editor/EntryNavigator.d.ts +22 -0
- package/dist/editor/EntryNavigator.js +353 -105
- package/dist/editor/FormHeader.d.ts +25 -0
- package/dist/editor/FormHeader.js +82 -0
- package/dist/editor/FormRenderer.d.ts +27 -3
- package/dist/editor/FormRenderer.js +171 -76
- package/dist/editor/PreviewFrame.d.ts +40 -0
- package/dist/editor/PreviewFrame.js +215 -0
- package/dist/editor/PreviewPane.d.ts +47 -0
- package/dist/editor/PreviewPane.js +147 -0
- package/dist/editor/PreviewToolbarTools.d.ts +22 -0
- package/dist/editor/PreviewToolbarTools.js +73 -0
- package/dist/editor/admin/SystemHealthPanel.d.ts +2 -1
- package/dist/editor/admin/SystemHealthPanel.js +163 -33
- package/dist/editor/admin/useSystemHealth.d.ts +18 -0
- package/dist/editor/admin/useSystemHealth.js +70 -1
- package/dist/editor/branch-status.d.ts +17 -0
- package/dist/editor/branch-status.js +29 -0
- package/dist/editor/canopy-path.d.ts +9 -6
- package/dist/editor/canopy-path.js +8 -11
- package/dist/editor/client-reference-resolver.d.ts +43 -16
- package/dist/editor/client-reference-resolver.js +155 -64
- package/dist/editor/comments/BranchComments.d.ts +1 -1
- package/dist/editor/comments/BranchComments.js +2 -2
- package/dist/editor/comments/CloseRefusedNotice.d.ts +4 -0
- package/dist/editor/comments/CloseRefusedNotice.js +7 -0
- package/dist/editor/comments/CommentThreadsPopover.d.ts +47 -0
- package/dist/editor/comments/CommentThreadsPopover.js +274 -0
- package/dist/editor/comments/CommentWriteAlert.d.ts +11 -0
- package/dist/editor/comments/CommentWriteAlert.js +13 -0
- package/dist/editor/comments/EntryComments.d.ts +10 -4
- package/dist/editor/comments/EntryComments.js +14 -17
- package/dist/editor/comments/FieldWrapper.d.ts +18 -4
- package/dist/editor/comments/FieldWrapper.js +62 -28
- package/dist/editor/comments/InlineCommentThread.d.ts +9 -2
- package/dist/editor/comments/InlineCommentThread.js +11 -47
- package/dist/editor/comments/ThreadCarousel.d.ts +1 -1
- package/dist/editor/comments/ThreadCarousel.js +18 -21
- package/dist/editor/comments/ThreadReplyControls.d.ts +28 -0
- package/dist/editor/comments/ThreadReplyControls.js +92 -0
- package/dist/editor/comments/field-comment-context.d.ts +14 -0
- package/dist/editor/comments/field-comment-context.js +14 -0
- package/dist/editor/comments/form-comments.d.ts +48 -0
- package/dist/editor/comments/form-comments.js +178 -0
- package/dist/editor/comments/item-anchor-copy.d.ts +6 -0
- package/dist/editor/comments/item-anchor-copy.js +6 -0
- package/dist/editor/comments/move-focus-if-unmoved.d.ts +6 -0
- package/dist/editor/comments/move-focus-if-unmoved.js +11 -0
- package/dist/editor/comments/popover-placement.d.ts +18 -0
- package/dist/editor/comments/popover-placement.js +58 -0
- package/dist/editor/comments/use-tracked-writes.d.ts +16 -0
- package/dist/editor/comments/use-tracked-writes.js +53 -0
- package/dist/editor/components/BranchAccessList.d.ts +15 -0
- package/dist/editor/components/BranchAccessList.js +52 -0
- package/dist/editor/components/BranchLockBanner.d.ts +22 -0
- package/dist/editor/components/BranchLockBanner.js +39 -0
- package/dist/editor/components/BranchPill.d.ts +32 -0
- package/dist/editor/components/BranchPill.js +33 -0
- package/dist/editor/components/BranchesDrawer.d.ts +11 -0
- package/dist/editor/components/BranchesDrawer.js +5 -0
- package/dist/editor/components/ConfirmDeleteModal.d.ts +6 -1
- package/dist/editor/components/ConfirmDeleteModal.js +3 -2
- package/dist/editor/components/DestructiveMenuItem.d.ts +7 -0
- package/dist/editor/components/DestructiveMenuItem.js +8 -0
- package/dist/editor/components/EditorCrashScreen.d.ts +17 -0
- package/dist/editor/components/EditorCrashScreen.js +22 -0
- package/dist/editor/components/EditorErrorBoundary.d.ts +38 -0
- package/dist/editor/components/EditorErrorBoundary.js +40 -0
- package/dist/editor/components/EditorHeader.d.ts +56 -106
- package/dist/editor/components/EditorHeader.js +130 -131
- package/dist/editor/components/EditorRail.d.ts +49 -0
- package/dist/editor/components/EditorRail.js +65 -0
- package/dist/editor/components/EntryBreadcrumb.d.ts +33 -0
- package/dist/editor/components/EntryBreadcrumb.js +56 -0
- package/dist/editor/components/EntryCreateModal.d.ts +12 -7
- package/dist/editor/components/EntryCreateModal.js +51 -57
- package/dist/editor/components/NoEditPermissionNotice.d.ts +6 -5
- package/dist/editor/components/NoEditPermissionNotice.js +5 -4
- package/dist/editor/components/QuickOpenPalette.d.ts +22 -0
- package/dist/editor/components/QuickOpenPalette.js +66 -0
- package/dist/editor/components/ReadOnlyDraftNotice.d.ts +11 -0
- package/dist/editor/components/ReadOnlyDraftNotice.js +12 -0
- package/dist/editor/components/ReasonedMenuItem.d.ts +15 -0
- package/dist/editor/components/ReasonedMenuItem.js +15 -0
- package/dist/editor/components/ReferencedByList.d.ts +11 -0
- package/dist/editor/components/ReferencedByList.js +28 -0
- package/dist/editor/components/RelativeTime.d.ts +10 -0
- package/dist/editor/components/RelativeTime.js +10 -0
- package/dist/editor/components/RenameEntryModal.d.ts +9 -1
- package/dist/editor/components/RenameEntryModal.js +19 -31
- package/dist/editor/components/RetryToastMessage.d.ts +9 -0
- package/dist/editor/components/RetryToastMessage.js +6 -0
- package/dist/editor/components/SaveAllResult.d.ts +25 -0
- package/dist/editor/components/SaveAllResult.js +86 -0
- package/dist/editor/components/SaveSplitButton.d.ts +30 -0
- package/dist/editor/components/SaveSplitButton.js +108 -0
- package/dist/editor/components/StagedChangesDrawer.d.ts +39 -0
- package/dist/editor/components/StagedChangesDrawer.js +92 -0
- package/dist/editor/components/UnavailableEntryNotice.d.ts +6 -0
- package/dist/editor/components/UnavailableEntryNotice.js +9 -0
- package/dist/editor/components/UnavailableTypeMessage.d.ts +6 -0
- package/dist/editor/components/UnavailableTypeMessage.js +9 -0
- package/dist/editor/components/UndoToastMessage.d.ts +18 -0
- package/dist/editor/components/UndoToastMessage.js +10 -0
- package/dist/editor/components/UserBadge.d.ts +2 -5
- package/dist/editor/components/UserBadge.js +62 -33
- package/dist/editor/components/ValidationSummary.d.ts +14 -0
- package/dist/editor/components/ValidationSummary.js +24 -0
- package/dist/editor/components/index.d.ts +2 -2
- package/dist/editor/components/index.js +2 -2
- package/dist/editor/context/ApiClientContext.d.ts +8 -2
- package/dist/editor/context/ApiClientContext.js +19 -8
- package/dist/editor/context/AssetContext.d.ts +13 -35
- package/dist/editor/context/AssetContext.js +12 -31
- package/dist/editor/context/EditorStateContext.d.ts +4 -4
- package/dist/editor/context/EditorStateContext.js +4 -4
- package/dist/editor/context/SWRProvider.d.ts +3 -3
- package/dist/editor/context/SiteMdxAllowContext.d.ts +4 -0
- package/dist/editor/context/SiteMdxAllowContext.js +7 -0
- package/dist/editor/context/index.d.ts +2 -1
- package/dist/editor/context/index.js +2 -1
- package/dist/editor/contrast.d.ts +19 -0
- package/dist/editor/contrast.js +41 -0
- package/dist/editor/copy.d.ts +18 -0
- package/dist/editor/copy.js +18 -0
- package/dist/editor/editor-config.js +16 -0
- package/dist/editor/editor-utils.d.ts +34 -8
- package/dist/editor/editor-utils.js +42 -20
- package/dist/editor/entry-search.d.ts +20 -0
- package/dist/editor/entry-search.js +34 -0
- package/dist/editor/field-breadcrumb.d.ts +12 -0
- package/dist/editor/field-breadcrumb.js +85 -0
- package/dist/editor/field-props.d.ts +86 -0
- package/dist/editor/field-props.js +27 -0
- package/dist/editor/field-target.d.ts +30 -0
- package/dist/editor/field-target.js +77 -0
- package/dist/editor/fields/BlockField.d.ts +12 -0
- package/dist/editor/fields/BlockField.js +89 -73
- package/dist/editor/fields/CodeField.d.ts +2 -0
- package/dist/editor/fields/CodeField.js +6 -2
- package/dist/editor/fields/DateTimeField.d.ts +4 -29
- package/dist/editor/fields/DateTimeField.js +79 -41
- package/dist/editor/fields/ExpandAllButton.d.ts +6 -0
- package/dist/editor/fields/ExpandAllButton.js +4 -0
- package/dist/editor/fields/FieldCrashFallback.d.ts +17 -0
- package/dist/editor/fields/FieldCrashFallback.js +30 -0
- package/dist/editor/fields/FieldLabel.d.ts +37 -0
- package/dist/editor/fields/FieldLabel.js +19 -0
- package/dist/editor/fields/ImageField.d.ts +4 -0
- package/dist/editor/fields/ImageField.js +29 -14
- package/dist/editor/fields/InlineGroupField.js +4 -3
- package/dist/editor/fields/ItemList.d.ts +42 -0
- package/dist/editor/fields/ItemList.js +130 -0
- package/dist/editor/fields/MarkdownField.d.ts +32 -0
- package/dist/editor/fields/MarkdownField.js +223 -46
- package/dist/editor/fields/MdxImageDialog.js +1 -1
- package/dist/editor/fields/NumberField.d.ts +2 -0
- package/dist/editor/fields/NumberField.js +8 -5
- package/dist/editor/fields/NumberListField.d.ts +2 -0
- package/dist/editor/fields/NumberListField.js +19 -16
- package/dist/editor/fields/ObjectField.d.ts +2 -7
- package/dist/editor/fields/ObjectField.js +15 -5
- package/dist/editor/fields/ObjectListField.d.ts +29 -0
- package/dist/editor/fields/ObjectListField.js +57 -0
- package/dist/editor/fields/ReferenceField.d.ts +2 -0
- package/dist/editor/fields/ReferenceField.js +34 -9
- package/dist/editor/fields/SelectField.d.ts +2 -0
- package/dist/editor/fields/SelectField.js +11 -2
- package/dist/editor/fields/StringListField.d.ts +4 -1
- package/dist/editor/fields/StringListField.js +22 -8
- package/dist/editor/fields/TextField.d.ts +2 -0
- package/dist/editor/fields/TextField.js +6 -2
- package/dist/editor/fields/ToggleField.d.ts +2 -0
- package/dist/editor/fields/ToggleField.js +8 -3
- package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +1 -1
- package/dist/editor/fields/entry-link/InsertEntryLink.js +1 -1
- package/dist/editor/fields/form-expansion.d.ts +9 -0
- package/dist/editor/fields/form-expansion.js +2 -0
- package/dist/editor/fields/item-expansion.d.ts +15 -0
- package/dist/editor/fields/item-expansion.js +60 -0
- package/dist/editor/fields/list-item-keys.d.ts +8 -0
- package/dist/editor/fields/list-item-keys.js +50 -0
- package/dist/editor/fields/list-item-title.d.ts +20 -0
- package/dist/editor/fields/list-item-title.js +85 -0
- package/dist/editor/fields/markdown-export-options.d.ts +2 -0
- package/dist/editor/fields/markdown-export-options.js +8 -0
- package/dist/editor/fields/markdown-fidelity-visitors.d.ts +11 -0
- package/dist/editor/fields/markdown-fidelity-visitors.js +160 -0
- package/dist/editor/fields/mdx-jsx-support.d.ts +5 -1
- package/dist/editor/fields/mdx-jsx-support.js +157 -69
- package/dist/editor/fields/rich-text-failures.d.ts +5 -0
- package/dist/editor/fields/rich-text-failures.js +26 -0
- package/dist/editor/fields/toolbar-wrap.d.ts +7 -0
- package/dist/editor/fields/toolbar-wrap.js +35 -0
- package/dist/editor/form-outline.d.ts +13 -0
- package/dist/editor/form-outline.js +70 -0
- package/dist/editor/group-manager/GroupCard.d.ts +4 -1
- package/dist/editor/group-manager/GroupCard.js +12 -3
- package/dist/editor/group-manager/GroupForm.js +1 -1
- package/dist/editor/group-manager/InternalGroupsTab.d.ts +2 -1
- package/dist/editor/group-manager/InternalGroupsTab.js +4 -4
- package/dist/editor/group-manager/MemberList.d.ts +2 -1
- package/dist/editor/group-manager/MemberList.js +15 -4
- package/dist/editor/group-manager/hooks/useGroupState.d.ts +5 -0
- package/dist/editor/group-manager/hooks/useGroupState.js +30 -7
- package/dist/editor/group-manager/index.js +34 -5
- package/dist/editor/group-manager/types.d.ts +5 -2
- package/dist/editor/hooks/create-branch-request.d.ts +20 -0
- package/dist/editor/hooks/create-branch-request.js +54 -0
- package/dist/editor/hooks/save-all.d.ts +68 -0
- package/dist/editor/hooks/save-all.js +61 -0
- package/dist/editor/hooks/useBranchActions.d.ts +11 -1
- package/dist/editor/hooks/useBranchActions.js +42 -19
- package/dist/editor/hooks/useBranchManager.d.ts +21 -0
- package/dist/editor/hooks/useBranchManager.js +321 -125
- package/dist/editor/hooks/useBranchesData.d.ts +8 -4
- package/dist/editor/hooks/useBranchesData.js +9 -5
- package/dist/editor/hooks/useCommentSystem.d.ts +14 -2
- package/dist/editor/hooks/useCommentSystem.js +79 -55
- package/dist/editor/hooks/useCommentsData.d.ts +11 -7
- package/dist/editor/hooks/useCommentsData.js +20 -19
- package/dist/editor/hooks/useDraftManager.d.ts +77 -3
- package/dist/editor/hooks/useDraftManager.js +887 -94
- package/dist/editor/hooks/useEditorLayout.d.ts +53 -7
- package/dist/editor/hooks/useEditorLayout.js +98 -20
- package/dist/editor/hooks/useEntriesData.d.ts +2 -1
- package/dist/editor/hooks/useEntriesData.js +18 -4
- package/dist/editor/hooks/useEntryManager.d.ts +41 -2
- package/dist/editor/hooks/useEntryManager.js +227 -41
- package/dist/editor/hooks/useGroupManager.js +13 -16
- package/dist/editor/hooks/usePermissionManager.js +14 -6
- package/dist/editor/hooks/usePreviewMarks.d.ts +21 -0
- package/dist/editor/hooks/usePreviewMarks.js +33 -0
- package/dist/editor/hooks/useReferenceResolution.d.ts +10 -3
- package/dist/editor/hooks/useReferenceResolution.js +34 -177
- package/dist/editor/hooks/useSchemaManager.d.ts +25 -6
- package/dist/editor/hooks/useSchemaManager.js +97 -39
- package/dist/editor/hooks/useUserMetadata.d.ts +14 -4
- package/dist/editor/hooks/useUserMetadata.js +76 -47
- package/dist/editor/hooks/user-metadata-batcher.d.ts +8 -0
- package/dist/editor/hooks/user-metadata-batcher.js +33 -0
- package/dist/editor/media/AssetCard.d.ts +6 -7
- package/dist/editor/media/AssetCard.js +24 -31
- package/dist/editor/media/AssetListRow.d.ts +16 -0
- package/dist/editor/media/AssetListRow.js +41 -0
- package/dist/editor/media/CropStep.js +6 -1
- package/dist/editor/media/MediaLibrary.d.ts +4 -3
- package/dist/editor/media/MediaLibrary.js +11 -4
- package/dist/editor/media/MediaLibraryBody.d.ts +3 -3
- package/dist/editor/media/MediaLibraryBody.js +195 -80
- package/dist/editor/media/MediaLibrarySkeleton.d.ts +6 -0
- package/dist/editor/media/MediaLibrarySkeleton.js +14 -0
- package/dist/editor/media/crop-math.d.ts +3 -4
- package/dist/editor/media/crop-math.js +11 -22
- package/dist/editor/media/editor-image-src.d.ts +8 -0
- package/dist/editor/media/editor-image-src.js +10 -0
- package/dist/editor/media/format-bytes.d.ts +2 -0
- package/dist/editor/media/format-bytes.js +23 -0
- package/dist/editor/media/media-library-prefs.d.ts +23 -0
- package/dist/editor/media/media-library-prefs.js +49 -0
- package/dist/editor/media/media-library-view.d.ts +34 -0
- package/dist/editor/media/media-library-view.js +40 -0
- package/dist/editor/media/useAssetLibrary.d.ts +30 -0
- package/dist/editor/media/useAssetLibrary.js +126 -0
- package/dist/editor/media/useStableAssetOrder.d.ts +11 -0
- package/dist/editor/media/useStableAssetOrder.js +37 -0
- package/dist/editor/media/useThumbnailFailure.d.ts +6 -0
- package/dist/editor/media/useThumbnailFailure.js +16 -0
- package/dist/editor/permission-manager/GroupSelector.js +2 -2
- package/dist/editor/permission-manager/PermissionEditor.d.ts +1 -1
- package/dist/editor/permission-manager/PermissionEditor.js +15 -4
- package/dist/editor/permission-manager/PermissionLevelBadge.js +1 -1
- package/dist/editor/permission-manager/PermissionTree.d.ts +1 -1
- package/dist/editor/permission-manager/UserSelector.d.ts +1 -1
- package/dist/editor/permission-manager/UserSelector.js +3 -3
- package/dist/editor/permission-manager/index.js +12 -3
- package/dist/editor/permission-manager/types.d.ts +5 -2
- package/dist/editor/platform.d.ts +3 -0
- package/dist/editor/platform.js +9 -0
- package/dist/editor/preview-asset-base.d.ts +9 -0
- package/dist/editor/preview-asset-base.js +17 -0
- package/dist/editor/preview-bridge.d.ts +51 -27
- package/dist/editor/preview-bridge.js +103 -144
- package/dist/editor/preview-marks.d.ts +15 -0
- package/dist/editor/preview-marks.js +74 -0
- package/dist/editor/preview-path.d.ts +12 -0
- package/dist/editor/preview-path.js +45 -1
- package/dist/editor/preview-scale.d.ts +27 -0
- package/dist/editor/preview-scale.js +34 -0
- package/dist/editor/raw-asset-base.d.ts +5 -0
- package/dist/editor/raw-asset-base.js +14 -0
- package/dist/editor/relative-time.d.ts +4 -4
- package/dist/editor/relative-time.js +16 -8
- package/dist/editor/schema-editor/CollectionEditor.d.ts +20 -3
- package/dist/editor/schema-editor/CollectionEditor.js +361 -62
- package/dist/editor/schema-editor/EntryTypeEditor.d.ts +5 -1
- package/dist/editor/schema-editor/EntryTypeEditor.js +11 -6
- package/dist/editor/schema-editor/collection-draft.d.ts +22 -0
- package/dist/editor/schema-editor/collection-draft.js +40 -0
- package/dist/editor/schema-editor/index.d.ts +1 -1
- package/dist/editor/schema-label.d.ts +10 -0
- package/dist/editor/schema-label.js +17 -0
- package/dist/editor/settings-conflict.d.ts +5 -0
- package/dist/editor/settings-conflict.js +8 -0
- package/dist/editor/shell/ActivityRail.d.ts +37 -0
- package/dist/editor/shell/ActivityRail.js +91 -0
- package/dist/editor/shell/EditorShell.d.ts +17 -0
- package/dist/editor/shell/EditorShell.js +31 -0
- package/dist/editor/shell/PaneLayout.d.ts +28 -0
- package/dist/editor/shell/PaneLayout.js +116 -0
- package/dist/editor/shell/SidePanelHost.d.ts +35 -0
- package/dist/editor/shell/SidePanelHost.js +184 -0
- package/dist/editor/shell/TopBar.d.ts +12 -0
- package/dist/editor/shell/TopBar.js +14 -0
- package/dist/editor/shell/host-chrome.d.ts +16 -0
- package/dist/editor/shell/host-chrome.js +69 -0
- package/dist/editor/slug.d.ts +16 -0
- package/dist/editor/slug.js +43 -0
- package/dist/editor/theme.d.ts +2 -1
- package/dist/editor/theme.js +314 -13
- package/dist/editor/unanswered.d.ts +17 -0
- package/dist/editor/unanswered.js +26 -0
- package/dist/editor/unavailable-entry-type.d.ts +20 -0
- package/dist/editor/unavailable-entry-type.js +28 -0
- package/dist/editor/undo-removal.d.ts +57 -0
- package/dist/editor/undo-removal.js +163 -0
- package/dist/editor/user-display.d.ts +4 -0
- package/dist/editor/user-display.js +5 -0
- package/dist/editor/utils/confirm-modal.d.ts +8 -0
- package/dist/editor/utils/confirm-modal.js +12 -0
- package/dist/editor/utils/editor-errors.d.ts +19 -0
- package/dist/editor/utils/editor-errors.js +32 -0
- package/dist/editor/utils/focus-return.d.ts +56 -0
- package/dist/editor/utils/focus-return.js +106 -0
- package/dist/entry-link-resolver.d.ts +11 -1
- package/dist/entry-link-resolver.js +65 -5
- package/dist/entry-schema-registry.d.ts +3 -3
- package/dist/entry-schema-registry.js +7 -4
- package/dist/entry-schema.d.ts +49 -3
- package/dist/entry-schema.js +18 -0
- package/dist/git-manager.d.ts +172 -10
- package/dist/git-manager.js +449 -107
- package/dist/github-service.d.ts +39 -2
- package/dist/github-service.js +101 -7
- package/dist/http/handler.js +74 -9
- package/dist/http/types.d.ts +2 -0
- package/dist/http/worker-not-ready.d.ts +9 -4
- package/dist/http/worker-not-ready.js +59 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/item-keys.d.ts +46 -0
- package/dist/item-keys.js +235 -0
- package/dist/operating-mode/editor-mode-check.d.ts +15 -0
- package/dist/operating-mode/editor-mode-check.js +24 -0
- package/dist/operating-mode/mode-env.d.ts +13 -14
- package/dist/operating-mode/mode-env.js +13 -14
- package/dist/operating-mode/types.d.ts +2 -2
- package/dist/paths/branch.d.ts +0 -1
- package/dist/paths/branch.js +1 -8
- package/dist/paths/index.d.ts +1 -1
- package/dist/paths/index.js +1 -1
- package/dist/paths/validation.js +0 -2
- package/dist/preview.d.ts +8 -0
- package/dist/preview.js +7 -0
- package/dist/reference-resolver.js +1 -0
- package/dist/resolve-canopy-user.d.ts +1 -1
- package/dist/resolve-canopy-user.js +1 -1
- package/dist/schema/meta-loader.d.ts +18 -8
- package/dist/schema/meta-loader.js +42 -9
- package/dist/schema/registry-fingerprint.d.ts +14 -0
- package/dist/schema/registry-fingerprint.js +63 -0
- package/dist/schema/resolver.d.ts +7 -4
- package/dist/schema/resolver.js +7 -6
- package/dist/schema/schema-store.d.ts +7 -10
- package/dist/schema/schema-store.js +13 -11
- package/dist/schema/schema-unavailable-error.d.ts +11 -0
- package/dist/schema/schema-unavailable-error.js +15 -0
- package/dist/schema/types.d.ts +22 -0
- package/dist/schema-registry-record.d.ts +35 -0
- package/dist/schema-registry-record.js +134 -0
- package/dist/server.d.ts +79 -9
- package/dist/server.js +62 -8
- package/dist/services.d.ts +33 -5
- package/dist/services.js +155 -37
- package/dist/settings-workspace.d.ts +23 -3
- package/dist/settings-workspace.js +159 -45
- package/dist/stamp-on-publish.d.ts +36 -0
- package/dist/stamp-on-publish.js +71 -0
- package/dist/static/index.d.ts +58 -5
- package/dist/static/index.js +133 -3
- package/dist/static/seo.d.ts +5 -5
- package/dist/static/seo.js +5 -5
- package/dist/submission-attribution.d.ts +8 -0
- package/dist/submission-attribution.js +22 -0
- package/dist/system-users.d.ts +12 -0
- package/dist/system-users.js +12 -0
- package/dist/task-queue/cms-task-queue.d.ts +1 -1
- package/dist/task-queue/cms-task-queue.js +1 -1
- package/dist/task-queue/index.d.ts +1 -1
- package/dist/task-queue/index.js +1 -1
- package/dist/task-queue/task-queue.d.ts +6 -0
- package/dist/task-queue/task-queue.js +29 -0
- package/dist/task-queue/worker-status.d.ts +35 -9
- package/dist/task-queue/worker-status.js +84 -17
- package/dist/types.d.ts +90 -9
- package/dist/url-collision.js +3 -3
- package/dist/url-exclusivity-fixtures.d.ts +8 -1
- package/dist/url-exclusivity-fixtures.js +11 -1
- package/dist/user.js +2 -1
- package/dist/utils/atomic-write.js +2 -1
- package/dist/utils/base-branch.d.ts +13 -0
- package/dist/utils/base-branch.js +23 -0
- package/dist/utils/content-serialize.d.ts +15 -7
- package/dist/utils/content-serialize.js +353 -51
- package/dist/utils/content-write-lock.d.ts +6 -1
- package/dist/utils/content-write-lock.js +21 -6
- package/dist/utils/debug.d.ts +1 -1
- package/dist/utils/debug.js +5 -3
- package/dist/utils/error.js +13 -2
- package/dist/utils/escape-text.d.ts +7 -0
- package/dist/utils/escape-text.js +27 -0
- package/dist/utils/fs.d.ts +12 -4
- package/dist/utils/fs.js +18 -5
- package/dist/utils/git.d.ts +21 -1
- package/dist/utils/git.js +63 -8
- package/dist/utils/json-source-splice.d.ts +14 -0
- package/dist/utils/json-source-splice.js +120 -0
- package/dist/utils/markdown-body-splice.d.ts +39 -0
- package/dist/utils/markdown-body-splice.js +421 -0
- package/dist/utils/occ-json-write.js +18 -12
- package/dist/utils/provision-log.d.ts +25 -0
- package/dist/utils/provision-log.js +49 -0
- package/dist/utils/provisioning-lock.d.ts +32 -1
- package/dist/utils/provisioning-lock.js +78 -32
- package/dist/utils/sanitize-href.d.ts +9 -23
- package/dist/utils/sanitize-href.js +14 -28
- package/dist/utils/title-field.d.ts +10 -0
- package/dist/utils/title-field.js +47 -0
- package/dist/utils/yaml-source-splice.d.ts +58 -0
- package/dist/utils/yaml-source-splice.js +515 -0
- package/dist/validation/block-structural-keys.d.ts +1 -1
- package/dist/validation/block-structural-keys.js +1 -1
- package/dist/validation/deletion-checker.d.ts +21 -29
- package/dist/validation/deletion-checker.js +41 -117
- package/dist/validation/entry-validator.d.ts +6 -10
- package/dist/validation/entry-validator.js +19 -26
- package/dist/validation/field-traversal.d.ts +22 -0
- package/dist/validation/field-traversal.js +39 -8
- package/dist/validation/markdown-safety.d.ts +68 -0
- package/dist/validation/markdown-safety.js +596 -0
- package/dist/validation/mdx-allowlist.d.ts +55 -0
- package/dist/validation/mdx-allowlist.js +183 -0
- package/dist/validation/reference-validator.d.ts +2 -0
- package/dist/validation/reference-validator.js +3 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/branch-policy.d.ts +124 -0
- package/dist/worker/branch-policy.js +243 -0
- package/dist/worker/bundle-header.d.ts +117 -0
- package/dist/worker/bundle-header.js +253 -0
- package/dist/worker/cms-worker.d.ts +236 -142
- package/dist/worker/cms-worker.js +772 -285
- package/dist/worker/gateway-egress.d.ts +43 -0
- package/dist/worker/gateway-egress.js +209 -0
- package/dist/worker/gateway-instance.d.ts +42 -0
- package/dist/worker/gateway-instance.js +129 -0
- package/dist/worker/git-process.d.ts +81 -0
- package/dist/worker/git-process.js +234 -0
- package/dist/worker/git-sync.d.ts +34 -17
- package/dist/worker/git-sync.js +394 -131
- package/dist/worker/github-auth.d.ts +9 -9
- package/dist/worker/github-auth.js +13 -15
- package/dist/worker/github-gateway-http.d.ts +62 -0
- package/dist/worker/github-gateway-http.js +977 -0
- package/dist/worker/github-gateway-wire.d.ts +590 -0
- package/dist/worker/github-gateway-wire.js +488 -0
- package/dist/worker/github-gateway.d.ts +238 -0
- package/dist/worker/github-gateway.js +726 -0
- package/dist/worker/github-mirror-upkeep.d.ts +75 -0
- package/dist/worker/github-mirror-upkeep.js +266 -0
- package/dist/worker/github-mirror.d.ts +364 -0
- package/dist/worker/github-mirror.js +1030 -0
- package/dist/worker/github-transfer.d.ts +56 -0
- package/dist/worker/github-transfer.js +444 -0
- package/dist/worker/history-rewrite.d.ts +4 -5
- package/dist/worker/history-rewrite.js +12 -17
- package/dist/worker/provisioned-workspace.d.ts +5 -5
- package/dist/worker/provisioned-workspace.js +5 -5
- package/dist/worker/rebase.d.ts +1 -1
- package/dist/worker/rebase.js +58 -54
- package/dist/worker/remote-git-maintenance.d.ts +3 -0
- package/dist/worker/remote-git-maintenance.js +12 -0
- package/dist/worker/schema-gate.d.ts +49 -0
- package/dist/worker/schema-gate.js +226 -0
- package/dist/worker/self-checks.d.ts +107 -0
- package/dist/worker/self-checks.js +358 -0
- package/dist/worker/shared-repo-git.d.ts +96 -0
- package/dist/worker/shared-repo-git.js +459 -0
- package/dist/worker/sparse-cone.d.ts +20 -0
- package/dist/worker/sparse-cone.js +136 -0
- package/dist/worker/stale-mirror.d.ts +82 -0
- package/dist/worker/stale-mirror.js +130 -0
- package/dist/worker/task-runner.d.ts +5 -3
- package/dist/worker/task-runner.js +287 -186
- package/dist/worker/worker-context.d.ts +36 -37
- package/package.json +35 -7
- package/dist/editor/EditorPanes.d.ts +0 -10
- package/dist/editor/EditorPanes.js +0 -98
- package/dist/editor/components/EditorFooter.d.ts +0 -11
- package/dist/editor/components/EditorFooter.js +0 -9
- package/dist/editor/components/EditorSidebar.d.ts +0 -67
- package/dist/editor/components/EditorSidebar.js +0 -22
|
@@ -1,32 +1,100 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import React, { Suspense, useId, useRef, useCallback, useEffect, useState } from 'react';
|
|
4
|
-
import { Alert,
|
|
3
|
+
import React, { Suspense, useId, useRef, useCallback, useEffect, useReducer, useState } from 'react';
|
|
4
|
+
import { Alert, Box, Button, Text, Textarea, useComputedColorScheme } from '@mantine/core';
|
|
5
5
|
import { InsertEntryLink } from './entry-link/index.js';
|
|
6
|
+
import { MARKDOWN_EXPORT_OPTIONS } from './markdown-export-options.js';
|
|
7
|
+
import { createMarkdownFidelityPlugin } from './markdown-fidelity-visitors.js';
|
|
6
8
|
import { MdxImageDialog } from './MdxImageDialog.js';
|
|
7
|
-
import {
|
|
9
|
+
import { createMdxJsxPlugins } from './mdx-jsx-support.js';
|
|
10
|
+
import { useApiClient, useAssetContext } from '../context/index.js';
|
|
11
|
+
import { editorImageSrc } from '../media/editor-image-src.js';
|
|
8
12
|
import { uploadAsset } from '../media/upload-asset.js';
|
|
13
|
+
import { useFieldCommentControl } from '../comments/field-comment-context.js';
|
|
9
14
|
import { FieldDescription, groupDescriptionProps } from './FieldDescription.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
import { FieldLabel } from './FieldLabel.js';
|
|
16
|
+
import { watchToolbarWrap } from './toolbar-wrap.js';
|
|
17
|
+
import { EditorErrorBoundary } from '../components/EditorErrorBoundary.js';
|
|
18
|
+
import { getErrorMessage, sanitizeErrorMessage } from '../../utils/error.js';
|
|
19
|
+
import { forgetRichTextFailure, recallRichTextFailure, rememberRichTextFailure, } from './rich-text-failures.js';
|
|
20
|
+
/** Text formats MDXEditor exports as an HTML tag, with that tag. */
|
|
21
|
+
const TAG_FORMATS = [
|
|
22
|
+
['underline', 'u'],
|
|
23
|
+
['subscript', 'sub'],
|
|
24
|
+
['superscript', 'sup'],
|
|
25
|
+
];
|
|
26
|
+
/** @internal Exported for the round-trip corpus test, which drives exactly this editor. */
|
|
27
|
+
export const MDXEditorLazy = React.lazy(async () => {
|
|
28
|
+
// MDXEditor's only code import (theme.tsx loads its CSS); see mdx-jsx-support for why.
|
|
29
|
+
const mdx = await import('@mdxeditor/editor');
|
|
30
|
+
const { MDXEditor, headingsPlugin, listsPlugin, quotePlugin, thematicBreakPlugin, markdownShortcutPlugin, linkPlugin, linkDialogPlugin, imagePlugin, tablePlugin, toolbarPlugin, codeBlockPlugin, codeMirrorPlugin, BoldItalicUnderlineToggles, BlockTypeSelect, ListsToggle, CreateLink, InsertImage, InsertTable, InsertThematicBreak, CodeToggle, InsertCodeBlock, UndoRedo, Separator, insertMarkdown$, usePublisher, useCellValues, saveImage$, closeImageDialog$, imageDialogState$, activeEditor$, $isImageNode, createRootEditorSubscription$, realmPlugin, Cell,
|
|
31
|
+
// MDXEditor's own lexical instance: lexical keeps the active editor state per module, so
|
|
32
|
+
// a separately resolved copy would throw inside this editor's `read()`.
|
|
33
|
+
lexical: { $getNodeByKey, $getSelection, $isRangeSelection, COMMAND_PRIORITY_CRITICAL, FORMAT_TEXT_COMMAND, SET_TEXT_FORMAT_COMMAND, mergeRegister, }, } = mdx;
|
|
34
|
+
const mdxJsxPlugins = createMdxJsxPlugins(mdx);
|
|
35
|
+
/** The text formats whose tag the field refuses, kept current as props change. */
|
|
36
|
+
const refusedFormats$ = Cell([]);
|
|
37
|
+
const refusedFormats = (htmlTags) => TAG_FORMATS.filter(([, tag]) => htmlTags?.has(tag) === false).map(([format]) => format);
|
|
38
|
+
/**
|
|
39
|
+
* Refuses adding a format whose tag the field refuses (toolbar, Cmd+U) and lets removing one
|
|
40
|
+
* through: Lexical toggles a range off when its selection has the format, and passes a nested
|
|
41
|
+
* editor's input commands up to the root's. Content is never rewritten: a stored tag stays for
|
|
42
|
+
* the server's unchanged-field rule, and a pasted one reaches the server, which refuses it.
|
|
43
|
+
*/
|
|
44
|
+
const tagFormatGuardPlugin = realmPlugin({
|
|
45
|
+
init(realm, params) {
|
|
46
|
+
realm.pub(refusedFormats$, refusedFormats(params?.htmlTags));
|
|
47
|
+
const refused = (format) => realm.getValue(refusedFormats$).includes(format);
|
|
48
|
+
const guard = (editor) => mergeRegister(editor.registerCommand(FORMAT_TEXT_COMMAND, (format, fromEditor) => {
|
|
49
|
+
if (!refused(format))
|
|
50
|
+
return false;
|
|
51
|
+
// Lexical formats the dispatching editor's pending selection; inside the root's
|
|
52
|
+
// update, where this runs for a nested command, `$getSelection()` is the root's.
|
|
53
|
+
const selection = fromEditor.read('pending', () => $getSelection());
|
|
54
|
+
return !($isRangeSelection(selection) && selection.hasFormat(format));
|
|
55
|
+
}, COMMAND_PRIORITY_CRITICAL), editor.registerCommand(SET_TEXT_FORMAT_COMMAND, (formats) => Object.entries(formats).some(([format, on]) => on === true && refused(format)), COMMAND_PRIORITY_CRITICAL));
|
|
56
|
+
realm.pub(createRootEditorSubscription$, guard);
|
|
57
|
+
},
|
|
58
|
+
update(realm, params) {
|
|
59
|
+
realm.pub(refusedFormats$, refusedFormats(params?.htmlTags));
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
const markdownFidelityPlugin = createMarkdownFidelityPlugin(mdx);
|
|
63
|
+
const EntryLinkToolbarButton = ({ onInsert }) => {
|
|
13
64
|
const insertMarkdown = usePublisher(insertMarkdown$);
|
|
14
|
-
return _jsx(InsertEntryLink, { onInsert: insertMarkdown });
|
|
65
|
+
return (_jsx(InsertEntryLink, { onInsert: (markdown) => onInsert(() => insertMarkdown(markdown), markdown) }));
|
|
15
66
|
};
|
|
16
67
|
/**
|
|
17
|
-
* Bridges mdxeditor's realm cells (reachable only once this lazy chunk has
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* `EntryLinkToolbarButton` above.
|
|
68
|
+
* Bridges mdxeditor's realm cells (reachable only once this lazy chunk has loaded) into
|
|
69
|
+
* MdxImageDialog's plain-props interface, so that component never imports `@mdxeditor/editor`
|
|
70
|
+
* at runtime — same pattern as `EntryLinkToolbarButton` above.
|
|
21
71
|
*/
|
|
22
72
|
const MdxImageDialogBridge = () => {
|
|
23
|
-
const [state] = useCellValues(imageDialogState$);
|
|
73
|
+
const [state, editor] = useCellValues(imageDialogState$, activeEditor$);
|
|
24
74
|
const saveImage = usePublisher(saveImage$);
|
|
25
75
|
const closeImageDialog = usePublisher(closeImageDialog$);
|
|
26
|
-
|
|
76
|
+
// With an `imagePreviewHandler`, MDXEditor seeds an edit dialog with the src the node was
|
|
77
|
+
// first rendered with, not its current one, so saving after a src change would revert it.
|
|
78
|
+
// Read the node's live src from the same editor `saveImage$` writes through.
|
|
79
|
+
const liveState = React.useMemo(() => {
|
|
80
|
+
if (state.type !== 'editing' || !editor)
|
|
81
|
+
return state;
|
|
82
|
+
const src = editor.getEditorState().read(() => {
|
|
83
|
+
const node = $getNodeByKey(state.nodeKey);
|
|
84
|
+
return $isImageNode(node) ? node.getSrc() : undefined;
|
|
85
|
+
});
|
|
86
|
+
return src === undefined
|
|
87
|
+
? state
|
|
88
|
+
: { ...state, initialValues: { ...state.initialValues, src } };
|
|
89
|
+
}, [state, editor]);
|
|
90
|
+
return _jsx(MdxImageDialog, { state: liveState, onSave: saveImage, onClose: closeImageDialog });
|
|
27
91
|
};
|
|
28
|
-
const WrappedEditor = ({ markdown, onChange, onError, editorRef, imageUploadHandler }) => {
|
|
29
|
-
|
|
92
|
+
const WrappedEditor = ({ markdown, onChange, onError, onInsert, editorRef, imageUploadHandler, imagePreviewHandler, htmlTags, readOnly, contentLabel, dark, }) => {
|
|
93
|
+
const underline = htmlTags?.has('u') ?? true;
|
|
94
|
+
const translation = (key, defaultValue, interpolations = {}) => key === 'contentArea.editableMarkdown' && contentLabel
|
|
95
|
+
? contentLabel
|
|
96
|
+
: Object.entries(interpolations).reduce((text, [name, v]) => text.replaceAll(`{{${name}}}`, String(v)), defaultValue);
|
|
97
|
+
return (_jsx(MDXEditor, { ref: editorRef, className: dark ? 'dark-theme' : undefined, markdown: markdown, readOnly: readOnly, translation: translation, onChange: onChange, onError: onError, toMarkdownOptions: MARKDOWN_EXPORT_OPTIONS, plugins: [
|
|
30
98
|
headingsPlugin(),
|
|
31
99
|
listsPlugin(),
|
|
32
100
|
quotePlugin(),
|
|
@@ -34,9 +102,17 @@ const MDXEditorLazy = React.lazy(async () => {
|
|
|
34
102
|
markdownShortcutPlugin(),
|
|
35
103
|
linkPlugin(),
|
|
36
104
|
linkDialogPlugin(),
|
|
37
|
-
imagePlugin({
|
|
105
|
+
imagePlugin({
|
|
106
|
+
imageUploadHandler,
|
|
107
|
+
imagePreviewHandler,
|
|
108
|
+
ImageDialog: MdxImageDialogBridge,
|
|
109
|
+
// A resized image exports as `<img>`.
|
|
110
|
+
disableImageResize: htmlTags?.has('img') === false,
|
|
111
|
+
}),
|
|
38
112
|
tablePlugin(),
|
|
39
113
|
...mdxJsxPlugins(),
|
|
114
|
+
markdownFidelityPlugin(),
|
|
115
|
+
tagFormatGuardPlugin({ htmlTags }),
|
|
40
116
|
codeBlockPlugin({ defaultCodeBlockLanguage: '' }),
|
|
41
117
|
codeMirrorPlugin({
|
|
42
118
|
codeBlockLanguages: {
|
|
@@ -54,25 +130,46 @@ const MDXEditorLazy = React.lazy(async () => {
|
|
|
54
130
|
markdown: 'Markdown',
|
|
55
131
|
},
|
|
56
132
|
}),
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
133
|
+
// MDXEditor's readOnly blocks only the toolbar's pointer events; its buttons still take the
|
|
134
|
+
// keyboard, and their commands edit the document.
|
|
135
|
+
// It reads plugins once, so the editor's key remounts it when `readOnly` changes.
|
|
136
|
+
...(readOnly
|
|
137
|
+
? []
|
|
138
|
+
: [
|
|
139
|
+
toolbarPlugin({
|
|
140
|
+
toolbarClassName: 'canopy-mdx-toolbar',
|
|
141
|
+
toolbarContents: () => (_jsxs(_Fragment, { children: [_jsx(UndoRedo, {}), _jsx(Separator, {}), _jsx(BoldItalicUnderlineToggles, { options: underline ? undefined : ['Bold', 'Italic'] }), _jsx(CodeToggle, {}), _jsx(Separator, {}), _jsx(BlockTypeSelect, {}), _jsx(Separator, {}), _jsx(ListsToggle, {}), _jsx(Separator, {}), _jsx(CreateLink, {}), _jsx(EntryLinkToolbarButton, { onInsert: onInsert }), _jsx(InsertImage, {}), _jsx(InsertTable, {}), _jsx(InsertThematicBreak, {}), _jsx(InsertCodeBlock, {})] })),
|
|
142
|
+
}),
|
|
143
|
+
]),
|
|
60
144
|
], contentEditableClassName: "canopy-mdx-content" }));
|
|
61
145
|
};
|
|
62
146
|
return { default: WrappedEditor };
|
|
63
147
|
});
|
|
64
148
|
const editorWrapperStyle = {
|
|
65
|
-
background: '
|
|
66
|
-
border: '1px solid var(--
|
|
149
|
+
background: 'var(--mantine-color-body)',
|
|
150
|
+
border: '1px solid var(--canopy-input-border)',
|
|
67
151
|
borderRadius: 'var(--mantine-radius-sm, 4px)',
|
|
68
152
|
overflow: 'hidden',
|
|
69
153
|
};
|
|
70
154
|
/**
|
|
71
155
|
* Mantine's global CSS reset strips styles from semantic HTML elements.
|
|
72
|
-
* Restore them inside the MDXEditor content area so formatting is visible.
|
|
156
|
+
* Restore them inside the MDXEditor content area so formatting is visible. The toolbar wraps
|
|
157
|
+
* rather than scrolling sideways (`toolbar-wrap.ts` hides a separator left at a row's edge).
|
|
158
|
+
* MDXEditor's dark theme leaves its page background white, so editor and popups take tokens; in
|
|
159
|
+
* dark mode its drop shadow vanishes over a subtle panel, so popups take a dropdown's border.
|
|
160
|
+
* Its popups portal into `<body>` at z-index 2, under the editor shell; they take the layer between
|
|
161
|
+
* the shell and Mantine's drawers and modals, so a drawer or modal still covers them.
|
|
73
162
|
*/
|
|
74
163
|
const EditorContentStyles = () => (_jsx("style", { children: `
|
|
75
164
|
.canopy-mdx-content { min-height: 120px; padding: 8px 12px; }
|
|
165
|
+
.mdxeditor .canopy-mdx-toolbar { flex-wrap: wrap; overflow-x: visible; }
|
|
166
|
+
.mdxeditor { --basePageBg: var(--mantine-color-body); }
|
|
167
|
+
.mdxeditor-popup-container { --basePageBg: var(--canopy-surface-subtle); }
|
|
168
|
+
body > .mdxeditor-popup-container { z-index: calc(var(--mantine-z-index-modal) - 1); }
|
|
169
|
+
[data-mantine-color-scheme='dark'] .mdxeditor-popup-container :is([data-radix-popper-content-wrapper] > *, [role='dialog']) {
|
|
170
|
+
border: 1px solid var(--mantine-color-default-border);
|
|
171
|
+
}
|
|
172
|
+
.canopy-mdx-toolbar [data-placeholder] > span:first-child { color: var(--mantine-color-placeholder); }
|
|
76
173
|
.canopy-mdx-content ul { list-style-type: disc; padding-left: 1.5em; margin: 0.5em 0; }
|
|
77
174
|
.canopy-mdx-content ol { list-style-type: decimal; padding-left: 1.5em; margin: 0.5em 0; }
|
|
78
175
|
.canopy-mdx-content ul ul { list-style-type: circle; }
|
|
@@ -85,17 +182,17 @@ const EditorContentStyles = () => (_jsx("style", { children: `
|
|
|
85
182
|
.canopy-mdx-content h5 { font-size: 0.83em; font-weight: 600; margin: 1.67em 0; }
|
|
86
183
|
.canopy-mdx-content h6 { font-size: 0.67em; font-weight: 600; margin: 2.33em 0; }
|
|
87
184
|
.canopy-mdx-content blockquote {
|
|
88
|
-
border-left: 3px solid var(--mantine-color-
|
|
185
|
+
border-left: 3px solid var(--mantine-color-default-border);
|
|
89
186
|
padding-left: 1em;
|
|
90
187
|
margin: 0.5em 0;
|
|
91
|
-
color:
|
|
188
|
+
color: inherit;
|
|
92
189
|
}
|
|
93
|
-
.canopy-mdx-content hr { border: none; border-top: 1px solid var(--mantine-color-
|
|
190
|
+
.canopy-mdx-content hr { border: none; border-top: 1px solid var(--mantine-color-default-border); margin: 1em 0; }
|
|
94
191
|
.canopy-mdx-content p { margin: 0.75em 0; }
|
|
95
|
-
.canopy-mdx-content a { color: var(--mantine-color-
|
|
192
|
+
.canopy-mdx-content a { color: var(--mantine-color-anchor); text-decoration: underline; }
|
|
96
193
|
.canopy-mdx-content img { max-width: 100%; height: auto; }
|
|
97
194
|
.canopy-mdx-content .canopy-mdx-jsx {
|
|
98
|
-
border: 1px dashed var(--mantine-color-
|
|
195
|
+
border: 1px dashed var(--mantine-color-default-border);
|
|
99
196
|
border-radius: 4px;
|
|
100
197
|
padding: 2px 8px;
|
|
101
198
|
margin: 0.5em 0;
|
|
@@ -104,18 +201,50 @@ const EditorContentStyles = () => (_jsx("style", { children: `
|
|
|
104
201
|
.canopy-mdx-content .canopy-mdx-jsx-tag {
|
|
105
202
|
font-family: var(--mantine-font-family-monospace, monospace);
|
|
106
203
|
font-size: 0.8em;
|
|
107
|
-
color: var(--mantine-color-
|
|
204
|
+
color: var(--mantine-color-dimmed);
|
|
108
205
|
}
|
|
109
206
|
` }));
|
|
110
|
-
const FallbackTextarea = ({ value
|
|
111
|
-
|
|
207
|
+
const FallbackTextarea = ({ value }) => (_jsx(Textarea, { value: value, placeholder: "Loading markdown editor...", autosize: true, minRows: 6, size: "sm", readOnly: true }));
|
|
208
|
+
/** The textarea over a markdown value; `failure` is why the rich-text editor could not open it. */
|
|
209
|
+
export const MarkdownSourceEditor = ({ label, value, onChange, failure, readOnly = false }) => (_jsxs(_Fragment, { children: [failure !== undefined && (_jsxs(Alert, { color: "yellow", variant: "light", mb: "xs", "data-testid": "markdown-source-fallback", children: [_jsx(Text, { size: "sm", children: readOnly
|
|
210
|
+
? "This content couldn't be opened in the visual editor, so it is shown as text."
|
|
211
|
+
: "This content couldn't be opened in the visual editor. You can still edit the text and save." }), _jsx(Text, { size: "xs", c: "dimmed", mt: 4, children: failure })] })), _jsx(Textarea, { value: value, readOnly: readOnly, onChange: (e) => {
|
|
212
|
+
if (!readOnly)
|
|
213
|
+
onChange(e.currentTarget.value);
|
|
214
|
+
}, "aria-label": label ? `${label} (source)` : 'Markdown source', "data-testid": "markdown-source-editor", autosize: true, minRows: 10, size: "sm", styles: { input: { fontFamily: 'var(--mantine-font-family-monospace)' } } })] }));
|
|
215
|
+
export const MarkdownField = ({ id, label, description, value, onChange, dataCanopyField, htmlTags, readOnly = false, }) => {
|
|
112
216
|
const generatedId = useId();
|
|
113
217
|
const inputId = id ?? generatedId;
|
|
218
|
+
const commentControl = useFieldCommentControl(dataCanopyField);
|
|
114
219
|
const editorRef = useRef(null);
|
|
115
220
|
const lastExternalValue = useRef(value);
|
|
116
221
|
const lastRejectedSource = useRef(null);
|
|
222
|
+
const pendingInsert = useRef(null);
|
|
117
223
|
const apiClient = useApiClient();
|
|
118
|
-
const [
|
|
224
|
+
const [sourceChosen, setSourceChosen] = useState(false);
|
|
225
|
+
// Text this field's author edited in the fallback, which stays in source with the reason.
|
|
226
|
+
const [editedFallback, setEditedFallback] = useState(null);
|
|
227
|
+
const [, rerender] = useReducer((n) => n + 1, 0);
|
|
228
|
+
const [rejectedInsert, setRejectedInsert] = useState(null);
|
|
229
|
+
const [editorGeneration, setEditorGeneration] = useState(0);
|
|
230
|
+
const rootRef = useRef(null);
|
|
231
|
+
const dark = useComputedColorScheme('light') === 'dark';
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
const root = rootRef.current;
|
|
234
|
+
return root ? watchToolbarWrap(root, '.canopy-mdx-toolbar') : undefined;
|
|
235
|
+
}, []);
|
|
236
|
+
// A read-only content area stays reachable by keyboard, like the read-only inputs beside it.
|
|
237
|
+
// Lexical drops it from the tab order once it is not editable, and mounts it after this runs.
|
|
238
|
+
useEffect(() => {
|
|
239
|
+
const root = rootRef.current;
|
|
240
|
+
if (!readOnly || !root)
|
|
241
|
+
return;
|
|
242
|
+
const focusable = () => root.querySelector('.canopy-mdx-content')?.setAttribute('tabindex', '0');
|
|
243
|
+
focusable();
|
|
244
|
+
const observer = new MutationObserver(focusable);
|
|
245
|
+
observer.observe(root, { childList: true, subtree: true });
|
|
246
|
+
return () => observer.disconnect();
|
|
247
|
+
}, [readOnly]);
|
|
119
248
|
// Drives both MDXEditor's drag/drop/paste upload and the custom image
|
|
120
249
|
// dialog's Upload tab via the same presign/finalize-or-proxied pipeline
|
|
121
250
|
// (media/upload-asset.ts). Memoized on the API client (itself memoized by
|
|
@@ -124,6 +253,9 @@ export const MarkdownField = ({ id, label, description, value, onChange, dataCan
|
|
|
124
253
|
const asset = await uploadAsset(apiClient, file);
|
|
125
254
|
return asset.src;
|
|
126
255
|
}, [apiClient]);
|
|
256
|
+
// MDXEditor displays each body image at whatever this resolves; the markdown keeps its src.
|
|
257
|
+
const { baseUrl: assetBaseUrl } = useAssetContext();
|
|
258
|
+
const imagePreviewHandler = useCallback(async (src) => editorImageSrc(src, assetBaseUrl), [assetBaseUrl]);
|
|
127
259
|
// Sync external value changes into the editor, recording them even while none
|
|
128
260
|
// is mounted, so a later return to an earlier value is still a change.
|
|
129
261
|
useEffect(() => {
|
|
@@ -133,34 +265,79 @@ export const MarkdownField = ({ id, label, description, value, onChange, dataCan
|
|
|
133
265
|
editorRef.current?.setMarkdown(value);
|
|
134
266
|
}, [value]);
|
|
135
267
|
const emitChange = useCallback((newValue) => {
|
|
268
|
+
if (readOnly)
|
|
269
|
+
return;
|
|
136
270
|
lastExternalValue.current = newValue;
|
|
271
|
+
setRejectedInsert(null);
|
|
137
272
|
onChange(newValue);
|
|
138
|
-
}, [onChange]);
|
|
273
|
+
}, [onChange, readOnly]);
|
|
139
274
|
// Dropped: MDXEditor's re-serialization of the document it was mounted with
|
|
140
275
|
// (flagged `initialMarkdownNormalize`; it would mark an unedited entry
|
|
141
|
-
// modified), and inserted markdown it rejected, which it
|
|
142
|
-
// whole document.
|
|
276
|
+
// modified), and inserted markdown it rejected, which it emits as if that
|
|
277
|
+
// snippet were the whole document.
|
|
143
278
|
const handleEditorChange = useCallback((newValue, initialMarkdownNormalize) => {
|
|
144
279
|
if (initialMarkdownNormalize || newValue === lastRejectedSource.current)
|
|
145
280
|
return;
|
|
146
281
|
emitChange(newValue);
|
|
147
282
|
}, [emitChange]);
|
|
283
|
+
// MDXEditor reports a rejected insert synchronously, inside insertMarkdown
|
|
284
|
+
// (with no selection it imports nothing, and reports nothing).
|
|
285
|
+
const handleInsert = useCallback((insert, markdown) => {
|
|
286
|
+
pendingInsert.current = markdown;
|
|
287
|
+
try {
|
|
288
|
+
insert();
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
pendingInsert.current = null;
|
|
292
|
+
}
|
|
293
|
+
}, []);
|
|
294
|
+
// A value the rich-text editor failed on opens as source, whether MDXEditor rejected it or
|
|
295
|
+
// crashed rendering it; rich-text-failures.ts holds the record for the session. Text edited
|
|
296
|
+
// from there stays in source in this field only, so it marks no other field.
|
|
297
|
+
const failure = recallRichTextFailure(value) ??
|
|
298
|
+
(editedFallback?.value === value ? editedFallback.reason : undefined);
|
|
299
|
+
const showSource = sourceChosen || failure !== undefined;
|
|
148
300
|
// MDXEditor can report the error while rendering (it imports as it is
|
|
149
|
-
// created), hence the microtask.
|
|
150
|
-
// `value`: MDXEditor is created from it, and gets
|
|
151
|
-
// the sync effect hands it a later value.
|
|
301
|
+
// created), hence the microtask. Any rejection outside an insert is the
|
|
302
|
+
// document, this render's `value`: MDXEditor is created from it, and gets
|
|
303
|
+
// this handler again before the sync effect hands it a later value. `source`
|
|
304
|
+
// can't identify it, because MDXEditor trims the document it mounts with.
|
|
305
|
+
// Either way MDXEditor emits no further edits until an import succeeds, so a
|
|
306
|
+
// rejected insert remounts it from the unchanged document.
|
|
152
307
|
const handleEditorError = useCallback(({ error, source }) => {
|
|
153
308
|
lastRejectedSource.current = source;
|
|
154
|
-
|
|
309
|
+
const isInsert = pendingInsert.current !== null && source === pendingInsert.current;
|
|
310
|
+
queueMicrotask(() => {
|
|
311
|
+
if (isInsert) {
|
|
312
|
+
setRejectedInsert(error);
|
|
313
|
+
setEditorGeneration((n) => n + 1);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
rememberRichTextFailure(value, error);
|
|
317
|
+
rerender();
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
}, [value]);
|
|
321
|
+
const handleEditorCrash = useCallback(({ error }) => {
|
|
322
|
+
rememberRichTextFailure(value, sanitizeErrorMessage(getErrorMessage(error)));
|
|
323
|
+
rerender();
|
|
155
324
|
}, [value]);
|
|
156
325
|
// Edits in the fallback stay in it: the edited text is no more likely to load.
|
|
157
|
-
const handleSourceChange =
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
: current);
|
|
326
|
+
const handleSourceChange = (newValue) => {
|
|
327
|
+
if (failure !== undefined)
|
|
328
|
+
setEditedFallback({ value: newValue, reason: failure });
|
|
161
329
|
emitChange(newValue);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
330
|
+
};
|
|
331
|
+
const toggleMode = () => {
|
|
332
|
+
if (!showSource) {
|
|
333
|
+
setSourceChosen(true);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
// Asking for rich text tries the editor again on a value it failed on.
|
|
337
|
+
forgetRichTextFailure(value);
|
|
338
|
+
setEditedFallback(null);
|
|
339
|
+
setSourceChosen(false);
|
|
340
|
+
rerender();
|
|
341
|
+
};
|
|
342
|
+
return (_jsxs("div", { ref: rootRef, id: inputId, "data-canopy-field": dataCanopyField, className: "canopy-markdown-field", ...groupDescriptionProps(inputId, description), children: [_jsx(Box, { mb: 4, children: _jsx(FieldLabel, { label: label, commentControl: commentControl, actions: !readOnly && (_jsx(Button, { variant: "subtle", size: "compact-xs", "data-testid": "markdown-mode-toggle", onClick: toggleMode, children: showSource ? 'Rich text' : 'Edit source' })) }) }), _jsx(FieldDescription, { baseId: inputId, description: description }), _jsx(EditorContentStyles, {}), showSource ? (_jsx(MarkdownSourceEditor, { label: label, value: value, onChange: handleSourceChange, failure: failure, readOnly: readOnly })) : (_jsxs("div", { style: editorWrapperStyle, children: [rejectedInsert !== null && (_jsxs(Alert, { color: "yellow", variant: "light", mb: "xs", withCloseButton: true, onClose: () => setRejectedInsert(null), "data-testid": "markdown-insert-rejected", children: [_jsx(Text, { size: "sm", children: "The rich-text editor can't show what was inserted, so nothing was added." }), _jsx(Text, { size: "xs", c: "dimmed", mt: 4, children: rejectedInsert })] })), _jsx(EditorErrorBoundary, { context: { boundary: 'rich-text', fieldPath: dataCanopyField }, fallback: () => null, onCaught: handleEditorCrash, children: _jsx(Suspense, { fallback: _jsx(FallbackTextarea, { value: value }), children: _jsx(MDXEditorLazy, { markdown: value, onChange: handleEditorChange, onError: handleEditorError, onInsert: handleInsert, editorRef: editorRef, imageUploadHandler: imageUploadHandler, imagePreviewHandler: imagePreviewHandler, htmlTags: htmlTags, readOnly: readOnly, contentLabel: label, dark: dark }, `${editorGeneration}-${readOnly}`) }) })] }))] }));
|
|
166
343
|
};
|
|
@@ -67,5 +67,5 @@ export const MdxImageDialog = ({ state, onSave, onClose }) => {
|
|
|
67
67
|
if (next)
|
|
68
68
|
setTab(next);
|
|
69
69
|
}, children: [_jsxs(Tabs.List, { children: [_jsx(Tabs.Tab, { value: "upload", "data-testid": "mdx-image-dialog-tab-upload", children: "Upload" }), _jsx(Tabs.Tab, { value: "library", "data-testid": "mdx-image-dialog-tab-library", children: "From library" }), _jsx(Tabs.Tab, { value: "url", "data-testid": "mdx-image-dialog-tab-url", children: "By URL" })] }), _jsx(Tabs.Panel, { value: "upload", pt: "sm", children: _jsxs(Stack, { gap: "xs", children: [_jsx(Dropzone, { onDrop: (files) => void handleDrop(files), onReject: handleReject, maxSize: MAX_UPLOAD_BYTES, accept: ACCEPTED_IMAGE_MIME_TYPES, loading: upload.uploading, multiple: false, "data-testid": "mdx-image-dialog-dropzone", children: _jsxs(Group, { justify: "center", gap: "xs", py: "sm", style: { pointerEvents: 'none' }, children: [_jsx(IconUpload, { size: 20 }), _jsx(Text, { size: "sm", children: "Drop an image here, or click to browse" })] }) }), upload.uploading &&
|
|
70
|
-
(upload.progress !== null ? (_jsx(Progress, { value: upload.progress * 100, size: "sm" })) : (_jsx(Progress, { value: 100, size: "sm", animated: true }))), (upload.error || dropError) && (_jsx(Alert, { icon: _jsx(IconAlertCircle, { size: 16 }), color: "red", children: upload.error || dropError }))] }) }), _jsx(Tabs.Panel, { value: "library", pt: "sm", children: _jsx("div", { style: { height: LIBRARY_PANEL_HEIGHT }, children: _jsx(MediaLibraryBody, { opened: tab === 'library', mode: "picker", onSelect: handlePickFromLibrary }) }) }), _jsx(Tabs.Panel, { value: "url", pt: "sm", children: _jsxs(Stack, { gap: "xs", children: [_jsx(TextInput, { label: "Image URL", placeholder: "https://example.com/image.png", value: urlValue, onChange: (event) => setUrlValue(event.currentTarget.value), "data-testid": "mdx-image-dialog-url" }), _jsx(Group, { justify: "flex-end", children: _jsx(Button, { onClick: handleUrlSubmit, disabled: !urlValue.trim(), "data-testid": "mdx-image-dialog-url-submit", children: "Insert" }) })] }) })] }), _jsx(Group, { justify: "flex-end", children: _jsx(Button, { variant: "default", onClick: onClose, children: "Cancel" }) })] }) }));
|
|
70
|
+
(upload.progress !== null ? (_jsx(Progress, { value: upload.progress * 100, size: "sm" })) : (_jsx(Progress, { value: 100, size: "sm", animated: true }))), (upload.error || dropError) && (_jsx(Alert, { icon: _jsx(IconAlertCircle, { size: 16 }), color: "red", children: upload.error || dropError }))] }) }), _jsx(Tabs.Panel, { value: "library", pt: "sm", children: _jsx("div", { style: { height: LIBRARY_PANEL_HEIGHT }, children: _jsx(MediaLibraryBody, { opened: tab === 'library', mode: "picker", onSelect: handlePickFromLibrary }) }) }), _jsx(Tabs.Panel, { value: "url", pt: "sm", children: _jsxs(Stack, { gap: "xs", children: [_jsx(TextInput, { label: "Image URL", placeholder: "https://example.com/image.png", value: urlValue, onChange: (event) => setUrlValue(event.currentTarget.value), "data-testid": "mdx-image-dialog-url" }), _jsx(Group, { justify: "flex-end", children: _jsx(Button, { size: "sm", onClick: handleUrlSubmit, disabled: !urlValue.trim(), "data-testid": "mdx-image-dialog-url-submit", children: "Insert" }) })] }) })] }), _jsx(Group, { justify: "flex-end", children: _jsx(Button, { size: "sm", variant: "default", onClick: onClose, children: "Cancel" }) })] }) }));
|
|
71
71
|
};
|
|
@@ -6,6 +6,8 @@ export interface NumberFieldProps {
|
|
|
6
6
|
value: number | undefined;
|
|
7
7
|
onChange: (value: number | undefined) => void;
|
|
8
8
|
dataCanopyField?: string;
|
|
9
|
+
/** Shows the value without accepting edits. */
|
|
10
|
+
readOnly?: boolean;
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* Editor for `type: 'number'` fields.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useId, useState } from 'react';
|
|
3
3
|
import { NumberInput } from '@mantine/core';
|
|
4
|
+
import { LabelledInput } from './FieldLabel.js';
|
|
4
5
|
/**
|
|
5
6
|
* The numeric meaning of whatever Mantine last reported, or `undefined` for
|
|
6
7
|
* "not filled in" / not yet a number (`''`, a lone `'-'`).
|
|
@@ -27,15 +28,17 @@ const toNumericValue = (raw) => {
|
|
|
27
28
|
* would silently store `0.5` after the second keystroke. See
|
|
28
29
|
* NumberField.test.tsx's keystroke matrix.
|
|
29
30
|
*/
|
|
30
|
-
export const NumberField = ({ id, label, description, value, onChange, dataCanopyField, }) => {
|
|
31
|
+
export const NumberField = ({ id, label, description, value, onChange, dataCanopyField, readOnly = false, }) => {
|
|
31
32
|
const generatedId = useId();
|
|
32
33
|
const inputId = id ?? generatedId;
|
|
33
34
|
const [inputValue, setInputValue] = useState(value ?? '');
|
|
34
35
|
// Shows the in-progress text only while it still matches the parent's
|
|
35
36
|
// value; once the parent's value diverges (load, discard, reload), that value wins.
|
|
36
37
|
const displayValue = toNumericValue(inputValue) === value ? inputValue : (value ?? '');
|
|
37
|
-
return (_jsx(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
return (_jsx(LabelledInput, { label: label, inputId: inputId, canopyPath: dataCanopyField, children: _jsx(NumberInput, { id: inputId, description: description, value: displayValue, size: "sm", readOnly: readOnly, onChange: (next) => {
|
|
39
|
+
if (readOnly)
|
|
40
|
+
return;
|
|
41
|
+
setInputValue(next);
|
|
42
|
+
onChange(toNumericValue(next));
|
|
43
|
+
}, "data-canopy-field": dataCanopyField }) }));
|
|
41
44
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useId, useState } from 'react';
|
|
3
3
|
import { TagsInput } from '@mantine/core';
|
|
4
|
+
import { LabelledInput } from './FieldLabel.js';
|
|
4
5
|
/**
|
|
5
6
|
* A tag is a number only if it is non-blank AND parses finitely.
|
|
6
7
|
*
|
|
@@ -27,23 +28,25 @@ const parseTag = (tag) => {
|
|
|
27
28
|
* on Enter indistinguishably from being accepted, with nothing to tell a
|
|
28
29
|
* typo from a broken field — so rejected text is named in the error slot.
|
|
29
30
|
*/
|
|
30
|
-
export const NumberListField = ({ id, label, description, value, onChange, dataCanopyField, }) => {
|
|
31
|
+
export const NumberListField = ({ id, label, description, value, onChange, dataCanopyField, readOnly = false, }) => {
|
|
31
32
|
const generatedId = useId();
|
|
32
33
|
const inputId = id ?? generatedId;
|
|
33
34
|
const [rejected, setRejected] = useState([]);
|
|
34
|
-
return (_jsx(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
return (_jsx(LabelledInput, { label: label, inputId: inputId, canopyPath: dataCanopyField, children: _jsx(TagsInput, { id: inputId, description: description, value: value.map(String), size: "sm", error: rejected.length > 0
|
|
36
|
+
? `Not ${rejected.length === 1 ? 'a number' : 'numbers'}: ${rejected.join(', ')}`
|
|
37
|
+
: undefined, readOnly: readOnly, onChange: (next) => {
|
|
38
|
+
if (readOnly)
|
|
39
|
+
return;
|
|
40
|
+
const parsed = [];
|
|
41
|
+
const dropped = [];
|
|
42
|
+
for (const tag of next) {
|
|
43
|
+
const num = parseTag(tag);
|
|
44
|
+
if (num === undefined)
|
|
45
|
+
dropped.push(tag);
|
|
46
|
+
else
|
|
47
|
+
parsed.push(num);
|
|
48
|
+
}
|
|
49
|
+
setRejected(dropped);
|
|
50
|
+
onChange(parsed);
|
|
51
|
+
}, splitChars: [], allowDuplicates: true, "data-canopy-field": dataCanopyField }) }));
|
|
49
52
|
};
|
|
@@ -3,6 +3,7 @@ import type { FieldConfig } from '../../config/index.js';
|
|
|
3
3
|
export type RenderField = (field: FieldConfig, value: unknown, onChange: (v: unknown) => void, path: Array<string | number>) => React.ReactNode;
|
|
4
4
|
export interface ObjectFieldProps {
|
|
5
5
|
label?: string;
|
|
6
|
+
required?: boolean;
|
|
6
7
|
description?: string;
|
|
7
8
|
fields: FieldConfig[];
|
|
8
9
|
value: Record<string, unknown> | undefined;
|
|
@@ -10,13 +11,7 @@ export interface ObjectFieldProps {
|
|
|
10
11
|
renderField: RenderField;
|
|
11
12
|
path: Array<string | number>;
|
|
12
13
|
dataCanopyField?: string;
|
|
13
|
-
/**
|
|
14
|
-
* When set, shows a "Clear" affordance that resets this field to unset
|
|
15
|
-
* (`undefined`) — used by `FormRenderer.tsx`'s non-list `case 'object'` so
|
|
16
|
-
* a required child can't strand the field present-but-invalid with no way
|
|
17
|
-
* back to "not filled in". Omitted for object-list items, which remove via
|
|
18
|
-
* the list's own per-item button.
|
|
19
|
-
*/
|
|
14
|
+
/** Set, shows a gray "Clear" resetting the field to `undefined`; omitted for list items. */
|
|
20
15
|
onRemove?: () => void;
|
|
21
16
|
}
|
|
22
17
|
export declare const ObjectField: React.FC<ObjectFieldProps>;
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useId } from 'react';
|
|
3
|
-
import { Button,
|
|
3
|
+
import { Button, Paper, Stack } from '@mantine/core';
|
|
4
|
+
import { isPlainRecord } from '../../validation/field-traversal.js';
|
|
4
5
|
import { formatCanopyPath } from '../canopy-path.js';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
import { useFieldCommentControl } from '../comments/field-comment-context.js';
|
|
7
|
+
import { EDITOR_ACTIONS } from '../copy.js';
|
|
8
|
+
import { groupDescriptionProps } from './FieldDescription.js';
|
|
9
|
+
import { FieldLabel } from './FieldLabel.js';
|
|
10
|
+
export const ObjectField = ({ label, required, description, fields, value, onChange, renderField, path, dataCanopyField, onRemove, }) => {
|
|
11
|
+
const current = isPlainRecord(value) ? value : {};
|
|
8
12
|
const descriptionBaseId = useId();
|
|
9
|
-
|
|
13
|
+
const commentControl = useFieldCommentControl(dataCanopyField ?? formatCanopyPath(path));
|
|
14
|
+
return (_jsx(Paper, { withBorder: true, p: "md", bg: "var(--canopy-surface-subtle)", "data-canopy-field": dataCanopyField ?? formatCanopyPath(path), shadow: "xs", ...groupDescriptionProps(descriptionBaseId, description), role: "group", "aria-label": label, "data-removal-focus": true, tabIndex: -1, children: _jsxs(Stack, { gap: "sm", children: [_jsx(FieldLabel, { label: label, required: required, description: description, descriptionBaseId: descriptionBaseId, commentControl: commentControl, actions: onRemove && (_jsx(Button, { variant: "subtle", color: "gray", "aria-label": label ? `${EDITOR_ACTIONS.clear} "${label}"` : undefined, onClick: (event) => {
|
|
15
|
+
// The button goes with the value, so focus moves to the object, not the page.
|
|
16
|
+
const field = event.currentTarget.closest('[data-removal-focus]');
|
|
17
|
+
onRemove();
|
|
18
|
+
field?.focus();
|
|
19
|
+
}, children: EDITOR_ACTIONS.clear })) }), _jsx(Stack, { gap: "sm", children: fields.map((field) => {
|
|
10
20
|
const fieldPath = [...path, field.name];
|
|
11
21
|
return (_jsx("div", { children: renderField(field, current[field.name], (next) => onChange({ ...current, [field.name]: next }), fieldPath) }, formatCanopyPath(fieldPath)));
|
|
12
22
|
}) })] }) }));
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ObjectFieldConfig } from '../../config/index.js';
|
|
3
|
+
import type { RenderField } from './ObjectField.js';
|
|
4
|
+
export interface ObjectListFieldProps {
|
|
5
|
+
/** Base for the description's id. */
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
field: ObjectFieldConfig;
|
|
9
|
+
items: Record<string, unknown>[];
|
|
10
|
+
/** One stable React key per item, from `useListItemKeys`. */
|
|
11
|
+
itemKeys: string[];
|
|
12
|
+
onChange: (next: Record<string, unknown>[]) => void;
|
|
13
|
+
renderField: RenderField;
|
|
14
|
+
path: Array<string | number>;
|
|
15
|
+
/** The list's canopy path, which picks its comment control. */
|
|
16
|
+
canopyPath: string;
|
|
17
|
+
readOnly: boolean;
|
|
18
|
+
/** Names the entry being edited; another entry starts with the default rows open. */
|
|
19
|
+
resetKey: string;
|
|
20
|
+
/** True when a field inside item `index` has a validation error. */
|
|
21
|
+
hasErrors?: (index: number) => boolean;
|
|
22
|
+
/** Called after Remove takes item `index` out, so the caller can offer Undo. */
|
|
23
|
+
onRemoved?: (index: number, item: Record<string, unknown>) => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A `list: true` object field: one collapsible row per item, titled from the item's content,
|
|
27
|
+
* holding the item's fields directly rather than in a second card.
|
|
28
|
+
*/
|
|
29
|
+
export declare const ObjectListField: React.FC<ObjectListFieldProps>;
|