canopycms 0.0.0 → 0.0.1
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/package.json +2 -3
- package/dist/__integration__/fixtures/content-seeds.d.ts +0 -43
- package/dist/__integration__/fixtures/content-seeds.d.ts.map +0 -1
- package/dist/__integration__/fixtures/content-seeds.js +0 -99
- package/dist/__integration__/fixtures/content-seeds.js.map +0 -1
- package/dist/__integration__/fixtures/schemas.d.ts +0 -12
- package/dist/__integration__/fixtures/schemas.d.ts.map +0 -1
- package/dist/__integration__/fixtures/schemas.js +0 -65
- package/dist/__integration__/fixtures/schemas.js.map +0 -1
- package/dist/__integration__/test-utils/api-client.d.ts +0 -123
- package/dist/__integration__/test-utils/api-client.d.ts.map +0 -1
- package/dist/__integration__/test-utils/api-client.js +0 -118
- package/dist/__integration__/test-utils/api-client.js.map +0 -1
- package/dist/__integration__/test-utils/multi-user.d.ts +0 -25
- package/dist/__integration__/test-utils/multi-user.d.ts.map +0 -1
- package/dist/__integration__/test-utils/multi-user.js +0 -105
- package/dist/__integration__/test-utils/multi-user.js.map +0 -1
- package/dist/__integration__/test-utils/test-workspace.d.ts +0 -25
- package/dist/__integration__/test-utils/test-workspace.d.ts.map +0 -1
- package/dist/__integration__/test-utils/test-workspace.js +0 -102
- package/dist/__integration__/test-utils/test-workspace.js.map +0 -1
- package/dist/editor/BranchManager.stories.d.ts +0 -8
- package/dist/editor/BranchManager.stories.d.ts.map +0 -1
- package/dist/editor/BranchManager.stories.js +0 -74
- package/dist/editor/BranchManager.stories.js.map +0 -1
- package/dist/editor/CanopyEditor.stories.d.ts +0 -7
- package/dist/editor/CanopyEditor.stories.d.ts.map +0 -1
- package/dist/editor/CanopyEditor.stories.js +0 -99
- package/dist/editor/CanopyEditor.stories.js.map +0 -1
- package/dist/editor/CommentsPanel.stories.d.ts +0 -10
- package/dist/editor/CommentsPanel.stories.d.ts.map +0 -1
- package/dist/editor/CommentsPanel.stories.js +0 -175
- package/dist/editor/CommentsPanel.stories.js.map +0 -1
- package/dist/editor/Editor.stories.d.ts +0 -7
- package/dist/editor/Editor.stories.d.ts.map +0 -1
- package/dist/editor/Editor.stories.js +0 -95
- package/dist/editor/Editor.stories.js.map +0 -1
- package/dist/editor/EditorPanes.stories.d.ts +0 -7
- package/dist/editor/EditorPanes.stories.d.ts.map +0 -1
- package/dist/editor/EditorPanes.stories.js +0 -116
- package/dist/editor/EditorPanes.stories.js.map +0 -1
- package/dist/editor/EntryNavigator.stories.d.ts +0 -8
- package/dist/editor/EntryNavigator.stories.d.ts.map +0 -1
- package/dist/editor/EntryNavigator.stories.js +0 -42
- package/dist/editor/EntryNavigator.stories.js.map +0 -1
- package/dist/editor/FormRenderer.stories.d.ts +0 -7
- package/dist/editor/FormRenderer.stories.d.ts.map +0 -1
- package/dist/editor/FormRenderer.stories.js +0 -115
- package/dist/editor/FormRenderer.stories.js.map +0 -1
- package/dist/editor/GroupManager.stories.d.ts +0 -19
- package/dist/editor/GroupManager.stories.d.ts.map +0 -1
- package/dist/editor/GroupManager.stories.js +0 -265
- package/dist/editor/GroupManager.stories.js.map +0 -1
- package/dist/editor/PermissionManager.stories.d.ts +0 -20
- package/dist/editor/PermissionManager.stories.d.ts.map +0 -1
- package/dist/editor/PermissionManager.stories.js +0 -506
- package/dist/editor/PermissionManager.stories.js.map +0 -1
- package/dist/editor/comments/FieldWrapper.stories.d.ts +0 -10
- package/dist/editor/comments/FieldWrapper.stories.d.ts.map +0 -1
- package/dist/editor/comments/FieldWrapper.stories.js +0 -173
- package/dist/editor/comments/FieldWrapper.stories.js.map +0 -1
- package/dist/editor/fields/BlockField.stories.d.ts +0 -7
- package/dist/editor/fields/BlockField.stories.d.ts.map +0 -1
- package/dist/editor/fields/BlockField.stories.js +0 -50
- package/dist/editor/fields/BlockField.stories.js.map +0 -1
- package/dist/editor/fields/fields.stories.d.ts +0 -8
- package/dist/editor/fields/fields.stories.d.ts.map +0 -1
- package/dist/editor/fields/fields.stories.js +0 -34
- package/dist/editor/fields/fields.stories.js.map +0 -1
- package/dist/test-utils/api-test-helpers.d.ts +0 -238
- package/dist/test-utils/api-test-helpers.d.ts.map +0 -1
- package/dist/test-utils/api-test-helpers.js +0 -347
- package/dist/test-utils/api-test-helpers.js.map +0 -1
- package/dist/test-utils/console-spy.d.ts +0 -56
- package/dist/test-utils/console-spy.d.ts.map +0 -1
- package/dist/test-utils/console-spy.js +0 -81
- package/dist/test-utils/console-spy.js.map +0 -1
- package/dist/test-utils/git-helpers.d.ts +0 -21
- package/dist/test-utils/git-helpers.d.ts.map +0 -1
- package/dist/test-utils/git-helpers.js +0 -23
- package/dist/test-utils/git-helpers.js.map +0 -1
- package/dist/test-utils/index.d.ts +0 -5
- package/dist/test-utils/index.d.ts.map +0 -1
- package/dist/test-utils/index.js +0 -4
- package/dist/test-utils/index.js.map +0 -1
- package/src/__integration__/errors/invalid-content.test.ts +0 -238
- package/src/__integration__/errors/permission-denied.test.ts +0 -220
- package/src/__integration__/fixtures/content-seeds.ts +0 -105
- package/src/__integration__/fixtures/schemas.ts +0 -67
- package/src/__integration__/initialization/prod-sim-init.test.ts +0 -139
- package/src/__integration__/permissions/path-permissions.test.ts +0 -314
- package/src/__integration__/permissions/role-permissions.test.ts +0 -354
- package/src/__integration__/permissions/settings-branch-isolation.test.ts +0 -317
- package/src/__integration__/settings/groups-api.test.ts +0 -403
- package/src/__integration__/test-utils/api-client.ts +0 -167
- package/src/__integration__/test-utils/multi-user.ts +0 -129
- package/src/__integration__/test-utils/test-workspace.ts +0 -130
- package/src/__integration__/user/user-context.test.ts +0 -174
- package/src/__integration__/validation/input-validation.test.ts +0 -166
- package/src/__integration__/workflows/api-editing-workflow.test.ts +0 -244
- package/src/__integration__/workflows/conflict-resolution.test.ts +0 -259
- package/src/__integration__/workflows/editing-workflow.test.ts +0 -205
- package/src/__integration__/workflows/review-workflow.test.ts +0 -260
- package/src/ai/__tests__/build.integration.test.ts +0 -224
- package/src/ai/__tests__/generate.integration.test.ts +0 -495
- package/src/ai/__tests__/handler.integration.test.ts +0 -212
- package/src/ai/__tests__/json-to-markdown.test.ts +0 -553
- package/src/ai/generate.ts +0 -410
- package/src/ai/handler.ts +0 -123
- package/src/ai/index.ts +0 -26
- package/src/ai/json-to-markdown.ts +0 -424
- package/src/ai/resolve-branch.ts +0 -34
- package/src/ai/types.ts +0 -160
- package/src/api/AGENTS.md +0 -81
- package/src/api/__test__/mock-client.ts +0 -404
- package/src/api/assets.test.ts +0 -140
- package/src/api/assets.ts +0 -154
- package/src/api/branch-merge.test.ts +0 -163
- package/src/api/branch-merge.ts +0 -113
- package/src/api/branch-review.test.ts +0 -297
- package/src/api/branch-review.ts +0 -136
- package/src/api/branch-status.test.ts +0 -85
- package/src/api/branch-status.ts +0 -153
- package/src/api/branch-withdraw.test.ts +0 -146
- package/src/api/branch-withdraw.ts +0 -81
- package/src/api/branch-workflow.integration.test.ts +0 -578
- package/src/api/branch.test.ts +0 -620
- package/src/api/branch.ts +0 -492
- package/src/api/client.test.ts +0 -349
- package/src/api/client.ts +0 -506
- package/src/api/comments.test.ts +0 -285
- package/src/api/comments.ts +0 -210
- package/src/api/content.test.ts +0 -345
- package/src/api/content.ts +0 -454
- package/src/api/entries.test.ts +0 -1339
- package/src/api/entries.ts +0 -650
- package/src/api/github-sync.ts +0 -144
- package/src/api/groups.test.ts +0 -1013
- package/src/api/groups.ts +0 -375
- package/src/api/guards.test.ts +0 -533
- package/src/api/guards.ts +0 -271
- package/src/api/index.ts +0 -87
- package/src/api/permissions.test.ts +0 -766
- package/src/api/permissions.ts +0 -334
- package/src/api/reference-options.ts +0 -118
- package/src/api/resolve-references.ts +0 -107
- package/src/api/route-builder.ts +0 -289
- package/src/api/schema.test.ts +0 -840
- package/src/api/schema.ts +0 -936
- package/src/api/security.test.ts +0 -233
- package/src/api/settings-helpers.ts +0 -84
- package/src/api/types.ts +0 -40
- package/src/api/user.test.ts +0 -127
- package/src/api/user.ts +0 -42
- package/src/api/validators.test.ts +0 -275
- package/src/api/validators.ts +0 -176
- package/src/asset-store.test.ts +0 -37
- package/src/asset-store.ts +0 -110
- package/src/auth/cache.ts +0 -7
- package/src/auth/caching-auth-plugin.test.ts +0 -154
- package/src/auth/caching-auth-plugin.ts +0 -109
- package/src/auth/context-helpers.ts +0 -75
- package/src/auth/file-based-auth-cache.test.ts +0 -257
- package/src/auth/file-based-auth-cache.ts +0 -279
- package/src/auth/index.ts +0 -12
- package/src/auth/plugin.ts +0 -51
- package/src/auth/types.ts +0 -38
- package/src/authorization/__tests__/branch.test.ts +0 -260
- package/src/authorization/__tests__/content.test.ts +0 -142
- package/src/authorization/__tests__/path.test.ts +0 -133
- package/src/authorization/__tests__/permissions-loader.test.ts +0 -200
- package/src/authorization/branch.ts +0 -94
- package/src/authorization/content.ts +0 -93
- package/src/authorization/groups/index.ts +0 -11
- package/src/authorization/groups/loader.ts +0 -127
- package/src/authorization/groups/schema.ts +0 -48
- package/src/authorization/helpers.ts +0 -48
- package/src/authorization/index.ts +0 -84
- package/src/authorization/path.ts +0 -112
- package/src/authorization/permissions/index.ts +0 -11
- package/src/authorization/permissions/loader.ts +0 -116
- package/src/authorization/permissions/schema.ts +0 -66
- package/src/authorization/test-utils.ts +0 -15
- package/src/authorization/types.ts +0 -66
- package/src/authorization/validation.test.ts +0 -100
- package/src/authorization/validation.ts +0 -62
- package/src/branch-metadata.test.ts +0 -168
- package/src/branch-metadata.ts +0 -166
- package/src/branch-registry.test.ts +0 -248
- package/src/branch-registry.ts +0 -152
- package/src/branch-schema-cache.test.ts +0 -275
- package/src/branch-schema-cache.ts +0 -189
- package/src/branch-workspace.test.ts +0 -183
- package/src/branch-workspace.ts +0 -124
- package/src/build/generate-ai-content.ts +0 -78
- package/src/build/index.ts +0 -8
- package/src/build-mode.ts +0 -27
- package/src/cli/generate-ai-content.ts +0 -100
- package/src/cli/init.test.ts +0 -240
- package/src/cli/templates/Dockerfile.cms.template +0 -19
- package/src/cli/templates/canopy.ts.template +0 -55
- package/src/cli/templates/canopycms.config.ts.template +0 -11
- package/src/cli/templates/deploy-cms.yml.template +0 -27
- package/src/cli/templates/edit-page.tsx.template +0 -32
- package/src/cli/templates/route.ts.template +0 -12
- package/src/cli/templates/schemas.ts.template +0 -16
- package/src/cli/templates.ts +0 -47
- package/src/client.ts +0 -12
- package/src/comment-store.test.ts +0 -442
- package/src/comment-store.ts +0 -301
- package/src/config/__tests__/config.test.ts +0 -513
- package/src/config/flatten.ts +0 -174
- package/src/config/helpers.ts +0 -167
- package/src/config/index.ts +0 -86
- package/src/config/schemas/collection.ts +0 -67
- package/src/config/schemas/config.ts +0 -77
- package/src/config/schemas/field.ts +0 -108
- package/src/config/schemas/media.ts +0 -27
- package/src/config/schemas/permissions.ts +0 -21
- package/src/config/types.ts +0 -321
- package/src/config/validation.ts +0 -70
- package/src/config-test.ts +0 -65
- package/src/config.ts +0 -11
- package/src/content-id-index.test.ts +0 -512
- package/src/content-id-index.ts +0 -479
- package/src/content-reader.test.ts +0 -478
- package/src/content-reader.ts +0 -214
- package/src/content-store.test.ts +0 -1126
- package/src/content-store.ts +0 -793
- package/src/context.ts +0 -111
- package/src/editor/BranchManager.stories.tsx +0 -80
- package/src/editor/BranchManager.test.tsx +0 -324
- package/src/editor/BranchManager.tsx +0 -461
- package/src/editor/CanopyEditor.stories.tsx +0 -128
- package/src/editor/CanopyEditor.test.tsx +0 -81
- package/src/editor/CanopyEditor.tsx +0 -73
- package/src/editor/CanopyEditorPage.test.tsx +0 -59
- package/src/editor/CanopyEditorPage.tsx +0 -25
- package/src/editor/CommentsPanel.stories.tsx +0 -184
- package/src/editor/CommentsPanel.tsx +0 -338
- package/src/editor/Editor.integration.test.tsx +0 -227
- package/src/editor/Editor.stories.tsx +0 -119
- package/src/editor/Editor.tsx +0 -1221
- package/src/editor/EditorPanes.stories.tsx +0 -256
- package/src/editor/EditorPanes.test.tsx +0 -77
- package/src/editor/EditorPanes.tsx +0 -180
- package/src/editor/EntryNavigator.stories.tsx +0 -65
- package/src/editor/EntryNavigator.test.tsx +0 -598
- package/src/editor/EntryNavigator.tsx +0 -665
- package/src/editor/FormRenderer.stories.tsx +0 -212
- package/src/editor/FormRenderer.test.tsx +0 -194
- package/src/editor/FormRenderer.tsx +0 -432
- package/src/editor/GroupManager.stories.tsx +0 -301
- package/src/editor/GroupManager.test.tsx +0 -682
- package/src/editor/GroupManager.tsx +0 -9
- package/src/editor/PermissionManager.stories.tsx +0 -539
- package/src/editor/PermissionManager.test.tsx +0 -864
- package/src/editor/PermissionManager.tsx +0 -12
- package/src/editor/canopy-path.test.ts +0 -23
- package/src/editor/canopy-path.ts +0 -52
- package/src/editor/client-reference-resolver.ts +0 -118
- package/src/editor/comments/BranchComments.tsx +0 -93
- package/src/editor/comments/EntryComments.tsx +0 -94
- package/src/editor/comments/FieldWrapper.stories.tsx +0 -210
- package/src/editor/comments/FieldWrapper.tsx +0 -129
- package/src/editor/comments/InlineCommentThread.test.tsx +0 -384
- package/src/editor/comments/InlineCommentThread.tsx +0 -246
- package/src/editor/comments/ThreadCarousel.test.tsx +0 -393
- package/src/editor/comments/ThreadCarousel.tsx +0 -525
- package/src/editor/components/ConfirmDeleteModal.tsx +0 -49
- package/src/editor/components/EditorContext.tsx +0 -49
- package/src/editor/components/EditorFooter.tsx +0 -47
- package/src/editor/components/EditorHeader.tsx +0 -492
- package/src/editor/components/EditorSidebar.tsx +0 -193
- package/src/editor/components/EntryCreateModal.tsx +0 -193
- package/src/editor/components/RenameEntryModal.tsx +0 -152
- package/src/editor/components/UserBadge.test.tsx +0 -274
- package/src/editor/components/UserBadge.tsx +0 -240
- package/src/editor/components/index.ts +0 -6
- package/src/editor/context/ApiClientContext.tsx +0 -56
- package/src/editor/context/EditorStateContext.tsx +0 -221
- package/src/editor/context/index.ts +0 -40
- package/src/editor/editor-config.test.ts +0 -385
- package/src/editor/editor-config.ts +0 -94
- package/src/editor/editor-utils.test.ts +0 -772
- package/src/editor/editor-utils.ts +0 -303
- package/src/editor/env.ts +0 -4
- package/src/editor/fields/BlockField.stories.tsx +0 -79
- package/src/editor/fields/BlockField.tsx +0 -267
- package/src/editor/fields/CodeField.tsx +0 -41
- package/src/editor/fields/MarkdownField.tsx +0 -205
- package/src/editor/fields/ObjectField.tsx +0 -71
- package/src/editor/fields/ReferenceField.tsx +0 -138
- package/src/editor/fields/SelectField.tsx +0 -76
- package/src/editor/fields/TextField.tsx +0 -35
- package/src/editor/fields/ToggleField.tsx +0 -37
- package/src/editor/fields/fields.stories.tsx +0 -40
- package/src/editor/group-manager/ExternalGroupsTab.tsx +0 -114
- package/src/editor/group-manager/GroupCard.tsx +0 -102
- package/src/editor/group-manager/GroupForm.tsx +0 -66
- package/src/editor/group-manager/InternalGroupsTab.tsx +0 -147
- package/src/editor/group-manager/MemberList.tsx +0 -184
- package/src/editor/group-manager/hooks/useExternalGroupSearch.ts +0 -63
- package/src/editor/group-manager/hooks/useGroupState.ts +0 -134
- package/src/editor/group-manager/hooks/useUserSearch.ts +0 -84
- package/src/editor/group-manager/index.tsx +0 -210
- package/src/editor/group-manager/types.ts +0 -28
- package/src/editor/hooks/README.md +0 -26
- package/src/editor/hooks/__test__/test-utils.tsx +0 -183
- package/src/editor/hooks/index.ts +0 -23
- package/src/editor/hooks/useBranchActions.test.tsx +0 -267
- package/src/editor/hooks/useBranchActions.tsx +0 -121
- package/src/editor/hooks/useBranchManager.test.tsx +0 -391
- package/src/editor/hooks/useBranchManager.tsx +0 -326
- package/src/editor/hooks/useCommentSystem.test.ts +0 -615
- package/src/editor/hooks/useCommentSystem.ts +0 -347
- package/src/editor/hooks/useDraftManager.test.ts +0 -375
- package/src/editor/hooks/useDraftManager.ts +0 -259
- package/src/editor/hooks/useEditorLayout.test.ts +0 -147
- package/src/editor/hooks/useEditorLayout.ts +0 -67
- package/src/editor/hooks/useEntryManager.test.ts +0 -588
- package/src/editor/hooks/useEntryManager.ts +0 -387
- package/src/editor/hooks/useGroupManager.test.ts +0 -277
- package/src/editor/hooks/useGroupManager.ts +0 -139
- package/src/editor/hooks/usePermissionManager.test.ts +0 -211
- package/src/editor/hooks/usePermissionManager.ts +0 -113
- package/src/editor/hooks/useReferenceResolution.ts +0 -248
- package/src/editor/hooks/useSchemaManager.test.ts +0 -370
- package/src/editor/hooks/useSchemaManager.ts +0 -310
- package/src/editor/hooks/useUserContext.tsx +0 -57
- package/src/editor/hooks/useUserMetadata.test.ts +0 -191
- package/src/editor/hooks/useUserMetadata.ts +0 -71
- package/src/editor/permission-manager/GroupSelector.tsx +0 -73
- package/src/editor/permission-manager/PermissionEditor.tsx +0 -321
- package/src/editor/permission-manager/PermissionLevelBadge.tsx +0 -53
- package/src/editor/permission-manager/PermissionTree.tsx +0 -237
- package/src/editor/permission-manager/UserSelector.tsx +0 -95
- package/src/editor/permission-manager/constants.tsx +0 -18
- package/src/editor/permission-manager/hooks/useGroupsAndUsers.ts +0 -153
- package/src/editor/permission-manager/hooks/usePermissionTree.ts +0 -200
- package/src/editor/permission-manager/index.tsx +0 -294
- package/src/editor/permission-manager/types.ts +0 -58
- package/src/editor/permission-manager/utils.ts +0 -179
- package/src/editor/preview-bridge.test.tsx +0 -50
- package/src/editor/preview-bridge.tsx +0 -294
- package/src/editor/schema-editor/CollectionEditor.test.tsx +0 -238
- package/src/editor/schema-editor/CollectionEditor.tsx +0 -520
- package/src/editor/schema-editor/EntryTypeEditor.test.tsx +0 -215
- package/src/editor/schema-editor/EntryTypeEditor.tsx +0 -367
- package/src/editor/schema-editor/index.ts +0 -19
- package/src/editor/setup-test-dom.ts +0 -10
- package/src/editor/test-setup.ts +0 -33
- package/src/editor/theme.tsx +0 -119
- package/src/editor/utils/env.ts +0 -39
- package/src/entry-schema-registry.test.ts +0 -281
- package/src/entry-schema-registry.ts +0 -121
- package/src/entry-schema.ts +0 -84
- package/src/git-manager.test.ts +0 -552
- package/src/git-manager.ts +0 -667
- package/src/github-service.test.ts +0 -312
- package/src/github-service.ts +0 -295
- package/src/http/handler.test.ts +0 -275
- package/src/http/handler.ts +0 -280
- package/src/http/index.ts +0 -11
- package/src/http/router.ts +0 -164
- package/src/http/types.ts +0 -44
- package/src/id.test.ts +0 -48
- package/src/id.ts +0 -22
- package/src/index.ts +0 -26
- package/src/operating-mode/__tests__/strategies.test.ts +0 -511
- package/src/operating-mode/client-safe-strategy.ts +0 -184
- package/src/operating-mode/client-unsafe-strategy.ts +0 -303
- package/src/operating-mode/client.ts +0 -13
- package/src/operating-mode/index.ts +0 -34
- package/src/operating-mode/types.ts +0 -186
- package/src/paths/__tests__/branch.test.ts +0 -53
- package/src/paths/__tests__/normalize.test.ts +0 -141
- package/src/paths/__tests__/resolve.test.ts +0 -207
- package/src/paths/__tests__/validation.test.ts +0 -61
- package/src/paths/branch.ts +0 -115
- package/src/paths/index.ts +0 -73
- package/src/paths/normalize-server.ts +0 -40
- package/src/paths/normalize.ts +0 -107
- package/src/paths/resolve.ts +0 -61
- package/src/paths/test-utils.ts +0 -37
- package/src/paths/types.ts +0 -68
- package/src/paths/validation.test.ts +0 -480
- package/src/paths/validation.ts +0 -391
- package/src/reference-resolver.test.ts +0 -107
- package/src/reference-resolver.ts +0 -157
- package/src/schema/index.ts +0 -29
- package/src/schema/meta-loader.ts +0 -366
- package/src/schema/resolver.ts +0 -83
- package/src/schema/schema-store-types.ts +0 -56
- package/src/schema/schema-store.test.ts +0 -816
- package/src/schema/schema-store.ts +0 -795
- package/src/schema/types.ts +0 -33
- package/src/schema-meta-loader.test.ts +0 -447
- package/src/server.ts +0 -15
- package/src/services.test.ts +0 -559
- package/src/services.ts +0 -373
- package/src/settings-branch-utils.ts +0 -53
- package/src/settings-workspace.ts +0 -156
- package/src/task-queue/README.md +0 -144
- package/src/task-queue/index.ts +0 -14
- package/src/task-queue/task-queue.test.ts +0 -524
- package/src/task-queue/task-queue.ts +0 -514
- package/src/task-queue/types.ts +0 -41
- package/src/test-utils/api-test-helpers.ts +0 -445
- package/src/test-utils/console-spy.test.ts +0 -14
- package/src/test-utils/console-spy.ts +0 -125
- package/src/test-utils/git-helpers.ts +0 -31
- package/src/test-utils/index.ts +0 -4
- package/src/types.ts +0 -54
- package/src/user.ts +0 -118
- package/src/utils/debug.test.ts +0 -114
- package/src/utils/debug.ts +0 -127
- package/src/utils/error.test.ts +0 -92
- package/src/utils/error.ts +0 -83
- package/src/utils/format.ts +0 -12
- package/src/validation/__tests__/field-traversal.test.ts +0 -263
- package/src/validation/deletion-checker.ts +0 -234
- package/src/validation/field-traversal.ts +0 -146
- package/src/validation/reference-validator.ts +0 -168
- package/src/worker/cms-worker-rebase.test.ts +0 -473
- package/src/worker/cms-worker.ts +0 -777
- package/src/worker/integration.test.ts +0 -289
- package/src/worker/task-queue-config.ts +0 -25
- package/src/worker/task-queue.test.ts +0 -452
- package/src/worker/task-queue.ts +0 -58
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
import type { CollectionItem, ListEntriesResponse } from '../api/entries'
|
|
2
|
-
import type { ContentFormat, EntrySchema, FlatSchemaItem } from '../config'
|
|
3
|
-
import type { FormValue } from './FormRenderer'
|
|
4
|
-
import type { EditorEntry, EditorCollection } from './Editor'
|
|
5
|
-
import type { TreeNodeData } from '@mantine/core'
|
|
6
|
-
// Import directly from normalize to avoid pulling in server-only branch.ts
|
|
7
|
-
import { normalizeCollectionPath } from '../paths/normalize'
|
|
8
|
-
export { normalizeCollectionPath }
|
|
9
|
-
|
|
10
|
-
export interface PreviewContext {
|
|
11
|
-
branchName?: string
|
|
12
|
-
previewBaseByCollection?: Record<string, string>
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const encodeSlug = (value?: string): string =>
|
|
16
|
-
(value ?? '')
|
|
17
|
-
.split('/')
|
|
18
|
-
.filter(Boolean)
|
|
19
|
-
.map((segment) => encodeURIComponent(segment))
|
|
20
|
-
.join('/')
|
|
21
|
-
|
|
22
|
-
export const buildPreviewSrc = (
|
|
23
|
-
entry: {
|
|
24
|
-
collectionPath?: string
|
|
25
|
-
collectionName?: string
|
|
26
|
-
slug?: string
|
|
27
|
-
itemType?: string
|
|
28
|
-
previewSrc?: string
|
|
29
|
-
},
|
|
30
|
-
{ branchName, previewBaseByCollection, contentRoot }: PreviewContext & { contentRoot?: string },
|
|
31
|
-
): string => {
|
|
32
|
-
if (entry.previewSrc) return entry.previewSrc
|
|
33
|
-
const appendBranch = (url: string) => {
|
|
34
|
-
if (!branchName) return url
|
|
35
|
-
const separator = url.includes('?') ? '&' : '?'
|
|
36
|
-
return `${url}${separator}branch=${encodeURIComponent(branchName)}`
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Root-level entries have collectionPath === contentRoot (e.g., 'content')
|
|
40
|
-
const isRootEntry = contentRoot && entry.collectionPath === contentRoot
|
|
41
|
-
|
|
42
|
-
if (isRootEntry) {
|
|
43
|
-
// Check for custom preview URL in previewBaseByCollection
|
|
44
|
-
const customPreview = previewBaseByCollection?.[`${contentRoot}/${entry.slug}`]
|
|
45
|
-
if (customPreview) {
|
|
46
|
-
return appendBranch(customPreview)
|
|
47
|
-
}
|
|
48
|
-
// Default root entries to root path
|
|
49
|
-
return appendBranch('/')
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const base =
|
|
53
|
-
(entry.collectionPath && previewBaseByCollection?.[entry.collectionPath]) ??
|
|
54
|
-
(entry.collectionName && previewBaseByCollection?.[entry.collectionName])
|
|
55
|
-
if (!base) {
|
|
56
|
-
// Build URL from collection path + slug
|
|
57
|
-
const collectionPath = entry.collectionPath ? normalizeCollectionPath(entry.collectionPath) : ''
|
|
58
|
-
const encoded = encodeSlug(entry.slug)
|
|
59
|
-
const segments = [collectionPath, encoded].filter(Boolean)
|
|
60
|
-
const url = segments.length > 0 ? `/${segments.join('/')}` : '/'
|
|
61
|
-
return appendBranch(url)
|
|
62
|
-
}
|
|
63
|
-
const trimmed = base.endsWith('/') ? base.slice(0, -1) : base
|
|
64
|
-
const encoded = encodeSlug(entry.slug)
|
|
65
|
-
const url = encoded ? `${trimmed}/${encoded}` : trimmed || '/'
|
|
66
|
-
return appendBranch(url)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export const normalizeContentPayload = (raw: unknown): FormValue => {
|
|
70
|
-
const candidate = raw as Record<string, unknown> | undefined
|
|
71
|
-
const data = (candidate?.data as Record<string, unknown> | undefined) ?? candidate
|
|
72
|
-
if (data && typeof data === 'object' && 'format' in data && 'data' in data) {
|
|
73
|
-
const format = data.format as ContentFormat
|
|
74
|
-
const payloadData = (data.data as Record<string, unknown>) ?? {}
|
|
75
|
-
if (format === 'json') return payloadData
|
|
76
|
-
return {
|
|
77
|
-
...payloadData,
|
|
78
|
-
body:
|
|
79
|
-
typeof (data as Record<string, unknown>).body === 'string'
|
|
80
|
-
? (data as Record<string, unknown>).body
|
|
81
|
-
: '',
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return (data as FormValue) ?? {}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export const buildWritePayload = (
|
|
88
|
-
entry: { collectionPath?: string; slug?: string; format?: ContentFormat },
|
|
89
|
-
value: FormValue,
|
|
90
|
-
) => {
|
|
91
|
-
if (!entry.format) return value
|
|
92
|
-
if (entry.format === 'json') {
|
|
93
|
-
return {
|
|
94
|
-
format: 'json' as const,
|
|
95
|
-
data: value,
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const { body, ...rest } = value
|
|
99
|
-
return {
|
|
100
|
-
format: entry.format,
|
|
101
|
-
data: rest,
|
|
102
|
-
body: typeof body === 'string' ? body : '',
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
interface BuildEntriesFromListParams {
|
|
107
|
-
response: ListEntriesResponse
|
|
108
|
-
branchName: string
|
|
109
|
-
resolvePreviewSrc: (
|
|
110
|
-
entry: Pick<CollectionItem, 'collectionPath' | 'collectionName' | 'slug' | 'entryType'>,
|
|
111
|
-
) => string
|
|
112
|
-
contentRoot: string
|
|
113
|
-
flatSchema: FlatSchemaItem[]
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export const buildEntriesFromListResponse = ({
|
|
117
|
-
response,
|
|
118
|
-
branchName,
|
|
119
|
-
resolvePreviewSrc,
|
|
120
|
-
contentRoot,
|
|
121
|
-
flatSchema,
|
|
122
|
-
}: BuildEntriesFromListParams): EditorEntry[] => {
|
|
123
|
-
return response.entries.map((entry) => {
|
|
124
|
-
// Resolve schema from flatSchema using parentPath + name
|
|
125
|
-
let schema: EntrySchema = []
|
|
126
|
-
if (entry.collectionPath && entry.entryType) {
|
|
127
|
-
const entryTypeItem = flatSchema.find(
|
|
128
|
-
(item) =>
|
|
129
|
-
item.type === 'entry-type' &&
|
|
130
|
-
item.parentPath === entry.collectionPath &&
|
|
131
|
-
item.name === entry.entryType,
|
|
132
|
-
)
|
|
133
|
-
if (entryTypeItem && entryTypeItem.type === 'entry-type') {
|
|
134
|
-
schema = entryTypeItem.schema
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const isRootEntry = entry.collectionPath === contentRoot
|
|
139
|
-
const apiPath = isRootEntry
|
|
140
|
-
? `/api/canopycms/${branchName}/content/${encodeURIComponent(entry.slug)}`
|
|
141
|
-
: `/api/canopycms/${branchName}/content/${encodeURIComponent(entry.collectionPath)}/${encodeURIComponent(entry.slug)}`
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
path: entry.logicalPath,
|
|
145
|
-
contentId: entry.contentId,
|
|
146
|
-
label: entry.title || entry.slug || entry.collectionName || entry.collectionPath,
|
|
147
|
-
status: entry.exists === false ? 'missing' : (entry.entryType ?? 'entry'),
|
|
148
|
-
schema: schema,
|
|
149
|
-
apiPath,
|
|
150
|
-
previewSrc: resolvePreviewSrc(entry),
|
|
151
|
-
collectionPath: entry.collectionPath,
|
|
152
|
-
collectionName: entry.collectionName,
|
|
153
|
-
slug: entry.slug,
|
|
154
|
-
format: entry.format,
|
|
155
|
-
type: 'entry' as const,
|
|
156
|
-
canEdit: entry.canEdit,
|
|
157
|
-
}
|
|
158
|
-
})
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Builds a map of collection IDs to their labels for breadcrumb display.
|
|
163
|
-
* Recursively walks through nested collections to build a flat map.
|
|
164
|
-
*
|
|
165
|
-
* @param collections - The collection tree structure
|
|
166
|
-
* @returns A Map where keys are collection IDs (paths) and values are labels
|
|
167
|
-
*/
|
|
168
|
-
export const buildCollectionLabels = (collections?: EditorCollection[]): Map<string, string> => {
|
|
169
|
-
const map = new Map<string, string>()
|
|
170
|
-
if (!collections) return map
|
|
171
|
-
|
|
172
|
-
const walk = (nodes: EditorCollection[]) => {
|
|
173
|
-
for (const c of nodes) {
|
|
174
|
-
map.set(c.path, c.label ?? c.name)
|
|
175
|
-
if (c.children) {
|
|
176
|
-
walk(c.children)
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
walk(collections)
|
|
181
|
-
return map
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Builds breadcrumb segments for an entry based on its collection hierarchy.
|
|
186
|
-
*
|
|
187
|
-
* @param currentEntry - The entry to build breadcrumbs for (or undefined for root)
|
|
188
|
-
* @param collectionLabels - Map of collection IDs to labels
|
|
189
|
-
* @returns Array of breadcrumb segment strings, starting with 'All Files'
|
|
190
|
-
*
|
|
191
|
-
* @example
|
|
192
|
-
* ```ts
|
|
193
|
-
* // Entry in nested collection
|
|
194
|
-
* const entry = { collectionPath: 'content/docs/guides', slug: 'config' }
|
|
195
|
-
* const labels = new Map([
|
|
196
|
-
* ['content', 'Content'],
|
|
197
|
-
* ['content/docs', 'Documentation'],
|
|
198
|
-
* ['content/docs/guides', 'Guides']
|
|
199
|
-
* ])
|
|
200
|
-
* buildBreadcrumbSegments(entry, labels)
|
|
201
|
-
* // Returns: ['All Files', 'Documentation', 'Guides']
|
|
202
|
-
* ```
|
|
203
|
-
*/
|
|
204
|
-
export const buildBreadcrumbSegments = (
|
|
205
|
-
currentEntry: EditorEntry | undefined,
|
|
206
|
-
collectionLabels: Map<string, string>,
|
|
207
|
-
): string[] => {
|
|
208
|
-
if (!currentEntry) return ['All Files']
|
|
209
|
-
const segments = ['All Files']
|
|
210
|
-
|
|
211
|
-
// Show collection hierarchy for entries that belong to a collection
|
|
212
|
-
if (currentEntry.collectionPath) {
|
|
213
|
-
// Split the collectionPath into path parts and build cumulative paths
|
|
214
|
-
// e.g., "content/documentation/guides" -> ["content/documentation", "content/documentation/guides"]
|
|
215
|
-
const parts = currentEntry.collectionPath.split('/').filter(Boolean)
|
|
216
|
-
for (let i = 1; i < parts.length; i++) {
|
|
217
|
-
const pathUpToHere = parts.slice(0, i + 1).join('/')
|
|
218
|
-
const label = collectionLabels.get(pathUpToHere)
|
|
219
|
-
if (label) {
|
|
220
|
-
segments.push(label)
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Add slug path segments (for nested slugs like "folder/file")
|
|
226
|
-
const slugSegments = (currentEntry.slug ?? '').split('/').filter(Boolean)
|
|
227
|
-
if (slugSegments.length > 1) {
|
|
228
|
-
segments.push(...slugSegments.slice(0, -1))
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return segments
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Calculates which collection nodes need to be expanded to show the path to a specific entry.
|
|
236
|
-
* Recursively walks the tree to find the target entry and marks all ancestor collections as expanded.
|
|
237
|
-
*
|
|
238
|
-
* @param entryPath - The entry path to find (e.g., "blog/my-post")
|
|
239
|
-
* @param treeData - The tree data structure from Mantine Tree
|
|
240
|
-
* @returns Record<string, boolean> - Expanded state object where keys are collection node values
|
|
241
|
-
*
|
|
242
|
-
* @example
|
|
243
|
-
* ```ts
|
|
244
|
-
* const treeData = [
|
|
245
|
-
* {
|
|
246
|
-
* value: 'collection:blog',
|
|
247
|
-
* children: [
|
|
248
|
-
* { value: 'blog/post-1' },
|
|
249
|
-
* {
|
|
250
|
-
* value: 'collection:blog/featured',
|
|
251
|
-
* children: [{ value: 'blog/featured/my-post' }]
|
|
252
|
-
* }
|
|
253
|
-
* ]
|
|
254
|
-
* }
|
|
255
|
-
* ]
|
|
256
|
-
* calculatePathToEntry('blog/featured/my-post', treeData)
|
|
257
|
-
* // Returns: { 'collection:blog': true, 'collection:blog/featured': true }
|
|
258
|
-
* ```
|
|
259
|
-
*/
|
|
260
|
-
export const calculatePathToEntry = (
|
|
261
|
-
entryPath: string | undefined,
|
|
262
|
-
treeData: TreeNodeData[],
|
|
263
|
-
): Record<string, boolean> => {
|
|
264
|
-
if (!entryPath) return {}
|
|
265
|
-
|
|
266
|
-
const pathToExpand: Record<string, boolean> = {}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Recursive function to find entry and mark parent collections as expanded.
|
|
270
|
-
* @param nodes - Current level of tree nodes to search
|
|
271
|
-
* @param ancestors - Accumulated ancestor node values (collection IDs) from root to current position
|
|
272
|
-
* @returns true if the target entry was found in this subtree
|
|
273
|
-
*/
|
|
274
|
-
const findAndMarkPath = (nodes: TreeNodeData[], ancestors: string[]): boolean => {
|
|
275
|
-
for (const node of nodes) {
|
|
276
|
-
// Found the target entry
|
|
277
|
-
if (node.value === entryPath) {
|
|
278
|
-
// Mark all ancestors as expanded
|
|
279
|
-
for (const ancestor of ancestors) {
|
|
280
|
-
pathToExpand[ancestor] = true
|
|
281
|
-
}
|
|
282
|
-
return true
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Search children recursively if they exist
|
|
286
|
-
if (node.children && node.children.length > 0) {
|
|
287
|
-
const currentPath = [...ancestors, node.value]
|
|
288
|
-
const found = findAndMarkPath(node.children, currentPath)
|
|
289
|
-
|
|
290
|
-
if (found) {
|
|
291
|
-
// Mark this node as expanded since the target was found in its subtree
|
|
292
|
-
pathToExpand[node.value] = true
|
|
293
|
-
return true
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
return false
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
findAndMarkPath(treeData, [])
|
|
302
|
-
return pathToExpand
|
|
303
|
-
}
|
package/src/editor/env.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
-
import { useState } from 'react'
|
|
3
|
-
|
|
4
|
-
import type { BlockConfig, FieldConfig } from '../../config'
|
|
5
|
-
import type { BlockInstance } from './BlockField'
|
|
6
|
-
import { BlockField } from './BlockField'
|
|
7
|
-
import { MarkdownField } from './MarkdownField'
|
|
8
|
-
import { TextField } from './TextField'
|
|
9
|
-
|
|
10
|
-
const meta: Meta<typeof BlockField> = {
|
|
11
|
-
title: 'Editor/Fields/Block',
|
|
12
|
-
component: BlockField,
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default meta
|
|
16
|
-
type Story = StoryObj<typeof BlockField>
|
|
17
|
-
|
|
18
|
-
export const Default: Story = {
|
|
19
|
-
render: () => {
|
|
20
|
-
const templates: BlockConfig[] = [
|
|
21
|
-
{
|
|
22
|
-
name: 'hero',
|
|
23
|
-
label: 'Hero',
|
|
24
|
-
fields: [
|
|
25
|
-
{ name: 'headline', type: 'string' },
|
|
26
|
-
{ name: 'body', type: 'markdown' },
|
|
27
|
-
],
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: 'cta',
|
|
31
|
-
label: 'CTA',
|
|
32
|
-
fields: [
|
|
33
|
-
{ name: 'title', type: 'string' },
|
|
34
|
-
{ name: 'ctaText', type: 'string' },
|
|
35
|
-
],
|
|
36
|
-
},
|
|
37
|
-
]
|
|
38
|
-
const [blocks, setBlocks] = useState<BlockInstance[]>([
|
|
39
|
-
{ template: 'hero', value: { headline: 'Hello', body: 'Intro text' } },
|
|
40
|
-
{
|
|
41
|
-
template: 'cta',
|
|
42
|
-
value: { title: 'Call to action', ctaText: 'Click me' },
|
|
43
|
-
},
|
|
44
|
-
])
|
|
45
|
-
|
|
46
|
-
const renderWidget = (field: FieldConfig, value: unknown, onChange: (v: unknown) => void) => {
|
|
47
|
-
if (field.type === 'string') {
|
|
48
|
-
return (
|
|
49
|
-
<TextField
|
|
50
|
-
label={field.label ?? field.name}
|
|
51
|
-
value={typeof value === 'string' ? value : ''}
|
|
52
|
-
onChange={(v) => onChange(v)}
|
|
53
|
-
/>
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
if (field.type === 'markdown' || field.type === 'mdx') {
|
|
57
|
-
return (
|
|
58
|
-
<MarkdownField
|
|
59
|
-
label={field.label ?? field.name}
|
|
60
|
-
value={typeof value === 'string' ? value : ''}
|
|
61
|
-
onChange={(v) => onChange(v)}
|
|
62
|
-
/>
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
return <div className="text-xs text-gray-500">Unsupported field: {field.type}</div>
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
<BlockField
|
|
70
|
-
label="Blocks"
|
|
71
|
-
templates={templates}
|
|
72
|
-
value={blocks}
|
|
73
|
-
onChange={(next) => setBlocks(next)}
|
|
74
|
-
renderField={(field, val, update, _path) => renderWidget(field, val, update)}
|
|
75
|
-
path={['blocks']}
|
|
76
|
-
/>
|
|
77
|
-
)
|
|
78
|
-
},
|
|
79
|
-
}
|
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
import React, { useMemo, useState } from 'react'
|
|
2
|
-
|
|
3
|
-
let blockKeyCounter = 0
|
|
4
|
-
|
|
5
|
-
import { ActionIcon, Button, Group, Paper, Select, Stack, Text } from '@mantine/core'
|
|
6
|
-
import {
|
|
7
|
-
DndContext,
|
|
8
|
-
type DragEndEvent,
|
|
9
|
-
KeyboardSensor,
|
|
10
|
-
PointerSensor,
|
|
11
|
-
useSensor,
|
|
12
|
-
useSensors,
|
|
13
|
-
} from '@dnd-kit/core'
|
|
14
|
-
import {
|
|
15
|
-
SortableContext,
|
|
16
|
-
arrayMove,
|
|
17
|
-
sortableKeyboardCoordinates,
|
|
18
|
-
useSortable,
|
|
19
|
-
verticalListSortingStrategy,
|
|
20
|
-
} from '@dnd-kit/sortable'
|
|
21
|
-
import { CSS } from '@dnd-kit/utilities'
|
|
22
|
-
|
|
23
|
-
import type { BlockConfig, FieldConfig } from '../../config'
|
|
24
|
-
import { formatCanopyPath } from '../canopy-path'
|
|
25
|
-
|
|
26
|
-
export interface BlockInstance {
|
|
27
|
-
template: string
|
|
28
|
-
value: Record<string, unknown>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type RenderField = (
|
|
32
|
-
field: FieldConfig,
|
|
33
|
-
value: unknown,
|
|
34
|
-
onChange: (v: unknown) => void,
|
|
35
|
-
path: Array<string | number>,
|
|
36
|
-
) => React.ReactNode
|
|
37
|
-
|
|
38
|
-
export interface BlockFieldProps {
|
|
39
|
-
label?: string
|
|
40
|
-
templates: BlockConfig[]
|
|
41
|
-
value: BlockInstance[]
|
|
42
|
-
onChange: (blocks: BlockInstance[]) => void
|
|
43
|
-
renderField: RenderField
|
|
44
|
-
path: Array<string | number>
|
|
45
|
-
dataCanopyField?: string
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const findTemplate = (templates: BlockConfig[], name: string) =>
|
|
49
|
-
templates.find((t) => t.name === name)
|
|
50
|
-
|
|
51
|
-
const SortableBlock: React.FC<{
|
|
52
|
-
id: string
|
|
53
|
-
children: React.ReactNode
|
|
54
|
-
}> = ({ id, children }) => {
|
|
55
|
-
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
|
56
|
-
id,
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
const style: React.CSSProperties = {
|
|
60
|
-
transform: CSS.Transform.toString(transform),
|
|
61
|
-
transition,
|
|
62
|
-
opacity: isDragging ? 0.85 : 1,
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<Paper ref={setNodeRef} withBorder radius="md" p="sm" shadow="xs" style={style}>
|
|
67
|
-
<Group align="flex-start" gap="sm">
|
|
68
|
-
<ActionIcon
|
|
69
|
-
key="drag-handle"
|
|
70
|
-
variant="subtle"
|
|
71
|
-
aria-label="Drag to reorder"
|
|
72
|
-
{...attributes}
|
|
73
|
-
{...listeners}
|
|
74
|
-
style={{ cursor: 'grab' }}
|
|
75
|
-
>
|
|
76
|
-
⇅
|
|
77
|
-
</ActionIcon>
|
|
78
|
-
<div key="content" style={{ flex: 1, minWidth: 0, width: '100%' }}>
|
|
79
|
-
{children}
|
|
80
|
-
</div>
|
|
81
|
-
</Group>
|
|
82
|
-
</Paper>
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export const BlockField: React.FC<BlockFieldProps> = ({
|
|
87
|
-
label,
|
|
88
|
-
templates,
|
|
89
|
-
value,
|
|
90
|
-
onChange,
|
|
91
|
-
renderField,
|
|
92
|
-
path,
|
|
93
|
-
dataCanopyField,
|
|
94
|
-
}) => {
|
|
95
|
-
const [itemKeys, setItemKeys] = useState<string[]>(() =>
|
|
96
|
-
value.map(() => `block-${blockKeyCounter++}`),
|
|
97
|
-
)
|
|
98
|
-
const [pendingTemplate, setPendingTemplate] = useState<string | null>(null)
|
|
99
|
-
|
|
100
|
-
// Sync itemKeys length with value length (adjust state during render)
|
|
101
|
-
if (value.length > itemKeys.length) {
|
|
102
|
-
const extras = Array.from(
|
|
103
|
-
{ length: value.length - itemKeys.length },
|
|
104
|
-
() => `block-${blockKeyCounter++}`,
|
|
105
|
-
)
|
|
106
|
-
setItemKeys((prev) => [...prev, ...extras])
|
|
107
|
-
} else if (value.length < itemKeys.length) {
|
|
108
|
-
setItemKeys((prev) => prev.slice(0, value.length))
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const sensors = useSensors(
|
|
112
|
-
useSensor(PointerSensor, { activationConstraint: { distance: 6 } }),
|
|
113
|
-
useSensor(KeyboardSensor, {
|
|
114
|
-
coordinateGetter: sortableKeyboardCoordinates,
|
|
115
|
-
}),
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
const addBlock = (templateName: string) => {
|
|
119
|
-
const template = findTemplate(templates, templateName)
|
|
120
|
-
if (!template) return
|
|
121
|
-
onChange([...value, { template: templateName, value: {} }])
|
|
122
|
-
setItemKeys((prev) => [
|
|
123
|
-
...prev,
|
|
124
|
-
`block-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
125
|
-
])
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const moveBlock = (from: number, to: number) => {
|
|
129
|
-
if (from === to || from < 0 || to < 0 || from >= value.length || to >= value.length) return
|
|
130
|
-
onChange(arrayMove(value, from, to))
|
|
131
|
-
setItemKeys((prev) => arrayMove(prev, from, to))
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const removeBlock = (index: number) => {
|
|
135
|
-
onChange(value.filter((_, idx) => idx !== index))
|
|
136
|
-
setItemKeys((prev) => prev.filter((_, idx) => idx !== index))
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const updateBlockValue = (index: number, val: Record<string, unknown>) => {
|
|
140
|
-
const next = [...value]
|
|
141
|
-
next[index] = { ...next[index], value: val }
|
|
142
|
-
onChange(next)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const handleDragEnd = (event: DragEndEvent) => {
|
|
146
|
-
const { active, over } = event
|
|
147
|
-
if (!over || active.id === over.id) return
|
|
148
|
-
|
|
149
|
-
const oldIndex = itemKeys.indexOf(String(active.id))
|
|
150
|
-
const newIndex = itemKeys.indexOf(String(over.id))
|
|
151
|
-
if (oldIndex === -1 || newIndex === -1) return
|
|
152
|
-
moveBlock(oldIndex, newIndex)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const selectableTemplates = useMemo(
|
|
156
|
-
() => templates.map((t) => ({ value: t.name, label: t.label ?? t.name })),
|
|
157
|
-
[templates],
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
return (
|
|
161
|
-
<Paper
|
|
162
|
-
withBorder
|
|
163
|
-
radius="md"
|
|
164
|
-
p="md"
|
|
165
|
-
bg="gray.0"
|
|
166
|
-
data-canopy-field={dataCanopyField ?? formatCanopyPath(path)}
|
|
167
|
-
shadow="xs"
|
|
168
|
-
>
|
|
169
|
-
<Stack gap="sm">
|
|
170
|
-
<Group justify="space-between">
|
|
171
|
-
<Text size="xs" fw={700} c="dimmed">
|
|
172
|
-
{label ?? 'Blocks'}
|
|
173
|
-
</Text>
|
|
174
|
-
<Select
|
|
175
|
-
aria-label="Add block"
|
|
176
|
-
placeholder="Add block..."
|
|
177
|
-
data={selectableTemplates}
|
|
178
|
-
value={pendingTemplate}
|
|
179
|
-
onChange={(next) => {
|
|
180
|
-
if (next) {
|
|
181
|
-
addBlock(next)
|
|
182
|
-
}
|
|
183
|
-
setPendingTemplate(null)
|
|
184
|
-
}}
|
|
185
|
-
allowDeselect
|
|
186
|
-
size="xs"
|
|
187
|
-
w={180}
|
|
188
|
-
/>
|
|
189
|
-
</Group>
|
|
190
|
-
|
|
191
|
-
<DndContext sensors={sensors} onDragEnd={handleDragEnd}>
|
|
192
|
-
<SortableContext items={itemKeys} strategy={verticalListSortingStrategy}>
|
|
193
|
-
<Stack gap="sm">
|
|
194
|
-
{value.map((block, idx) => {
|
|
195
|
-
const template = findTemplate(templates, block.template)
|
|
196
|
-
const currentPath = [...path, idx]
|
|
197
|
-
|
|
198
|
-
return (
|
|
199
|
-
<SortableBlock key={itemKeys[idx]} id={itemKeys[idx]}>
|
|
200
|
-
<Stack gap="xs">
|
|
201
|
-
<Group justify="space-between" align="flex-start">
|
|
202
|
-
<Text size="sm" fw={600}>
|
|
203
|
-
{template?.label ?? block.template ?? 'Unknown block'}
|
|
204
|
-
</Text>
|
|
205
|
-
<Group gap={4}>
|
|
206
|
-
<ActionIcon
|
|
207
|
-
variant="light"
|
|
208
|
-
aria-label="Move block up"
|
|
209
|
-
disabled={idx === 0}
|
|
210
|
-
onClick={() => moveBlock(idx, idx - 1)}
|
|
211
|
-
>
|
|
212
|
-
↑
|
|
213
|
-
</ActionIcon>
|
|
214
|
-
<ActionIcon
|
|
215
|
-
variant="light"
|
|
216
|
-
aria-label="Move block down"
|
|
217
|
-
disabled={idx === value.length - 1}
|
|
218
|
-
onClick={() => moveBlock(idx, idx + 1)}
|
|
219
|
-
>
|
|
220
|
-
↓
|
|
221
|
-
</ActionIcon>
|
|
222
|
-
<Button
|
|
223
|
-
variant="subtle"
|
|
224
|
-
color="red"
|
|
225
|
-
size="xs"
|
|
226
|
-
onClick={() => removeBlock(idx)}
|
|
227
|
-
>
|
|
228
|
-
Remove
|
|
229
|
-
</Button>
|
|
230
|
-
</Group>
|
|
231
|
-
</Group>
|
|
232
|
-
|
|
233
|
-
{template ? (
|
|
234
|
-
<Stack gap="sm">
|
|
235
|
-
{template.fields.map((f: FieldConfig) => (
|
|
236
|
-
<React.Fragment key={f.name}>
|
|
237
|
-
{renderField(
|
|
238
|
-
f,
|
|
239
|
-
block.value?.[f.name],
|
|
240
|
-
(next) =>
|
|
241
|
-
updateBlockValue(idx, {
|
|
242
|
-
...block.value,
|
|
243
|
-
[f.name]: next,
|
|
244
|
-
}),
|
|
245
|
-
[...currentPath, f.name],
|
|
246
|
-
)}
|
|
247
|
-
</React.Fragment>
|
|
248
|
-
))}
|
|
249
|
-
</Stack>
|
|
250
|
-
) : (
|
|
251
|
-
<Text size="xs" c="red">
|
|
252
|
-
No template found for "{block.template}"
|
|
253
|
-
</Text>
|
|
254
|
-
)}
|
|
255
|
-
</Stack>
|
|
256
|
-
</SortableBlock>
|
|
257
|
-
)
|
|
258
|
-
})}
|
|
259
|
-
</Stack>
|
|
260
|
-
</SortableContext>
|
|
261
|
-
</DndContext>
|
|
262
|
-
</Stack>
|
|
263
|
-
</Paper>
|
|
264
|
-
)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
export default BlockField
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import React, { useId } from 'react'
|
|
2
|
-
|
|
3
|
-
import { Textarea } from '@mantine/core'
|
|
4
|
-
|
|
5
|
-
export interface CodeFieldProps {
|
|
6
|
-
id?: string
|
|
7
|
-
label?: string
|
|
8
|
-
value: string
|
|
9
|
-
onChange: (value: string) => void
|
|
10
|
-
language?: string
|
|
11
|
-
dataCanopyField?: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Placeholder for Monaco integration; host app can provide custom renderer for production.
|
|
15
|
-
export const CodeField: React.FC<CodeFieldProps> = ({
|
|
16
|
-
id,
|
|
17
|
-
label,
|
|
18
|
-
value,
|
|
19
|
-
onChange,
|
|
20
|
-
language,
|
|
21
|
-
dataCanopyField,
|
|
22
|
-
}) => {
|
|
23
|
-
const generatedId = useId()
|
|
24
|
-
const inputId = id ?? generatedId
|
|
25
|
-
return (
|
|
26
|
-
<Textarea
|
|
27
|
-
id={inputId}
|
|
28
|
-
label={label}
|
|
29
|
-
value={value}
|
|
30
|
-
onChange={(e) => onChange(e.currentTarget.value)}
|
|
31
|
-
placeholder={language ? `Code (${language})` : 'Code'}
|
|
32
|
-
autosize
|
|
33
|
-
minRows={6}
|
|
34
|
-
size="sm"
|
|
35
|
-
data-canopy-field={dataCanopyField}
|
|
36
|
-
styles={{ input: { fontFamily: 'Menlo, Consolas, monospace' } }}
|
|
37
|
-
/>
|
|
38
|
-
)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export default CodeField
|