@bettercms-ai/convert 0.20.1 → 0.22.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/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @bettercms-ai/convert
2
2
 
3
+ ## 0.22.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c0afef4: The emitted helper (v11) paints the same section design keys as `blockStyleToCss`: `theme`, `bg`/`bgCustom`, `bgImage` (http(s) or root-relative only), `corner`, `shadow`, `fullHeight`, `opacity`, `borderTop`/`borderBottom` and the px padding sliders. A converted section root's `data-bcms-style` promise, that its inline style is `blockStyleToCss(block.style)`, holds again for sections styled in the visual editor. Helpers at v10 and older are recognised and upgraded. The content column (`contentWidth`) is still not drawn on converted roots.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [c0afef4]
12
+ - Updated dependencies [5c6db8e]
13
+ - @bettercms-ai/types@1.30.0
14
+
15
+ ## 0.21.0
16
+
17
+ ### Minor Changes
18
+
19
+ - a99b2c3: A site's own section root can now declare that it applies the section's style. `sectionRootAttrs(block)` from `@bettercms-ai/astro` and `@bettercms-ai/next` returns `data-bcms-block`, `data-bcms-style` and the block's `blockStyleToCss(block.style)` as `style`. Spread it on the root: `<section {...sectionRootAttrs(block)}>`. When the block has no style there is no `style` attribute, but the marker is still there. The BetterCMS editor shows a section's spacing handles on a site-rendered page only when every section root it finds carries the marker. `bettercms-convert --componentize` now stamps the marker and the placement's style on every section root it extracts: the helper (v10) inlines the same declarations, and the registry passes each placement's `block.style` down as `blockStyle`. A root that already has its own `style` attribute, or a spread, is left unmarked.
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies [d84d241]
24
+ - @bettercms-ai/types@1.29.0
25
+
3
26
  ## 0.20.1
4
27
 
5
28
  ### Patch Changes
package/dist/cli.js CHANGED
@@ -4190,7 +4190,7 @@ async function bindImages(ctx, targets) {
4190
4190
 
4191
4191
  // src/helper.ts
4192
4192
  import { createHash as createHash2 } from "crypto";
4193
- var HELPER_VERSION = 9;
4193
+ var HELPER_VERSION = 11;
4194
4194
  var HEADER = "// @bettercms-ai/convert helper v";
4195
4195
  var HELPER_PATH = Object.fromEntries(
4196
4196
  Object.keys(DIALECT_RULES).map((d) => [d, DIALECT_RULES[d].helper])
@@ -4514,6 +4514,144 @@ export function bcmsFields(
4514
4514
  return (own ? undefined : moved(key, rest.slice(key?.length ?? 0))) ?? lane + rest;
4515
4515
  };
4516
4516
  }
4517
+
4518
+ /**
4519
+ * C3/C4 \u2014 a section's \`block.style\` as the inline CSS the BetterCMS SDK draws for it.
4520
+ *
4521
+ * A converted section root carries \`data-bcms-style\` beside \`data-bcms-block\`, which is the
4522
+ * site's promise to the editor that "this root's inline style is \`blockStyleToCss(block.style)\`"
4523
+ * \u2014 and the editor only offers a section's spacing handles where every stamped root makes it.
4524
+ * This is \`blockStyleToCss\` from \`@bettercms-ai/types\`, INLINED for the same reason \`readPath\`
4525
+ * is (zero dependencies); \`helper.test.ts\` runs it against the original over a token table.
4526
+ */
4527
+ const STYLE_SPACE_PX: Record<string, string> = { none: "0", xs: "4px", sm: "8px", md: "16px", lg: "32px", xl: "64px" };
4528
+ const STYLE_FONT_SIZE_REM: Record<string, string> = {
4529
+ sm: "0.875rem",
4530
+ base: "1rem",
4531
+ lg: "1.25rem",
4532
+ xl: "1.5rem",
4533
+ "2xl": "2rem",
4534
+ "3xl": "2.25rem",
4535
+ "4xl": "3rem",
4536
+ "5xl": "4rem",
4537
+ };
4538
+ const STYLE_WEIGHT: Record<string, number> = { normal: 400, medium: 500, semibold: 600, bold: 700 };
4539
+ const STYLE_RADIUS_PX: Record<string, string> = { none: "0", sm: "4px", md: "8px", lg: "16px", full: "9999px" };
4540
+ const STYLE_COLOR_HEX: Record<string, string> = { white: "#ffffff", black: "#111111", muted: "#6b7280", mutedBg: "#f3f4f6", primary: "#111111", accent: "#4f46e5" };
4541
+ const STYLE_SPACE_TOKEN_PX: Record<string, string> = {
4542
+ "section-space-none": "0px",
4543
+ "section-space-small": "32px",
4544
+ "section-space-medium": "64px",
4545
+ "section-space-large": "96px",
4546
+ "section-space-xlarge": "144px",
4547
+ "section-space-nav-overlap": "-72px",
4548
+ "space-none": "0px",
4549
+ "space-s": "8px",
4550
+ "space-m": "16px",
4551
+ "space-l": "32px",
4552
+ "space-xl": "64px",
4553
+ };
4554
+
4555
+ const STYLE_THEME: Record<string, { colorScheme: string; background: string; color: string }> = {
4556
+ light: { colorScheme: "light", background: "#ffffff", color: "#111111" },
4557
+ dark: { colorScheme: "dark", background: "#0b0b0d", color: "#f7f8f8" },
4558
+ };
4559
+ const STYLE_BG: Record<string, string> = {
4560
+ surface: "var(--bcms-bg-surface,#ffffff)",
4561
+ muted: "var(--bcms-bg-muted,#f3f4f6)",
4562
+ accent: "var(--bcms-bg-accent,#4f46e5)",
4563
+ dark: "var(--bcms-bg-dark,#0b0b0d)",
4564
+ };
4565
+ const STYLE_CORNER_PX: Record<string, string> = { "0": "0", s: "4px", m: "8px", l: "16px", xl: "24px", "2xl": "32px" };
4566
+ const STYLE_SHADOW: Record<string, string> = {
4567
+ "0": "none",
4568
+ s: "0 1px 2px rgba(0,0,0,.06)",
4569
+ m: "0 4px 12px rgba(0,0,0,.08)",
4570
+ l: "0 10px 24px rgba(0,0,0,.10)",
4571
+ xl: "0 24px 48px rgba(0,0,0,.14)",
4572
+ };
4573
+ const STYLE_HAIRLINE = "1px solid rgba(0,0,0,.10)";
4574
+
4575
+ /** A map entry by OWN key only, so a token like \`constructor\` never reaches CSS. */
4576
+ function styleOwn<T>(map: Record<string, T>, key: unknown): T | undefined {
4577
+ return typeof key === "string" && Object.prototype.hasOwnProperty.call(map, key) ? map[key] : undefined;
4578
+ }
4579
+
4580
+ /** An http(s) or root-relative url with nothing that could end \`url(\u2026)\`; "" for anything else. */
4581
+ function styleSafeUrl(raw: unknown): string {
4582
+ if (typeof raw !== "string") return "";
4583
+ const url = raw.trim();
4584
+ if (!url || url.length > 2000) return "";
4585
+ if (/["'()\\;]|\\s/.test(url)) return "";
4586
+ if (url.startsWith("//")) return "";
4587
+ if (url.startsWith("/")) return url;
4588
+ return /^https?:\\/\\//i.test(url) ? url : "";
4589
+ }
4590
+
4591
+ function styleClamp(n: number, lo: number, hi: number): number {
4592
+ return Math.min(hi, Math.max(lo, n));
4593
+ }
4594
+
4595
+ function styleSpaceToken(token: unknown): string {
4596
+ const px = typeof token === "string" ? STYLE_SPACE_TOKEN_PX[token] : undefined;
4597
+ return px ? "var(--bcms-" + token + "," + px + ")" : "";
4598
+ }
4599
+
4600
+ /** The declarations as a React-style object (camelCase), or undefined when there are none. */
4601
+ export function bcmsSectionStyle(style: unknown): Record<string, string | number> | undefined {
4602
+ if (!style || typeof style !== "object") return undefined;
4603
+ const s = style as Record<string, string>;
4604
+ const css: Record<string, string | number> = {};
4605
+ if (s.paddingY && STYLE_SPACE_PX[s.paddingY] != null) { css.paddingTop = STYLE_SPACE_PX[s.paddingY]!; css.paddingBottom = STYLE_SPACE_PX[s.paddingY]!; }
4606
+ if (s.paddingX && STYLE_SPACE_PX[s.paddingX] != null) { css.paddingLeft = STYLE_SPACE_PX[s.paddingX]!; css.paddingRight = STYLE_SPACE_PX[s.paddingX]!; }
4607
+ const top = styleSpaceToken(s.spaceTop);
4608
+ // A negative band that pulls content under the nav makes sense at the TOP only (SPACE_BOTTOM_TOKENS).
4609
+ const bottom = /^section-space-(none|small|medium|large|xlarge)$/.test(String(s.spaceBottom)) ? styleSpaceToken(s.spaceBottom) : "";
4610
+ const sides = styleSpaceToken(s.spaceSides);
4611
+ const navOverlap = s.spaceTop === "section-space-nav-overlap";
4612
+ if (top && !navOverlap) css.paddingTop = top;
4613
+ if (bottom) css.paddingBottom = bottom;
4614
+ if (sides) { css.paddingLeft = sides; css.paddingRight = sides; }
4615
+ if (s.marginTop && STYLE_SPACE_PX[s.marginTop] != null) css.marginTop = STYLE_SPACE_PX[s.marginTop]!;
4616
+ if (top && navOverlap) { css.marginTop = top; css.paddingTop = "calc(-1 * " + top + ")"; }
4617
+ if (s.marginBottom && STYLE_SPACE_PX[s.marginBottom] != null) css.marginBottom = STYLE_SPACE_PX[s.marginBottom]!;
4618
+ if (s.background && STYLE_COLOR_HEX[s.background]) css.background = STYLE_COLOR_HEX[s.background]!;
4619
+ if (s.textColor && STYLE_COLOR_HEX[s.textColor]) css.color = STYLE_COLOR_HEX[s.textColor]!;
4620
+ if (s.align === "left" || s.align === "center" || s.align === "right") css.textAlign = s.align;
4621
+ if (s.fontSize && STYLE_FONT_SIZE_REM[s.fontSize]) css.fontSize = STYLE_FONT_SIZE_REM[s.fontSize]!;
4622
+ if (s.weight && STYLE_WEIGHT[s.weight] != null) css.fontWeight = STYLE_WEIGHT[s.weight]!;
4623
+ if (s.radius && STYLE_RADIUS_PX[s.radius] != null) css.borderRadius = STYLE_RADIUS_PX[s.radius]!;
4624
+ const theme = styleOwn(STYLE_THEME, s.theme);
4625
+ if (theme) { css.colorScheme = theme.colorScheme; css.background = theme.background; css.color = theme.color; }
4626
+ if (s.bg === "custom") { if (/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(s.bgCustom ?? "")) css.background = s.bgCustom!; }
4627
+ else if (styleOwn(STYLE_BG, s.bg)) css.background = styleOwn(STYLE_BG, s.bg)!;
4628
+ const bgImage = styleSafeUrl(s.bgImage);
4629
+ if (bgImage) { css.backgroundImage = "url(" + bgImage + ")"; css.backgroundSize = "cover"; css.backgroundPosition = "center"; }
4630
+ const n = style as Record<string, unknown>;
4631
+ if (!top && typeof n.paddingTop === "number") css.paddingTop = styleClamp(n.paddingTop, 0, 240) + "px";
4632
+ if (!bottom && typeof n.paddingBottom === "number") css.paddingBottom = styleClamp(n.paddingBottom, 0, 240) + "px";
4633
+ if (!sides && typeof n.paddingSides === "number") {
4634
+ const px = styleClamp(n.paddingSides, 0, 240) + "px";
4635
+ css.paddingLeft = px; css.paddingRight = px;
4636
+ }
4637
+ if (n.fullHeight === true) { css.minHeight = "100vh"; css.display = "flex"; css.flexDirection = "column"; css.justifyContent = "center"; }
4638
+ if (typeof n.opacity === "number") css.opacity = styleClamp(n.opacity, 0, 100) / 100;
4639
+ if (styleOwn(STYLE_CORNER_PX, s.corner) != null) { css.borderRadius = styleOwn(STYLE_CORNER_PX, s.corner)!; css.overflow = "hidden"; }
4640
+ if (styleOwn(STYLE_SHADOW, s.shadow)) css.boxShadow = styleOwn(STYLE_SHADOW, s.shadow)!;
4641
+ if (n.borderTop === true) css.borderTop = STYLE_HAIRLINE;
4642
+ if (n.borderBottom === true) css.borderBottom = STYLE_HAIRLINE;
4643
+ if ((style as { border?: unknown }).border === true) css.border = "1px solid currentColor";
4644
+ return Object.keys(css).length ? css : undefined;
4645
+ }
4646
+
4647
+ /** The same declarations as an Astro \`style\` string (kebab-case, \`;\`-joined), or undefined. */
4648
+ export function bcmsSectionStyleText(style: unknown): string | undefined {
4649
+ const css = bcmsSectionStyle(style);
4650
+ if (!css) return undefined;
4651
+ return Object.entries(css)
4652
+ .map(([k, v]) => k.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()) + ":" + v)
4653
+ .join(";");
4654
+ }
4517
4655
  `;
4518
4656
  }
4519
4657
  var HISTORICAL = {
@@ -4525,7 +4663,9 @@ var HISTORICAL = {
4525
4663
  5: "0472fe3ffdcfa49e719c7b9da9cb63b6eba637cc22783bb00c3c2cadd3d800da",
4526
4664
  6: "c907e7245578d947c6541b1ab2c5c6659ea7789a1519e895b4a4aed122c6f649",
4527
4665
  7: "f22514ef48c50230985e605bea6cf9bf41756384a7dc80603e347baba9b65d93",
4528
- 8: "484e72852d2b496e016a1f2a2b1a77bb993f8a163b290781522d6114b6519bb9"
4666
+ 8: "484e72852d2b496e016a1f2a2b1a77bb993f8a163b290781522d6114b6519bb9",
4667
+ 9: "d6441db2ff4578c3c5d4b7d461f3374b89247ef6ba9e0d535791e840d803e4f3",
4668
+ 10: "ffcb59862972b15a67c5951e9f3b94ce5b8b50345e6aac2bfa5789cfc3136910"
4529
4669
  },
4530
4670
  "src/lib/bcms-content.ts": {
4531
4671
  1: "ae72ae14d706943d481c880d70da5789b4816ba1ba319a10fbc63ded4d0b42f2",
@@ -4535,7 +4675,9 @@ var HISTORICAL = {
4535
4675
  5: "c8c93655320fa65664611ba84541f3fa7bfa292cc9a2fd655373440b99049fba",
4536
4676
  6: "63f1fa29e47e70c5193b81ee24addb238ba100247124d55bc17713102d587df5",
4537
4677
  7: "ed92fb88de953d9951abbe6a1edae1d048f4050eaf6fb607ac08167f94d8bcc7",
4538
- 8: "1a6f669d76d42effe77f082436e9d21c0efa6fc499e919e3d116bc3a27a7f3d5"
4678
+ 8: "1a6f669d76d42effe77f082436e9d21c0efa6fc499e919e3d116bc3a27a7f3d5",
4679
+ 9: "eee9fb1635ae7d6f8f33676a1f390600ca24a62ee992775252273c73a011c494",
4680
+ 10: "35bb7c0e30f5c41fe68e52259ec4764de6de2734064fa0e1521f3338eef0a09e"
4539
4681
  },
4540
4682
  "composables/bcms-content.ts": {
4541
4683
  1: "c272412532efed7db64b6cf313b2bed4291f9951430412289c8a588b7f565407",
@@ -4545,7 +4687,9 @@ var HISTORICAL = {
4545
4687
  5: "0472fe3ffdcfa49e719c7b9da9cb63b6eba637cc22783bb00c3c2cadd3d800da",
4546
4688
  6: "c907e7245578d947c6541b1ab2c5c6659ea7789a1519e895b4a4aed122c6f649",
4547
4689
  7: "f22514ef48c50230985e605bea6cf9bf41756384a7dc80603e347baba9b65d93",
4548
- 8: "484e72852d2b496e016a1f2a2b1a77bb993f8a163b290781522d6114b6519bb9"
4690
+ 8: "484e72852d2b496e016a1f2a2b1a77bb993f8a163b290781522d6114b6519bb9",
4691
+ 9: "d6441db2ff4578c3c5d4b7d461f3374b89247ef6ba9e0d535791e840d803e4f3",
4692
+ 10: "ffcb59862972b15a67c5951e9f3b94ce5b8b50345e6aac2bfa5789cfc3136910"
4549
4693
  }
4550
4694
  };
4551
4695
  var sha256 = (value) => createHash2("sha256").update(value, "utf8").digest("hex");
@@ -8303,7 +8447,9 @@ function componentFile(dialect, path, fromFile, content, parsed, resolved, pathP
8303
8447
  ]);
8304
8448
  const fieldsName = introduced.get("bcmsFields");
8305
8449
  const fieldName = introduced.get("bcmsField");
8306
- const { markup, bound, duplicates } = sectionMarkup(content, resolved.root, fieldName);
8450
+ const styleReader = dialect === "astro" ? "bcmsSectionStyleText" : "bcmsSectionStyle";
8451
+ const styled = rootStyleIsFree(content, resolved.root) && !reserved.has("blockStyle") && !reserved.has(styleReader);
8452
+ const { markup, bound, duplicates } = sectionMarkup(content, resolved.root, fieldName, styled ? styleReader : null);
8307
8453
  if (duplicates.length > 0) return { content: "", free: [], duplicates, unconvertible: [] };
8308
8454
  const groupKey = resolved.section.groupKey;
8309
8455
  const locals = helperLocals(parsed, dialect, fromFile);
@@ -8314,7 +8460,8 @@ function componentFile(dialect, path, fromFile, content, parsed, resolved, pathP
8314
8460
  .../* @__PURE__ */ new Set([
8315
8461
  ...helperNames.map((h) => alias(h.name, h.local)),
8316
8462
  flat3 ? "bcmsSectionPaths" : "bcmsSection",
8317
- ...bound > 0 ? [alias("bcmsFields", fieldsName)] : []
8463
+ ...bound > 0 ? [alias("bcmsFields", fieldsName)] : [],
8464
+ ...styled ? [styleReader] : []
8318
8465
  ])
8319
8466
  ].sort();
8320
8467
  const helperSpecifier = relativeImport(path, HELPER_PATH[dialect]).replace(/\.tsx?$/, "");
@@ -8363,9 +8510,11 @@ interface Props {
8363
8510
  /** The instance's own copy, when an editor gave it any. Wins over the bound group. */
8364
8511
  overrides?: Record<string, unknown>;
8365
8512
  /** The page snapshot, so one component can serve every page whose group has this shape. */
8366
- page?: unknown;
8513
+ page?: unknown;${styled ? `
8514
+ /** The placement's \`block.style\` \u2014 painted on the root, which says so with \`data-bcms-style\`. */
8515
+ blockStyle?: Record<string, unknown>;` : ""}
8367
8516
  }
8368
- const { blockId, bind, bindPaths, overrides, page } = Astro.props;
8517
+ const { blockId, bind, bindPaths, overrides, page${styled ? ", blockStyle" : ""} } = Astro.props;
8369
8518
  ${carriedDeclarations}${declared}
8370
8519
  ---
8371
8520
  ${markup}
@@ -8377,7 +8526,7 @@ ${styles}
8377
8526
  ${[...carried, `import { ${helpers.join(", ")} } from "${helperSpecifier}";`, `import type { BcmsSectionProps } from "${relativeImport(path, SECTIONS_LIB).replace(/\.tsx?$/, "")}";`].join("\n")}
8378
8527
  ${carriedDeclarations === "" ? "" : `
8379
8528
  ${carriedDeclarations}`}
8380
- export default function ${resolved.name}({ blockId, bind, bindPaths, overrides, page }: BcmsSectionProps) {
8529
+ export default function ${resolved.name}({ blockId, bind, bindPaths, overrides, page${styled ? ", blockStyle" : ""} }: BcmsSectionProps) {
8381
8530
  ${declared.split("\n").join("\n ")}
8382
8531
  return (
8383
8532
  ${markup}
@@ -8385,13 +8534,21 @@ export default function ${resolved.name}({ blockId, bind, bindPaths, overrides,
8385
8534
  }
8386
8535
  ` };
8387
8536
  }
8388
- function sectionMarkup(content, root, fieldName) {
8537
+ function rootStyleIsFree(content, root) {
8538
+ return root.attrs.every((attr) => {
8539
+ if (attr.name === "style") return false;
8540
+ const open = attr.expressionRange ? content.slice(attr.expressionRange.start, attr.expressionRange.end).trimStart() : "";
8541
+ return !open.startsWith("{...");
8542
+ });
8543
+ }
8544
+ function sectionMarkup(content, root, fieldName, styleReader = null) {
8389
8545
  const slice = new MagicString5(content.slice(root.start, root.end));
8390
8546
  const at = (offset) => offset - root.start;
8547
+ const stamp = styleReader ? `data-bcms-block={blockId} data-bcms-style="" style={${styleReader}(blockStyle)}` : "data-bcms-block={blockId}";
8391
8548
  const stamps = root.attrs.filter((attr) => attr.name === "data-bcms-block");
8392
- if (stamps.length === 0) slice.appendLeft(at(root.attrInsertAt), " data-bcms-block={blockId}");
8549
+ if (stamps.length === 0) slice.appendLeft(at(root.attrInsertAt), ` ${stamp}`);
8393
8550
  else {
8394
- slice.overwrite(at(stamps[0].range.start), at(stamps[0].range.end), "data-bcms-block={blockId}");
8551
+ slice.overwrite(at(stamps[0].range.start), at(stamps[0].range.end), stamp);
8395
8552
  for (const extra of stamps.slice(1)) {
8396
8553
  let from = extra.range.start;
8397
8554
  while (from > root.start && /\s/.test(content[from - 1])) from--;
@@ -8742,6 +8899,8 @@ export interface BcmsSectionProps {
8742
8899
  bindPaths?: Record<string, string>;
8743
8900
  overrides?: Record<string, unknown>;
8744
8901
  page?: unknown;
8902
+ /** The placement's \`block.style\`, which the section root paints (\`data-bcms-style\`). */
8903
+ blockStyle?: Record<string, unknown>;
8745
8904
  }
8746
8905
 
8747
8906
  /** One placement on one page: which component, bound to which page field group. */
@@ -8752,6 +8911,8 @@ export interface BcmsPlacement {
8752
8911
  /** A locale copy's own field path for each prop, when its keys differ from the component's. */
8753
8912
  bindPaths?: Record<string, string>;
8754
8913
  overrides?: Record<string, unknown>;
8914
+ /** The placement's \`block.style\` \u2014 what the editor's section spacing and style controls write. */
8915
+ style?: Record<string, unknown>;
8755
8916
  }
8756
8917
 
8757
8918
  /** The page's own snapshot \u2014 the copy every placement on it reads through \`bind\`. */
@@ -8813,6 +8974,7 @@ export function pageSections(pageSlug: string): BcmsPlacement[] {
8813
8974
  bind: typeof props.bind === "string" ? props.bind : undefined,
8814
8975
  bindPaths: isRecord(props.bindPaths) ? (props.bindPaths as Record<string, string>) : undefined,
8815
8976
  overrides: isRecord(props.overrides) ? props.overrides : undefined,
8977
+ style: isRecord(block.style) ? block.style : undefined,
8816
8978
  });
8817
8979
  }
8818
8980
  return out;
@@ -8879,6 +9041,7 @@ const snapshot = pageSnapshot(page);
8879
9041
  bind={placement.bind}
8880
9042
  bindPaths={placement.bindPaths}
8881
9043
  overrides={placement.overrides}
9044
+ blockStyle={placement.style}
8882
9045
  page={snapshot}
8883
9046
  />
8884
9047
  ) : null;
@@ -8902,6 +9065,7 @@ export default function Sections({ page }: { page: string }) {
8902
9065
  bind={placement.bind}
8903
9066
  bindPaths={placement.bindPaths}
8904
9067
  overrides={placement.overrides}
9068
+ blockStyle={placement.style}
8905
9069
  page={snapshot}
8906
9070
  />
8907
9071
  ) : null;
@@ -10176,7 +10340,7 @@ function styleEdits(before, after) {
10176
10340
  // package.json
10177
10341
  var package_default = {
10178
10342
  name: "@bettercms-ai/convert",
10179
- version: "0.20.1",
10343
+ version: "0.22.0",
10180
10344
  type: "module",
10181
10345
  description: "Deterministic codemod that turns an imported site's text-matched bindings into declared ones \u2014 parser offsets in, one magic-string pass out, plus a receipt of everything it could not do.",
10182
10346
  bin: {
@@ -10209,7 +10373,7 @@ var package_default = {
10209
10373
  dependencies: {
10210
10374
  "@astrojs/compiler": "^2.13.1",
10211
10375
  "@babel/parser": "^7.29.2",
10212
- "@bettercms-ai/types": "^1.25.0",
10376
+ "@bettercms-ai/types": "^1.30.0",
10213
10377
  "@vue/compiler-sfc": "^3.5.0",
10214
10378
  "magic-string": "^0.30.21",
10215
10379
  parse5: "^7.3.0",