create-nextblock 0.16.1 → 0.16.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 +1 -1
- 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 +50 -50
- package/project.json +19 -19
- package/templates/nextblock-template/.dockerignore +1 -1
- package/templates/nextblock-template/.swcrc +30 -30
- package/templates/nextblock-template/AGENTS.md +9 -9
- package/templates/nextblock-template/CLAUDE.md +1 -1
- 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/.well-known/ucp/route.ts +16 -16
- package/templates/nextblock-template/app/ToasterProvider.tsx +26 -26
- package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -7
- package/templates/nextblock-template/app/actions/contactSellerActions.test.ts +280 -280
- package/templates/nextblock-template/app/actions/contactSellerActions.ts +222 -222
- package/templates/nextblock-template/app/actions/email-retry.test.ts +62 -62
- package/templates/nextblock-template/app/actions/email.ts +241 -241
- package/templates/nextblock-template/app/actions/formActions.ts +245 -245
- package/templates/nextblock-template/app/actions/interactions.ts +489 -489
- package/templates/nextblock-template/app/actions/threadActions.ts +166 -166
- package/templates/nextblock-template/app/actions/visibilityActions.ts +210 -210
- package/templates/nextblock-template/app/api/ai/seo/alt-text/route.ts +221 -221
- package/templates/nextblock-template/app/api/ai/seo/metadata/route.ts +186 -186
- package/templates/nextblock-template/app/api/checkout/freemius/sync/route.ts +29 -29
- package/templates/nextblock-template/app/api/checkout/route.ts +162 -162
- package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +3092 -3092
- package/templates/nextblock-template/app/api/mcp/route.ts +415 -415
- 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/api/view/route.ts +114 -114
- package/templates/nextblock-template/app/api/visual-editing/block-draft/route.ts +47 -47
- package/templates/nextblock-template/app/api/visual-editing/product-draft/route.ts +47 -47
- 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/checkout/UcpCartHydrator.tsx +20 -20
- package/templates/nextblock-template/app/checkout/page.tsx +57 -57
- package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +558 -558
- package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +241 -241
- package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
- package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +304 -304
- package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +406 -406
- package/templates/nextblock-template/app/cms/components/ContactReminderBanner.tsx +75 -75
- package/templates/nextblock-template/app/cms/components/CortexAiActiveContext.tsx +23 -23
- package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
- package/templates/nextblock-template/app/cms/components/FeatureImageField.tsx +254 -254
- package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
- package/templates/nextblock-template/app/cms/components/PaymentsReminderBanner.tsx +58 -58
- package/templates/nextblock-template/app/cms/components/VisibilityBadge.tsx +62 -62
- package/templates/nextblock-template/app/cms/components/VisibilityControl.tsx +542 -542
- package/templates/nextblock-template/app/cms/coupons/[id]/edit/page.tsx +16 -16
- package/templates/nextblock-template/app/cms/coupons/page.tsx +16 -16
- package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
- package/templates/nextblock-template/app/cms/dashboard/components/DashboardComponents.tsx +200 -200
- package/templates/nextblock-template/app/cms/inquiries/actions.ts +66 -66
- package/templates/nextblock-template/app/cms/inquiries/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/interactions/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/layout.tsx +101 -101
- 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/MediaGridClient.tsx +69 -69
- package/templates/nextblock-template/app/cms/messages/MessagesClient.tsx +661 -661
- package/templates/nextblock-template/app/cms/messages/actions.ts +404 -404
- package/templates/nextblock-template/app/cms/messages/loadInbox.ts +333 -333
- package/templates/nextblock-template/app/cms/messages/page.tsx +87 -87
- package/templates/nextblock-template/app/cms/messages/require-admin.ts +37 -37
- package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
- package/templates/nextblock-template/app/cms/pages/components/PageForm.tsx +649 -649
- package/templates/nextblock-template/app/cms/posts/components/PostForm.tsx +618 -618
- package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +370 -370
- package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
- 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/revisions/actions.ts +332 -332
- package/templates/nextblock-template/app/cms/revisions/service.test.ts +498 -498
- package/templates/nextblock-template/app/cms/revisions/service.ts +569 -569
- package/templates/nextblock-template/app/cms/revisions/utils.ts +304 -304
- package/templates/nextblock-template/app/cms/settings/cortex-ai/CortexAiSettingsClient.tsx +948 -948
- package/templates/nextblock-template/app/cms/settings/cortex-ai/McpServerSettingsCard.tsx +628 -628
- package/templates/nextblock-template/app/cms/settings/cortex-ai/mcp-actions.ts +230 -230
- package/templates/nextblock-template/app/cms/settings/cortex-ai/require-admin.ts +34 -34
- 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/components/EmailForm.tsx +227 -227
- package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
- package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +276 -276
- package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
- package/templates/nextblock-template/app/cms/settings/global-css/components/ThemeEditor.tsx +382 -382
- package/templates/nextblock-template/app/cms/settings/global-css/components/ThemeManager.tsx +267 -267
- package/templates/nextblock-template/app/cms/settings/global-css/page.tsx +40 -40
- package/templates/nextblock-template/app/cms/settings/global-css/theme-actions.ts +259 -259
- 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/new/page.tsx +8 -8
- package/templates/nextblock-template/app/cms/settings/seo/RedirectsCard.tsx +514 -514
- package/templates/nextblock-template/app/cms/settings/seo/RobotsCard.tsx +529 -529
- package/templates/nextblock-template/app/cms/settings/seo/SeoSettingsClient.tsx +57 -57
- package/templates/nextblock-template/app/cms/settings/seo/actions.ts +448 -448
- package/templates/nextblock-template/app/cms/settings/seo/mappers.ts +93 -93
- package/templates/nextblock-template/app/cms/settings/seo/page.tsx +46 -46
- package/templates/nextblock-template/app/cms/settings/seo/require-admin.ts +47 -47
- package/templates/nextblock-template/app/cms/settings/site-scripts/page.tsx +51 -51
- 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/layout.tsx +671 -671
- package/templates/nextblock-template/app/lib/seo.ts +319 -319
- package/templates/nextblock-template/app/lib/ucp/protocol.ts +190 -190
- package/templates/nextblock-template/app/lib/ucp/server.test.ts +56 -56
- package/templates/nextblock-template/app/product/[slug]/page.tsx +502 -502
- 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/providers.tsx +96 -96
- package/templates/nextblock-template/app/robots.ts +123 -123
- package/templates/nextblock-template/app/thread/ThreadView.tsx +164 -164
- package/templates/nextblock-template/app/thread/[token]/route.ts +57 -57
- package/templates/nextblock-template/app/thread/layout.tsx +15 -15
- package/templates/nextblock-template/app/thread/page.tsx +98 -98
- package/templates/nextblock-template/app/ucp/v1/carts/[id]/cancel/route.ts +38 -38
- package/templates/nextblock-template/app/ucp/v1/carts/[id]/route.ts +68 -68
- package/templates/nextblock-template/app/ucp/v1/carts/route.ts +35 -35
- package/templates/nextblock-template/app/ucp/v1/catalog/lookup/route.ts +35 -35
- package/templates/nextblock-template/app/ucp/v1/catalog/product/route.ts +35 -35
- package/templates/nextblock-template/app/ucp/v1/catalog/search/route.ts +34 -34
- package/templates/nextblock-template/components/BlockRenderer.tsx +312 -312
- package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
- package/templates/nextblock-template/components/CartTranslator.tsx +210 -210
- package/templates/nextblock-template/components/ContactSellerSection.tsx +188 -188
- package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
- package/templates/nextblock-template/components/DeferredCartTranslator.tsx +51 -51
- package/templates/nextblock-template/components/DeferredGlobalSearch.tsx +68 -68
- package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
- package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
- package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
- package/templates/nextblock-template/components/GlobalSearch.tsx +557 -557
- package/templates/nextblock-template/components/Header.tsx +38 -38
- package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
- package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
- package/templates/nextblock-template/components/PostCommentsSection.tsx +378 -378
- package/templates/nextblock-template/components/ProductReviewsSection.tsx +426 -426
- package/templates/nextblock-template/components/SiteScripts.tsx +56 -56
- package/templates/nextblock-template/components/StaffReplies.tsx +102 -102
- 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/PostsGridClient.tsx +48 -48
- package/templates/nextblock-template/components/blocks/TestimonialBlock.tsx +9 -9
- package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
- package/templates/nextblock-template/components/blocks/publicRendererLoaders.ts +25 -25
- package/templates/nextblock-template/components/blocks/renderers/ButtonBlockRenderer.tsx +92 -92
- package/templates/nextblock-template/components/blocks/renderers/CartBlockRenderer.tsx +18 -18
- package/templates/nextblock-template/components/blocks/renderers/CheckoutBlockRenderer.tsx +20 -20
- package/templates/nextblock-template/components/blocks/renderers/FeaturedProductBlockRenderer.tsx +25 -25
- package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +385 -385
- package/templates/nextblock-template/components/blocks/renderers/PostsGridBlockRenderer.tsx +24 -24
- package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +157 -157
- package/templates/nextblock-template/components/blocks/renderers/ProductGridBlockRenderer.tsx +34 -34
- package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +612 -612
- package/templates/nextblock-template/components/blocks/renderers/TestimonialBlockRenderer.tsx +57 -57
- package/templates/nextblock-template/components/blocks/renderers/inline/AlertWidgetRenderer.tsx +2 -2
- package/templates/nextblock-template/components/blocks/renderers/inline/CtaWidgetRenderer.tsx +2 -2
- package/templates/nextblock-template/components/blocks/types.ts +7 -7
- package/templates/nextblock-template/components/commerce/PaymentReadinessBoundary.tsx +32 -32
- 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/seo/GenerateMetaButton.tsx +137 -137
- package/templates/nextblock-template/components/seo/PageSeoAuditSection.tsx +244 -244
- package/templates/nextblock-template/components/seo/SeoAuditPanel.tsx +749 -749
- package/templates/nextblock-template/components/seo/SeoIssueList.tsx +195 -195
- package/templates/nextblock-template/components/seo/SeoScoreDial.tsx +144 -144
- package/templates/nextblock-template/components/seo/SocialPreview.tsx +243 -243
- package/templates/nextblock-template/components/seo/SocialPreviewDialog.tsx +110 -110
- package/templates/nextblock-template/components/submit-button.tsx +23 -23
- package/templates/nextblock-template/components/theme-icon.tsx +78 -78
- package/templates/nextblock-template/components/theme-switcher.tsx +85 -85
- package/templates/nextblock-template/context/AuthContext.tsx +23 -23
- package/templates/nextblock-template/context/ThemeCatalogContext.tsx +44 -44
- package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
- package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +77 -77
- package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +372 -372
- package/templates/nextblock-template/docs/14-MESSAGES-INBOX.md +309 -309
- package/templates/nextblock-template/docs/README.md +42 -42
- package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +12506 -12506
- 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/auth-redirects.ts +46 -46
- package/templates/nextblock-template/lib/blocks/blockColors.test.ts +134 -134
- package/templates/nextblock-template/lib/blocks/blockColors.ts +175 -175
- package/templates/nextblock-template/lib/blocks/blockRegistry.ts +761 -761
- package/templates/nextblock-template/lib/blocks/inlineScriptNonce.ts +20 -20
- package/templates/nextblock-template/lib/cms/contact-reminder.ts +64 -64
- package/templates/nextblock-template/lib/cms/payments-reminder.test.ts +135 -0
- package/templates/nextblock-template/lib/cms/payments-reminder.ts +30 -23
- package/templates/nextblock-template/lib/cms/unread-messages.ts +42 -42
- package/templates/nextblock-template/lib/commerce/seller-contact.ts +162 -162
- package/templates/nextblock-template/lib/config/email-settings.ts +323 -323
- package/templates/nextblock-template/lib/config/email-tls.test.ts +57 -57
- package/templates/nextblock-template/lib/cortex-ai/alt-text-request.ts +86 -86
- package/templates/nextblock-template/lib/cortex-ai/sandbox-headers.ts +60 -60
- package/templates/nextblock-template/lib/email/placeholder-address.test.ts +59 -59
- package/templates/nextblock-template/lib/email/placeholder-address.ts +39 -39
- package/templates/nextblock-template/lib/messages/thread-reference.test.ts +70 -70
- package/templates/nextblock-template/lib/messages/thread-token.test.ts +93 -93
- package/templates/nextblock-template/lib/messages/thread-token.ts +157 -157
- package/templates/nextblock-template/lib/messages/threads.ts +579 -579
- package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
- package/templates/nextblock-template/lib/publishing/viewUrl.ts +26 -26
- package/templates/nextblock-template/lib/search/types.ts +27 -27
- package/templates/nextblock-template/lib/seo/alt-text-write-back.test.ts +154 -154
- package/templates/nextblock-template/lib/seo/alt-text-write-back.ts +109 -109
- package/templates/nextblock-template/lib/seo/block-content.ts +123 -123
- package/templates/nextblock-template/lib/seo/fix-prompts.test.ts +242 -242
- package/templates/nextblock-template/lib/seo/fix-prompts.ts +204 -204
- package/templates/nextblock-template/lib/seo/page-audit-context.tsx +140 -140
- package/templates/nextblock-template/lib/seo/page-document.test.ts +350 -350
- package/templates/nextblock-template/lib/seo/page-document.ts +412 -412
- package/templates/nextblock-template/lib/seo/redirect-store.test.ts +479 -479
- package/templates/nextblock-template/lib/seo/redirect-store.ts +466 -466
- package/templates/nextblock-template/lib/seo/robots-settings-signature.test.ts +102 -102
- package/templates/nextblock-template/lib/seo/robots-settings-signature.ts +41 -41
- package/templates/nextblock-template/lib/seo/robots-txt.test.ts +370 -370
- package/templates/nextblock-template/lib/seo/robots-txt.ts +510 -510
- package/templates/nextblock-template/lib/setup/migrations-bundle.ts +177 -177
- package/templates/nextblock-template/lib/site-scripts/revisions.ts +71 -71
- package/templates/nextblock-template/lib/site-scripts/types.ts +46 -46
- package/templates/nextblock-template/lib/site-url.test.ts +89 -89
- package/templates/nextblock-template/lib/site-url.ts +102 -102
- package/templates/nextblock-template/lib/themes/buildThemeCss.ts +124 -124
- package/templates/nextblock-template/lib/themes/tokenColor.ts +31 -31
- package/templates/nextblock-template/lib/themes/tokens.ts +143 -143
- package/templates/nextblock-template/lib/visual-editing/draft-content.test.ts +105 -105
- package/templates/nextblock-template/lib/visual-editing/draft-route.test.ts +42 -42
- package/templates/nextblock-template/lib/visual-editing/edit-info.test.ts +143 -143
- package/templates/nextblock-template/lib/visual-editing/edit-info.ts +94 -94
- package/templates/nextblock-template/lib/visual-editing/product-drafts.test.ts +81 -81
- package/templates/nextblock-template/lib/zod-config.ts +5 -5
- package/templates/nextblock-template/next-env.d.ts +1 -2
- package/templates/nextblock-template/package.json +1 -1
- package/templates/nextblock-template/postcss.config.js +6 -6
- package/templates/nextblock-template/scripts/backup.js +115 -115
- package/templates/nextblock-template/scripts/restore.js +385 -385
- package/templates/nextblock-template/scripts/validate-editor-block-schema.ts +112 -112
- package/templates/nextblock-template/tailwind.config.js +25 -25
- package/templates/nextblock-template/tools/build-migrate.mjs +102 -102
- package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
- package/templates/nextblock-template/tools/deploy-supabase.js +159 -159
- package/templates/nextblock-template/tools/lib/migrate-core.mjs +569 -569
- package/templates/nextblock-template/tools/update.mjs +1303 -1303
- package/tsconfig.base.json +3 -3
|
@@ -1,140 +1,140 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Shared live state for the page-level SEO audit.
|
|
5
|
-
*
|
|
6
|
-
* WHY THIS EXISTS. The pieces an SEO audit needs are spread across three sibling
|
|
7
|
-
* components that have no relationship to each other: `BlockEditorArea` owns the
|
|
8
|
-
* live `blocks` array (it is the only client component that holds all of them),
|
|
9
|
-
* `PageForm` / `PostForm` own the meta title and description as local form state,
|
|
10
|
-
* and the panel that has to grade all of it renders next to both. Lifting any one
|
|
11
|
-
* of those into another would mean restructuring an edit screen that works; a small
|
|
12
|
-
* context is the cheaper and less invasive answer.
|
|
13
|
-
*
|
|
14
|
-
* WHY THE AUDIT IS PAGE-LEVEL AND NOT PER-BLOCK. The first version of this feature
|
|
15
|
-
* mounted the panel inside the rich-text editor, where it could only ever see one
|
|
16
|
-
* block. That produced confidently wrong results: a single paragraph block was told
|
|
17
|
-
* it had no H1 and fewer than 300 words, neither of which is a property a block can
|
|
18
|
-
* have. Headings in NextBlock live in two places — standalone `heading` blocks and
|
|
19
|
-
* h1–h6 nodes inside a `text` block's HTML — and blocks nested in a `section` live
|
|
20
|
-
* in `content.column_blocks[column][index]` rather than in the top-level array. Only
|
|
21
|
-
* something walking the whole tree can answer "does this page have exactly one H1".
|
|
22
|
-
* So the page-level panel is the real audit, and the in-editor panel is narrowed to
|
|
23
|
-
* the handful of checks a single block genuinely owns.
|
|
24
|
-
*
|
|
25
|
-
* PUBLISHING IS EFFECT-DRIVEN, deliberately. Both producers call their setter from a
|
|
26
|
-
* `useEffect` keyed on the value they publish, never during render — a child calling
|
|
27
|
-
* a parent's setState while rendering is the classic "Cannot update a component
|
|
28
|
-
* while rendering a different component" warning, and with the blocks array it would
|
|
29
|
-
* be an update loop rather than just a warning.
|
|
30
|
-
*
|
|
31
|
-
* `usePageSeo()` RETURNS NULL OUTSIDE A PROVIDER on purpose. `PageForm`,
|
|
32
|
-
* `BlockEditorArea` and `TextBlockEditor` are all reachable from surfaces that have
|
|
33
|
-
* no page-level audit — the product editor is the live example — and none of them
|
|
34
|
-
* should crash or need a second code path just because the panel is absent.
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
import React, {
|
|
38
|
-
createContext,
|
|
39
|
-
useCallback,
|
|
40
|
-
useContext,
|
|
41
|
-
useMemo,
|
|
42
|
-
useState,
|
|
43
|
-
type ReactNode,
|
|
44
|
-
} from 'react';
|
|
45
|
-
|
|
46
|
-
/** Everything the page-level audit grades, as it currently stands in the editor. */
|
|
47
|
-
export interface PageSeoSnapshot {
|
|
48
|
-
/**
|
|
49
|
-
* The live blocks array, exactly as `BlockEditorArea` holds it. Typed as
|
|
50
|
-
* `unknown[]` because this module has no business knowing the block union — the
|
|
51
|
-
* walker in `page-document.ts` is defensive about shape anyway, since block
|
|
52
|
-
* content is `Json` and a custom block can carry anything.
|
|
53
|
-
*/
|
|
54
|
-
blocks: unknown[];
|
|
55
|
-
metaDescription: string | null;
|
|
56
|
-
metaTitle: string | null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface PageSeoContextValue {
|
|
60
|
-
/** The focus keyphrase, held here so it survives switching between blocks. */
|
|
61
|
-
keyword: string;
|
|
62
|
-
setBlocks: (blocks: unknown[]) => void;
|
|
63
|
-
setKeyword: (keyword: string) => void;
|
|
64
|
-
setMeta: (meta: { metaDescription: string | null; metaTitle: string | null }) => void;
|
|
65
|
-
snapshot: PageSeoSnapshot;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const PageSeoContext = createContext<PageSeoContextValue | null>(null);
|
|
69
|
-
|
|
70
|
-
export interface PageSeoProviderProps {
|
|
71
|
-
children: ReactNode;
|
|
72
|
-
initialBlocks?: unknown[];
|
|
73
|
-
initialMetaDescription?: string | null;
|
|
74
|
-
initialMetaTitle?: string | null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function PageSeoProvider({
|
|
78
|
-
children,
|
|
79
|
-
initialBlocks = [],
|
|
80
|
-
initialMetaDescription = null,
|
|
81
|
-
initialMetaTitle = null,
|
|
82
|
-
}: PageSeoProviderProps) {
|
|
83
|
-
const [blocks, setBlocksState] = useState<unknown[]>(initialBlocks);
|
|
84
|
-
const [keyword, setKeyword] = useState('');
|
|
85
|
-
const [meta, setMetaState] = useState<{
|
|
86
|
-
metaDescription: string | null;
|
|
87
|
-
metaTitle: string | null;
|
|
88
|
-
}>({ metaDescription: initialMetaDescription, metaTitle: initialMetaTitle });
|
|
89
|
-
|
|
90
|
-
// Both setters bail when nothing actually changed. The blocks array is rebuilt on
|
|
91
|
-
// every keystroke in a block editor, so without the length-and-identity check
|
|
92
|
-
// below every character typed would re-render the provider and every consumer of
|
|
93
|
-
// this context, not just the panel that debounces its own work.
|
|
94
|
-
const setBlocks = useCallback((next: unknown[]) => {
|
|
95
|
-
setBlocksState((previous) =>
|
|
96
|
-
previous.length === next.length && previous.every((block, index) => block === next[index])
|
|
97
|
-
? previous
|
|
98
|
-
: next,
|
|
99
|
-
);
|
|
100
|
-
}, []);
|
|
101
|
-
|
|
102
|
-
const setMeta = useCallback(
|
|
103
|
-
(next: { metaDescription: string | null; metaTitle: string | null }) => {
|
|
104
|
-
setMetaState((previous) =>
|
|
105
|
-
previous.metaDescription === next.metaDescription && previous.metaTitle === next.metaTitle
|
|
106
|
-
? previous
|
|
107
|
-
: next,
|
|
108
|
-
);
|
|
109
|
-
},
|
|
110
|
-
[],
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
const value = useMemo<PageSeoContextValue>(
|
|
114
|
-
() => ({
|
|
115
|
-
keyword,
|
|
116
|
-
setBlocks,
|
|
117
|
-
setKeyword,
|
|
118
|
-
setMeta,
|
|
119
|
-
snapshot: {
|
|
120
|
-
blocks,
|
|
121
|
-
metaDescription: meta.metaDescription,
|
|
122
|
-
metaTitle: meta.metaTitle,
|
|
123
|
-
},
|
|
124
|
-
}),
|
|
125
|
-
[blocks, keyword, meta.metaDescription, meta.metaTitle, setBlocks, setMeta],
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
return <PageSeoContext.Provider value={value}>{children}</PageSeoContext.Provider>;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* The page-level audit state, or `null` when there is no provider above.
|
|
133
|
-
*
|
|
134
|
-
* Callers must handle the null case rather than asserting — see the module docblock
|
|
135
|
-
* for why an edit surface without a page-level panel is a supported configuration
|
|
136
|
-
* and not a mistake.
|
|
137
|
-
*/
|
|
138
|
-
export function usePageSeo(): PageSeoContextValue | null {
|
|
139
|
-
return useContext(PageSeoContext);
|
|
140
|
-
}
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared live state for the page-level SEO audit.
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS EXISTS. The pieces an SEO audit needs are spread across three sibling
|
|
7
|
+
* components that have no relationship to each other: `BlockEditorArea` owns the
|
|
8
|
+
* live `blocks` array (it is the only client component that holds all of them),
|
|
9
|
+
* `PageForm` / `PostForm` own the meta title and description as local form state,
|
|
10
|
+
* and the panel that has to grade all of it renders next to both. Lifting any one
|
|
11
|
+
* of those into another would mean restructuring an edit screen that works; a small
|
|
12
|
+
* context is the cheaper and less invasive answer.
|
|
13
|
+
*
|
|
14
|
+
* WHY THE AUDIT IS PAGE-LEVEL AND NOT PER-BLOCK. The first version of this feature
|
|
15
|
+
* mounted the panel inside the rich-text editor, where it could only ever see one
|
|
16
|
+
* block. That produced confidently wrong results: a single paragraph block was told
|
|
17
|
+
* it had no H1 and fewer than 300 words, neither of which is a property a block can
|
|
18
|
+
* have. Headings in NextBlock live in two places — standalone `heading` blocks and
|
|
19
|
+
* h1–h6 nodes inside a `text` block's HTML — and blocks nested in a `section` live
|
|
20
|
+
* in `content.column_blocks[column][index]` rather than in the top-level array. Only
|
|
21
|
+
* something walking the whole tree can answer "does this page have exactly one H1".
|
|
22
|
+
* So the page-level panel is the real audit, and the in-editor panel is narrowed to
|
|
23
|
+
* the handful of checks a single block genuinely owns.
|
|
24
|
+
*
|
|
25
|
+
* PUBLISHING IS EFFECT-DRIVEN, deliberately. Both producers call their setter from a
|
|
26
|
+
* `useEffect` keyed on the value they publish, never during render — a child calling
|
|
27
|
+
* a parent's setState while rendering is the classic "Cannot update a component
|
|
28
|
+
* while rendering a different component" warning, and with the blocks array it would
|
|
29
|
+
* be an update loop rather than just a warning.
|
|
30
|
+
*
|
|
31
|
+
* `usePageSeo()` RETURNS NULL OUTSIDE A PROVIDER on purpose. `PageForm`,
|
|
32
|
+
* `BlockEditorArea` and `TextBlockEditor` are all reachable from surfaces that have
|
|
33
|
+
* no page-level audit — the product editor is the live example — and none of them
|
|
34
|
+
* should crash or need a second code path just because the panel is absent.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import React, {
|
|
38
|
+
createContext,
|
|
39
|
+
useCallback,
|
|
40
|
+
useContext,
|
|
41
|
+
useMemo,
|
|
42
|
+
useState,
|
|
43
|
+
type ReactNode,
|
|
44
|
+
} from 'react';
|
|
45
|
+
|
|
46
|
+
/** Everything the page-level audit grades, as it currently stands in the editor. */
|
|
47
|
+
export interface PageSeoSnapshot {
|
|
48
|
+
/**
|
|
49
|
+
* The live blocks array, exactly as `BlockEditorArea` holds it. Typed as
|
|
50
|
+
* `unknown[]` because this module has no business knowing the block union — the
|
|
51
|
+
* walker in `page-document.ts` is defensive about shape anyway, since block
|
|
52
|
+
* content is `Json` and a custom block can carry anything.
|
|
53
|
+
*/
|
|
54
|
+
blocks: unknown[];
|
|
55
|
+
metaDescription: string | null;
|
|
56
|
+
metaTitle: string | null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface PageSeoContextValue {
|
|
60
|
+
/** The focus keyphrase, held here so it survives switching between blocks. */
|
|
61
|
+
keyword: string;
|
|
62
|
+
setBlocks: (blocks: unknown[]) => void;
|
|
63
|
+
setKeyword: (keyword: string) => void;
|
|
64
|
+
setMeta: (meta: { metaDescription: string | null; metaTitle: string | null }) => void;
|
|
65
|
+
snapshot: PageSeoSnapshot;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const PageSeoContext = createContext<PageSeoContextValue | null>(null);
|
|
69
|
+
|
|
70
|
+
export interface PageSeoProviderProps {
|
|
71
|
+
children: ReactNode;
|
|
72
|
+
initialBlocks?: unknown[];
|
|
73
|
+
initialMetaDescription?: string | null;
|
|
74
|
+
initialMetaTitle?: string | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function PageSeoProvider({
|
|
78
|
+
children,
|
|
79
|
+
initialBlocks = [],
|
|
80
|
+
initialMetaDescription = null,
|
|
81
|
+
initialMetaTitle = null,
|
|
82
|
+
}: PageSeoProviderProps) {
|
|
83
|
+
const [blocks, setBlocksState] = useState<unknown[]>(initialBlocks);
|
|
84
|
+
const [keyword, setKeyword] = useState('');
|
|
85
|
+
const [meta, setMetaState] = useState<{
|
|
86
|
+
metaDescription: string | null;
|
|
87
|
+
metaTitle: string | null;
|
|
88
|
+
}>({ metaDescription: initialMetaDescription, metaTitle: initialMetaTitle });
|
|
89
|
+
|
|
90
|
+
// Both setters bail when nothing actually changed. The blocks array is rebuilt on
|
|
91
|
+
// every keystroke in a block editor, so without the length-and-identity check
|
|
92
|
+
// below every character typed would re-render the provider and every consumer of
|
|
93
|
+
// this context, not just the panel that debounces its own work.
|
|
94
|
+
const setBlocks = useCallback((next: unknown[]) => {
|
|
95
|
+
setBlocksState((previous) =>
|
|
96
|
+
previous.length === next.length && previous.every((block, index) => block === next[index])
|
|
97
|
+
? previous
|
|
98
|
+
: next,
|
|
99
|
+
);
|
|
100
|
+
}, []);
|
|
101
|
+
|
|
102
|
+
const setMeta = useCallback(
|
|
103
|
+
(next: { metaDescription: string | null; metaTitle: string | null }) => {
|
|
104
|
+
setMetaState((previous) =>
|
|
105
|
+
previous.metaDescription === next.metaDescription && previous.metaTitle === next.metaTitle
|
|
106
|
+
? previous
|
|
107
|
+
: next,
|
|
108
|
+
);
|
|
109
|
+
},
|
|
110
|
+
[],
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const value = useMemo<PageSeoContextValue>(
|
|
114
|
+
() => ({
|
|
115
|
+
keyword,
|
|
116
|
+
setBlocks,
|
|
117
|
+
setKeyword,
|
|
118
|
+
setMeta,
|
|
119
|
+
snapshot: {
|
|
120
|
+
blocks,
|
|
121
|
+
metaDescription: meta.metaDescription,
|
|
122
|
+
metaTitle: meta.metaTitle,
|
|
123
|
+
},
|
|
124
|
+
}),
|
|
125
|
+
[blocks, keyword, meta.metaDescription, meta.metaTitle, setBlocks, setMeta],
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
return <PageSeoContext.Provider value={value}>{children}</PageSeoContext.Provider>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The page-level audit state, or `null` when there is no provider above.
|
|
133
|
+
*
|
|
134
|
+
* Callers must handle the null case rather than asserting — see the module docblock
|
|
135
|
+
* for why an edit surface without a page-level panel is a supported configuration
|
|
136
|
+
* and not a mistake.
|
|
137
|
+
*/
|
|
138
|
+
export function usePageSeo(): PageSeoContextValue | null {
|
|
139
|
+
return useContext(PageSeoContext);
|
|
140
|
+
}
|