@bettercms-ai/astro 0.14.5 → 0.15.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.
@@ -32,7 +32,7 @@ import type { DeliveryComponent, DeliveryForm } from "@bettercms-ai/sdk";
32
32
  import { applyOverrides } from "@bettercms-ai/ui/resolve";
33
33
  import { addressFor } from "../dist/bindings.js";
34
34
  import { portableTextToHtml } from "@bettercms-ai/richtext/html";
35
- import { readStructuredContent } from "@bettercms-ai/richtext";
35
+ import { embeddedComponentBlock, readStructuredContent } from "@bettercms-ai/richtext";
36
36
  import { headingParts, richtextHeadingLevel } from "@bettercms-ai/richtext/heading";
37
37
  import BcmsImage from "./BcmsImage.astro";
38
38
  import BcmsForm from "./BcmsForm.astro";
@@ -60,6 +60,11 @@ interface Props {
60
60
  * does not declare gets no address, rather than one that binds to no field.
61
61
  * Internal; do not pass. */
62
62
  fieldAddresses?: Map<string, string>;
63
+ /** Set when `blocks` is a component EMBEDDED in rich text: the address of that rich-text field,
64
+ * which names the instance (`<owner>#<_key>`), or null when the field has none — so the
65
+ * embed gets no addresses rather than a bare `_key` that resolves to nothing.
66
+ * Internal; do not pass. */
67
+ owner?: string | null;
63
68
  class?: string;
64
69
  }
65
70
 
@@ -76,6 +81,7 @@ const {
76
81
  nested = false,
77
82
  containerAttrs = {},
78
83
  fieldAddresses,
84
+ owner,
79
85
  class: className,
80
86
  } = Astro.props;
81
87
  /** What `bind`/`bindProps` read. One object, so every call site cannot disagree. */
@@ -125,6 +131,30 @@ function resolveComponent(
125
131
  return { id: def.id, blocks: resolved, props: def.props ?? [] };
126
132
  }
127
133
 
134
+ /** What an embedded component's slot looks like in the rendered html — content cannot spell a comment. */
135
+ const EMBED_SLOT = "<!--bcms-embed-->";
136
+
137
+ /**
138
+ * Portable Text → the html around each embedded component, plus those components as ordinary
139
+ * `component` blocks. An Astro component cannot render to a string inside `portableTextToHtml`,
140
+ * so the hook leaves a slot and the template mounts each embed there through `<Astro.self>` —
141
+ * the same `component` case (resolver, cycle guard, identity attributes) a placed instance uses.
142
+ * `chunks` has one more entry than `embeds`.
143
+ */
144
+ function richTextParts(structured: NonNullable<ReturnType<typeof readStructuredContent>>, base: string | null) {
145
+ const embeds: ContentBlock[] = [];
146
+ const html = portableTextToHtml(structured, {
147
+ ...(base ? { bindingBase: base } : {}),
148
+ renderBlock: (v) => {
149
+ const block = embeddedComponentBlock(v);
150
+ if (!block) return undefined;
151
+ embeds.push(block as ContentBlock);
152
+ return EMBED_SLOT;
153
+ },
154
+ });
155
+ return { chunks: html.split(EMBED_SLOT), embeds };
156
+ }
157
+
128
158
  /**
129
159
  * Depth-limited scan for a block type anywhere in the tree, components included, so the
130
160
  * slider/tabs runtimes only ship on pages that use them — the same gating render-page.ts
@@ -247,7 +277,17 @@ const pass = { forms, components, turnstileSiteKey, nested: true, fieldAddresses
247
277
  const structured = readStructuredContent(p);
248
278
  if (structured) {
249
279
  const base = addressFor(block.id, "html", bctx);
250
- return <div class="bcms-text" {...blockAttrs} set:html={portableTextToHtml(structured, base ? { bindingBase: base } : {})} />;
280
+ const { chunks, embeds } = richTextParts(structured, base);
281
+ return (
282
+ <div class="bcms-text" {...blockAttrs}>
283
+ {chunks.map((html, i) => (
284
+ <Fragment>
285
+ <Fragment set:html={html} />
286
+ {embeds[i] && <Astro.self blocks={[embeds[i]]} depth={depth} seen={seen} {...pass} owner={base} />}
287
+ </Fragment>
288
+ ))}
289
+ </div>
290
+ );
251
291
  }
252
292
  return <div class="bcms-text" {...bind(block.id, "html", "richtext", bctx)} {...blockAttrs} set:html={p.html ?? ""} />;
253
293
  }
@@ -474,7 +514,7 @@ const pass = { forms, components, turnstileSiteKey, nested: true, fieldAddresses
474
514
  "data-bcms-instance": block.id,
475
515
  }}
476
516
  {...pass}
477
- fieldAddresses={componentInstanceAddresses(block.id, resolved.props)}
517
+ fieldAddresses={owner === null ? new Map() : componentInstanceAddresses(block.id, resolved.props, owner)}
478
518
  />
479
519
  );
480
520
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bettercms-ai/astro",
3
- "version": "0.14.5",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
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",
@@ -54,11 +54,11 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@bettercms-ai/image-url": "^0.3.0",
57
- "@bettercms-ai/next": "^0.13.4",
58
- "@bettercms-ai/richtext": "^0.5.0",
57
+ "@bettercms-ai/next": "^0.14.0",
58
+ "@bettercms-ai/richtext": "^0.7.0",
59
59
  "@bettercms-ai/sdk": "^1.22.0",
60
- "@bettercms-ai/types": "^1.19.0",
61
- "@bettercms-ai/ui": "^0.8.2",
60
+ "@bettercms-ai/types": "^1.20.0",
61
+ "@bettercms-ai/ui": "^0.9.0",
62
62
  "libphonenumber-js": "^1.13.13"
63
63
  },
64
64
  "peerDependencies": {