create-nextblock 0.14.1 → 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/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/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
|
@@ -157,7 +157,9 @@ export default function ImageBlockEditor({ content, onChange }: BlockEditorProps
|
|
|
157
157
|
width={content.width}
|
|
158
158
|
height={content.height}
|
|
159
159
|
className="rounded-md object-contain"
|
|
160
|
-
|
|
160
|
+
// width/height auto so the CSS max-height scales both axes and
|
|
161
|
+
// keeps the intrinsic aspect ratio (next/image warns otherwise)
|
|
162
|
+
style={{ width: 'auto', height: 'auto', maxWidth: '100%', maxHeight: '200px' }}
|
|
161
163
|
placeholder={content.blur_data_url ? "blur" : "empty"}
|
|
162
164
|
blurDataURL={content.blur_data_url || undefined}
|
|
163
165
|
/>
|
|
@@ -175,7 +177,7 @@ export default function ImageBlockEditor({ content, onChange }: BlockEditorProps
|
|
|
175
177
|
width={300}
|
|
176
178
|
height={200}
|
|
177
179
|
className="rounded-md object-contain max-h-40 block"
|
|
178
|
-
style={{
|
|
180
|
+
style={{ width: 'auto', height: 'auto', maxWidth: '100%' }}
|
|
179
181
|
/>
|
|
180
182
|
<Button
|
|
181
183
|
type="button" variant="destructive" size="icon"
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
// app/cms/blocks/editors/TextBlockEditor.tsx
|
|
2
2
|
'use client';
|
|
3
3
|
|
|
4
|
-
import React, { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
5
|
-
import dynamic from 'next/dynamic';
|
|
6
|
-
import type { Extensions } from '@tiptap/core';
|
|
7
|
-
import MediaPickerDialog from '../../media/components/MediaPickerDialog';
|
|
4
|
+
import React, { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import dynamic from 'next/dynamic';
|
|
6
|
+
import type { Extensions } from '@tiptap/core';
|
|
7
|
+
import MediaPickerDialog from '../../media/components/MediaPickerDialog';
|
|
8
8
|
import { Label } from '@nextblock-cms/ui';
|
|
9
9
|
import { BlockEditorProps } from '../components/BlockEditorModal';
|
|
10
|
-
import { resolveMediaUrl } from '../../../../lib/media/resolveMediaUrl';
|
|
11
|
-
import { useCortexAiActive } from '../../components/CortexAiActiveContext';
|
|
12
|
-
import {
|
|
13
|
-
createDynamicCustomBlockExtensions,
|
|
14
|
-
type DynamicCustomBlockEditorDefinition,
|
|
15
|
-
} from '../../../../lib/editor/dynamic-extensions';
|
|
10
|
+
import { resolveMediaUrl } from '../../../../lib/media/resolveMediaUrl';
|
|
11
|
+
import { useCortexAiActive } from '../../components/CortexAiActiveContext';
|
|
12
|
+
import {
|
|
13
|
+
createDynamicCustomBlockExtensions,
|
|
14
|
+
type DynamicCustomBlockEditorDefinition,
|
|
15
|
+
} from '../../../../lib/editor/dynamic-extensions';
|
|
16
16
|
|
|
17
17
|
// Props expected by NotionEditor
|
|
18
18
|
type NotionEditorProps = {
|
|
19
19
|
content: string;
|
|
20
20
|
onChange: (html: string) => void;
|
|
21
|
-
openImagePicker?: () => Promise<{ src: string; alt?: string; width?: number | null; height?: number | null; blurDataURL?: string | null } | null>;
|
|
22
|
-
className?: string;
|
|
23
|
-
showAiPrompt?: boolean;
|
|
24
|
-
dynamicExtensions?: Extensions;
|
|
25
|
-
};
|
|
21
|
+
openImagePicker?: () => Promise<{ src: string; alt?: string; width?: number | null; height?: number | null; blurDataURL?: string | null } | null>;
|
|
22
|
+
className?: string;
|
|
23
|
+
showAiPrompt?: boolean;
|
|
24
|
+
dynamicExtensions?: Extensions;
|
|
25
|
+
};
|
|
26
26
|
|
|
27
27
|
// Use the alias that resolves in your repo; if you mapped @nextblock-cms/editor, swap it here.
|
|
28
28
|
const NotionEditor = dynamic<NotionEditorProps>(
|
|
@@ -40,91 +40,91 @@ export default function TextBlockEditor({
|
|
|
40
40
|
className,
|
|
41
41
|
}: BlockEditorProps<Partial<TextBlockContent>>) {
|
|
42
42
|
const labelId = useId();
|
|
43
|
-
const [pickerOpen, setPickerOpen] = useState(false);
|
|
44
|
-
const [dynamicDefinitions, setDynamicDefinitions] = useState<DynamicCustomBlockEditorDefinition[]>([]);
|
|
45
|
-
const [dynamicDefinitionsLoaded, setDynamicDefinitionsLoaded] = useState(false);
|
|
46
|
-
const isCortexAiActive = useCortexAiActive();
|
|
47
|
-
const resolverRef = useRef<null | ((v: any) => void)>(null);
|
|
43
|
+
const [pickerOpen, setPickerOpen] = useState(false);
|
|
44
|
+
const [dynamicDefinitions, setDynamicDefinitions] = useState<DynamicCustomBlockEditorDefinition[]>([]);
|
|
45
|
+
const [dynamicDefinitionsLoaded, setDynamicDefinitionsLoaded] = useState(false);
|
|
46
|
+
const isCortexAiActive = useCortexAiActive();
|
|
47
|
+
const resolverRef = useRef<null | ((v: any) => void)>(null);
|
|
48
48
|
const openImagePicker = useCallback(() => {
|
|
49
49
|
setPickerOpen(true);
|
|
50
50
|
return new Promise<{ src: string; alt?: string; width?: number | null; height?: number | null; blurDataURL?: string | null } | null>((resolve) => {
|
|
51
51
|
resolverRef.current = resolve;
|
|
52
|
-
});
|
|
53
|
-
}, []);
|
|
54
|
-
const dynamicExtensions = useMemo(
|
|
55
|
-
() => createDynamicCustomBlockExtensions(dynamicDefinitions),
|
|
56
|
-
[dynamicDefinitions]
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
let isActive = true;
|
|
61
|
-
|
|
62
|
-
async function loadDynamicDefinitions() {
|
|
63
|
-
try {
|
|
64
|
-
const response = await fetch('/api/custom-blocks/editor-definitions', {
|
|
65
|
-
cache: 'no-store',
|
|
66
|
-
method: 'GET',
|
|
67
|
-
});
|
|
68
|
-
const payload = (await response.json()) as {
|
|
69
|
-
definitions?: DynamicCustomBlockEditorDefinition[];
|
|
70
|
-
error?: string;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
if (!isActive) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (!response.ok) {
|
|
78
|
-
console.error(
|
|
79
|
-
'[TextBlockEditor] Failed to load custom block editor definitions:',
|
|
80
|
-
payload.error
|
|
81
|
-
);
|
|
82
|
-
setDynamicDefinitions([]);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
setDynamicDefinitions(payload.definitions ?? []);
|
|
87
|
-
} catch (error) {
|
|
88
|
-
if (isActive) {
|
|
89
|
-
console.error('[TextBlockEditor] Failed to load custom block editor definitions:', error);
|
|
90
|
-
setDynamicDefinitions([]);
|
|
91
|
-
}
|
|
92
|
-
} finally {
|
|
93
|
-
if (isActive) {
|
|
94
|
-
setDynamicDefinitionsLoaded(true);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
void loadDynamicDefinitions();
|
|
100
|
-
|
|
101
|
-
return () => {
|
|
102
|
-
isActive = false;
|
|
103
|
-
};
|
|
104
|
-
}, []);
|
|
105
|
-
|
|
106
|
-
return (
|
|
52
|
+
});
|
|
53
|
+
}, []);
|
|
54
|
+
const dynamicExtensions = useMemo(
|
|
55
|
+
() => createDynamicCustomBlockExtensions(dynamicDefinitions),
|
|
56
|
+
[dynamicDefinitions]
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
let isActive = true;
|
|
61
|
+
|
|
62
|
+
async function loadDynamicDefinitions() {
|
|
63
|
+
try {
|
|
64
|
+
const response = await fetch('/api/custom-blocks/editor-definitions', {
|
|
65
|
+
cache: 'no-store',
|
|
66
|
+
method: 'GET',
|
|
67
|
+
});
|
|
68
|
+
const payload = (await response.json()) as {
|
|
69
|
+
definitions?: DynamicCustomBlockEditorDefinition[];
|
|
70
|
+
error?: string;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (!isActive) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
console.error(
|
|
79
|
+
'[TextBlockEditor] Failed to load custom block editor definitions:',
|
|
80
|
+
payload.error
|
|
81
|
+
);
|
|
82
|
+
setDynamicDefinitions([]);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setDynamicDefinitions(payload.definitions ?? []);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (isActive) {
|
|
89
|
+
console.error('[TextBlockEditor] Failed to load custom block editor definitions:', error);
|
|
90
|
+
setDynamicDefinitions([]);
|
|
91
|
+
}
|
|
92
|
+
} finally {
|
|
93
|
+
if (isActive) {
|
|
94
|
+
setDynamicDefinitionsLoaded(true);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
void loadDynamicDefinitions();
|
|
100
|
+
|
|
101
|
+
return () => {
|
|
102
|
+
isActive = false;
|
|
103
|
+
};
|
|
104
|
+
}, []);
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
107
|
<div className="h-full flex flex-col">
|
|
108
108
|
<Label htmlFor={labelId} className="sr-only">
|
|
109
109
|
Text Content
|
|
110
110
|
</Label>
|
|
111
111
|
|
|
112
112
|
<div id={labelId} role="group" aria-labelledby={labelId} className="flex-1 min-h-0 flex flex-col">
|
|
113
|
-
{dynamicDefinitionsLoaded ? (
|
|
114
|
-
<NotionEditor
|
|
115
|
-
key={dynamicDefinitions.map((definition) => definition.id).join('|') || 'static'}
|
|
116
|
-
content={content?.html_content ?? ''}
|
|
117
|
-
dynamicExtensions={dynamicExtensions}
|
|
118
|
-
onChange={(html) => onChange({ html_content: html })}
|
|
119
|
-
openImagePicker={openImagePicker}
|
|
120
|
-
className={className}
|
|
121
|
-
showAiPrompt={isCortexAiActive}
|
|
122
|
-
/>
|
|
123
|
-
) : (
|
|
124
|
-
<div className="flex min-h-[500px] flex-1 items-center justify-center rounded-lg border bg-background text-sm text-muted-foreground">
|
|
125
|
-
Loading editor...
|
|
126
|
-
</div>
|
|
127
|
-
)}
|
|
113
|
+
{dynamicDefinitionsLoaded ? (
|
|
114
|
+
<NotionEditor
|
|
115
|
+
key={dynamicDefinitions.map((definition) => definition.id).join('|') || 'static'}
|
|
116
|
+
content={content?.html_content ?? ''}
|
|
117
|
+
dynamicExtensions={dynamicExtensions}
|
|
118
|
+
onChange={(html) => onChange({ html_content: html })}
|
|
119
|
+
openImagePicker={openImagePicker}
|
|
120
|
+
className={className}
|
|
121
|
+
showAiPrompt={isCortexAiActive}
|
|
122
|
+
/>
|
|
123
|
+
) : (
|
|
124
|
+
<div className="flex min-h-[500px] flex-1 items-center justify-center rounded-lg border bg-background text-sm text-muted-foreground">
|
|
125
|
+
Loading editor...
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
128
128
|
|
|
129
129
|
{/* Hidden controlled MediaPickerDialog for image selection */}
|
|
130
130
|
<div className="sr-only" aria-hidden>
|
|
@@ -1,122 +1,122 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
4
|
-
import { useRouter } from 'next/navigation';
|
|
5
|
-
import { Check, ExternalLink, Github, Loader2 } from 'lucide-react';
|
|
6
|
-
import { Button } from '@nextblock-cms/ui';
|
|
7
|
-
import { startGithubConnect, pollGithubConnect } from './github-connect-actions';
|
|
8
|
-
|
|
9
|
-
type Phase = 'idle' | 'starting' | 'awaiting' | 'installed' | 'error';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* One-click "Connect GitHub" via the OAuth device flow. On authorization, the server
|
|
13
|
-
* installs the upstream-sync workflow into the repo (the file Vercel's clone strips).
|
|
14
|
-
* No PAT, no env config — the public client id is baked into the app.
|
|
15
|
-
*/
|
|
16
|
-
export default function ConnectGitHubButton() {
|
|
17
|
-
const router = useRouter();
|
|
18
|
-
const [phase, setPhase] = useState<Phase>('idle');
|
|
19
|
-
const [userCode, setUserCode] = useState('');
|
|
20
|
-
const [verificationUri, setVerificationUri] = useState('https://github.com/login/device');
|
|
21
|
-
const [error, setError] = useState('');
|
|
22
|
-
|
|
23
|
-
// Cancel any in-flight polling when the component unmounts.
|
|
24
|
-
const activeRef = useRef(false);
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
activeRef.current = true;
|
|
27
|
-
return () => {
|
|
28
|
-
activeRef.current = false;
|
|
29
|
-
};
|
|
30
|
-
}, []);
|
|
31
|
-
|
|
32
|
-
const poll = useCallback(async (intervalMs: number) => {
|
|
33
|
-
if (!activeRef.current) return;
|
|
34
|
-
const result = await pollGithubConnect();
|
|
35
|
-
if (!activeRef.current) return;
|
|
36
|
-
|
|
37
|
-
if (result.status === 'installed') {
|
|
38
|
-
setPhase('installed');
|
|
39
|
-
// Re-render the dashboard so the onboarding step picks up the now-active workflow
|
|
40
|
-
// (the step then flips to done and this control is replaced).
|
|
41
|
-
router.refresh();
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
if (result.status === 'error') {
|
|
45
|
-
setError(result.error);
|
|
46
|
-
setPhase('error');
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
// pending — back off a little on slow_down, then poll again.
|
|
50
|
-
const next = result.slowDown ? intervalMs + 5000 : intervalMs;
|
|
51
|
-
setTimeout(() => void poll(next), next);
|
|
52
|
-
}, [router]);
|
|
53
|
-
|
|
54
|
-
const connect = useCallback(async () => {
|
|
55
|
-
setError('');
|
|
56
|
-
setPhase('starting');
|
|
57
|
-
const res = await startGithubConnect();
|
|
58
|
-
if (!activeRef.current) return;
|
|
59
|
-
if (!res.ok || !res.userCode) {
|
|
60
|
-
setError(res.error ?? 'Could not start GitHub connect.');
|
|
61
|
-
setPhase('error');
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
setUserCode(res.userCode);
|
|
65
|
-
if (res.verificationUri) setVerificationUri(res.verificationUri);
|
|
66
|
-
setPhase('awaiting');
|
|
67
|
-
const intervalMs = Math.max((res.interval ?? 5) + 1, 5) * 1000;
|
|
68
|
-
setTimeout(() => void poll(intervalMs), intervalMs);
|
|
69
|
-
}, [poll]);
|
|
70
|
-
|
|
71
|
-
if (phase === 'installed') {
|
|
72
|
-
return (
|
|
73
|
-
<div className="flex items-center gap-1.5 text-sm font-medium text-emerald-600 dark:text-emerald-400">
|
|
74
|
-
<Check className="h-4 w-4" />
|
|
75
|
-
Connected — workflow installed
|
|
76
|
-
</div>
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (phase === 'awaiting') {
|
|
81
|
-
return (
|
|
82
|
-
<div className="flex flex-col items-end gap-1.5 text-right">
|
|
83
|
-
<Button asChild size="sm" variant="outline">
|
|
84
|
-
<a href={verificationUri} target="_blank" rel="noopener noreferrer">
|
|
85
|
-
Authorize on GitHub
|
|
86
|
-
<ExternalLink className="ml-1 h-3.5 w-3.5" />
|
|
87
|
-
</a>
|
|
88
|
-
</Button>
|
|
89
|
-
<p className="text-xs text-muted-foreground">
|
|
90
|
-
Enter code{' '}
|
|
91
|
-
<span className="font-mono font-semibold tracking-wider text-foreground">{userCode}</span>
|
|
92
|
-
</p>
|
|
93
|
-
<p className="flex items-center gap-1 text-xs text-muted-foreground">
|
|
94
|
-
<Loader2 className="h-3 w-3 animate-spin" />
|
|
95
|
-
Waiting for authorization…
|
|
96
|
-
</p>
|
|
97
|
-
</div>
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<div className="flex flex-col items-end gap-1">
|
|
103
|
-
<Button
|
|
104
|
-
size="sm"
|
|
105
|
-
variant="outline"
|
|
106
|
-
onClick={() => void connect()}
|
|
107
|
-
disabled={phase === 'starting'}
|
|
108
|
-
className="shrink-0"
|
|
109
|
-
>
|
|
110
|
-
{phase === 'starting' ? (
|
|
111
|
-
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
|
112
|
-
) : (
|
|
113
|
-
<Github className="mr-1 h-3.5 w-3.5" />
|
|
114
|
-
)}
|
|
115
|
-
{phase === 'starting' ? 'Starting…' : 'Connect GitHub'}
|
|
116
|
-
</Button>
|
|
117
|
-
{phase === 'error' && (
|
|
118
|
-
<p className="max-w-[16rem] text-right text-xs text-red-600 dark:text-red-400">{error}</p>
|
|
119
|
-
)}
|
|
120
|
-
</div>
|
|
121
|
-
);
|
|
122
|
-
}
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { useRouter } from 'next/navigation';
|
|
5
|
+
import { Check, ExternalLink, Github, Loader2 } from 'lucide-react';
|
|
6
|
+
import { Button } from '@nextblock-cms/ui';
|
|
7
|
+
import { startGithubConnect, pollGithubConnect } from './github-connect-actions';
|
|
8
|
+
|
|
9
|
+
type Phase = 'idle' | 'starting' | 'awaiting' | 'installed' | 'error';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* One-click "Connect GitHub" via the OAuth device flow. On authorization, the server
|
|
13
|
+
* installs the upstream-sync workflow into the repo (the file Vercel's clone strips).
|
|
14
|
+
* No PAT, no env config — the public client id is baked into the app.
|
|
15
|
+
*/
|
|
16
|
+
export default function ConnectGitHubButton() {
|
|
17
|
+
const router = useRouter();
|
|
18
|
+
const [phase, setPhase] = useState<Phase>('idle');
|
|
19
|
+
const [userCode, setUserCode] = useState('');
|
|
20
|
+
const [verificationUri, setVerificationUri] = useState('https://github.com/login/device');
|
|
21
|
+
const [error, setError] = useState('');
|
|
22
|
+
|
|
23
|
+
// Cancel any in-flight polling when the component unmounts.
|
|
24
|
+
const activeRef = useRef(false);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
activeRef.current = true;
|
|
27
|
+
return () => {
|
|
28
|
+
activeRef.current = false;
|
|
29
|
+
};
|
|
30
|
+
}, []);
|
|
31
|
+
|
|
32
|
+
const poll = useCallback(async (intervalMs: number) => {
|
|
33
|
+
if (!activeRef.current) return;
|
|
34
|
+
const result = await pollGithubConnect();
|
|
35
|
+
if (!activeRef.current) return;
|
|
36
|
+
|
|
37
|
+
if (result.status === 'installed') {
|
|
38
|
+
setPhase('installed');
|
|
39
|
+
// Re-render the dashboard so the onboarding step picks up the now-active workflow
|
|
40
|
+
// (the step then flips to done and this control is replaced).
|
|
41
|
+
router.refresh();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (result.status === 'error') {
|
|
45
|
+
setError(result.error);
|
|
46
|
+
setPhase('error');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// pending — back off a little on slow_down, then poll again.
|
|
50
|
+
const next = result.slowDown ? intervalMs + 5000 : intervalMs;
|
|
51
|
+
setTimeout(() => void poll(next), next);
|
|
52
|
+
}, [router]);
|
|
53
|
+
|
|
54
|
+
const connect = useCallback(async () => {
|
|
55
|
+
setError('');
|
|
56
|
+
setPhase('starting');
|
|
57
|
+
const res = await startGithubConnect();
|
|
58
|
+
if (!activeRef.current) return;
|
|
59
|
+
if (!res.ok || !res.userCode) {
|
|
60
|
+
setError(res.error ?? 'Could not start GitHub connect.');
|
|
61
|
+
setPhase('error');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
setUserCode(res.userCode);
|
|
65
|
+
if (res.verificationUri) setVerificationUri(res.verificationUri);
|
|
66
|
+
setPhase('awaiting');
|
|
67
|
+
const intervalMs = Math.max((res.interval ?? 5) + 1, 5) * 1000;
|
|
68
|
+
setTimeout(() => void poll(intervalMs), intervalMs);
|
|
69
|
+
}, [poll]);
|
|
70
|
+
|
|
71
|
+
if (phase === 'installed') {
|
|
72
|
+
return (
|
|
73
|
+
<div className="flex items-center gap-1.5 text-sm font-medium text-emerald-600 dark:text-emerald-400">
|
|
74
|
+
<Check className="h-4 w-4" />
|
|
75
|
+
Connected — workflow installed
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (phase === 'awaiting') {
|
|
81
|
+
return (
|
|
82
|
+
<div className="flex flex-col items-end gap-1.5 text-right">
|
|
83
|
+
<Button asChild size="sm" variant="outline">
|
|
84
|
+
<a href={verificationUri} target="_blank" rel="noopener noreferrer">
|
|
85
|
+
Authorize on GitHub
|
|
86
|
+
<ExternalLink className="ml-1 h-3.5 w-3.5" />
|
|
87
|
+
</a>
|
|
88
|
+
</Button>
|
|
89
|
+
<p className="text-xs text-muted-foreground">
|
|
90
|
+
Enter code{' '}
|
|
91
|
+
<span className="font-mono font-semibold tracking-wider text-foreground">{userCode}</span>
|
|
92
|
+
</p>
|
|
93
|
+
<p className="flex items-center gap-1 text-xs text-muted-foreground">
|
|
94
|
+
<Loader2 className="h-3 w-3 animate-spin" />
|
|
95
|
+
Waiting for authorization…
|
|
96
|
+
</p>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<div className="flex flex-col items-end gap-1">
|
|
103
|
+
<Button
|
|
104
|
+
size="sm"
|
|
105
|
+
variant="outline"
|
|
106
|
+
onClick={() => void connect()}
|
|
107
|
+
disabled={phase === 'starting'}
|
|
108
|
+
className="shrink-0"
|
|
109
|
+
>
|
|
110
|
+
{phase === 'starting' ? (
|
|
111
|
+
<Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
|
|
112
|
+
) : (
|
|
113
|
+
<Github className="mr-1 h-3.5 w-3.5" />
|
|
114
|
+
)}
|
|
115
|
+
{phase === 'starting' ? 'Starting…' : 'Connect GitHub'}
|
|
116
|
+
</Button>
|
|
117
|
+
{phase === 'error' && (
|
|
118
|
+
<p className="max-w-[16rem] text-right text-xs text-red-600 dark:text-red-400">{error}</p>
|
|
119
|
+
)}
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import React, { createContext, useContext, useEffect, useMemo, useState } from "react";
|
|
4
|
-
|
|
5
|
-
export type CortexAiContentType = "page" | "post" | "product";
|
|
6
|
-
|
|
7
|
-
export type CortexAiPageContext = {
|
|
8
|
-
contentType: CortexAiContentType;
|
|
9
|
-
currentEditor?: {
|
|
10
|
-
blockId?: number | string | null;
|
|
11
|
-
blockType?: string | null;
|
|
12
|
-
field?: string | null;
|
|
13
|
-
};
|
|
14
|
-
entityId: number | string;
|
|
15
|
-
languageId?: number | null;
|
|
16
|
-
slug?: string | null;
|
|
17
|
-
title?: string | null;
|
|
18
|
-
translationGroupId?: string | null;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
type CortexAiPageContextValue = {
|
|
22
|
-
pageContext: CortexAiPageContext | null;
|
|
23
|
-
setPageContext: (context: CortexAiPageContext | null) => void;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const CortexAiPageContextStore = createContext<CortexAiPageContextValue | null>(null);
|
|
27
|
-
|
|
28
|
-
export function CortexAiPageContextProvider({ children }: { children: React.ReactNode }) {
|
|
29
|
-
const [pageContext, setPageContext] = useState<CortexAiPageContext | null>(null);
|
|
30
|
-
const value = useMemo(() => ({ pageContext, setPageContext }), [pageContext]);
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<CortexAiPageContextStore.Provider value={value}>
|
|
34
|
-
{children}
|
|
35
|
-
</CortexAiPageContextStore.Provider>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function useCortexAiPageContext() {
|
|
40
|
-
return useContext(CortexAiPageContextStore);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function CortexAiPageContextRegistrar({
|
|
44
|
-
context,
|
|
45
|
-
}: {
|
|
46
|
-
context: CortexAiPageContext;
|
|
47
|
-
}) {
|
|
48
|
-
const store = useCortexAiPageContext();
|
|
49
|
-
const setPageContext = store?.setPageContext;
|
|
50
|
-
const serializedContext = JSON.stringify(context);
|
|
51
|
-
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
setPageContext?.(context);
|
|
54
|
-
return () => setPageContext?.(null);
|
|
55
|
-
}, [serializedContext, setPageContext]);
|
|
56
|
-
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useContext, useEffect, useMemo, useState } from "react";
|
|
4
|
+
|
|
5
|
+
export type CortexAiContentType = "page" | "post" | "product";
|
|
6
|
+
|
|
7
|
+
export type CortexAiPageContext = {
|
|
8
|
+
contentType: CortexAiContentType;
|
|
9
|
+
currentEditor?: {
|
|
10
|
+
blockId?: number | string | null;
|
|
11
|
+
blockType?: string | null;
|
|
12
|
+
field?: string | null;
|
|
13
|
+
};
|
|
14
|
+
entityId: number | string;
|
|
15
|
+
languageId?: number | null;
|
|
16
|
+
slug?: string | null;
|
|
17
|
+
title?: string | null;
|
|
18
|
+
translationGroupId?: string | null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type CortexAiPageContextValue = {
|
|
22
|
+
pageContext: CortexAiPageContext | null;
|
|
23
|
+
setPageContext: (context: CortexAiPageContext | null) => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const CortexAiPageContextStore = createContext<CortexAiPageContextValue | null>(null);
|
|
27
|
+
|
|
28
|
+
export function CortexAiPageContextProvider({ children }: { children: React.ReactNode }) {
|
|
29
|
+
const [pageContext, setPageContext] = useState<CortexAiPageContext | null>(null);
|
|
30
|
+
const value = useMemo(() => ({ pageContext, setPageContext }), [pageContext]);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<CortexAiPageContextStore.Provider value={value}>
|
|
34
|
+
{children}
|
|
35
|
+
</CortexAiPageContextStore.Provider>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function useCortexAiPageContext() {
|
|
40
|
+
return useContext(CortexAiPageContextStore);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function CortexAiPageContextRegistrar({
|
|
44
|
+
context,
|
|
45
|
+
}: {
|
|
46
|
+
context: CortexAiPageContext;
|
|
47
|
+
}) {
|
|
48
|
+
const store = useCortexAiPageContext();
|
|
49
|
+
const setPageContext = store?.setPageContext;
|
|
50
|
+
const serializedContext = JSON.stringify(context);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
setPageContext?.(context);
|
|
54
|
+
return () => setPageContext?.(null);
|
|
55
|
+
}, [serializedContext, setPageContext]);
|
|
56
|
+
|
|
57
|
+
return null;
|
|
58
|
+
}
|