@cmssy/cli 10.7.0 → 10.9.0

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.
@@ -0,0 +1,75 @@
1
+ import {
2
+ CmssyBlock,
3
+ buildBlockContext,
4
+ buildBlockMap,
5
+ type CmssyLayoutGroup,
6
+ } from "@cmssy/react";
7
+ import { CmssyLazyLayout } from "@cmssy/react/client";
8
+ import { blocks } from "./blocks";
9
+
10
+ export interface LayoutSlotProps {
11
+ groups: CmssyLayoutGroup[];
12
+ position: "header" | "footer";
13
+ locale: string;
14
+ defaultLocale: string;
15
+ enabledLocales: string[];
16
+ /** Set on a VERIFIED editor request; the loader reports it as `isEdit`. */
17
+ edit?: { editorOrigin: string | string[] };
18
+ }
19
+
20
+ /**
21
+ * The header and footer, which are layout blocks rather than markup you own.
22
+ *
23
+ * A React island: Astro renders it on the server for visitors (no client JS),
24
+ * and with `client:load` on the edit route, where the bridge needs the browser.
25
+ *
26
+ * Two renders, one per mode, and the difference is the point: server-rendered
27
+ * for visitors, and through the edit bridge for the editor. A header still
28
+ * server-rendered in edit mode is one the editor can select and cannot fill.
29
+ */
30
+ export default function LayoutSlot({
31
+ groups,
32
+ position,
33
+ locale,
34
+ defaultLocale,
35
+ enabledLocales,
36
+ edit,
37
+ }: LayoutSlotProps) {
38
+ if (edit) {
39
+ return (
40
+ <CmssyLazyLayout
41
+ groups={groups}
42
+ position={position}
43
+ locale={locale}
44
+ defaultLocale={defaultLocale}
45
+ enabledLocales={enabledLocales}
46
+ edit={edit}
47
+ load={() => import("./blocks")}
48
+ />
49
+ );
50
+ }
51
+
52
+ const group = groups.find((candidate) => candidate.position === position);
53
+ if (!group) return null;
54
+
55
+ const blockMap = buildBlockMap(blocks);
56
+ const context = buildBlockContext(locale, defaultLocale, enabledLocales);
57
+
58
+ return (
59
+ <>
60
+ {[...group.blocks]
61
+ .filter((block) => block.isActive !== false)
62
+ .sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
63
+ .map((block) => (
64
+ <CmssyBlock
65
+ key={block.id}
66
+ block={block}
67
+ blockMap={blockMap}
68
+ locale={locale}
69
+ defaultLocale={defaultLocale}
70
+ context={context}
71
+ />
72
+ ))}
73
+ </>
74
+ );
75
+ }
@@ -2,10 +2,11 @@
2
2
  import { loadCmssyPage } from "@cmssy/astro";
3
3
  import { cmssy } from "../cmssy.config";
4
4
  import { Blocks } from "../components/Blocks";
5
+ import LayoutSlot from "../cmssy/layout-slot";
5
6
 
6
7
  export const prerender = false;
7
8
 
8
- const { page, locale, defaultLocale, enabledLocales } = await loadCmssyPage(
9
+ const { page, layouts, locale, defaultLocale, enabledLocales } = await loadCmssyPage(
9
10
  cmssy,
10
11
  Astro.request,
11
12
  Astro.url,
@@ -22,6 +23,13 @@ if (!page) {
22
23
  <title>{page?.seoTitle?.[locale] ?? page?.displayName?.[locale] ?? "cmssy on Astro"}</title>
23
24
  </head>
24
25
  <body>
26
+ <LayoutSlot
27
+ groups={layouts}
28
+ position="header"
29
+ locale={locale}
30
+ defaultLocale={defaultLocale}
31
+ enabledLocales={enabledLocales}
32
+ />
25
33
  {
26
34
  page ? (
27
35
  <Blocks
@@ -34,5 +42,12 @@ if (!page) {
34
42
  <main><h1>Not found</h1></main>
35
43
  )
36
44
  }
45
+ <LayoutSlot
46
+ groups={layouts}
47
+ position="footer"
48
+ locale={locale}
49
+ defaultLocale={defaultLocale}
50
+ enabledLocales={enabledLocales}
51
+ />
37
52
  </body>
38
53
  </html>
@@ -2,12 +2,13 @@
2
2
  import { loadCmssyPage } from "@cmssy/astro";
3
3
  import { cmssy } from "../../cmssy.config";
4
4
  import CmssyEditor from "../../cmssy/editor";
5
+ import LayoutSlot from "../../cmssy/layout-slot";
5
6
 
6
7
  // The editor iframe lands here (the middleware rewrites verified requests). It
7
8
  // must be dynamic: a prerendered page never sees the query string.
8
9
  export const prerender = false;
9
10
 
10
- const { page, locale, defaultLocale, enabledLocales } = await loadCmssyPage(
11
+ const { page, layouts, locale, defaultLocale, enabledLocales } = await loadCmssyPage(
11
12
  cmssy,
12
13
  Astro.request,
13
14
  Astro.url,
@@ -21,6 +22,15 @@ const editorOrigin = cmssy.editorOrigin ?? "*";
21
22
  <title>cmssy editor</title>
22
23
  </head>
23
24
  <body>
25
+ <LayoutSlot
26
+ client:load
27
+ groups={layouts}
28
+ position="header"
29
+ locale={locale}
30
+ defaultLocale={defaultLocale}
31
+ enabledLocales={enabledLocales}
32
+ edit={{ editorOrigin }}
33
+ />
24
34
  <CmssyEditor
25
35
  client:load
26
36
  page={page}
@@ -29,5 +39,14 @@ const editorOrigin = cmssy.editorOrigin ?? "*";
29
39
  enabledLocales={enabledLocales}
30
40
  edit={{ editorOrigin }}
31
41
  />
42
+ <LayoutSlot
43
+ client:load
44
+ groups={layouts}
45
+ position="footer"
46
+ locale={locale}
47
+ defaultLocale={defaultLocale}
48
+ enabledLocales={enabledLocales}
49
+ edit={{ editorOrigin }}
50
+ />
32
51
  </body>
33
52
  </html>
@@ -0,0 +1,72 @@
1
+ import {
2
+ CmssyBlock,
3
+ buildBlockContext,
4
+ buildBlockMap,
5
+ type CmssyLayoutGroup,
6
+ } from "@cmssy/react";
7
+ import { CmssyLazyLayout } from "@cmssy/react/client";
8
+ import { blocks } from "./blocks";
9
+
10
+ export interface LayoutSlotProps {
11
+ groups: CmssyLayoutGroup[];
12
+ position: "header" | "footer";
13
+ locale: string;
14
+ defaultLocale: string;
15
+ enabledLocales: string[];
16
+ /** Set on a VERIFIED editor request; the loader reports it as `isEdit`. */
17
+ edit?: { editorOrigin: string | string[] };
18
+ }
19
+
20
+ /**
21
+ * The header and footer, which are layout blocks rather than markup you own.
22
+ *
23
+ * Two renders, one per mode, and the difference is the point: server-rendered
24
+ * for visitors, and through the edit bridge for the editor. A header still
25
+ * server-rendered in edit mode is one the editor can select and cannot fill.
26
+ */
27
+ export function LayoutSlot({
28
+ groups,
29
+ position,
30
+ locale,
31
+ defaultLocale,
32
+ enabledLocales,
33
+ edit,
34
+ }: LayoutSlotProps) {
35
+ if (edit) {
36
+ return (
37
+ <CmssyLazyLayout
38
+ groups={groups}
39
+ position={position}
40
+ locale={locale}
41
+ defaultLocale={defaultLocale}
42
+ enabledLocales={enabledLocales}
43
+ edit={edit}
44
+ load={() => import("./blocks")}
45
+ />
46
+ );
47
+ }
48
+
49
+ const group = groups.find((candidate) => candidate.position === position);
50
+ if (!group) return null;
51
+
52
+ const blockMap = buildBlockMap(blocks);
53
+ const context = buildBlockContext(locale, defaultLocale, enabledLocales);
54
+
55
+ return (
56
+ <>
57
+ {[...group.blocks]
58
+ .filter((block) => block.isActive !== false)
59
+ .sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
60
+ .map((block) => (
61
+ <CmssyBlock
62
+ key={block.id}
63
+ block={block}
64
+ blockMap={blockMap}
65
+ locale={locale}
66
+ defaultLocale={defaultLocale}
67
+ context={context}
68
+ />
69
+ ))}
70
+ </>
71
+ );
72
+ }
@@ -3,6 +3,7 @@ import { createCmssyHeaders, createCmssyLoader } from "@cmssy/remix";
3
3
  import { cmssy } from "../../cmssy.config";
4
4
  import { blocks } from "../cmssy/blocks";
5
5
  import { CmssyEditor } from "../cmssy/editor";
6
+ import { LayoutSlot } from "../cmssy/layout-slot";
6
7
  import type { Route } from "./+types/page";
7
8
 
8
9
  export const loader = createCmssyLoader(cmssy);
@@ -14,6 +15,7 @@ export const headers = createCmssyHeaders(cmssy);
14
15
  export default function CmssyPage({ loaderData }: Route.ComponentProps) {
15
16
  const {
16
17
  page,
18
+ layouts,
17
19
  locale,
18
20
  defaultLocale,
19
21
  enabledLocales,
@@ -28,40 +30,63 @@ export default function CmssyPage({ loaderData }: Route.ComponentProps) {
28
30
 
29
31
  // A verified editor request renders the same page through the edit bridge.
30
32
  // No separate route: a React Router page always sees its query string.
33
+ const slot = (position: "header" | "footer") => (
34
+ <LayoutSlot
35
+ groups={layouts}
36
+ position={position}
37
+ locale={locale}
38
+ defaultLocale={defaultLocale}
39
+ enabledLocales={enabledLocales}
40
+ edit={isEdit ? { editorOrigin } : undefined}
41
+ />
42
+ );
43
+
31
44
  if (isEdit) {
32
45
  return (
33
- <CmssyEditor
34
- page={page}
35
- locale={locale}
36
- defaultLocale={defaultLocale}
37
- enabledLocales={enabledLocales}
38
- edit={{ editorOrigin }}
39
- />
46
+ <>
47
+ {slot("header")}
48
+ <CmssyEditor
49
+ page={page}
50
+ locale={locale}
51
+ defaultLocale={defaultLocale}
52
+ enabledLocales={enabledLocales}
53
+ edit={{ editorOrigin }}
54
+ />
55
+ {slot("footer")}
56
+ </>
40
57
  );
41
58
  }
42
59
 
43
60
  if (!page)
44
61
  return (
45
- <main>
46
- <h1>Not found</h1>
47
- </main>
62
+ <>
63
+ {slot("header")}
64
+ <main>
65
+ <h1>Not found</h1>
66
+ </main>
67
+ {slot("footer")}
68
+ </>
48
69
  );
49
70
 
50
71
  const blockMap = buildBlockMap(blocks);
51
72
  const context = buildBlockContext(locale, defaultLocale, enabledLocales);
52
73
 
53
74
  return (
54
- <main>
55
- {(page.blocks ?? []).map((block) => (
56
- <CmssyBlock
57
- key={block.id}
58
- block={block}
59
- blockMap={blockMap}
60
- locale={locale}
61
- defaultLocale={defaultLocale}
62
- context={context}
63
- />
64
- ))}
65
- </main>
75
+ <>
76
+ {slot("header")}
77
+ <main>
78
+ {(page.blocks ?? []).map((block) => (
79
+ <CmssyBlock
80
+ key={block.id}
81
+ block={block}
82
+ blockMap={blockMap}
83
+ locale={locale}
84
+ defaultLocale={defaultLocale}
85
+ context={context}
86
+ />
87
+ ))}
88
+ </main>
89
+ {slot("footer")}
90
+ </>
66
91
  );
67
92
  }
package/dist/index.js CHANGED
@@ -187,6 +187,7 @@ var FRAMEWORKS = [
187
187
  "src/middleware.ts",
188
188
  "src/cmssy/blocks.ts",
189
189
  "src/cmssy/editor.tsx",
190
+ "src/cmssy/layout-slot.tsx",
190
191
  "src/cmssy/hero.tsx",
191
192
  "src/components/Blocks.tsx",
192
193
  "src/pages/[...path].astro",
@@ -204,6 +205,7 @@ var FRAMEWORKS = [
204
205
  "app/routes.ts",
205
206
  "app/cmssy/blocks.ts",
206
207
  "app/cmssy/editor.tsx",
208
+ "app/cmssy/layout-slot.tsx",
207
209
  "app/cmssy/hero.tsx",
208
210
  "app/routes/page.tsx"
209
211
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmssy/cli",
3
- "version": "10.7.0",
3
+ "version": "10.9.0",
4
4
  "description": "The cmssy CLI: `cmssy init` wires cmssy into an existing Next.js, Astro or React Router app; `cmssy link` connects it to a workspace and verifies the editor wiring.",
5
5
  "keywords": [
6
6
  "cmssy",
@@ -27,7 +27,7 @@
27
27
  "assets"
28
28
  ],
29
29
  "dependencies": {
30
- "@cmssy/core": "10.7.0"
30
+ "@cmssy/core": "10.9.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^20.0.0",