@bettercms-ai/astro 0.9.1 → 0.9.2
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/components/BcmsBlocks.astro +26 -2
- package/dist/bindings.d.ts +42 -0
- package/dist/bindings.js +26 -0
- package/dist/bindings.js.map +1 -0
- package/package.json +9 -3
|
@@ -27,9 +27,12 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import type { ContentBlock } from "@bettercms-ai/types";
|
|
29
29
|
import { blockStyleToCss, componentInstanceAddresses } from "@bettercms-ai/types";
|
|
30
|
-
import { bind, bindProps } from "../
|
|
30
|
+
import { bind, bindProps } from "../dist/bindings.js";
|
|
31
31
|
import type { DeliveryComponent, DeliveryForm } from "@bettercms-ai/sdk";
|
|
32
32
|
import { applyOverrides } from "@bettercms-ai/ui/resolve";
|
|
33
|
+
import { addressFor } from "../dist/bindings.js";
|
|
34
|
+
import { portableTextToHtml } from "@bettercms-ai/richtext/html";
|
|
35
|
+
import { readStructuredContent } from "@bettercms-ai/richtext";
|
|
33
36
|
import BcmsImage from "./BcmsImage.astro";
|
|
34
37
|
import BcmsForm from "./BcmsForm.astro";
|
|
35
38
|
|
|
@@ -206,8 +209,29 @@ const pass = { forms, components, turnstileSiteKey, nested: true, fieldAddresses
|
|
|
206
209
|
// `richtext` and `text` carry the same payload and differ only in the authoring
|
|
207
210
|
// surface, so delivery renders them identically — the hosted renderer does the same.
|
|
208
211
|
case "text":
|
|
209
|
-
case "richtext":
|
|
212
|
+
case "richtext": {
|
|
213
|
+
/**
|
|
214
|
+
* STRUCTURED CONTENT WINS. This lane read `p.html` unconditionally while the hosted
|
|
215
|
+
* renderer preferred `p.content`, so a block whose structured value had moved past its
|
|
216
|
+
* derived cache rendered the stale cache on Astro sites only — the same per-lane
|
|
217
|
+
* divergence that once made `richtext` render nothing here at all.
|
|
218
|
+
*
|
|
219
|
+
* Serialised to a STRING rather than mounting React. `@portabletext/react` would drag
|
|
220
|
+
* `@astrojs/react` into every customer project, which is a breaking install-time change
|
|
221
|
+
* for a rendering result that is identical. `set:html` is already what this line did;
|
|
222
|
+
* the difference is that the string is now generated by our own escaping serialiser
|
|
223
|
+
* instead of coming out of storage, which REDUCES the trust surface.
|
|
224
|
+
*
|
|
225
|
+
* The per-block `#<_key>` addresses are stamped by the serialiser, so one paragraph
|
|
226
|
+
* inside the field is clickable rather than the field being one opaque string.
|
|
227
|
+
*/
|
|
228
|
+
const structured = readStructuredContent(p);
|
|
229
|
+
if (structured) {
|
|
230
|
+
const base = addressFor(block.id, "html", bctx);
|
|
231
|
+
return <div class="bcms-text" {...blockAttrs} set:html={portableTextToHtml(structured, base ? { bindingBase: base } : {})} />;
|
|
232
|
+
}
|
|
210
233
|
return <div class="bcms-text" {...bind(block.id, "html", "richtext", bctx)} {...blockAttrs} set:html={p.html ?? ""} />;
|
|
234
|
+
}
|
|
211
235
|
case "image": {
|
|
212
236
|
// Matches the Next adapter: the image block is click-to-select on the Visual Editor
|
|
213
237
|
// canvas like heading/text/button, rather than dock-only. `kind="image"` means select
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { BindingKind } from '@bettercms-ai/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Visual Editor addresses for the Astro renderer.
|
|
5
|
+
*
|
|
6
|
+
* Identical contract to `@bettercms-ai/next`'s `bindings.ts`, and the SAME reason for existing:
|
|
7
|
+
* inside a component instance the definition's block ids address nothing, because they are
|
|
8
|
+
* shared by every instance of it. Stamping them produces an orphan on one placement and a
|
|
9
|
+
* duplicate that drops BOTH on two.
|
|
10
|
+
*
|
|
11
|
+
* Kept as a `.ts` module rather than inlined in the `.astro` frontmatter so the logic is
|
|
12
|
+
* testable without an Astro compiler, and so the two SDKs cannot drift from each other by
|
|
13
|
+
* having one file edited and not the other.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
interface BindingCtx {
|
|
17
|
+
/** Present only inside a component instance, where it is AUTHORITATIVE. */
|
|
18
|
+
fieldAddresses?: Map<string, string>;
|
|
19
|
+
}
|
|
20
|
+
/** The address a write lands on, or null when none exists. */
|
|
21
|
+
declare function addressFor(id: string | undefined, prop: string, ctx: BindingCtx): string | null;
|
|
22
|
+
/**
|
|
23
|
+
* Attributes for a value rendered as this element's own content. Astro omits `undefined`
|
|
24
|
+
* attributes, so an empty object spreads to nothing.
|
|
25
|
+
*/
|
|
26
|
+
declare function bind(id: string | undefined, prop: string, kind: BindingKind, ctx: BindingCtx): Record<string, string>;
|
|
27
|
+
/**
|
|
28
|
+
* Attributes for values that live in an ATTRIBUTE of this element. `data-bcms-field` addresses
|
|
29
|
+
* one element and an element cannot hold two, so these ride `data-bcms-props`:
|
|
30
|
+
* `<address>|<kind>|<domAttribute>`, semicolon separated.
|
|
31
|
+
*
|
|
32
|
+
* An entry whose value is absent is dropped — `setFieldPath` refuses to create a final key, so
|
|
33
|
+
* the address would be a write the editor rejects.
|
|
34
|
+
*/
|
|
35
|
+
declare function bindProps(id: string | undefined, entries: readonly {
|
|
36
|
+
prop: string;
|
|
37
|
+
kind: BindingKind;
|
|
38
|
+
attr: string;
|
|
39
|
+
present: boolean;
|
|
40
|
+
}[], ctx: BindingCtx): Record<string, string>;
|
|
41
|
+
|
|
42
|
+
export { type BindingCtx, addressFor, bind, bindProps };
|
package/dist/bindings.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/bindings.ts
|
|
2
|
+
import { formatPropsAttribute } from "@bettercms-ai/types";
|
|
3
|
+
function addressFor(id, prop, ctx) {
|
|
4
|
+
if (!id) return null;
|
|
5
|
+
const key = `${id}__${prop}`;
|
|
6
|
+
if (ctx.fieldAddresses) return ctx.fieldAddresses.get(key) ?? null;
|
|
7
|
+
return key;
|
|
8
|
+
}
|
|
9
|
+
function bind(id, prop, kind, ctx) {
|
|
10
|
+
const address = addressFor(id, prop, ctx);
|
|
11
|
+
return address ? { "data-bcms-field": address, "data-bcms-kind": kind } : {};
|
|
12
|
+
}
|
|
13
|
+
function bindProps(id, entries, ctx) {
|
|
14
|
+
const resolved = entries.flatMap(({ prop, kind, attr, present }) => {
|
|
15
|
+
if (!present) return [];
|
|
16
|
+
const address = addressFor(id, prop, ctx);
|
|
17
|
+
return address ? [{ address, kind, attr }] : [];
|
|
18
|
+
});
|
|
19
|
+
return resolved.length ? { "data-bcms-props": formatPropsAttribute(resolved) } : {};
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
addressFor,
|
|
23
|
+
bind,
|
|
24
|
+
bindProps
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=bindings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bindings.ts"],"sourcesContent":["/**\n * Visual Editor addresses for the Astro renderer.\n *\n * Identical contract to `@bettercms-ai/next`'s `bindings.ts`, and the SAME reason for existing:\n * inside a component instance the definition's block ids address nothing, because they are\n * shared by every instance of it. Stamping them produces an orphan on one placement and a\n * duplicate that drops BOTH on two.\n *\n * Kept as a `.ts` module rather than inlined in the `.astro` frontmatter so the logic is\n * testable without an Astro compiler, and so the two SDKs cannot drift from each other by\n * having one file edited and not the other.\n */\nimport type { BindingKind } from \"@bettercms-ai/types\";\nimport { formatPropsAttribute } from \"@bettercms-ai/types\";\n\nexport interface BindingCtx {\n /** Present only inside a component instance, where it is AUTHORITATIVE. */\n fieldAddresses?: Map<string, string>;\n}\n\n/** The address a write lands on, or null when none exists. */\nexport function addressFor(id: string | undefined, prop: string, ctx: BindingCtx): string | null {\n if (!id) return null;\n const key = `${id}__${prop}`;\n if (ctx.fieldAddresses) return ctx.fieldAddresses.get(key) ?? null;\n return key;\n}\n\n/**\n * Attributes for a value rendered as this element's own content. Astro omits `undefined`\n * attributes, so an empty object spreads to nothing.\n */\nexport function bind(\n id: string | undefined,\n prop: string,\n kind: BindingKind,\n ctx: BindingCtx,\n): Record<string, string> {\n const address = addressFor(id, prop, ctx);\n return address ? { \"data-bcms-field\": address, \"data-bcms-kind\": kind } : {};\n}\n\n/**\n * Attributes for values that live in an ATTRIBUTE of this element. `data-bcms-field` addresses\n * one element and an element cannot hold two, so these ride `data-bcms-props`:\n * `<address>|<kind>|<domAttribute>`, semicolon separated.\n *\n * An entry whose value is absent is dropped — `setFieldPath` refuses to create a final key, so\n * the address would be a write the editor rejects.\n */\nexport function bindProps(\n id: string | undefined,\n entries: readonly { prop: string; kind: BindingKind; attr: string; present: boolean }[],\n ctx: BindingCtx,\n): Record<string, string> {\n const resolved = entries.flatMap(({ prop, kind, attr, present }) => {\n if (!present) return [];\n const address = addressFor(id, prop, ctx);\n return address ? [{ address, kind, attr }] : [];\n });\n return resolved.length ? { \"data-bcms-props\": formatPropsAttribute(resolved) } : {};\n}\n"],"mappings":";AAaA,SAAS,4BAA4B;AAQ9B,SAAS,WAAW,IAAwB,MAAc,KAAgC;AAC/F,MAAI,CAAC,GAAI,QAAO;AAChB,QAAM,MAAM,GAAG,EAAE,KAAK,IAAI;AAC1B,MAAI,IAAI,eAAgB,QAAO,IAAI,eAAe,IAAI,GAAG,KAAK;AAC9D,SAAO;AACT;AAMO,SAAS,KACd,IACA,MACA,MACA,KACwB;AACxB,QAAM,UAAU,WAAW,IAAI,MAAM,GAAG;AACxC,SAAO,UAAU,EAAE,mBAAmB,SAAS,kBAAkB,KAAK,IAAI,CAAC;AAC7E;AAUO,SAAS,UACd,IACA,SACA,KACwB;AACxB,QAAM,WAAW,QAAQ,QAAQ,CAAC,EAAE,MAAM,MAAM,MAAM,QAAQ,MAAM;AAClE,QAAI,CAAC,QAAS,QAAO,CAAC;AACtB,UAAM,UAAU,WAAW,IAAI,MAAM,GAAG;AACxC,WAAO,UAAU,CAAC,EAAE,SAAS,MAAM,KAAK,CAAC,IAAI,CAAC;AAAA,EAChD,CAAC;AACD,SAAO,SAAS,SAAS,EAAE,mBAAmB,qBAAqB,QAAQ,EAAE,IAAI,CAAC;AACpF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bettercms-ai/astro",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "The BetterCMS adapter for Astro
|
|
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.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
@@ -18,6 +18,11 @@
|
|
|
18
18
|
"import": "./dist/runtime.js",
|
|
19
19
|
"default": "./dist/runtime.js"
|
|
20
20
|
},
|
|
21
|
+
"./bindings": {
|
|
22
|
+
"types": "./dist/bindings.d.ts",
|
|
23
|
+
"import": "./dist/bindings.js",
|
|
24
|
+
"default": "./dist/bindings.js"
|
|
25
|
+
},
|
|
21
26
|
"./middleware": {
|
|
22
27
|
"types": "./dist/middleware-entry.d.ts",
|
|
23
28
|
"import": "./dist/middleware-entry.js",
|
|
@@ -50,7 +55,8 @@
|
|
|
50
55
|
"@bettercms-ai/image-url": "^0.3.0",
|
|
51
56
|
"@bettercms-ai/sdk": "^1.13.1",
|
|
52
57
|
"@bettercms-ai/types": "^1.11.0",
|
|
53
|
-
"@bettercms-ai/ui": "^0.6.0"
|
|
58
|
+
"@bettercms-ai/ui": "^0.6.0",
|
|
59
|
+
"@bettercms-ai/richtext": "^0.1.0"
|
|
54
60
|
},
|
|
55
61
|
"peerDependencies": {
|
|
56
62
|
"astro": ">=4"
|