@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.
Files changed (249) hide show
  1. package/README.md +48 -110
  2. package/bin/{aci → aci-darwin-arm64} +0 -0
  3. package/bin/aci.js +99 -6
  4. package/dist/assets/index.d.ts +3 -3
  5. package/dist/assets/index.js +3 -3
  6. package/dist/astro/index.d.ts +14 -3
  7. package/dist/astro/index.js +24 -5
  8. package/dist/content/assets.d.ts +17 -0
  9. package/dist/content/assets.js +65 -0
  10. package/dist/content/index.d.ts +2 -0
  11. package/dist/content/index.js +2 -0
  12. package/dist/content/provider.d.ts +19 -0
  13. package/dist/content/provider.js +7 -0
  14. package/dist/content/resolve-slots.d.ts +9 -0
  15. package/dist/content/resolve-slots.js +24 -0
  16. package/dist/content/types.d.ts +1 -1
  17. package/dist/define-component.d.ts +1 -2
  18. package/dist/define-component.js +73 -11
  19. package/dist/define-layout.d.ts +2 -0
  20. package/dist/define-layout.js +3 -0
  21. package/dist/dev/content-watch.d.ts +37 -0
  22. package/dist/dev/content-watch.js +154 -0
  23. package/dist/dev/index.d.ts +24 -43
  24. package/dist/dev/index.js +4 -169
  25. package/dist/index.d.ts +14 -5
  26. package/dist/index.js +8 -8
  27. package/dist/next/asset-route.d.ts +2 -0
  28. package/dist/next/asset-route.js +12 -4
  29. package/dist/next/config.d.ts +2 -2
  30. package/dist/next/config.js +6 -6
  31. package/dist/next/content-watch.js +6 -1
  32. package/dist/next/index.d.ts +2 -2
  33. package/dist/next/index.js +2 -2
  34. package/dist/next/middleware.d.ts +7 -3
  35. package/dist/next/middleware.js +12 -7
  36. package/dist/next/page.d.ts +16 -15
  37. package/dist/next/page.js +30 -10
  38. package/dist/next/render.d.ts +5 -0
  39. package/dist/next/render.js +17 -0
  40. package/dist/next/root-layout.d.ts +4 -0
  41. package/dist/next/root-layout.js +5 -0
  42. package/dist/next/server.d.ts +14 -13
  43. package/dist/next/server.js +13 -3
  44. package/dist/providers/file.d.ts +2 -0
  45. package/dist/providers/file.js +61 -1
  46. package/dist/providers/s3.d.ts +1 -0
  47. package/dist/providers/s3.js +14 -1
  48. package/dist/react/Media.d.ts +24 -0
  49. package/dist/react/Media.js +30 -0
  50. package/dist/react/Picture.d.ts +12 -0
  51. package/dist/react/Picture.js +11 -0
  52. package/dist/react/VideoPlayer.d.ts +13 -0
  53. package/dist/react/{GradialVideoPlayer.js → VideoPlayer.js} +6 -5
  54. package/dist/react/index.d.ts +333 -3
  55. package/dist/react/index.js +89 -3
  56. package/dist/registry.d.ts +15 -0
  57. package/dist/registry.js +10 -0
  58. package/dist/testing/index.d.ts +3 -0
  59. package/dist/testing/index.js +15 -2
  60. package/dist/types/block-ref.d.ts +26 -0
  61. package/dist/types/block-ref.js +28 -0
  62. package/dist/types/component.d.ts +36 -41
  63. package/dist/types/config.d.ts +1 -2
  64. package/dist/types/image.d.ts +33 -20
  65. package/dist/types/image.js +42 -50
  66. package/dist/types/index.d.ts +1 -0
  67. package/dist/types/index.js +1 -0
  68. package/dist/types/link.d.ts +38 -0
  69. package/dist/types/link.js +58 -0
  70. package/dist/types/media.d.ts +16 -35
  71. package/dist/types/media.js +15 -84
  72. package/dist/types/page.d.ts +1 -2
  73. package/dist/types/render-mode.d.ts +0 -1
  74. package/dist/types/renderer.d.ts +1 -1
  75. package/dist/types/rich-text.d.ts +40 -0
  76. package/dist/types/rich-text.js +39 -0
  77. package/dist/types/video.d.ts +7 -13
  78. package/dist/types/video.js +3 -3
  79. package/package.json +32 -74
  80. package/src/cli/compile-registry.mjs +236 -59
  81. package/src/cli/validate-content.mjs +38 -75
  82. package/src/types/image.ts +79 -60
  83. package/templates/astro/template/.aci.yaml +27 -0
  84. package/templates/astro/template/.storybook/main.ts +79 -0
  85. package/templates/astro/template/.storybook/manager.ts +6 -0
  86. package/templates/astro/template/.storybook/preview.ts +33 -0
  87. package/templates/astro/template/astro.config.mjs +37 -0
  88. package/templates/astro/template/package.json.tmpl +41 -0
  89. package/templates/astro/template/src/cms/contracts/layouts/index.ts +18 -0
  90. package/templates/astro/template/src/cms/registry.ts +28 -0
  91. package/templates/astro/template/src/components/chrome/Footer.astro +107 -0
  92. package/templates/astro/template/src/components/chrome/Header.astro +94 -0
  93. package/templates/astro/template/src/components/elements/AtomicBlock.astro +15 -0
  94. package/templates/astro/template/src/components/elements/AtomicBlocks.astro +16 -0
  95. package/templates/astro/template/src/components/elements/Button.astro +51 -0
  96. package/templates/astro/template/src/components/elements/ButtonBlock.astro +25 -0
  97. package/templates/astro/template/src/components/elements/Container.astro +11 -0
  98. package/templates/astro/template/src/components/elements/Icon.astro +31 -0
  99. package/templates/astro/template/src/components/elements/Logo.astro +29 -0
  100. package/templates/astro/template/src/components/elements/RemoteImage.astro +41 -0
  101. package/templates/astro/template/src/components/sections/ContainerSection.astro +32 -0
  102. package/templates/astro/template/src/components/sections/FeatureCard.astro +23 -0
  103. package/templates/astro/template/src/components/sections/FeatureGrid.astro +35 -0
  104. package/templates/astro/template/src/components/sections/HomeHero.astro +51 -0
  105. package/templates/astro/template/src/components/sections/StatsBar.astro +33 -0
  106. package/templates/astro/template/src/design-system/styles.css +399 -0
  107. package/templates/astro/template/src/design-system/tokens/colors.css +60 -0
  108. package/templates/astro/template/src/design-system/tokens/components.css +69 -0
  109. package/templates/astro/template/src/design-system/tokens/index.css +6 -0
  110. package/templates/astro/template/src/design-system/tokens/radii.css +5 -0
  111. package/templates/astro/template/src/design-system/tokens/shadows.css +5 -0
  112. package/templates/astro/template/src/design-system/tokens/spacing.css +13 -0
  113. package/templates/astro/template/src/design-system/tokens/typography.css +24 -0
  114. package/templates/astro/template/src/env.d.ts +1 -0
  115. package/templates/astro/template/src/fixtures/atomicBlocks.ts +33 -0
  116. package/templates/astro/template/src/fixtures/composition.ts +31 -0
  117. package/templates/astro/template/src/fixtures/index.ts +17 -0
  118. package/templates/astro/template/src/fixtures/sections.ts +39 -0
  119. package/templates/astro/template/src/layouts/SiteLayout.astro +30 -0
  120. package/templates/astro/template/src/pages/[...slug].astro +61 -0
  121. package/templates/astro/template/src/render/RenderPage.astro +66 -0
  122. package/templates/astro/template/src/stories/CompositionStoryFrame.astro +15 -0
  123. package/templates/astro/template/src/stories/HomeHero.stories.ts +24 -0
  124. package/templates/astro/template/src/stories/SectionStoryFrame.astro +16 -0
  125. package/templates/astro/template/src/stories/StoryDocFrame.astro +31 -0
  126. package/templates/astro/template/tsconfig.json +11 -0
  127. package/templates/common/.agents/AGENTS.md +37 -0
  128. package/templates/common/.agents/skills/aci/SKILL.md +162 -0
  129. package/templates/common/.agents/skills/authoring/SKILL.md +590 -0
  130. package/templates/common/.agents/skills/authoring/references/block-registry.md +151 -0
  131. package/templates/common/.agents/skills/authoring/references/contracts.md +153 -0
  132. package/templates/common/.agents/skills/authoring/references/design-system.md +171 -0
  133. package/templates/common/.agents/skills/authoring/references/image-assets.md +73 -0
  134. package/templates/common/.agents/skills/authoring/references/repo-structure.md +157 -0
  135. package/templates/common/.agents/skills/migration/SKILL.md +160 -0
  136. package/templates/common/.agents/skills/migration/references/analyzing-source.md +215 -0
  137. package/templates/common/.agents/skills/migration/references/component-migration.md +200 -0
  138. package/templates/common/.agents/skills/migration/references/selector-map.md +31 -0
  139. package/templates/common/.content/.gradial-dam/blobs/sha256/7f/bf/7fbfc7fe230ff12688c496f660119e41d9725c4504d82e310c15665c73c09292 +0 -0
  140. package/templates/common/.content/.gradial-dam/blobs/sha256/a0/f5/a0f506689de9b15a7055f70103a3f02cd3ba105771ae2de6754df2ede1210d3a +0 -0
  141. package/templates/common/.content/.gradial-dam/blobs/sha256/dd/b7/ddb7e9642be08317bcd4b2bd26b58ba8d0d34dd7239e03e684a4f3634fa3845b +0 -0
  142. package/templates/common/.content/.gradial-dam/blobs/sha256/fb/32/fb3260e8286ea8231ea1c77b9c0be92d6edd9026565731eb14cfdcfd6b1b75b3 +0 -0
  143. package/templates/common/.content/config/site.json +192 -0
  144. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/asset.json +9 -0
  145. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/editorial.json +14 -0
  146. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/governance.json +23 -0
  147. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/versions/v0001.json +37 -0
  148. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/asset.json +9 -0
  149. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/editorial.json +14 -0
  150. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/governance.json +23 -0
  151. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/versions/v0001.json +37 -0
  152. package/templates/common/.content/fragments/navbar/_index.json +25 -0
  153. package/templates/common/.content/fragments/site-footer/_index.json +62 -0
  154. package/templates/common/.content/pages/about/_index.json +51 -0
  155. package/templates/common/.content/pages/home/_index.json +112 -0
  156. package/templates/common/.content/schemas/site.schema.json +123 -0
  157. package/templates/common/README.md.tmpl +45 -0
  158. package/templates/common/contracts/components/button.contract.ts +19 -0
  159. package/templates/common/contracts/components/container.contract.ts +19 -0
  160. package/templates/common/contracts/components/featureCard.contract.ts +15 -0
  161. package/templates/common/contracts/components/featureGrid.contract.ts +16 -0
  162. package/templates/common/contracts/components/homeHero.contract.ts +36 -0
  163. package/templates/common/contracts/components/index.ts +21 -0
  164. package/templates/common/contracts/components/siteFooter.contract.ts +45 -0
  165. package/templates/common/contracts/components/siteNavigation.contract.ts +30 -0
  166. package/templates/common/contracts/components/statsBar.contract.ts +19 -0
  167. package/templates/common/design-system/styles.css +346 -0
  168. package/templates/common/design-system/tokens/colors.css +110 -0
  169. package/templates/common/design-system/tokens/components.css +86 -0
  170. package/templates/common/design-system/tokens/index.css +6 -0
  171. package/templates/common/design-system/tokens/radii.css +5 -0
  172. package/templates/common/design-system/tokens/shadows.css +9 -0
  173. package/templates/common/design-system/tokens/spacing.css +26 -0
  174. package/templates/common/design-system/tokens/typography.css +32 -0
  175. package/templates/common/eslint-rules/no-raw-tailwind-values.js +127 -0
  176. package/templates/common/public/assets/Dwire-model.png +0 -0
  177. package/templates/common/public/assets/bare-metal-logo.png +0 -0
  178. package/templates/common/public/assets/meridian-logo.svg +8 -0
  179. package/templates/common/public/assets/seattle.avif +0 -0
  180. package/templates/nextjs/template/.aci.yaml +25 -0
  181. package/templates/nextjs/template/.storybook/main.ts +13 -0
  182. package/templates/nextjs/template/.storybook/preview.ts +34 -0
  183. package/templates/nextjs/template/eslint.config.mjs +36 -0
  184. package/templates/nextjs/template/next-env.d.ts +6 -0
  185. package/templates/nextjs/template/next.config.ts +8 -0
  186. package/templates/nextjs/template/package.json.tmpl +46 -0
  187. package/templates/nextjs/template/postcss.config.mjs +7 -0
  188. package/templates/nextjs/template/src/app/[[...slug]]/page.tsx +8 -0
  189. package/templates/nextjs/template/src/app/gradial/assets/[releaseId]/[...path]/route.ts +31 -0
  190. package/templates/nextjs/template/src/app/layout.tsx +29 -0
  191. package/templates/nextjs/template/src/cms/contracts/components/button.contract.ts +19 -0
  192. package/templates/nextjs/template/src/cms/contracts/components/container.contract.ts +20 -0
  193. package/templates/nextjs/template/src/cms/contracts/components/homeHero.contract.ts +36 -0
  194. package/templates/nextjs/template/src/cms/contracts/components/index.ts +15 -0
  195. package/templates/nextjs/template/src/cms/contracts/components/siteFooter.contract.ts +45 -0
  196. package/templates/nextjs/template/src/cms/contracts/components/siteNavigation.contract.ts +30 -0
  197. package/templates/nextjs/template/src/cms/contracts/layouts/index.ts +16 -0
  198. package/templates/nextjs/template/src/cms/registry.ts +28 -0
  199. package/templates/nextjs/template/src/components/chrome/Footer.tsx +86 -0
  200. package/templates/nextjs/template/src/components/chrome/Header.tsx +74 -0
  201. package/templates/nextjs/template/src/components/elements/AtomicBlock.tsx +13 -0
  202. package/templates/nextjs/template/src/components/elements/Button.tsx +40 -0
  203. package/templates/nextjs/template/src/components/elements/ButtonBlock.tsx +18 -0
  204. package/templates/nextjs/template/src/components/elements/Container.tsx +14 -0
  205. package/templates/nextjs/template/src/components/elements/Icon.tsx +29 -0
  206. package/templates/nextjs/template/src/components/elements/Logo.tsx +38 -0
  207. package/templates/nextjs/template/src/components/sections/ContainerSection.tsx +28 -0
  208. package/templates/nextjs/template/src/components/sections/FeatureCard.tsx +25 -0
  209. package/templates/nextjs/template/src/components/sections/FeatureGrid.tsx +36 -0
  210. package/templates/nextjs/template/src/components/sections/HomeHero.tsx +62 -0
  211. package/templates/nextjs/template/src/components/sections/StatsBar.tsx +30 -0
  212. package/templates/nextjs/template/src/middleware.ts +4 -0
  213. package/templates/nextjs/template/src/stories/Button.stories.tsx +38 -0
  214. package/templates/nextjs/template/src/stories/Header.stories.tsx +30 -0
  215. package/templates/nextjs/template/src/stories/HomeHero.stories.tsx +33 -0
  216. package/templates/nextjs/template/tsconfig.json +23 -0
  217. package/templates/nextjs/template/vercel.json +6 -0
  218. package/dist/content/cache.test.d.ts +0 -1
  219. package/dist/content/cache.test.js +0 -86
  220. package/dist/content/contract.d.ts +0 -83
  221. package/dist/content/contract.js +0 -104
  222. package/dist/content/tailwind-validator.d.ts +0 -6
  223. package/dist/content/tailwind-validator.js +0 -31
  224. package/dist/content/validation.d.ts +0 -108
  225. package/dist/content/validation.js +0 -184
  226. package/dist/next/preview-banner.d.ts +0 -4
  227. package/dist/next/preview-banner.js +0 -51
  228. package/dist/next/preview.d.ts +0 -7
  229. package/dist/next/preview.js +0 -37
  230. package/dist/react/GradialMedia.d.ts +0 -24
  231. package/dist/react/GradialMedia.js +0 -31
  232. package/dist/react/GradialPicture.d.ts +0 -14
  233. package/dist/react/GradialPicture.js +0 -30
  234. package/dist/react/GradialVideoPlayer.d.ts +0 -13
  235. package/dist/sveltekit/index.d.ts +0 -23
  236. package/dist/sveltekit/index.js +0 -62
  237. package/dist/sveltekit/preview.d.ts +0 -5
  238. package/dist/sveltekit/preview.js +0 -33
  239. package/src/cli/verify-renderer.mjs +0 -73
  240. package/src/types/component.ts +0 -59
  241. package/src/types/config.ts +0 -38
  242. package/src/types/data.ts +0 -47
  243. package/src/types/index.ts +0 -10
  244. package/src/types/layout.ts +0 -29
  245. package/src/types/media.ts +0 -125
  246. package/src/types/page.ts +0 -48
  247. package/src/types/render-mode.ts +0 -18
  248. package/src/types/renderer.ts +0 -83
  249. package/src/types/video.ts +0 -66
@@ -1,26 +1,26 @@
1
1
  import { createContentWatchWebpackPlugin } from './content-watch.js';
2
- export function withGradialAci(nextConfig = {}, options = {}) {
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 gradialRewrites = [
9
+ const rewrites = [
10
10
  {
11
11
  source: `${assetPrefix}/:path*`,
12
- destination: `/api/aci-assets${assetPrefix}/:path*`,
12
+ destination: `/gradial/assets/local${assetPrefix}/:path*`,
13
13
  },
14
14
  ];
15
15
  if (!userRewrites)
16
- return gradialRewrites;
16
+ return rewrites;
17
17
  const userResult = await (typeof userRewrites === 'function' ? userRewrites() : userRewrites);
18
18
  if (Array.isArray(userResult)) {
19
- return [...gradialRewrites, ...userResult];
19
+ return [...rewrites, ...userResult];
20
20
  }
21
21
  return {
22
22
  ...userResult,
23
- beforeFiles: [...gradialRewrites, ...(userResult.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');
@@ -1,4 +1,4 @@
1
1
  export { DevRefresh, type DevRefreshProps } from './dev-refresh.js';
2
- export { withGradialAci, type WithGradialOptions } from './config.js';
2
+ export { withAci, type WithAciOptions } from './config.js';
3
3
  export { isPreviewMode } from './preview-mode.js';
4
- export { createPreviewBannerHTML, createPreviewCookies, extractPreviewToken, resolvePreviewContext, PREVIEW_HEADER, RELEASE_HEADER, PREVIEW_TOKEN_COOKIE, PREVIEW_RELEASE_COOKIE, type PreviewContext, type PreviewClaims, type PreviewCookie, } from '../preview/core.js';
4
+ export { createPage, type CreatePageOptions, type SlotPropsFn } from './page.js';
@@ -1,4 +1,4 @@
1
1
  export { DevRefresh } from './dev-refresh.js';
2
- export { withGradialAci } from './config.js';
2
+ export { withAci } from './config.js';
3
3
  export { isPreviewMode } from './preview-mode.js';
4
- export { createPreviewBannerHTML, createPreviewCookies, extractPreviewToken, resolvePreviewContext, PREVIEW_HEADER, RELEASE_HEADER, PREVIEW_TOKEN_COOKIE, PREVIEW_RELEASE_COOKIE, } from '../preview/core.js';
4
+ export { createPage } from './page.js';
@@ -1,7 +1,11 @@
1
1
  import { NextResponse, type NextRequest } from 'next/server.js';
2
- export interface GradialMiddlewareConfig {
3
- siteId: string;
2
+ export interface MiddlewareConfig {
3
+ siteId?: string;
4
4
  edgeConfig?: string;
5
+ previewSignKey?: string;
5
6
  deploymentId?: string;
6
7
  }
7
- export declare function createGradialMiddleware(config: GradialMiddlewareConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
8
+ export declare function createMiddleware(config?: MiddlewareConfig): (request: NextRequest) => Promise<NextResponse<unknown>>;
9
+ export declare const config: {
10
+ matcher: string[];
11
+ };
@@ -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 createGradialMiddleware(config) {
7
- return async function gradialMiddleware(request) {
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 = gradialHeaders(request.headers);
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
- function gradialHeaders(headers) {
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('[gradial/mw] GRADIAL_PREVIEW_SIGN_KEY not set — preview tokens will be ignored');
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();
@@ -1,38 +1,39 @@
1
- import type { ComponentType } from 'react';
2
1
  import type { Metadata } from 'next';
3
- import { type KernelPage, type KernelSiteConfig } from '../content/index.js';
4
- export interface AciPageProps {
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 { createAciPage } from '@gradial/aci/next/page';
24
- * import { CmsPage } from '@/components/CmsPage';
19
+ * import { createPage } from '@gradial/aci/next';
20
+ * import { registry } from '@/cms/registry';
25
21
  *
26
- * const page = createAciPage({ CmsPage });
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
- * and notFound handling. The site only provides its CmsPage component.
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 createAciPage({ CmsPage }: CreateAciPageOptions): {
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 { PageNotFoundError, routeFromNextParams, getRenderInput, } from './server.js';
4
- import { routeMetadataForContent, } from '../content/index.js';
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 { createAciPage } from '@gradial/aci/next/page';
12
- * import { CmsPage } from '@/components/CmsPage';
13
+ * import { createPage } from '@gradial/aci/next';
14
+ * import { registry } from '@/cms/registry';
13
15
  *
14
- * const page = createAciPage({ CmsPage });
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
- * and notFound handling. The site only provides its CmsPage component.
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 createAciPage({ CmsPage }) {
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 || input.page.status !== 'published')
51
+ if (!input.page)
45
52
  notFound();
46
- return _jsx(CmsPage, { page: input.page, siteConfig: input.siteConfig });
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,4 @@
1
+ import type { ReactNode } from 'react';
2
+ export declare function RootLayout({ children }: {
3
+ children: ReactNode;
4
+ }): import("react").JSX.Element;
@@ -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
+ }
@@ -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 GradialFetchConfig {
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 GradialRenderInput<TPage, TSite extends GradialSiteConfig = GradialSiteConfig> {
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 GradialSiteConfig {
25
+ export interface SiteConfig {
26
26
  domain?: string;
27
27
  defaultLocale?: string;
28
28
  }
29
- export declare function getPage<T = unknown>(route?: string, config?: GradialFetchConfig): Promise<T>;
30
- export declare function getSiteConfig<T extends GradialSiteConfig = GradialSiteConfig>(config?: GradialFetchConfig): Promise<T>;
31
- export declare function getFragment<T = unknown>(name: string, config?: GradialFetchConfig): Promise<T>;
32
- export declare function getRoutes(config?: GradialFetchConfig): Promise<string[]>;
33
- export declare function getRenderInput<TPage, TSite extends GradialSiteConfig = GradialSiteConfig>(route?: string, config?: GradialFetchConfig): Promise<GradialRenderInput<TPage, TSite>>;
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 generateGradialStaticParams(): Promise<Array<{
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?: GradialFetchConfig): Promise<Response>;
48
- export declare function resolveReleaseId(config?: GradialFetchConfig): Promise<string>;
48
+ export declare function getReleaseAssetResponse(releaseId: string, assetPath: string, config?: FetchConfig): Promise<Response>;
49
+ export declare function resolveReleaseId(config?: FetchConfig): Promise<string>;
@@ -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 generateGradialStaticParams() {
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();
@@ -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
  }
@@ -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
+ }
@@ -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;
@@ -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 GradialVideo as a <video> element.
4
+ * Renders a Video as a <video> element.
4
5
  *
5
- * - Uses poster.fallback.src for the poster attribute (single URL — browser limitation).
6
+ * - Uses the primary source from poster for the poster attribute.
6
7
  * - Includes <source> elements for format variants when available.
7
- * - Falls back to src attribute on <video> when no source variants exist.
8
+ * - Uses the src attribute on <video> when no source variants exist.
8
9
  */
9
- export function GradialVideoPlayer({ video, ...attrs }) {
10
- const posterSrc = video.poster?.fallback.src;
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,