create-nextblock 0.14.0 → 0.14.2
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/docker-template/.dockerignore +23 -23
- package/docker-template/.env.docker.example +69 -69
- package/docker-template/docker/db/init/99-jwt.sql +6 -6
- package/docker-template/docker/db/init/99-roles.sql +25 -25
- package/docker-template/docker/kong/kong.yml +112 -112
- package/docker-template/scripts/docker-setup.mjs +310 -310
- package/libs/db/tsconfig.lib.json +3 -3
- package/libs/editor/tsconfig.lib.json +3 -3
- package/libs/ui/tsconfig.lib.json +3 -3
- package/libs/utils/tsconfig.json +3 -3
- package/package.json +1 -1
- package/project.json +19 -19
- package/templates/nextblock-template/.browserslistrc +11 -11
- package/templates/nextblock-template/.dockerignore +23 -23
- package/templates/nextblock-template/README.md +34 -34
- package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -9
- package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -27
- package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +46 -46
- package/templates/nextblock-template/app/(auth-pages)/two-factor/page.tsx +51 -51
- package/templates/nextblock-template/app/ToasterProvider.tsx +17 -17
- package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -7
- package/templates/nextblock-template/app/actions/consent.ts +57 -57
- package/templates/nextblock-template/app/actions/postActions.ts +146 -146
- package/templates/nextblock-template/app/actions/twoFactorEmail.ts +22 -22
- package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +153 -153
- package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +96 -96
- package/templates/nextblock-template/app/api/brand/email-logo/route.ts +48 -48
- package/templates/nextblock-template/app/api/cms/check-updates/route.ts +44 -44
- package/templates/nextblock-template/app/api/cms/full-backup/export/route.ts +33 -33
- package/templates/nextblock-template/app/api/cms/full-backup/restore/route.ts +63 -63
- package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +3490 -3490
- package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +5749 -5749
- package/templates/nextblock-template/app/api/cron/sync-currencies/route.ts +39 -39
- package/templates/nextblock-template/app/api/custom-blocks/db-relations/route.ts +92 -92
- package/templates/nextblock-template/app/api/custom-blocks/editor-definitions/route.ts +43 -43
- package/templates/nextblock-template/app/api/media/library/route.ts +69 -69
- package/templates/nextblock-template/app/api/media/r2-presigned/route.ts +53 -53
- package/templates/nextblock-template/app/api/media/record/route.ts +160 -160
- package/templates/nextblock-template/app/api/search/route.ts +43 -43
- package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +441 -441
- package/templates/nextblock-template/app/auth/callback/route.ts +31 -31
- package/templates/nextblock-template/app/cart/page.tsx +7 -7
- package/templates/nextblock-template/app/cms/blocks/components/CustomBlockEditorPreview.tsx +160 -160
- package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
- package/templates/nextblock-template/app/cms/blocks/editors/DynamicCustomBlockEditor.tsx +167 -167
- package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +4 -2
- package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +90 -90
- package/templates/nextblock-template/app/cms/components/ConnectGitHubButton.tsx +122 -122
- package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
- package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
- package/templates/nextblock-template/app/cms/components/SystemAlertsBanner.tsx +112 -112
- package/templates/nextblock-template/app/cms/components/TwoFactorReminderBanner.tsx +45 -45
- package/templates/nextblock-template/app/cms/components/github-connect-actions.ts +102 -102
- package/templates/nextblock-template/app/cms/components/system-alerts-actions.ts +31 -31
- package/templates/nextblock-template/app/cms/custom-blocks/[id]/edit/page.tsx +66 -66
- package/templates/nextblock-template/app/cms/custom-blocks/actions.ts +519 -519
- package/templates/nextblock-template/app/cms/custom-blocks/components/BlocksLibraryTransferControls.tsx +256 -256
- package/templates/nextblock-template/app/cms/custom-blocks/components/DBRelationSelect.tsx +384 -384
- package/templates/nextblock-template/app/cms/custom-blocks/components/ImageR2Picker.tsx +221 -221
- package/templates/nextblock-template/app/cms/custom-blocks/new/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/custom-blocks/page.tsx +438 -438
- package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
- package/templates/nextblock-template/app/cms/dashboard/components/DashboardOnboarding.tsx +130 -130
- package/templates/nextblock-template/app/cms/import-export/actions.ts +226 -226
- package/templates/nextblock-template/app/cms/layout.tsx +73 -73
- package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -273
- package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -122
- package/templates/nextblock-template/app/cms/media/components/MediaEditForm.tsx +26 -26
- package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -69
- package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
- package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/products/categories/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
- package/templates/nextblock-template/app/cms/products/new/page.tsx +135 -135
- package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -133
- package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -5
- package/templates/nextblock-template/app/cms/promotions/PromotionsWorkspace.tsx +456 -456
- package/templates/nextblock-template/app/cms/promotions/actions.ts +115 -115
- package/templates/nextblock-template/app/cms/promotions/page.tsx +31 -31
- package/templates/nextblock-template/app/cms/revisions/service.ts +19 -19
- package/templates/nextblock-template/app/cms/revisions/utils.ts +132 -132
- package/templates/nextblock-template/app/cms/settings/backup-restore/BackupRestoreWorkspace.tsx +1004 -1004
- package/templates/nextblock-template/app/cms/settings/backup-restore/page.tsx +29 -29
- package/templates/nextblock-template/app/cms/settings/bot-protection/page.tsx +24 -24
- package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +688 -688
- package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -331
- package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -494
- package/templates/nextblock-template/app/cms/settings/email/page.tsx +28 -28
- package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
- package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
- package/templates/nextblock-template/app/cms/settings/google-analytics/page.tsx +26 -26
- package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
- package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -339
- package/templates/nextblock-template/app/cms/settings/logos/components/DeleteLogoButton.tsx +21 -21
- package/templates/nextblock-template/app/cms/settings/logos/components/LogoForm.tsx +23 -21
- package/templates/nextblock-template/app/cms/settings/logos/components/SetActiveLogoButton.tsx +42 -42
- package/templates/nextblock-template/app/cms/settings/logos/components/SiteSeoSettingsForm.tsx +133 -133
- package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
- package/templates/nextblock-template/app/cms/settings/packages/package-card.tsx +122 -122
- package/templates/nextblock-template/app/cms/settings/privacy/page.tsx +27 -27
- package/templates/nextblock-template/app/cms/settings/registration/page.tsx +27 -27
- package/templates/nextblock-template/app/cms/settings/security/components/SecurityPanel.tsx +9 -7
- package/templates/nextblock-template/app/cms/settings/security/page.tsx +33 -33
- package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -21
- package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -20
- package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
- package/templates/nextblock-template/app/lib/site-settings.ts +105 -105
- package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -16
- package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -9
- package/templates/nextblock-template/app/profile/account-links.ts +22 -22
- package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
- package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
- package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -128
- package/templates/nextblock-template/app/profile/password/actions.ts +59 -59
- package/templates/nextblock-template/app/profile/password/page.tsx +27 -27
- package/templates/nextblock-template/app/setup/SetupWizard.tsx +678 -678
- package/templates/nextblock-template/app/setup/layout.tsx +13 -13
- package/templates/nextblock-template/app/setup/page.tsx +111 -111
- package/templates/nextblock-template/app/sitemap.ts +130 -130
- package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
- package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
- package/templates/nextblock-template/components/DeferredGoogleAnalytics.tsx +70 -70
- package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
- package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
- package/templates/nextblock-template/components/FeatureImageHero.tsx +47 -47
- package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
- package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
- package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
- package/templates/nextblock-template/components/PublicEnvBootstrap.tsx +30 -30
- package/templates/nextblock-template/components/ResponsiveNav.tsx +14 -14
- package/templates/nextblock-template/components/auth/AuthBotProtection.tsx +182 -182
- package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -12
- package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -12
- package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
- package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +249 -249
- package/templates/nextblock-template/components/blocks/types.ts +7 -7
- package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
- package/templates/nextblock-template/components/form-message.tsx +32 -32
- package/templates/nextblock-template/components/media/YouTubeFacade.tsx +105 -105
- package/templates/nextblock-template/components/media/youtube-embed-replace.tsx +32 -32
- package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +170 -170
- package/templates/nextblock-template/components/privacy/ConsentGatedAnalytics.tsx +70 -70
- package/templates/nextblock-template/components/renderers/CachedDynamicLayoutEngine.tsx +28 -28
- package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.test.tsx +166 -166
- package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.tsx +471 -471
- package/templates/nextblock-template/components/submit-button.tsx +23 -23
- package/templates/nextblock-template/components/theme-switcher.tsx +8 -8
- package/templates/nextblock-template/components/visual-editing/DeferredVisualEditing.tsx +21 -21
- package/templates/nextblock-template/context/AuthContext.tsx +23 -23
- package/templates/nextblock-template/context/language-rest-client.ts +32 -32
- package/templates/nextblock-template/docker/db/init/99-jwt.sql +6 -6
- package/templates/nextblock-template/docker/db/init/99-roles.sql +25 -25
- package/templates/nextblock-template/docker/kong/kong.yml +112 -112
- package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
- package/templates/nextblock-template/docs/02-ECOMMERCE-CAPABILITIES.md +364 -364
- package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +202 -202
- package/templates/nextblock-template/docs/04-DATABASE-AND-AUTH.md +246 -246
- package/templates/nextblock-template/docs/06-CLI-AND-SCAFFOLDING.md +176 -176
- package/templates/nextblock-template/docs/07-BLOCK-SDK-AND-EXTENSIBILITY.md +146 -146
- package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +222 -222
- package/templates/nextblock-template/docs/11-SELF-HOSTED-DOCKER.md +173 -173
- package/templates/nextblock-template/docs/12-VERCEL-DEPLOYMENT.md +170 -170
- package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +151 -151
- package/templates/nextblock-template/docs/README.md +39 -39
- package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -21
- package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -101
- package/templates/nextblock-template/index.d.ts +7 -7
- package/templates/nextblock-template/lib/app-secrets.ts +39 -39
- package/templates/nextblock-template/lib/auth/cookies.ts +47 -47
- package/templates/nextblock-template/lib/auth/crypto.ts +45 -45
- package/templates/nextblock-template/lib/auth/trustedDevices.ts +92 -92
- package/templates/nextblock-template/lib/auth-redirects.ts +46 -46
- package/templates/nextblock-template/lib/blocks/README.md +13 -13
- package/templates/nextblock-template/lib/botProtection/verify.ts +134 -134
- package/templates/nextblock-template/lib/cms-transfer/server.ts +2243 -2243
- package/templates/nextblock-template/lib/cms-transfer/types.ts +145 -145
- package/templates/nextblock-template/lib/custom-block-definitions.ts +87 -87
- package/templates/nextblock-template/lib/custom-block-r2-upload-shared.ts +178 -178
- package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +140 -140
- package/templates/nextblock-template/lib/custom-block-r2-upload.ts +88 -88
- package/templates/nextblock-template/lib/custom-block-relations.test.ts +227 -227
- package/templates/nextblock-template/lib/custom-block-relations.ts +279 -279
- package/templates/nextblock-template/lib/custom-block-safelist.ts +14 -14
- package/templates/nextblock-template/lib/editor/dynamic-extension-core.test.ts +172 -172
- package/templates/nextblock-template/lib/editor/dynamic-extension-core.ts +213 -213
- package/templates/nextblock-template/lib/editor/dynamic-extension-loader.ts +22 -22
- package/templates/nextblock-template/lib/editor/dynamic-extensions.tsx +193 -193
- package/templates/nextblock-template/lib/email/branding-format.test.ts +133 -133
- package/templates/nextblock-template/lib/email/branding-format.ts +123 -123
- package/templates/nextblock-template/lib/email/branding.ts +76 -76
- package/templates/nextblock-template/lib/full-backup/manifest.test.ts +121 -121
- package/templates/nextblock-template/lib/full-backup/manifest.ts +206 -206
- package/templates/nextblock-template/lib/full-backup/server.ts +743 -743
- package/templates/nextblock-template/lib/logos/active-logo.ts +53 -53
- package/templates/nextblock-template/lib/media/resolveMediaUrl.ts +54 -54
- package/templates/nextblock-template/lib/media/youtube.ts +99 -99
- package/templates/nextblock-template/lib/onboarding/actions.ts +31 -31
- package/templates/nextblock-template/lib/onboarding/status.ts +222 -222
- package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
- package/templates/nextblock-template/lib/privacy/consent-client.ts +57 -57
- package/templates/nextblock-template/lib/privacy/contact-emails.ts +64 -64
- package/templates/nextblock-template/lib/privacy/settings.ts +115 -115
- package/templates/nextblock-template/lib/privacy/types.ts +69 -69
- package/templates/nextblock-template/lib/promotions/server.test.ts +74 -74
- package/templates/nextblock-template/lib/promotions/server.ts +741 -741
- package/templates/nextblock-template/lib/resolve-block-relations.test.ts +142 -142
- package/templates/nextblock-template/lib/resolve-block-relations.ts +255 -255
- package/templates/nextblock-template/lib/search/types.ts +27 -27
- package/templates/nextblock-template/lib/setup/actions.ts +460 -460
- package/templates/nextblock-template/lib/setup/env-status.ts +125 -125
- package/templates/nextblock-template/lib/setup/env-write.ts +111 -111
- package/templates/nextblock-template/lib/setup/migrations-bundle.ts +87 -87
- package/templates/nextblock-template/lib/setup/provisioning.ts +59 -59
- package/templates/nextblock-template/lib/setup/schema-apply.ts +408 -408
- package/templates/nextblock-template/lib/setup/system-config.ts +105 -105
- package/templates/nextblock-template/lib/setup/types.ts +18 -18
- package/templates/nextblock-template/lib/site-url.ts +48 -48
- package/templates/nextblock-template/lib/storage/provider.ts +66 -66
- package/templates/nextblock-template/lib/storage/supabase-storage.ts +103 -103
- package/templates/nextblock-template/lib/updates/check-upstream.ts +441 -441
- package/templates/nextblock-template/lib/updates/github-device.ts +206 -206
- package/templates/nextblock-template/lib/updates/repo-identity.ts +56 -56
- package/templates/nextblock-template/next-env.d.ts +1 -0
- package/templates/nextblock-template/package.json +6 -6
- package/templates/nextblock-template/postcss.config.js +6 -6
- package/templates/nextblock-template/scripts/backup.js +115 -115
- package/templates/nextblock-template/scripts/docker-setup.mjs +310 -310
- package/templates/nextblock-template/scripts/restore.js +385 -385
- package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +98 -98
- package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +62 -62
- package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +537 -537
- package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +58 -58
- package/templates/nextblock-template/scripts/verify-custom-block-definitions.ts +188 -188
- package/templates/nextblock-template/scripts/verify-dynamic-custom-block-extensions.ts +123 -123
- package/templates/nextblock-template/scripts/verify-dynamic-layout-engine.tsx +133 -133
- package/templates/nextblock-template/scripts/verify-milestone-2-custom-blocks.ts +65 -65
- package/templates/nextblock-template/tailwind.config.js +25 -25
- package/templates/nextblock-template/tools/build-migrate.mjs +209 -209
- package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
- package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
- package/templates/nextblock-template/types/jsdom.d.ts +6 -6
- package/tsconfig.base.json +3 -3
|
@@ -1,222 +1,222 @@
|
|
|
1
|
-
# 10 Custom Blocks (Data-Driven CRUD)
|
|
2
|
-
|
|
3
|
-
NextBlock lets editors create their own block types at runtime, directly from
|
|
4
|
-
the CMS, with no code deploy. A custom block is defined as data — typed fields
|
|
5
|
-
plus a recursive layout schema — stored in Supabase and rendered on the public
|
|
6
|
-
site by a dynamic layout engine instead of a compiled React component.
|
|
7
|
-
|
|
8
|
-
This is a separate, complementary system to the code-defined built-in blocks in
|
|
9
|
-
`apps/nextblock/lib/blocks/blockRegistry.ts`. See
|
|
10
|
-
[03-CMS-AND-EDITOR.md](./03-CMS-AND-EDITOR.md) for the built-in block system and
|
|
11
|
-
[07-BLOCK-SDK-AND-EXTENSIBILITY.md](./07-BLOCK-SDK-AND-EXTENSIBILITY.md) for how
|
|
12
|
-
the three extensibility layers relate.
|
|
13
|
-
|
|
14
|
-
## The Core Idea
|
|
15
|
-
|
|
16
|
-
- A **custom block definition** is a row in `custom_block_definitions`.
|
|
17
|
-
- A **custom block instance** is just an ordinary `blocks` row whose
|
|
18
|
-
`block_type` equals a definition's `slug`.
|
|
19
|
-
|
|
20
|
-
Because an instance is a normal block, custom blocks drop into the page builder
|
|
21
|
-
exactly like built-ins: they can sit at the top level of a page/post or nest
|
|
22
|
-
inside `section` columns, and they participate in ordering, drag-and-drop, and
|
|
23
|
-
revisions without special-casing.
|
|
24
|
-
|
|
25
|
-
## Data Model
|
|
26
|
-
|
|
27
|
-
The table is created in
|
|
28
|
-
`libs/db/src/supabase/migrations/00000000000023_setup_custom_block_definitions.sql`.
|
|
29
|
-
|
|
30
|
-
`public.custom_block_definitions`:
|
|
31
|
-
|
|
32
|
-
| Column | Notes |
|
|
33
|
-
| :-- | :-- |
|
|
34
|
-
| `id` | `uuid` primary key |
|
|
35
|
-
| `slug` | unique, `^[a-z][a-z0-9-]*$`; this is the block instance's `block_type` |
|
|
36
|
-
| `name` | display name (non-empty) |
|
|
37
|
-
| `description` | optional, defaults to `''` |
|
|
38
|
-
| `fields` | `jsonb` field declarations; DB `CHECK` via `is_valid_custom_block_fields()` |
|
|
39
|
-
| `layout_schema` | `jsonb` layout tree; DB `CHECK` via `is_valid_custom_block_layout_schema()` |
|
|
40
|
-
| `is_original` | `false` when the row was produced by duplicating another definition |
|
|
41
|
-
|
|
42
|
-
The migration also defines:
|
|
43
|
-
|
|
44
|
-
- `is_valid_custom_block_fields(jsonb)` and
|
|
45
|
-
`is_valid_custom_block_layout_schema(jsonb)` — immutable validation functions
|
|
46
|
-
used as table `CHECK` constraints, so malformed definitions are rejected at
|
|
47
|
-
the database layer even if application validation is bypassed.
|
|
48
|
-
- `duplicate_block_definition(target_id uuid)` — `SECURITY DEFINER` RPC that
|
|
49
|
-
copies a definition, auto-suffixing the slug (`-copy`, `-copy-2`, …), naming
|
|
50
|
-
it `"<name> Copy"`, and setting `is_original = false`. Restricted to
|
|
51
|
-
`ADMIN`/`WRITER` (or `service_role`).
|
|
52
|
-
|
|
53
|
-
### Row Level Security
|
|
54
|
-
|
|
55
|
-
- Public `SELECT` (definitions must be readable to render on the public site).
|
|
56
|
-
- `INSERT` / `UPDATE` / `DELETE` for authenticated users whose role is `ADMIN`
|
|
57
|
-
or `WRITER`.
|
|
58
|
-
- Full access for `service_role`.
|
|
59
|
-
|
|
60
|
-
## Field Types
|
|
61
|
-
|
|
62
|
-
Application-side schemas live in `libs/utils/src/lib/custom-blocks.ts` and are
|
|
63
|
-
exported from `@nextblock-cms/utils`. Every field shares a base of `key`
|
|
64
|
-
(`^[a-z][a-z0-9_]*$`, unique within a block), `label`, optional `description`,
|
|
65
|
-
and `required`. The `type` discriminates four variants:
|
|
66
|
-
|
|
67
|
-
| Type | Purpose | Notable options |
|
|
68
|
-
| :-- | :-- | :-- |
|
|
69
|
-
| `text` | single-line / plain text | `default_value`, `placeholder`, `min_length`, `max_length` |
|
|
70
|
-
| `rich-text` | HTML rich text | `default_value`, `placeholder` |
|
|
71
|
-
| `image_r2` | image stored in R2 | `accept[]`, `max_bytes`, `default_value` = `{ object_key, url, alt, width, height, … }` |
|
|
72
|
-
| `db_relation` | reference rows in a table | `table`, `value_column` (default `id`), `display_column` (default `title`), `multiple`, `filters` |
|
|
73
|
-
|
|
74
|
-
The CMS authoring components map onto these types: `ImageR2Picker` for
|
|
75
|
-
`image_r2`, `DBRelationSelect` for `db_relation`, and the rich-text editor for
|
|
76
|
-
`rich-text`.
|
|
77
|
-
|
|
78
|
-
## Layout Schema
|
|
79
|
-
|
|
80
|
-
`layout_schema` is a recursive discriminated union (`customBlockLayoutNodeSchema`)
|
|
81
|
-
with two node types:
|
|
82
|
-
|
|
83
|
-
- **`container`** — `{ type: 'container', as?, className?, children: [] }`.
|
|
84
|
-
Groups other nodes.
|
|
85
|
-
- **`field_render`** — `{ type: 'field_render', field_key, as?, className?,
|
|
86
|
-
column?, emptyFallback? }`. Renders a single field's value.
|
|
87
|
-
|
|
88
|
-
Rules and helpers:
|
|
89
|
-
|
|
90
|
-
- `as` is restricted to a safe HTML element set (`article`, `aside`,
|
|
91
|
-
`blockquote`, `div`, `figure`, `figcaption`, `h2`, `h3`, `img`, `p`,
|
|
92
|
-
`section`, `span`).
|
|
93
|
-
- `className` accepts Tailwind utility classes.
|
|
94
|
-
- `column` lets a `field_render` bound to a `db_relation` field surface a
|
|
95
|
-
specific column of the resolved record, so one relation field can render
|
|
96
|
-
several columns (e.g. a product's title and price). `emptyFallback` renders
|
|
97
|
-
when the value is empty.
|
|
98
|
-
- Every `field_key` referenced in the layout must exist in `fields`
|
|
99
|
-
(`assertLayoutFieldKeysExist` enforces this in `customBlockDefinitionCreateSchema`).
|
|
100
|
-
- `orderCustomBlockFieldsByLayout()` orders fields to match the layout's
|
|
101
|
-
depth-first `field_render` order for the editor form; `buildCustomBlockCopySlug()`
|
|
102
|
-
mirrors the SQL duplicate-slug logic on the client.
|
|
103
|
-
|
|
104
|
-
Exported Zod surfaces: `customBlockDefinitionCreateSchema`,
|
|
105
|
-
`customBlockDefinitionUpdateSchema`, and `customBlockDefinitionRowSchema`, with
|
|
106
|
-
inferred types `CustomBlockDefinition`, `CustomBlockDefinitionCreateInput`, and
|
|
107
|
-
`CustomBlockDefinitionUpdateInput`.
|
|
108
|
-
|
|
109
|
-
## CMS CRUD Surface
|
|
110
|
-
|
|
111
|
-
Everything lives under `apps/nextblock/app/cms/custom-blocks`:
|
|
112
|
-
|
|
113
|
-
- `page.tsx` — searchable grid/list of definitions with duplicate and delete.
|
|
114
|
-
- `new/page.tsx` and `[id]/edit/page.tsx` — authoring screens.
|
|
115
|
-
- `components/BlockComposer.tsx` — the field + layout composer.
|
|
116
|
-
- `components/DBRelationSelect.tsx`, `components/ImageR2Picker.tsx` — field-type
|
|
117
|
-
editors.
|
|
118
|
-
- `components/BlocksLibraryTransferControls.tsx` — import/export UI.
|
|
119
|
-
|
|
120
|
-
Server actions in `app/cms/custom-blocks/actions.ts` all gate on
|
|
121
|
-
`requireCmsWriter` (`ADMIN`/`WRITER`) and revalidate the
|
|
122
|
-
`custom-block-definitions` cache tag plus `/cms/blocks` and `/cms/custom-blocks`:
|
|
123
|
-
|
|
124
|
-
- `listCustomBlockDefinitions`, `getCustomBlockDefinition`
|
|
125
|
-
- `createCustomBlockDefinition`, `updateCustomBlockDefinition`,
|
|
126
|
-
`deleteCustomBlockDefinition`
|
|
127
|
-
- `duplicateCustomBlockDefinition` (calls the `duplicate_block_definition` RPC)
|
|
128
|
-
- `exportBlocksLibraryAction`, `dryRunBlocksLibraryImportAction`,
|
|
129
|
-
`applyBlocksLibraryImportAction`
|
|
130
|
-
|
|
131
|
-
## Rendering
|
|
132
|
-
|
|
133
|
-
The loader is `apps/nextblock/lib/custom-block-definitions.ts`:
|
|
134
|
-
|
|
135
|
-
- `getCachedCustomBlockDefinitions()` and
|
|
136
|
-
`getCachedCustomBlockDefinitionBySlug()` read through `getSsgSupabaseClient`
|
|
137
|
-
and `unstable_cache` (tag `custom-block-definitions`, 60s revalidate).
|
|
138
|
-
- The by-slug path falls back to a live (uncached) read when the cache misses,
|
|
139
|
-
so a freshly saved block renders immediately instead of showing
|
|
140
|
-
"Unsupported block type" during the revalidation window.
|
|
141
|
-
|
|
142
|
-
`apps/nextblock/components/BlockRenderer.tsx` dispatches blocks: if a
|
|
143
|
-
`block_type` has no built-in renderer, it looks the slug up via
|
|
144
|
-
`getCachedCustomBlockDefinitionBySlug()` and renders through
|
|
145
|
-
`CachedDynamicLayoutEngine` (which wraps
|
|
146
|
-
`components/renderers/DynamicLayoutEngine.tsx`). The dynamic engine walks the
|
|
147
|
-
`layout_schema`, renders `container` nodes as their `as` element with the given
|
|
148
|
-
classes, and resolves each `field_render` against the instance content
|
|
149
|
-
(including `db_relation` lookups). If neither a built-in renderer nor a custom
|
|
150
|
-
definition matches, the renderer shows an "Unsupported block type" notice with
|
|
151
|
-
the offending slug.
|
|
152
|
-
|
|
153
|
-
CMS-side editing of an instance uses
|
|
154
|
-
`app/cms/blocks/editors/DynamicCustomBlockEditor.tsx` and
|
|
155
|
-
`app/cms/blocks/components/CustomBlockEditorPreview.tsx`.
|
|
156
|
-
|
|
157
|
-
## Import / Export / Backup / Restore
|
|
158
|
-
|
|
159
|
-
Custom blocks are portable as a JSON "Blocks Library" bundle. The backend is
|
|
160
|
-
`apps/nextblock/lib/cms-transfer/server.ts`:
|
|
161
|
-
|
|
162
|
-
- `exportBlocksLibraryBundle()` serializes all definitions.
|
|
163
|
-
- `dryRunBlocksLibraryImport()` previews an import without writing.
|
|
164
|
-
- `applyBlocksLibraryImport()` applies it.
|
|
165
|
-
|
|
166
|
-
A bundle entry (`BackupCustomBlockRecord` in
|
|
167
|
-
`apps/nextblock/lib/cms-transfer/types.ts`) carries `slug`, `name`,
|
|
168
|
-
`description`, `fields`, `layout_schema`, and `is_original`. Imports run under a
|
|
169
|
-
conflict mode of `create_new` or `overwrite_existing`, and return a summary of
|
|
170
|
-
`created` / `updated` / `skipped` rows plus warnings and errors. Exports are
|
|
171
|
-
named `nextblock-blocks-library-<YYYY-MM-DD>.json`.
|
|
172
|
-
|
|
173
|
-
Custom blocks also ride along inside the broader content backup bundle
|
|
174
|
-
(`CmsBackupBundleV1.custom_blocks`, optional for backward compatibility),
|
|
175
|
-
surfaced at `/cms/import-export` via `ContentTransferControls.tsx`.
|
|
176
|
-
|
|
177
|
-
## Cortex AI "Build Widget"
|
|
178
|
-
|
|
179
|
-
Cortex AI can generate a custom block definition from a prompt:
|
|
180
|
-
|
|
181
|
-
- Route: `apps/nextblock/app/api/ai/cortex/build-widget/route.ts`.
|
|
182
|
-
- Helpers: `libs/cortex/src/lib/cortex-widget-registry.ts` and the custom-block
|
|
183
|
-
agent tools in `libs/cortex/src/lib/ai-global-agent-custom-block-tools.ts`.
|
|
184
|
-
- After a Cortex-driven change, the front end dispatches a
|
|
185
|
-
`nextblock:cortex-data-changed` event; the custom-blocks list listens for it
|
|
186
|
-
(and for window focus) and refetches so the library stays in sync without a
|
|
187
|
-
reload.
|
|
188
|
-
|
|
189
|
-
See [08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md](./08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md)
|
|
190
|
-
for the surrounding AI architecture and credential model.
|
|
191
|
-
|
|
192
|
-
## Verification
|
|
193
|
-
|
|
194
|
-
```bash
|
|
195
|
-
# Validate the custom block definition schemas / fixtures
|
|
196
|
-
npx tsx apps/nextblock/scripts/verify-custom-block-definitions.ts
|
|
197
|
-
|
|
198
|
-
# Exercise the dynamic layout engine
|
|
199
|
-
npx tsx apps/nextblock/scripts/verify-dynamic-layout-engine.tsx
|
|
200
|
-
|
|
201
|
-
# Cortex AI widget builder (needs OPENROUTER_API_KEY or stored BYOK)
|
|
202
|
-
npm run verify:cortex-ai-build-widget
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
Relevant Vitest files:
|
|
206
|
-
|
|
207
|
-
- `apps/nextblock/components/renderers/DynamicLayoutEngine.test.tsx`
|
|
208
|
-
- `libs/cortex/src/lib/cortex-widget-registry.test.ts`
|
|
209
|
-
- `libs/cortex/src/lib/cortex-widget-schema.test.tsx`
|
|
210
|
-
|
|
211
|
-
## Notes for Contributors
|
|
212
|
-
|
|
213
|
-
- `custom_block_definitions` was added after the squashed migration baseline
|
|
214
|
-
(migration `00000000000023`). Per the production rule in
|
|
215
|
-
[05-DEVELOPER-GUIDE.md](./05-DEVELOPER-GUIDE.md) and the root `AGENTS.md`,
|
|
216
|
-
schema changes here must be new, forward-only migrations.
|
|
217
|
-
- After changing the table or seed data, regenerate the sandbox reset payload
|
|
218
|
-
with `npm run generate:sandbox` (the generated SQL already includes the
|
|
219
|
-
custom blocks migration).
|
|
220
|
-
- The slug is the public contract: renaming a definition's slug orphans every
|
|
221
|
-
existing instance that references the old slug (they fall back to
|
|
222
|
-
"Unsupported block type" until re-pointed).
|
|
1
|
+
# 10 Custom Blocks (Data-Driven CRUD)
|
|
2
|
+
|
|
3
|
+
NextBlock lets editors create their own block types at runtime, directly from
|
|
4
|
+
the CMS, with no code deploy. A custom block is defined as data — typed fields
|
|
5
|
+
plus a recursive layout schema — stored in Supabase and rendered on the public
|
|
6
|
+
site by a dynamic layout engine instead of a compiled React component.
|
|
7
|
+
|
|
8
|
+
This is a separate, complementary system to the code-defined built-in blocks in
|
|
9
|
+
`apps/nextblock/lib/blocks/blockRegistry.ts`. See
|
|
10
|
+
[03-CMS-AND-EDITOR.md](./03-CMS-AND-EDITOR.md) for the built-in block system and
|
|
11
|
+
[07-BLOCK-SDK-AND-EXTENSIBILITY.md](./07-BLOCK-SDK-AND-EXTENSIBILITY.md) for how
|
|
12
|
+
the three extensibility layers relate.
|
|
13
|
+
|
|
14
|
+
## The Core Idea
|
|
15
|
+
|
|
16
|
+
- A **custom block definition** is a row in `custom_block_definitions`.
|
|
17
|
+
- A **custom block instance** is just an ordinary `blocks` row whose
|
|
18
|
+
`block_type` equals a definition's `slug`.
|
|
19
|
+
|
|
20
|
+
Because an instance is a normal block, custom blocks drop into the page builder
|
|
21
|
+
exactly like built-ins: they can sit at the top level of a page/post or nest
|
|
22
|
+
inside `section` columns, and they participate in ordering, drag-and-drop, and
|
|
23
|
+
revisions without special-casing.
|
|
24
|
+
|
|
25
|
+
## Data Model
|
|
26
|
+
|
|
27
|
+
The table is created in
|
|
28
|
+
`libs/db/src/supabase/migrations/00000000000023_setup_custom_block_definitions.sql`.
|
|
29
|
+
|
|
30
|
+
`public.custom_block_definitions`:
|
|
31
|
+
|
|
32
|
+
| Column | Notes |
|
|
33
|
+
| :-- | :-- |
|
|
34
|
+
| `id` | `uuid` primary key |
|
|
35
|
+
| `slug` | unique, `^[a-z][a-z0-9-]*$`; this is the block instance's `block_type` |
|
|
36
|
+
| `name` | display name (non-empty) |
|
|
37
|
+
| `description` | optional, defaults to `''` |
|
|
38
|
+
| `fields` | `jsonb` field declarations; DB `CHECK` via `is_valid_custom_block_fields()` |
|
|
39
|
+
| `layout_schema` | `jsonb` layout tree; DB `CHECK` via `is_valid_custom_block_layout_schema()` |
|
|
40
|
+
| `is_original` | `false` when the row was produced by duplicating another definition |
|
|
41
|
+
|
|
42
|
+
The migration also defines:
|
|
43
|
+
|
|
44
|
+
- `is_valid_custom_block_fields(jsonb)` and
|
|
45
|
+
`is_valid_custom_block_layout_schema(jsonb)` — immutable validation functions
|
|
46
|
+
used as table `CHECK` constraints, so malformed definitions are rejected at
|
|
47
|
+
the database layer even if application validation is bypassed.
|
|
48
|
+
- `duplicate_block_definition(target_id uuid)` — `SECURITY DEFINER` RPC that
|
|
49
|
+
copies a definition, auto-suffixing the slug (`-copy`, `-copy-2`, …), naming
|
|
50
|
+
it `"<name> Copy"`, and setting `is_original = false`. Restricted to
|
|
51
|
+
`ADMIN`/`WRITER` (or `service_role`).
|
|
52
|
+
|
|
53
|
+
### Row Level Security
|
|
54
|
+
|
|
55
|
+
- Public `SELECT` (definitions must be readable to render on the public site).
|
|
56
|
+
- `INSERT` / `UPDATE` / `DELETE` for authenticated users whose role is `ADMIN`
|
|
57
|
+
or `WRITER`.
|
|
58
|
+
- Full access for `service_role`.
|
|
59
|
+
|
|
60
|
+
## Field Types
|
|
61
|
+
|
|
62
|
+
Application-side schemas live in `libs/utils/src/lib/custom-blocks.ts` and are
|
|
63
|
+
exported from `@nextblock-cms/utils`. Every field shares a base of `key`
|
|
64
|
+
(`^[a-z][a-z0-9_]*$`, unique within a block), `label`, optional `description`,
|
|
65
|
+
and `required`. The `type` discriminates four variants:
|
|
66
|
+
|
|
67
|
+
| Type | Purpose | Notable options |
|
|
68
|
+
| :-- | :-- | :-- |
|
|
69
|
+
| `text` | single-line / plain text | `default_value`, `placeholder`, `min_length`, `max_length` |
|
|
70
|
+
| `rich-text` | HTML rich text | `default_value`, `placeholder` |
|
|
71
|
+
| `image_r2` | image stored in R2 | `accept[]`, `max_bytes`, `default_value` = `{ object_key, url, alt, width, height, … }` |
|
|
72
|
+
| `db_relation` | reference rows in a table | `table`, `value_column` (default `id`), `display_column` (default `title`), `multiple`, `filters` |
|
|
73
|
+
|
|
74
|
+
The CMS authoring components map onto these types: `ImageR2Picker` for
|
|
75
|
+
`image_r2`, `DBRelationSelect` for `db_relation`, and the rich-text editor for
|
|
76
|
+
`rich-text`.
|
|
77
|
+
|
|
78
|
+
## Layout Schema
|
|
79
|
+
|
|
80
|
+
`layout_schema` is a recursive discriminated union (`customBlockLayoutNodeSchema`)
|
|
81
|
+
with two node types:
|
|
82
|
+
|
|
83
|
+
- **`container`** — `{ type: 'container', as?, className?, children: [] }`.
|
|
84
|
+
Groups other nodes.
|
|
85
|
+
- **`field_render`** — `{ type: 'field_render', field_key, as?, className?,
|
|
86
|
+
column?, emptyFallback? }`. Renders a single field's value.
|
|
87
|
+
|
|
88
|
+
Rules and helpers:
|
|
89
|
+
|
|
90
|
+
- `as` is restricted to a safe HTML element set (`article`, `aside`,
|
|
91
|
+
`blockquote`, `div`, `figure`, `figcaption`, `h2`, `h3`, `img`, `p`,
|
|
92
|
+
`section`, `span`).
|
|
93
|
+
- `className` accepts Tailwind utility classes.
|
|
94
|
+
- `column` lets a `field_render` bound to a `db_relation` field surface a
|
|
95
|
+
specific column of the resolved record, so one relation field can render
|
|
96
|
+
several columns (e.g. a product's title and price). `emptyFallback` renders
|
|
97
|
+
when the value is empty.
|
|
98
|
+
- Every `field_key` referenced in the layout must exist in `fields`
|
|
99
|
+
(`assertLayoutFieldKeysExist` enforces this in `customBlockDefinitionCreateSchema`).
|
|
100
|
+
- `orderCustomBlockFieldsByLayout()` orders fields to match the layout's
|
|
101
|
+
depth-first `field_render` order for the editor form; `buildCustomBlockCopySlug()`
|
|
102
|
+
mirrors the SQL duplicate-slug logic on the client.
|
|
103
|
+
|
|
104
|
+
Exported Zod surfaces: `customBlockDefinitionCreateSchema`,
|
|
105
|
+
`customBlockDefinitionUpdateSchema`, and `customBlockDefinitionRowSchema`, with
|
|
106
|
+
inferred types `CustomBlockDefinition`, `CustomBlockDefinitionCreateInput`, and
|
|
107
|
+
`CustomBlockDefinitionUpdateInput`.
|
|
108
|
+
|
|
109
|
+
## CMS CRUD Surface
|
|
110
|
+
|
|
111
|
+
Everything lives under `apps/nextblock/app/cms/custom-blocks`:
|
|
112
|
+
|
|
113
|
+
- `page.tsx` — searchable grid/list of definitions with duplicate and delete.
|
|
114
|
+
- `new/page.tsx` and `[id]/edit/page.tsx` — authoring screens.
|
|
115
|
+
- `components/BlockComposer.tsx` — the field + layout composer.
|
|
116
|
+
- `components/DBRelationSelect.tsx`, `components/ImageR2Picker.tsx` — field-type
|
|
117
|
+
editors.
|
|
118
|
+
- `components/BlocksLibraryTransferControls.tsx` — import/export UI.
|
|
119
|
+
|
|
120
|
+
Server actions in `app/cms/custom-blocks/actions.ts` all gate on
|
|
121
|
+
`requireCmsWriter` (`ADMIN`/`WRITER`) and revalidate the
|
|
122
|
+
`custom-block-definitions` cache tag plus `/cms/blocks` and `/cms/custom-blocks`:
|
|
123
|
+
|
|
124
|
+
- `listCustomBlockDefinitions`, `getCustomBlockDefinition`
|
|
125
|
+
- `createCustomBlockDefinition`, `updateCustomBlockDefinition`,
|
|
126
|
+
`deleteCustomBlockDefinition`
|
|
127
|
+
- `duplicateCustomBlockDefinition` (calls the `duplicate_block_definition` RPC)
|
|
128
|
+
- `exportBlocksLibraryAction`, `dryRunBlocksLibraryImportAction`,
|
|
129
|
+
`applyBlocksLibraryImportAction`
|
|
130
|
+
|
|
131
|
+
## Rendering
|
|
132
|
+
|
|
133
|
+
The loader is `apps/nextblock/lib/custom-block-definitions.ts`:
|
|
134
|
+
|
|
135
|
+
- `getCachedCustomBlockDefinitions()` and
|
|
136
|
+
`getCachedCustomBlockDefinitionBySlug()` read through `getSsgSupabaseClient`
|
|
137
|
+
and `unstable_cache` (tag `custom-block-definitions`, 60s revalidate).
|
|
138
|
+
- The by-slug path falls back to a live (uncached) read when the cache misses,
|
|
139
|
+
so a freshly saved block renders immediately instead of showing
|
|
140
|
+
"Unsupported block type" during the revalidation window.
|
|
141
|
+
|
|
142
|
+
`apps/nextblock/components/BlockRenderer.tsx` dispatches blocks: if a
|
|
143
|
+
`block_type` has no built-in renderer, it looks the slug up via
|
|
144
|
+
`getCachedCustomBlockDefinitionBySlug()` and renders through
|
|
145
|
+
`CachedDynamicLayoutEngine` (which wraps
|
|
146
|
+
`components/renderers/DynamicLayoutEngine.tsx`). The dynamic engine walks the
|
|
147
|
+
`layout_schema`, renders `container` nodes as their `as` element with the given
|
|
148
|
+
classes, and resolves each `field_render` against the instance content
|
|
149
|
+
(including `db_relation` lookups). If neither a built-in renderer nor a custom
|
|
150
|
+
definition matches, the renderer shows an "Unsupported block type" notice with
|
|
151
|
+
the offending slug.
|
|
152
|
+
|
|
153
|
+
CMS-side editing of an instance uses
|
|
154
|
+
`app/cms/blocks/editors/DynamicCustomBlockEditor.tsx` and
|
|
155
|
+
`app/cms/blocks/components/CustomBlockEditorPreview.tsx`.
|
|
156
|
+
|
|
157
|
+
## Import / Export / Backup / Restore
|
|
158
|
+
|
|
159
|
+
Custom blocks are portable as a JSON "Blocks Library" bundle. The backend is
|
|
160
|
+
`apps/nextblock/lib/cms-transfer/server.ts`:
|
|
161
|
+
|
|
162
|
+
- `exportBlocksLibraryBundle()` serializes all definitions.
|
|
163
|
+
- `dryRunBlocksLibraryImport()` previews an import without writing.
|
|
164
|
+
- `applyBlocksLibraryImport()` applies it.
|
|
165
|
+
|
|
166
|
+
A bundle entry (`BackupCustomBlockRecord` in
|
|
167
|
+
`apps/nextblock/lib/cms-transfer/types.ts`) carries `slug`, `name`,
|
|
168
|
+
`description`, `fields`, `layout_schema`, and `is_original`. Imports run under a
|
|
169
|
+
conflict mode of `create_new` or `overwrite_existing`, and return a summary of
|
|
170
|
+
`created` / `updated` / `skipped` rows plus warnings and errors. Exports are
|
|
171
|
+
named `nextblock-blocks-library-<YYYY-MM-DD>.json`.
|
|
172
|
+
|
|
173
|
+
Custom blocks also ride along inside the broader content backup bundle
|
|
174
|
+
(`CmsBackupBundleV1.custom_blocks`, optional for backward compatibility),
|
|
175
|
+
surfaced at `/cms/import-export` via `ContentTransferControls.tsx`.
|
|
176
|
+
|
|
177
|
+
## Cortex AI "Build Widget"
|
|
178
|
+
|
|
179
|
+
Cortex AI can generate a custom block definition from a prompt:
|
|
180
|
+
|
|
181
|
+
- Route: `apps/nextblock/app/api/ai/cortex/build-widget/route.ts`.
|
|
182
|
+
- Helpers: `libs/cortex/src/lib/cortex-widget-registry.ts` and the custom-block
|
|
183
|
+
agent tools in `libs/cortex/src/lib/ai-global-agent-custom-block-tools.ts`.
|
|
184
|
+
- After a Cortex-driven change, the front end dispatches a
|
|
185
|
+
`nextblock:cortex-data-changed` event; the custom-blocks list listens for it
|
|
186
|
+
(and for window focus) and refetches so the library stays in sync without a
|
|
187
|
+
reload.
|
|
188
|
+
|
|
189
|
+
See [08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md](./08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md)
|
|
190
|
+
for the surrounding AI architecture and credential model.
|
|
191
|
+
|
|
192
|
+
## Verification
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# Validate the custom block definition schemas / fixtures
|
|
196
|
+
npx tsx apps/nextblock/scripts/verify-custom-block-definitions.ts
|
|
197
|
+
|
|
198
|
+
# Exercise the dynamic layout engine
|
|
199
|
+
npx tsx apps/nextblock/scripts/verify-dynamic-layout-engine.tsx
|
|
200
|
+
|
|
201
|
+
# Cortex AI widget builder (needs OPENROUTER_API_KEY or stored BYOK)
|
|
202
|
+
npm run verify:cortex-ai-build-widget
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Relevant Vitest files:
|
|
206
|
+
|
|
207
|
+
- `apps/nextblock/components/renderers/DynamicLayoutEngine.test.tsx`
|
|
208
|
+
- `libs/cortex/src/lib/cortex-widget-registry.test.ts`
|
|
209
|
+
- `libs/cortex/src/lib/cortex-widget-schema.test.tsx`
|
|
210
|
+
|
|
211
|
+
## Notes for Contributors
|
|
212
|
+
|
|
213
|
+
- `custom_block_definitions` was added after the squashed migration baseline
|
|
214
|
+
(migration `00000000000023`). Per the production rule in
|
|
215
|
+
[05-DEVELOPER-GUIDE.md](./05-DEVELOPER-GUIDE.md) and the root `AGENTS.md`,
|
|
216
|
+
schema changes here must be new, forward-only migrations.
|
|
217
|
+
- After changing the table or seed data, regenerate the sandbox reset payload
|
|
218
|
+
with `npm run generate:sandbox` (the generated SQL already includes the
|
|
219
|
+
custom blocks migration).
|
|
220
|
+
- The slug is the public contract: renaming a definition's slug orphans every
|
|
221
|
+
existing instance that references the old slug (they fall back to
|
|
222
|
+
"Unsupported block type" until re-pointed).
|