@gradial/aci 0.1.20-preview.2 → 0.1.20
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/README.md +48 -110
- package/bin/{aci → aci-darwin-arm64} +0 -0
- package/bin/aci.js +99 -6
- package/dist/assets/index.d.ts +3 -3
- package/dist/assets/index.js +3 -3
- package/dist/astro/index.d.ts +14 -3
- package/dist/astro/index.js +24 -5
- package/dist/content/assets.d.ts +17 -0
- package/dist/content/assets.js +65 -0
- package/dist/content/index.d.ts +2 -0
- package/dist/content/index.js +2 -0
- package/dist/content/provider.d.ts +19 -0
- package/dist/content/provider.js +7 -0
- package/dist/content/resolve-slots.d.ts +9 -0
- package/dist/content/resolve-slots.js +24 -0
- package/dist/content/types.d.ts +1 -1
- package/dist/define-component.d.ts +1 -2
- package/dist/define-component.js +73 -11
- package/dist/define-layout.d.ts +2 -0
- package/dist/define-layout.js +3 -0
- package/dist/dev/content-watch.d.ts +37 -0
- package/dist/dev/content-watch.js +154 -0
- package/dist/dev/index.d.ts +24 -43
- package/dist/dev/index.js +4 -169
- package/dist/index.d.ts +14 -5
- package/dist/index.js +8 -8
- package/dist/next/asset-route.d.ts +2 -0
- package/dist/next/asset-route.js +12 -4
- package/dist/next/config.d.ts +2 -2
- package/dist/next/config.js +6 -6
- package/dist/next/content-watch.js +6 -1
- package/dist/next/index.d.ts +2 -2
- package/dist/next/index.js +2 -2
- package/dist/next/middleware.d.ts +7 -3
- package/dist/next/middleware.js +12 -7
- package/dist/next/page.d.ts +16 -15
- package/dist/next/page.js +30 -10
- package/dist/next/render.d.ts +5 -0
- package/dist/next/render.js +17 -0
- package/dist/next/root-layout.d.ts +4 -0
- package/dist/next/root-layout.js +5 -0
- package/dist/next/server.d.ts +14 -13
- package/dist/next/server.js +13 -3
- package/dist/providers/file.d.ts +2 -0
- package/dist/providers/file.js +61 -1
- package/dist/providers/s3.d.ts +1 -0
- package/dist/providers/s3.js +14 -1
- package/dist/react/Media.d.ts +24 -0
- package/dist/react/Media.js +30 -0
- package/dist/react/Picture.d.ts +12 -0
- package/dist/react/Picture.js +11 -0
- package/dist/react/VideoPlayer.d.ts +13 -0
- package/dist/react/{GradialVideoPlayer.js → VideoPlayer.js} +6 -5
- package/dist/react/index.d.ts +333 -3
- package/dist/react/index.js +89 -3
- package/dist/registry.d.ts +15 -0
- package/dist/registry.js +10 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.js +15 -2
- package/dist/types/block-ref.d.ts +26 -0
- package/dist/types/block-ref.js +28 -0
- package/dist/types/component.d.ts +36 -41
- package/dist/types/config.d.ts +1 -2
- package/dist/types/image.d.ts +33 -20
- package/dist/types/image.js +42 -50
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/link.d.ts +38 -0
- package/dist/types/link.js +58 -0
- package/dist/types/media.d.ts +16 -35
- package/dist/types/media.js +15 -84
- package/dist/types/page.d.ts +1 -2
- package/dist/types/render-mode.d.ts +0 -1
- package/dist/types/renderer.d.ts +1 -1
- package/dist/types/rich-text.d.ts +40 -0
- package/dist/types/rich-text.js +39 -0
- package/dist/types/video.d.ts +7 -13
- package/dist/types/video.js +3 -3
- package/package.json +32 -74
- package/src/cli/compile-registry.mjs +236 -59
- package/src/cli/validate-content.mjs +38 -75
- package/src/types/image.ts +79 -60
- package/templates/astro/template/.aci.yaml +27 -0
- package/templates/astro/template/.storybook/main.ts +79 -0
- package/templates/astro/template/.storybook/manager.ts +6 -0
- package/templates/astro/template/.storybook/preview.ts +33 -0
- package/templates/astro/template/astro.config.mjs +37 -0
- package/templates/astro/template/package.json.tmpl +41 -0
- package/templates/astro/template/src/cms/contracts/layouts/index.ts +18 -0
- package/templates/astro/template/src/cms/registry.ts +28 -0
- package/templates/astro/template/src/components/chrome/Footer.astro +107 -0
- package/templates/astro/template/src/components/chrome/Header.astro +94 -0
- package/templates/astro/template/src/components/elements/AtomicBlock.astro +15 -0
- package/templates/astro/template/src/components/elements/AtomicBlocks.astro +16 -0
- package/templates/astro/template/src/components/elements/Button.astro +51 -0
- package/templates/astro/template/src/components/elements/ButtonBlock.astro +25 -0
- package/templates/astro/template/src/components/elements/Container.astro +11 -0
- package/templates/astro/template/src/components/elements/Icon.astro +31 -0
- package/templates/astro/template/src/components/elements/Logo.astro +29 -0
- package/templates/astro/template/src/components/elements/RemoteImage.astro +41 -0
- package/templates/astro/template/src/components/sections/ContainerSection.astro +32 -0
- package/templates/astro/template/src/components/sections/FeatureCard.astro +23 -0
- package/templates/astro/template/src/components/sections/FeatureGrid.astro +35 -0
- package/templates/astro/template/src/components/sections/HomeHero.astro +51 -0
- package/templates/astro/template/src/components/sections/StatsBar.astro +33 -0
- package/templates/astro/template/src/design-system/styles.css +399 -0
- package/templates/astro/template/src/design-system/tokens/colors.css +60 -0
- package/templates/astro/template/src/design-system/tokens/components.css +69 -0
- package/templates/astro/template/src/design-system/tokens/index.css +6 -0
- package/templates/astro/template/src/design-system/tokens/radii.css +5 -0
- package/templates/astro/template/src/design-system/tokens/shadows.css +5 -0
- package/templates/astro/template/src/design-system/tokens/spacing.css +13 -0
- package/templates/astro/template/src/design-system/tokens/typography.css +24 -0
- package/templates/astro/template/src/env.d.ts +1 -0
- package/templates/astro/template/src/fixtures/atomicBlocks.ts +33 -0
- package/templates/astro/template/src/fixtures/composition.ts +31 -0
- package/templates/astro/template/src/fixtures/index.ts +17 -0
- package/templates/astro/template/src/fixtures/sections.ts +39 -0
- package/templates/astro/template/src/layouts/SiteLayout.astro +30 -0
- package/templates/astro/template/src/pages/[...slug].astro +61 -0
- package/templates/astro/template/src/render/RenderPage.astro +66 -0
- package/templates/astro/template/src/stories/CompositionStoryFrame.astro +15 -0
- package/templates/astro/template/src/stories/HomeHero.stories.ts +24 -0
- package/templates/astro/template/src/stories/SectionStoryFrame.astro +16 -0
- package/templates/astro/template/src/stories/StoryDocFrame.astro +31 -0
- package/templates/astro/template/tsconfig.json +11 -0
- package/templates/common/.agents/AGENTS.md +37 -0
- package/templates/common/.agents/skills/aci/SKILL.md +162 -0
- package/templates/common/.agents/skills/authoring/SKILL.md +590 -0
- package/templates/common/.agents/skills/authoring/references/block-registry.md +151 -0
- package/templates/common/.agents/skills/authoring/references/contracts.md +153 -0
- package/templates/common/.agents/skills/authoring/references/design-system.md +171 -0
- package/templates/common/.agents/skills/authoring/references/image-assets.md +73 -0
- package/templates/common/.agents/skills/authoring/references/repo-structure.md +157 -0
- package/templates/common/.agents/skills/migration/SKILL.md +160 -0
- package/templates/common/.agents/skills/migration/references/analyzing-source.md +215 -0
- package/templates/common/.agents/skills/migration/references/component-migration.md +200 -0
- package/templates/common/.agents/skills/migration/references/selector-map.md +31 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/7f/bf/7fbfc7fe230ff12688c496f660119e41d9725c4504d82e310c15665c73c09292 +0 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/a0/f5/a0f506689de9b15a7055f70103a3f02cd3ba105771ae2de6754df2ede1210d3a +0 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/dd/b7/ddb7e9642be08317bcd4b2bd26b58ba8d0d34dd7239e03e684a4f3634fa3845b +0 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/fb/32/fb3260e8286ea8231ea1c77b9c0be92d6edd9026565731eb14cfdcfd6b1b75b3 +0 -0
- package/templates/common/.content/config/site.json +192 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/asset.json +9 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/editorial.json +14 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/governance.json +23 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/versions/v0001.json +37 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/asset.json +9 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/editorial.json +14 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/governance.json +23 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/versions/v0001.json +37 -0
- package/templates/common/.content/fragments/navbar/_index.json +25 -0
- package/templates/common/.content/fragments/site-footer/_index.json +62 -0
- package/templates/common/.content/pages/about/_index.json +51 -0
- package/templates/common/.content/pages/home/_index.json +112 -0
- package/templates/common/.content/schemas/site.schema.json +123 -0
- package/templates/common/README.md.tmpl +45 -0
- package/templates/common/contracts/components/button.contract.ts +19 -0
- package/templates/common/contracts/components/container.contract.ts +19 -0
- package/templates/common/contracts/components/featureCard.contract.ts +15 -0
- package/templates/common/contracts/components/featureGrid.contract.ts +16 -0
- package/templates/common/contracts/components/homeHero.contract.ts +36 -0
- package/templates/common/contracts/components/index.ts +21 -0
- package/templates/common/contracts/components/siteFooter.contract.ts +45 -0
- package/templates/common/contracts/components/siteNavigation.contract.ts +30 -0
- package/templates/common/contracts/components/statsBar.contract.ts +19 -0
- package/templates/common/design-system/styles.css +346 -0
- package/templates/common/design-system/tokens/colors.css +110 -0
- package/templates/common/design-system/tokens/components.css +86 -0
- package/templates/common/design-system/tokens/index.css +6 -0
- package/templates/common/design-system/tokens/radii.css +5 -0
- package/templates/common/design-system/tokens/shadows.css +9 -0
- package/templates/common/design-system/tokens/spacing.css +26 -0
- package/templates/common/design-system/tokens/typography.css +32 -0
- package/templates/common/eslint-rules/no-raw-tailwind-values.js +127 -0
- package/templates/common/public/assets/Dwire-model.png +0 -0
- package/templates/common/public/assets/bare-metal-logo.png +0 -0
- package/templates/common/public/assets/meridian-logo.svg +8 -0
- package/templates/common/public/assets/seattle.avif +0 -0
- package/templates/nextjs/template/.aci.yaml +25 -0
- package/templates/nextjs/template/.storybook/main.ts +13 -0
- package/templates/nextjs/template/.storybook/preview.ts +34 -0
- package/templates/nextjs/template/eslint.config.mjs +36 -0
- package/templates/nextjs/template/next-env.d.ts +6 -0
- package/templates/nextjs/template/next.config.ts +8 -0
- package/templates/nextjs/template/package.json.tmpl +46 -0
- package/templates/nextjs/template/postcss.config.mjs +7 -0
- package/templates/nextjs/template/src/app/[[...slug]]/page.tsx +8 -0
- package/templates/nextjs/template/src/app/gradial/assets/[releaseId]/[...path]/route.ts +31 -0
- package/templates/nextjs/template/src/app/layout.tsx +29 -0
- package/templates/nextjs/template/src/cms/contracts/components/button.contract.ts +19 -0
- package/templates/nextjs/template/src/cms/contracts/components/container.contract.ts +20 -0
- package/templates/nextjs/template/src/cms/contracts/components/homeHero.contract.ts +36 -0
- package/templates/nextjs/template/src/cms/contracts/components/index.ts +15 -0
- package/templates/nextjs/template/src/cms/contracts/components/siteFooter.contract.ts +45 -0
- package/templates/nextjs/template/src/cms/contracts/components/siteNavigation.contract.ts +30 -0
- package/templates/nextjs/template/src/cms/contracts/layouts/index.ts +16 -0
- package/templates/nextjs/template/src/cms/registry.ts +28 -0
- package/templates/nextjs/template/src/components/chrome/Footer.tsx +86 -0
- package/templates/nextjs/template/src/components/chrome/Header.tsx +74 -0
- package/templates/nextjs/template/src/components/elements/AtomicBlock.tsx +13 -0
- package/templates/nextjs/template/src/components/elements/Button.tsx +40 -0
- package/templates/nextjs/template/src/components/elements/ButtonBlock.tsx +18 -0
- package/templates/nextjs/template/src/components/elements/Container.tsx +14 -0
- package/templates/nextjs/template/src/components/elements/Icon.tsx +29 -0
- package/templates/nextjs/template/src/components/elements/Logo.tsx +38 -0
- package/templates/nextjs/template/src/components/sections/ContainerSection.tsx +28 -0
- package/templates/nextjs/template/src/components/sections/FeatureCard.tsx +25 -0
- package/templates/nextjs/template/src/components/sections/FeatureGrid.tsx +36 -0
- package/templates/nextjs/template/src/components/sections/HomeHero.tsx +62 -0
- package/templates/nextjs/template/src/components/sections/StatsBar.tsx +30 -0
- package/templates/nextjs/template/src/middleware.ts +4 -0
- package/templates/nextjs/template/src/stories/Button.stories.tsx +38 -0
- package/templates/nextjs/template/src/stories/Header.stories.tsx +30 -0
- package/templates/nextjs/template/src/stories/HomeHero.stories.tsx +33 -0
- package/templates/nextjs/template/tsconfig.json +23 -0
- package/templates/nextjs/template/vercel.json +6 -0
- package/dist/content/cache.test.d.ts +0 -1
- package/dist/content/cache.test.js +0 -86
- package/dist/content/contract.d.ts +0 -83
- package/dist/content/contract.js +0 -104
- package/dist/content/tailwind-validator.d.ts +0 -6
- package/dist/content/tailwind-validator.js +0 -31
- package/dist/content/validation.d.ts +0 -108
- package/dist/content/validation.js +0 -184
- package/dist/next/preview-banner.d.ts +0 -4
- package/dist/next/preview-banner.js +0 -51
- package/dist/next/preview.d.ts +0 -7
- package/dist/next/preview.js +0 -37
- package/dist/react/GradialMedia.d.ts +0 -24
- package/dist/react/GradialMedia.js +0 -31
- package/dist/react/GradialPicture.d.ts +0 -14
- package/dist/react/GradialPicture.js +0 -30
- package/dist/react/GradialVideoPlayer.d.ts +0 -13
- package/dist/sveltekit/index.d.ts +0 -23
- package/dist/sveltekit/index.js +0 -62
- package/dist/sveltekit/preview.d.ts +0 -5
- package/dist/sveltekit/preview.js +0 -33
- package/src/cli/verify-renderer.mjs +0 -73
- package/src/types/component.ts +0 -59
- package/src/types/config.ts +0 -38
- package/src/types/data.ts +0 -47
- package/src/types/index.ts +0 -10
- package/src/types/layout.ts +0 -29
- package/src/types/media.ts +0 -125
- package/src/types/page.ts +0 -48
- package/src/types/render-mode.ts +0 -18
- package/src/types/renderer.ts +0 -83
- package/src/types/video.ts +0 -66
package/dist/next/config.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { createContentWatchWebpackPlugin } from './content-watch.js';
|
|
2
|
-
export function
|
|
2
|
+
export function withAci(nextConfig = {}, options = {}) {
|
|
3
3
|
const assetPrefix = options.assetPrefix || '/.gradial-dam';
|
|
4
4
|
const userRewrites = nextConfig.rewrites;
|
|
5
5
|
const userWebpack = nextConfig.webpack;
|
|
6
6
|
return {
|
|
7
7
|
...nextConfig,
|
|
8
8
|
rewrites: async () => {
|
|
9
|
-
const
|
|
9
|
+
const rewrites = [
|
|
10
10
|
{
|
|
11
11
|
source: `${assetPrefix}/:path*`,
|
|
12
|
-
destination: `/
|
|
12
|
+
destination: `/gradial/assets/local${assetPrefix}/:path*`,
|
|
13
13
|
},
|
|
14
14
|
];
|
|
15
15
|
if (!userRewrites)
|
|
16
|
-
return
|
|
16
|
+
return rewrites;
|
|
17
17
|
const userResult = await (typeof userRewrites === 'function' ? userRewrites() : userRewrites);
|
|
18
18
|
if (Array.isArray(userResult)) {
|
|
19
|
-
return [...
|
|
19
|
+
return [...rewrites, ...userResult];
|
|
20
20
|
}
|
|
21
21
|
return {
|
|
22
22
|
...userResult,
|
|
23
|
-
beforeFiles: [...
|
|
23
|
+
beforeFiles: [...rewrites, ...(userResult.beforeFiles ?? [])],
|
|
24
24
|
};
|
|
25
25
|
},
|
|
26
26
|
webpack: (config, context) => {
|
|
@@ -80,14 +80,19 @@ function startContentWatcher() {
|
|
|
80
80
|
}
|
|
81
81
|
rebuild('startup');
|
|
82
82
|
try {
|
|
83
|
-
fs.watch(src, { recursive: true }, (_event, filename) => {
|
|
83
|
+
const watcher = fs.watch(src, { recursive: true }, (_event, filename) => {
|
|
84
84
|
if (!filename)
|
|
85
85
|
return;
|
|
86
|
+
if (filename.split(path.sep).includes('.gradial-dam'))
|
|
87
|
+
return;
|
|
86
88
|
clearTimeout(timer);
|
|
87
89
|
timer = setTimeout(() => {
|
|
88
90
|
rebuild(path.relative(src, path.resolve(src, filename)));
|
|
89
91
|
}, 100);
|
|
90
92
|
});
|
|
93
|
+
watcher.on('error', (error) => {
|
|
94
|
+
console.warn(`[aci] content watch error: ${error.message}`);
|
|
95
|
+
});
|
|
91
96
|
}
|
|
92
97
|
catch {
|
|
93
98
|
console.warn('[aci] fs.watch not supported on this platform, content watch disabled');
|
package/dist/next/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { DevRefresh, type DevRefreshProps } from './dev-refresh.js';
|
|
2
|
-
export {
|
|
2
|
+
export { withAci, type WithAciOptions } from './config.js';
|
|
3
3
|
export { isPreviewMode } from './preview-mode.js';
|
|
4
|
-
export {
|
|
4
|
+
export { createPage, type CreatePageOptions, type SlotPropsFn } from './page.js';
|
package/dist/next/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { DevRefresh } from './dev-refresh.js';
|
|
2
|
-
export {
|
|
2
|
+
export { withAci } from './config.js';
|
|
3
3
|
export { isPreviewMode } from './preview-mode.js';
|
|
4
|
-
export {
|
|
4
|
+
export { createPage } from './page.js';
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { NextResponse, type NextRequest } from 'next/server.js';
|
|
2
|
-
export interface
|
|
3
|
-
siteId
|
|
2
|
+
export interface MiddlewareConfig {
|
|
3
|
+
siteId?: string;
|
|
4
4
|
edgeConfig?: string;
|
|
5
|
+
previewSignKey?: string;
|
|
5
6
|
deploymentId?: string;
|
|
6
7
|
}
|
|
7
|
-
export declare function
|
|
8
|
+
export declare function createMiddleware(config?: MiddlewareConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
9
|
+
export declare const config: {
|
|
10
|
+
matcher: string[];
|
|
11
|
+
};
|
package/dist/next/middleware.js
CHANGED
|
@@ -3,8 +3,8 @@ import { getUncachedEdgeConfigValue } from './edge-config.js';
|
|
|
3
3
|
import { PREVIEW_TOKEN_COOKIE, PREVIEW_RELEASE_COOKIE, PREVIEW_HEADER, RELEASE_HEADER, extractPreviewToken, resolvePreviewContext, createPreviewCookies, previewSignKey, } from '../preview/core.js';
|
|
4
4
|
const ROUTE_HEADER = 'x-gradial-route';
|
|
5
5
|
const DISPATCH_HEADER = 'x-gradial-dispatch';
|
|
6
|
-
export function
|
|
7
|
-
return async function
|
|
6
|
+
export function createMiddleware(config = {}) {
|
|
7
|
+
return async function middleware(request) {
|
|
8
8
|
const url = request.nextUrl;
|
|
9
9
|
// ?leave_preview=1 → clear cookies, redirect to clean URL
|
|
10
10
|
if (url.searchParams.get('leave_preview') === '1') {
|
|
@@ -14,7 +14,7 @@ export function createGradialMiddleware(config) {
|
|
|
14
14
|
response.cookies.delete(PREVIEW_RELEASE_COOKIE);
|
|
15
15
|
return response;
|
|
16
16
|
}
|
|
17
|
-
const requestHeaders =
|
|
17
|
+
const requestHeaders = headers(request.headers);
|
|
18
18
|
// Try preview token first; if valid, use that release.
|
|
19
19
|
// If no token or invalid, fall through to active release from edge config.
|
|
20
20
|
const preview = await resolvePreviewFromRequest(request, config);
|
|
@@ -38,7 +38,12 @@ export function createGradialMiddleware(config) {
|
|
|
38
38
|
return response;
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
export const config = {
|
|
42
|
+
matcher: [
|
|
43
|
+
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
function headers(headers) {
|
|
42
47
|
const next = new Headers(headers);
|
|
43
48
|
next.delete(RELEASE_HEADER);
|
|
44
49
|
next.delete(ROUTE_HEADER);
|
|
@@ -54,12 +59,12 @@ async function resolvePreviewFromRequest(request, config) {
|
|
|
54
59
|
});
|
|
55
60
|
if (!token)
|
|
56
61
|
return null;
|
|
57
|
-
const signKey = previewSignKey();
|
|
62
|
+
const signKey = config.previewSignKey || previewSignKey();
|
|
58
63
|
if (!signKey) {
|
|
59
|
-
console.warn('[
|
|
64
|
+
console.warn('[aci/mw] GRADIAL_PREVIEW_SIGN_KEY not set — preview tokens will be ignored');
|
|
60
65
|
return null;
|
|
61
66
|
}
|
|
62
|
-
return await resolvePreviewContext({ token, siteId: config.siteId, signKey });
|
|
67
|
+
return await resolvePreviewContext({ token, siteId: config.siteId || '', signKey });
|
|
63
68
|
}
|
|
64
69
|
function cookieMap(cookies) {
|
|
65
70
|
const map = new Map();
|
package/dist/next/page.d.ts
CHANGED
|
@@ -1,38 +1,39 @@
|
|
|
1
|
-
import type { ComponentType } from 'react';
|
|
2
1
|
import type { Metadata } from 'next';
|
|
3
|
-
import { type KernelPage
|
|
4
|
-
|
|
5
|
-
page: KernelPage;
|
|
6
|
-
siteConfig: KernelSiteConfig;
|
|
7
|
-
}
|
|
8
|
-
export interface CreateAciPageOptions {
|
|
9
|
-
/** The site's CmsPage component that renders chrome + content blocks. */
|
|
10
|
-
CmsPage: ComponentType<AciPageProps>;
|
|
11
|
-
}
|
|
2
|
+
import { type KernelPage } from '../content/index.js';
|
|
3
|
+
import type { Registry } from '../registry.js';
|
|
12
4
|
type NextPageProps = {
|
|
13
5
|
params: Promise<{
|
|
14
6
|
slug?: string[];
|
|
15
7
|
}>;
|
|
16
8
|
};
|
|
9
|
+
export type SlotPropsFn = (slotName: string, page: KernelPage) => Record<string, unknown> | undefined;
|
|
10
|
+
export interface CreatePageOptions {
|
|
11
|
+
slotProps?: SlotPropsFn;
|
|
12
|
+
}
|
|
17
13
|
/**
|
|
18
14
|
* Creates the catch-all route handler for a Next.js ACI site.
|
|
19
15
|
*
|
|
20
16
|
* Usage in `src/app/[[...slug]]/page.tsx`:
|
|
21
17
|
*
|
|
22
18
|
* ```tsx
|
|
23
|
-
* import {
|
|
24
|
-
* import {
|
|
19
|
+
* import { createPage } from '@gradial/aci/next';
|
|
20
|
+
* import { registry } from '@/cms/registry';
|
|
25
21
|
*
|
|
26
|
-
* const page =
|
|
22
|
+
* const page = createPage(registry);
|
|
27
23
|
* export const dynamic = page.dynamic;
|
|
28
24
|
* export const generateMetadata = page.generateMetadata;
|
|
29
25
|
* export default page.default;
|
|
30
26
|
* ```
|
|
31
27
|
*
|
|
32
28
|
* The SDK owns param normalization, content loading, metadata generation,
|
|
33
|
-
*
|
|
29
|
+
* notFound handling, layout resolution, and block rendering via the registry.
|
|
30
|
+
*
|
|
31
|
+
* Layout slots: createPage loads the page's layout, iterates all slots, and
|
|
32
|
+
* fills each from the page's regions. If a slot is empty, it falls back to
|
|
33
|
+
* the layout's fragment defaults (loaded via getFragment). This lets chrome
|
|
34
|
+
* be CMS-managed fragments instead of hardcoded component imports.
|
|
34
35
|
*/
|
|
35
|
-
export declare function
|
|
36
|
+
export declare function createPage(registry: Registry, options?: CreatePageOptions): {
|
|
36
37
|
dynamic: "force-dynamic";
|
|
37
38
|
generateMetadata: ({ params }: NextPageProps) => Promise<Metadata>;
|
|
38
39
|
default: ({ params }: NextPageProps) => Promise<import("react").JSX.Element>;
|
package/dist/next/page.js
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { notFound } from 'next/navigation.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { Fragment } from 'react';
|
|
4
|
+
import { PageNotFoundError, routeFromNextParams, getRenderInput, getLayout, getFragment, } from './server.js';
|
|
5
|
+
import { routeMetadataForContent, resolveSlots, } from '../content/index.js';
|
|
6
|
+
import { renderBlocks } from './render.js';
|
|
5
7
|
/**
|
|
6
8
|
* Creates the catch-all route handler for a Next.js ACI site.
|
|
7
9
|
*
|
|
8
10
|
* Usage in `src/app/[[...slug]]/page.tsx`:
|
|
9
11
|
*
|
|
10
12
|
* ```tsx
|
|
11
|
-
* import {
|
|
12
|
-
* import {
|
|
13
|
+
* import { createPage } from '@gradial/aci/next';
|
|
14
|
+
* import { registry } from '@/cms/registry';
|
|
13
15
|
*
|
|
14
|
-
* const page =
|
|
16
|
+
* const page = createPage(registry);
|
|
15
17
|
* export const dynamic = page.dynamic;
|
|
16
18
|
* export const generateMetadata = page.generateMetadata;
|
|
17
19
|
* export default page.default;
|
|
18
20
|
* ```
|
|
19
21
|
*
|
|
20
22
|
* The SDK owns param normalization, content loading, metadata generation,
|
|
21
|
-
*
|
|
23
|
+
* notFound handling, layout resolution, and block rendering via the registry.
|
|
24
|
+
*
|
|
25
|
+
* Layout slots: createPage loads the page's layout, iterates all slots, and
|
|
26
|
+
* fills each from the page's regions. If a slot is empty, it falls back to
|
|
27
|
+
* the layout's fragment defaults (loaded via getFragment). This lets chrome
|
|
28
|
+
* be CMS-managed fragments instead of hardcoded component imports.
|
|
22
29
|
*/
|
|
23
|
-
export function
|
|
30
|
+
export function createPage(registry, options) {
|
|
24
31
|
const dynamic = 'force-dynamic';
|
|
25
32
|
async function generateMetadata({ params }) {
|
|
26
33
|
const route = await routeFromNextParams(params);
|
|
@@ -41,9 +48,22 @@ export function createAciPage({ CmsPage }) {
|
|
|
41
48
|
const route = await routeFromNextParams(params);
|
|
42
49
|
try {
|
|
43
50
|
const input = await getRenderInput(route);
|
|
44
|
-
if (!input.page
|
|
51
|
+
if (!input.page)
|
|
45
52
|
notFound();
|
|
46
|
-
|
|
53
|
+
const layout = await getLayout(input.page.layout);
|
|
54
|
+
const resolvedSlots = await resolveSlots(layout, input.page.regions || {}, (id) => getFragment(id));
|
|
55
|
+
const renderedSlots = await Promise.all(layout.slots.map(async (slot) => {
|
|
56
|
+
const blocks = resolvedSlots[slot.name];
|
|
57
|
+
if (!blocks?.length)
|
|
58
|
+
return null;
|
|
59
|
+
const extraProps = options?.slotProps?.(slot.name, input.page);
|
|
60
|
+
const renderedBlocks = await renderBlocks(blocks, registry, extraProps);
|
|
61
|
+
if (slot.name === 'main') {
|
|
62
|
+
return (_jsx("main", { id: "content-main", children: renderedBlocks }, "main"));
|
|
63
|
+
}
|
|
64
|
+
return (_jsx(Fragment, { children: renderedBlocks }, slot.name));
|
|
65
|
+
}));
|
|
66
|
+
return _jsx(_Fragment, { children: renderedSlots.filter(Boolean) });
|
|
47
67
|
}
|
|
48
68
|
catch (error) {
|
|
49
69
|
if (error instanceof PageNotFoundError)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { KernelPageBlock } from '../content/types.js';
|
|
3
|
+
import type { Registry } from '../registry.js';
|
|
4
|
+
export declare function renderBlock(block: KernelPageBlock, registry: Registry, extraProps?: Record<string, unknown>): Promise<ReactElement>;
|
|
5
|
+
export declare function renderBlocks(blocks: readonly KernelPageBlock[], registry: Registry, extraProps?: Record<string, unknown>): Promise<ReactElement[]>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export async function renderBlock(block, registry, extraProps) {
|
|
3
|
+
const entry = registry.find((r) => r.id === block.component);
|
|
4
|
+
if (!entry) {
|
|
5
|
+
return (_jsxs("div", { className: "mx-auto my-4 max-w-[var(--max-width-container)] rounded-card border-2 border-red-600 bg-red-50 px-5 py-4 font-mono text-sm leading-relaxed text-red-900", children: [_jsx("strong", { children: "Unknown block:" }), " ", _jsx("code", { children: block.component }), _jsx("br", {}), _jsxs("span", { className: "text-red-800 opacity-70", children: ["id: ", block.id] })] }, block.id));
|
|
6
|
+
}
|
|
7
|
+
const Component = entry.component;
|
|
8
|
+
const hasBlockSlots = !!entry.contract.blockSlots;
|
|
9
|
+
const componentProps = { ...block.props, ...extraProps };
|
|
10
|
+
if (hasBlockSlots) {
|
|
11
|
+
componentProps.renderChildren = (blocks) => renderBlocks(blocks, registry, extraProps);
|
|
12
|
+
}
|
|
13
|
+
return _jsx(Component, { ...componentProps }, block.id);
|
|
14
|
+
}
|
|
15
|
+
export async function renderBlocks(blocks, registry, extraProps) {
|
|
16
|
+
return Promise.all(blocks.map((block) => renderBlock(block, registry, extraProps)));
|
|
17
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DevRefresh } from './dev-refresh.js';
|
|
3
|
+
export function RootLayout({ children }) {
|
|
4
|
+
return (_jsx("html", { lang: "en", children: _jsxs("body", { children: [children, _jsx(DevRefresh, {})] }) }));
|
|
5
|
+
}
|
package/dist/next/server.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { FragmentNotFoundError, PageNotFoundError } from '../content/provider.js';
|
|
1
|
+
import { FragmentNotFoundError, LayoutNotFoundError, PageNotFoundError } from '../content/provider.js';
|
|
2
2
|
import type { RenderInput } from '../content/types.js';
|
|
3
|
-
export { FragmentNotFoundError, PageNotFoundError };
|
|
4
|
-
export interface
|
|
3
|
+
export { FragmentNotFoundError, PageNotFoundError, LayoutNotFoundError };
|
|
4
|
+
export interface FetchConfig {
|
|
5
5
|
siteId?: string;
|
|
6
6
|
bucket?: string;
|
|
7
7
|
keyPrefix?: string;
|
|
@@ -14,7 +14,7 @@ export interface GradialFetchConfig {
|
|
|
14
14
|
edgeConfig?: string;
|
|
15
15
|
deploymentId?: string;
|
|
16
16
|
}
|
|
17
|
-
export interface
|
|
17
|
+
export interface ServerRenderInput<TPage, TSite extends SiteConfig = SiteConfig> {
|
|
18
18
|
route: string;
|
|
19
19
|
domain: string;
|
|
20
20
|
locale: string;
|
|
@@ -22,15 +22,16 @@ export interface GradialRenderInput<TPage, TSite extends GradialSiteConfig = Gra
|
|
|
22
22
|
page: TPage;
|
|
23
23
|
fragments?: Record<string, unknown>;
|
|
24
24
|
}
|
|
25
|
-
export interface
|
|
25
|
+
export interface SiteConfig {
|
|
26
26
|
domain?: string;
|
|
27
27
|
defaultLocale?: string;
|
|
28
28
|
}
|
|
29
|
-
export declare function getPage<T = unknown>(route?: string, config?:
|
|
30
|
-
export declare function getSiteConfig<T extends
|
|
31
|
-
export declare function getFragment<T = unknown>(name: string, config?:
|
|
32
|
-
export declare function
|
|
33
|
-
export declare function
|
|
29
|
+
export declare function getPage<T = unknown>(route?: string, config?: FetchConfig): Promise<T>;
|
|
30
|
+
export declare function getSiteConfig<T extends SiteConfig = SiteConfig>(config?: FetchConfig): Promise<T>;
|
|
31
|
+
export declare function getFragment<T = unknown>(name: string, config?: FetchConfig): Promise<T>;
|
|
32
|
+
export declare function getLayout<T = unknown>(name: string, config?: FetchConfig): Promise<T>;
|
|
33
|
+
export declare function getRoutes(config?: FetchConfig): Promise<string[]>;
|
|
34
|
+
export declare function getRenderInput<TPage, TSite extends SiteConfig = SiteConfig>(route?: string, config?: FetchConfig): Promise<ServerRenderInput<TPage, TSite>>;
|
|
34
35
|
export declare function getPageRuntimeRenderInput(): Promise<RenderInput | null>;
|
|
35
36
|
export { isPreviewMode } from './preview-mode.js';
|
|
36
37
|
export declare function routeFromNextParams(params?: Promise<{
|
|
@@ -38,11 +39,11 @@ export declare function routeFromNextParams(params?: Promise<{
|
|
|
38
39
|
}> | {
|
|
39
40
|
slug?: string[];
|
|
40
41
|
}): Promise<string>;
|
|
41
|
-
export declare function
|
|
42
|
+
export declare function generateStaticParams(): Promise<Array<{
|
|
42
43
|
slug?: string[];
|
|
43
44
|
}>>;
|
|
44
45
|
export declare class ReleaseAssetNotFoundError extends Error {
|
|
45
46
|
constructor(releaseId: string, assetPath: string, cause?: unknown);
|
|
46
47
|
}
|
|
47
|
-
export declare function getReleaseAssetResponse(releaseId: string, assetPath: string, config?:
|
|
48
|
-
export declare function resolveReleaseId(config?:
|
|
48
|
+
export declare function getReleaseAssetResponse(releaseId: string, assetPath: string, config?: FetchConfig): Promise<Response>;
|
|
49
|
+
export declare function resolveReleaseId(config?: FetchConfig): Promise<string>;
|
package/dist/next/server.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { headers } from 'next/headers.js';
|
|
2
|
-
import { FragmentNotFoundError, PageNotFoundError, } from '../content/provider.js';
|
|
2
|
+
import { FragmentNotFoundError, LayoutNotFoundError, PageNotFoundError, } from '../content/provider.js';
|
|
3
3
|
import { getCachedManifest, getCachedSiteConfig } from '../content/cache.js';
|
|
4
4
|
import { S3ContentProvider } from '../providers/s3.js';
|
|
5
5
|
import { GRADIAL_RENDER_INPUT_HEADER, getPendingRenderInput } from '../runtime/page.js';
|
|
6
6
|
import { getUncachedEdgeConfigValue } from './edge-config.js';
|
|
7
7
|
const RELEASE_HEADER = 'x-gradial-release-id';
|
|
8
8
|
const PREVIEW_HEADER = 'x-gradial-preview';
|
|
9
|
-
export { FragmentNotFoundError, PageNotFoundError };
|
|
9
|
+
export { FragmentNotFoundError, PageNotFoundError, LayoutNotFoundError };
|
|
10
10
|
export async function getPage(route = '', config = {}) {
|
|
11
11
|
const runtimeInput = await getPageRuntimeRenderInput();
|
|
12
12
|
if (runtimeInput)
|
|
@@ -31,6 +31,16 @@ export async function getFragment(name, config = {}) {
|
|
|
31
31
|
const provider = await resolveProviderWithFallback(config);
|
|
32
32
|
return await provider.getFragment(name);
|
|
33
33
|
}
|
|
34
|
+
export async function getLayout(name, config = {}) {
|
|
35
|
+
const runtimeInput = await getPageRuntimeRenderInput();
|
|
36
|
+
if (runtimeInput) {
|
|
37
|
+
const layouts = runtimeInput.layouts || {};
|
|
38
|
+
if (name in layouts)
|
|
39
|
+
return layouts[name];
|
|
40
|
+
}
|
|
41
|
+
const provider = await resolveProviderWithFallback(config);
|
|
42
|
+
return await provider.getLayout(name);
|
|
43
|
+
}
|
|
34
44
|
export async function getRoutes(config = {}) {
|
|
35
45
|
const provider = await resolveProviderWithFallback(config);
|
|
36
46
|
const routes = await provider.listRoutes();
|
|
@@ -74,7 +84,7 @@ export async function routeFromNextParams(params) {
|
|
|
74
84
|
return '/';
|
|
75
85
|
return `/${resolved.slug.join('/')}/`;
|
|
76
86
|
}
|
|
77
|
-
export async function
|
|
87
|
+
export async function generateStaticParams() {
|
|
78
88
|
const { FileContentProvider } = await import('../providers/file.js');
|
|
79
89
|
const provider = new FileContentProvider();
|
|
80
90
|
const routes = await provider.listRoutes();
|
package/dist/providers/file.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export declare class FileContentProvider implements ContentProvider {
|
|
|
7
7
|
getSiteConfig<T = unknown>(): Promise<T>;
|
|
8
8
|
getPage<T = unknown>(route: string): Promise<T>;
|
|
9
9
|
getFragment<T = unknown>(id: string): Promise<T>;
|
|
10
|
+
getLayout<T = unknown>(name: string): Promise<T>;
|
|
10
11
|
manifest(): Promise<CompiledManifest>;
|
|
12
|
+
fetchRaw(key: string): Promise<Response>;
|
|
11
13
|
private readJSON;
|
|
12
14
|
}
|
package/dist/providers/file.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { FragmentNotFoundError, PageNotFoundError } from '../content/provider.js';
|
|
3
|
+
import { FragmentNotFoundError, LayoutNotFoundError, PageNotFoundError } from '../content/provider.js';
|
|
4
4
|
import { normalizeRoute } from '../content/routes.js';
|
|
5
5
|
export class FileContentProvider {
|
|
6
6
|
root;
|
|
@@ -46,12 +46,72 @@ export class FileContentProvider {
|
|
|
46
46
|
throw new FragmentNotFoundError(id, error);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
async getLayout(name) {
|
|
50
|
+
const manifest = await this.manifest();
|
|
51
|
+
const layouts = manifest.layouts;
|
|
52
|
+
if (!layouts || !layouts[name]) {
|
|
53
|
+
throw new LayoutNotFoundError(name);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return await this.readJSON(`layouts/${name}.json`);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
throw new LayoutNotFoundError(name, error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
49
62
|
async manifest() {
|
|
50
63
|
this.#manifest ??= this.readJSON('manifest.json');
|
|
51
64
|
return await this.#manifest;
|
|
52
65
|
}
|
|
66
|
+
async fetchRaw(key) {
|
|
67
|
+
const relativeKey = key.replace(/^\/+/, '');
|
|
68
|
+
const filePath = path.resolve(this.root, relativeKey);
|
|
69
|
+
const rel = path.relative(this.root, filePath);
|
|
70
|
+
if (rel === '' || rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
71
|
+
return new Response('not found', { status: 404 });
|
|
72
|
+
}
|
|
73
|
+
let body;
|
|
74
|
+
try {
|
|
75
|
+
body = await fs.readFile(filePath);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return new Response('not found', { status: 404 });
|
|
79
|
+
}
|
|
80
|
+
return new Response(new Uint8Array(body), {
|
|
81
|
+
status: 200,
|
|
82
|
+
headers: {
|
|
83
|
+
'content-type': contentType(filePath),
|
|
84
|
+
'cache-control': 'public, max-age=31536000, immutable',
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
53
88
|
async readJSON(key) {
|
|
54
89
|
const raw = await fs.readFile(path.join(this.root, key), 'utf8');
|
|
55
90
|
return JSON.parse(raw);
|
|
56
91
|
}
|
|
57
92
|
}
|
|
93
|
+
function contentType(filePath) {
|
|
94
|
+
switch (path.extname(filePath).toLowerCase()) {
|
|
95
|
+
case '.webp':
|
|
96
|
+
return 'image/webp';
|
|
97
|
+
case '.svg':
|
|
98
|
+
return 'image/svg+xml';
|
|
99
|
+
case '.jpg':
|
|
100
|
+
case '.jpeg':
|
|
101
|
+
return 'image/jpeg';
|
|
102
|
+
case '.png':
|
|
103
|
+
return 'image/png';
|
|
104
|
+
case '.avif':
|
|
105
|
+
return 'image/avif';
|
|
106
|
+
case '.gif':
|
|
107
|
+
return 'image/gif';
|
|
108
|
+
case '.css':
|
|
109
|
+
return 'text/css';
|
|
110
|
+
case '.js':
|
|
111
|
+
return 'application/javascript';
|
|
112
|
+
case '.json':
|
|
113
|
+
return 'application/json';
|
|
114
|
+
default:
|
|
115
|
+
return 'application/octet-stream';
|
|
116
|
+
}
|
|
117
|
+
}
|
package/dist/providers/s3.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare class S3ContentProvider implements ContentProvider {
|
|
|
17
17
|
getSiteConfig<T = unknown>(): Promise<T>;
|
|
18
18
|
getPage<T = unknown>(route: string): Promise<T>;
|
|
19
19
|
getFragment<T = unknown>(id: string): Promise<T>;
|
|
20
|
+
getLayout<T = unknown>(name: string): Promise<T>;
|
|
20
21
|
manifest(): Promise<CompiledManifest>;
|
|
21
22
|
fetchRaw(key: string): Promise<Response>;
|
|
22
23
|
private getJSON;
|
package/dist/providers/s3.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
|
-
import { FragmentNotFoundError, PageNotFoundError } from '../content/provider.js';
|
|
2
|
+
import { FragmentNotFoundError, LayoutNotFoundError, PageNotFoundError } from '../content/provider.js';
|
|
3
3
|
import { normalizeRoute } from '../content/routes.js';
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
5
5
|
// S3 content provider — reads compiled content files from S3
|
|
@@ -45,6 +45,19 @@ export class S3ContentProvider {
|
|
|
45
45
|
throw new FragmentNotFoundError(id, error);
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
+
async getLayout(name) {
|
|
49
|
+
const manifest = await this.manifest();
|
|
50
|
+
const layouts = manifest.layouts;
|
|
51
|
+
if (!layouts || !layouts[name]) {
|
|
52
|
+
throw new LayoutNotFoundError(name);
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return await this.getJSON(`layouts/${name}.json`);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
throw new LayoutNotFoundError(name, error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
48
61
|
async manifest() {
|
|
49
62
|
this.#manifest ??= this.getJSON('manifest.json');
|
|
50
63
|
return await this.#manifest;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type CSSProperties, type ImgHTMLAttributes, type VideoHTMLAttributes } from 'react';
|
|
2
|
+
import type { Asset } from '../types/media.js';
|
|
3
|
+
export interface MediaProps {
|
|
4
|
+
/** The compiler-resolved media to render. */
|
|
5
|
+
media: Asset;
|
|
6
|
+
/** CSS class applied to the rendered element. */
|
|
7
|
+
className?: string;
|
|
8
|
+
/** Inline styles applied to the rendered element. */
|
|
9
|
+
style?: CSSProperties;
|
|
10
|
+
/** Loading strategy for images. Defaults to 'lazy'. */
|
|
11
|
+
loading?: 'eager' | 'lazy';
|
|
12
|
+
/** Additional image attributes (only applied when media is an image). */
|
|
13
|
+
imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'alt'>;
|
|
14
|
+
/** Additional video attributes (only applied when media is a video). */
|
|
15
|
+
videoProps?: Omit<VideoHTMLAttributes<HTMLVideoElement>, 'src' | 'poster' | 'width' | 'height'>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Polymorphic media component — renders an Image or Video as the appropriate
|
|
19
|
+
* HTML element based on $type.
|
|
20
|
+
*
|
|
21
|
+
* - gradial.image → <img> with srcset via Picture
|
|
22
|
+
* - gradial.video → <video> via VideoPlayer
|
|
23
|
+
*/
|
|
24
|
+
export declare function Media({ media, className, style, loading, imgProps, videoProps, }: MediaProps): import("react").FunctionComponentElement<import("./VideoPlayer.js").VideoPlayerProps> | import("react").FunctionComponentElement<import("./Picture.js").PictureProps>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
import { Picture } from './Picture.js';
|
|
3
|
+
import { VideoPlayer } from './VideoPlayer.js';
|
|
4
|
+
/**
|
|
5
|
+
* Polymorphic media component — renders an Image or Video as the appropriate
|
|
6
|
+
* HTML element based on $type.
|
|
7
|
+
*
|
|
8
|
+
* - gradial.image → <img> with srcset via Picture
|
|
9
|
+
* - gradial.video → <video> via VideoPlayer
|
|
10
|
+
*/
|
|
11
|
+
export function Media({ media, className, style, loading = 'lazy', imgProps, videoProps, }) {
|
|
12
|
+
switch (media.$type) {
|
|
13
|
+
case 'gradial.video':
|
|
14
|
+
return createElement(VideoPlayer, {
|
|
15
|
+
video: media,
|
|
16
|
+
className,
|
|
17
|
+
style,
|
|
18
|
+
...videoProps,
|
|
19
|
+
});
|
|
20
|
+
case 'gradial.image':
|
|
21
|
+
default:
|
|
22
|
+
return createElement(Picture, {
|
|
23
|
+
image: media,
|
|
24
|
+
className,
|
|
25
|
+
style,
|
|
26
|
+
loading,
|
|
27
|
+
...imgProps,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ImgHTMLAttributes } from 'react';
|
|
2
|
+
import { type Image } from '../types/image.js';
|
|
3
|
+
export interface PictureProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'alt' | 'width' | 'height'> {
|
|
4
|
+
image: Image;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Renders an Image as a responsive <img> element.
|
|
8
|
+
*
|
|
9
|
+
* Delegates attribute computation to the shared `imageAttrs()` pure function
|
|
10
|
+
* so React, Astro, and Svelte all produce identical output.
|
|
11
|
+
*/
|
|
12
|
+
export declare function Picture({ image, ...attrs }: PictureProps): import("react").DetailedReactHTMLElement<import("../types/image.js").ImageAttrs, HTMLElement>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
import { imageAttrs } from '../types/image.js';
|
|
3
|
+
/**
|
|
4
|
+
* Renders an Image as a responsive <img> element.
|
|
5
|
+
*
|
|
6
|
+
* Delegates attribute computation to the shared `imageAttrs()` pure function
|
|
7
|
+
* so React, Astro, and Svelte all produce identical output.
|
|
8
|
+
*/
|
|
9
|
+
export function Picture({ image, ...attrs }) {
|
|
10
|
+
return createElement('img', imageAttrs(image, attrs));
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type VideoHTMLAttributes } from 'react';
|
|
2
|
+
import type { Video } from '../types/video.js';
|
|
3
|
+
export interface VideoPlayerProps extends Omit<VideoHTMLAttributes<HTMLVideoElement>, 'src' | 'poster' | 'width' | 'height'> {
|
|
4
|
+
video: Video;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Renders a Video as a <video> element.
|
|
8
|
+
*
|
|
9
|
+
* - Uses the primary source from poster for the poster attribute.
|
|
10
|
+
* - Includes <source> elements for format variants when available.
|
|
11
|
+
* - Uses the src attribute on <video> when no source variants exist.
|
|
12
|
+
*/
|
|
13
|
+
export declare function VideoPlayer({ video, ...attrs }: VideoPlayerProps): import("react").DetailedReactHTMLElement<Record<string, unknown>, HTMLElement>;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { createElement } from 'react';
|
|
2
|
+
import { primaryImageSource } from '../types/image.js';
|
|
2
3
|
/**
|
|
3
|
-
* Renders a
|
|
4
|
+
* Renders a Video as a <video> element.
|
|
4
5
|
*
|
|
5
|
-
* - Uses poster
|
|
6
|
+
* - Uses the primary source from poster for the poster attribute.
|
|
6
7
|
* - Includes <source> elements for format variants when available.
|
|
7
|
-
* -
|
|
8
|
+
* - Uses the src attribute on <video> when no source variants exist.
|
|
8
9
|
*/
|
|
9
|
-
export function
|
|
10
|
-
const posterSrc = video.poster
|
|
10
|
+
export function VideoPlayer({ video, ...attrs }) {
|
|
11
|
+
const posterSrc = video.poster ? primaryImageSource(video.poster).src : undefined;
|
|
11
12
|
const hasSources = video.sources && video.sources.length > 0;
|
|
12
13
|
const videoAttrs = {
|
|
13
14
|
poster: posterSrc,
|