@bettercms-ai/astro 0.10.0 → 0.11.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.
package/README.md CHANGED
@@ -112,9 +112,58 @@ or on-demand-revalidation hook.
112
112
  | `@bettercms-ai/astro/components/BcmsBlocks.astro` | Render a page's `blockJson` — every block type the builder can author (heading, text/richtext, image, button, spacer, video, columns, form, section, navbar, footer, slider, tabs, component), with block style tokens applied. |
113
113
  | `@bettercms-ai/astro/components/BcmsForm.astro` | Render + submit a form (conditional fields, honeypot, Turnstile). |
114
114
  | `@bettercms-ai/astro/components/BcmsImage.astro` | Optimized image with a 1x/2x srcset via the media transform endpoint. |
115
+ | `@bettercms-ai/astro/components/BcmsLiveBlocks.astro` | `BcmsBlocks` as a hydrated island that lets the Visual Editor re-render your DRAFTS with your own components and CSS. See below. |
115
116
 
116
117
  Markup is class-driven and unstyled — you own the CSS.
117
118
 
119
+ ## Live editing on a static site — `<BcmsLiveBlocks>`
120
+
121
+ When the Visual Editor frames a static build it can only show what was built, so editing a
122
+ draft falls back to the platform's approximate renderer and the canvas stops looking like your
123
+ site. `<BcmsLiveBlocks>` fixes that for pages whose body is a block tree: it renders the same
124
+ markup `<BcmsBlocks>` does, but as a `client:load` island that accepts draft documents from the
125
+ editor over `postMessage` (same-origin, parent frame only, and only after the editor's own
126
+ handshake — a third-party page that iframes your site is on a foreign origin and gets nothing).
127
+ Nothing is fetched by the page and no credentials enter it.
128
+
129
+ It needs a JSX renderer, which a component cannot add for you:
130
+
131
+ ```sh
132
+ npm i @astrojs/preact preact
133
+ ```
134
+
135
+ ```js
136
+ // astro.config.mjs
137
+ import preact from "@astrojs/preact";
138
+ export default defineConfig({ integrations: [bettercms(), preact({ compat: true })] });
139
+ ```
140
+
141
+ ```astro
142
+ ---
143
+ import BcmsLiveBlocks from "@bettercms-ai/astro/components/BcmsLiveBlocks.astro";
144
+ const page = await getPage(Astro.params.slug);
145
+ const { forms } = await readForms();
146
+ ---
147
+ <BcmsLiveBlocks slug={page.slug} blocks={page.blocks} forms={forms} />
148
+ ```
149
+
150
+ `compat: true` is required — the island reuses the React `<BcmsDraftBridge>` from
151
+ `@bettercms-ai/next` so both SDKs speak one protocol. Roughly 10 kB of Preact plus the block
152
+ renderer; `@astrojs/react` works too if your project already uses it.
153
+
154
+ ⚠️ **It emits the React class names, not this package's.** `<BcmsBlocks>` from
155
+ [`@bettercms-ai/next`](https://www.npmjs.com/package/@bettercms-ai/next) classes its output
156
+ `bcms-block bcms-block--text`, `bcms-button--primary`, `bcms-column`; `BcmsBlocks.astro` above
157
+ classes the same tree `bcms-text`, `bcms-btn-primary`, `bcms-cols`. They are NOT interchangeable: a
158
+ stylesheet written for the `.astro` grammar styles nothing on this island, so swapping a page over
159
+ unstyles it. Cover both spellings — the starters wrap them in one `:is()` selector for exactly this
160
+ reason — or take the class list from `@bettercms-ai/next`'s `src/blocks.tsx`.
161
+
162
+ **It only helps pages you render through `BcmsBlocks`.** A hand-written `.astro` layout, or
163
+ content pulled into your own components, is invisible to the bridge — for those, structural
164
+ draft preview needs a server-rendered draft route instead (`output: 'server'` + the draft-mode
165
+ routes below).
166
+
118
167
  ## Draft mode
119
168
 
120
169
  The integration injects `/api/bcms/draft/enable?token=<jwt>&redirect=/path` and
@@ -448,7 +448,12 @@ const pass = { forms, components, turnstileSiteKey, nested: true, fieldAddresses
448
448
  blocks={resolved.blocks}
449
449
  depth={depth + 1}
450
450
  seen={[...seen, resolved.id]}
451
- containerAttrs={blockAttrs}
451
+ containerAttrs={{
452
+ ...blockAttrs,
453
+ "data-bcms-component": resolved.id,
454
+ "data-bcms-variant": "base",
455
+ "data-bcms-instance": block.id,
456
+ }}
452
457
  {...pass}
453
458
  fieldAddresses={componentInstanceAddresses(block.id, resolved.props)}
454
459
  />
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,72 @@
1
+ ---
2
+ /**
3
+ * <BcmsLiveBlocks> — <BcmsBlocks>, but as a hydrated island that speaks the visual editor's
4
+ * draft bridge (E5 / FLO-1178).
5
+ *
6
+ * WHY IT IS NOT A .astro RENDERER. An .astro component is server-only: once the page is built
7
+ * there is nothing on the client to hand a new document to, so an Astro site can only show the
8
+ * editor a STATIC build and the canvas falls back to the platform's approximate renderer for
9
+ * every draft. The bridge needs a live component tree, and the only one that already exists,
10
+ * is at parity with this package, and speaks the protocol is <BcmsDraftBridge> from
11
+ * @bettercms-ai/next — which imports nothing from `next/*` (React and @bettercms-ai/* only),
12
+ * so it mounts here unchanged. Reused rather than reimplemented: one protocol, one twin to
13
+ * keep in step instead of two.
14
+ *
15
+ * RUNTIME: Preact + `compat`, verified by the P2-1 spike (see ../SPIKE.md — SSR and hydration
16
+ * of a heading + PortableText richtext + BcmsForm tree, both clean). The cost is real and worth
17
+ * naming: preact plus its compat aliases on the island (~10 kB of runtime, so `react` itself is
18
+ * never bundled even though @bettercms-ai/ui declares React peers), and `@bettercms-ai/next` as a
19
+ * dependency of this package.
20
+ *
21
+ * 🔴 THE CLASS NAMES ARE THE REACT GRAMMAR, NOT THIS PACKAGE'S. <BcmsBlocks> in @bettercms-ai/next
22
+ * emits `bcms-block bcms-block--text`, `bcms-button--primary`, `bcms-column`; BcmsBlocks.astro
23
+ * emits `bcms-text`, `bcms-btn-primary`, `bcms-cols`. Same markup shape, different hooks — a
24
+ * stylesheet written against the .astro grammar styles NOTHING here, and swapping a page from
25
+ * <BcmsBlocks> to <BcmsLiveBlocks> unstyles it. Either cover both spellings (the starters use
26
+ * `:is()` for exactly this) or read the React classes off @bettercms-ai/next's src/blocks.tsx.
27
+ *
28
+ * The consumer supplies the renderer, because an integration cannot be added from a component:
29
+ *
30
+ * // astro.config.mjs
31
+ * import preact from "@astrojs/preact";
32
+ * export default defineConfig({ integrations: [bettercms(), preact({ compat: true })] });
33
+ *
34
+ * ---
35
+ * import BcmsLiveBlocks from "@bettercms-ai/astro/components/BcmsLiveBlocks.astro";
36
+ * const page = await getPage(Astro.params.slug);
37
+ * ---
38
+ * <BcmsLiveBlocks slug={page.slug} blocks={page.blocks} forms={forms} />
39
+ *
40
+ * Standalone the island renders exactly what <BcmsBlocks> renders (inside the
41
+ * `data-bcms-canvas="bridge"` marker <div> that <BcmsDraftBridge> emits); the bridge only
42
+ * wakes when the editor frames the page and pushes a draft. This only helps pages whose body
43
+ * IS a block tree — a hand-written .astro layout is unreachable from here by construction.
44
+ */
45
+ import { BcmsDraftBridge } from "@bettercms-ai/next/draft-bridge";
46
+ import type { ContentBlock } from "@bettercms-ai/types";
47
+ import type { DeliveryComponent, DeliveryForm } from "@bettercms-ai/sdk";
48
+
49
+ interface Props {
50
+ /** The page's slug. A pushed draft is applied only when its slug matches. */
51
+ slug: string;
52
+ blocks: ContentBlock[];
53
+ forms?: DeliveryForm[];
54
+ components?: DeliveryComponent[];
55
+ turnstileSiteKey?: string;
56
+ apiBase?: string;
57
+ class?: string;
58
+ }
59
+
60
+ const { slug, blocks, forms, components, turnstileSiteKey, apiBase, class: className } = Astro.props;
61
+ ---
62
+
63
+ <BcmsDraftBridge
64
+ client:load
65
+ slug={slug}
66
+ blocks={blocks}
67
+ forms={forms}
68
+ components={components}
69
+ turnstileSiteKey={turnstileSiteKey}
70
+ apiBase={apiBase}
71
+ className={className}
72
+ />
File without changes
File without changes
package/env.d.ts CHANGED
File without changes
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@bettercms-ai/astro",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
- "description": "The BetterCMS adapter for Astro \u2014 a `bettercms()` integration, a `bettercms:client` virtual module, typed content loaders, draft preview, and native .astro rendering components.",
5
+ "description": "The BetterCMS adapter for Astro — a `bettercms()` integration, a `bettercms:client` virtual module, typed content loaders, draft preview, and native .astro rendering components.",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
@@ -31,6 +31,7 @@
31
31
  "./draft-enable": "./dist/routes/draft-enable.js",
32
32
  "./draft-disable": "./dist/routes/draft-disable.js",
33
33
  "./components/BcmsBlocks.astro": "./components/BcmsBlocks.astro",
34
+ "./components/BcmsLiveBlocks.astro": "./components/BcmsLiveBlocks.astro",
34
35
  "./components/BcmsField.astro": "./components/BcmsField.astro",
35
36
  "./components/BcmsForm.astro": "./components/BcmsForm.astro",
36
37
  "./components/BcmsSearch.astro": "./components/BcmsSearch.astro",
@@ -56,20 +57,28 @@
56
57
  "@bettercms-ai/sdk": "^1.14.0",
57
58
  "@bettercms-ai/types": "^1.11.0",
58
59
  "@bettercms-ai/ui": "^0.6.0",
59
- "@bettercms-ai/richtext": "^0.1.0"
60
+ "@bettercms-ai/richtext": "^0.2.0",
61
+ "@bettercms-ai/next": "^0.11.2"
60
62
  },
61
63
  "peerDependencies": {
62
- "astro": ">=4"
64
+ "astro": ">=4",
65
+ "@astrojs/preact": ">=4"
63
66
  },
64
67
  "devDependencies": {
65
68
  "@types/node": "^20",
66
69
  "astro": "^5.0.0",
67
70
  "tsup": "^8.5.1",
68
71
  "typescript": "^5",
69
- "vitest": "^4.1.4"
72
+ "vitest": "^4.1.4",
73
+ "@types/react": "^19.0.0"
70
74
  },
71
75
  "publishConfig": {
72
76
  "registry": "https://registry.npmjs.org/",
73
77
  "access": "public"
78
+ },
79
+ "peerDependenciesMeta": {
80
+ "@astrojs/preact": {
81
+ "optional": true
82
+ }
74
83
  }
75
84
  }