@bettercms-ai/ui 0.6.1 → 0.7.1
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 +0 -0
- package/dist/{Video-CZkWQ3Hm.d.ts → Video-Bl_XrTrM.d.ts} +16 -1
- package/dist/blocks.d.ts +1 -1
- package/dist/blocks.js +110 -1
- package/dist/blocks.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +110 -1
- package/dist/index.js.map +1 -1
- package/dist/registry.js +110 -1
- package/dist/registry.js.map +1 -1
- package/dist/renderer.js +110 -1
- package/dist/renderer.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
File without changes
|
|
@@ -2,10 +2,25 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
2
2
|
import { HeadingProps, TextProps, PortableTextDocument, ImageProps, ButtonProps, SpacerProps, ColumnsProps, ContentBlock, VideoProps } from '@bettercms-ai/types';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The kinds an ELEMENT may declare, pinned to the canonical union rather than spelled again.
|
|
7
|
+
*
|
|
8
|
+
* `BindingKind` covers both lanes; `url`, `number` and `array` belong to the ATTRIBUTE lane
|
|
9
|
+
* (`data-bcms-props`), where a kind travels beside the DOM attribute it writes. An element's own
|
|
10
|
+
* `data-bcms-kind` names how its CONTENT is edited, and there are three answers.
|
|
11
|
+
*
|
|
12
|
+
* Declared as a VALUE with `satisfies`, not as `Extract<BindingKind, …>`: `Extract` of a member
|
|
13
|
+
* that no longer exists is silently the empty set, so a rename over there would narrow this type
|
|
14
|
+
* instead of failing. `satisfies readonly BindingKind[]` fails on this line, which is the point —
|
|
15
|
+
* hand-listing is how this file came to disagree with `block-bindings.ts` about how many kinds
|
|
16
|
+
* exist in the first place.
|
|
17
|
+
*/
|
|
18
|
+
declare const ELEMENT_BINDING_KINDS: readonly ["text", "richtext", "image"];
|
|
19
|
+
type ElementBindingKind = (typeof ELEMENT_BINDING_KINDS)[number];
|
|
5
20
|
/** Preview-only attributes understood by the BetterCMS visual editor. */
|
|
6
21
|
type BcmsBindingProps = {
|
|
7
22
|
"data-bcms-field"?: string;
|
|
8
|
-
"data-bcms-kind"?:
|
|
23
|
+
"data-bcms-kind"?: ElementBindingKind;
|
|
9
24
|
};
|
|
10
25
|
|
|
11
26
|
interface HeadingBlockProps extends HeadingProps, BcmsBindingProps {
|
package/dist/blocks.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { a as Button, C as Columns, H as Heading, I as Image, S as Spacer, T as Text, V as Video } from './Video-
|
|
1
|
+
export { a as Button, C as Columns, H as Heading, I as Image, S as Spacer, T as Text, V as Video } from './Video-Bl_XrTrM.js';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
import { SectionProps, ContentBlock, NavbarProps, FooterProps, SliderProps, TabsProps, ComponentProps, FormProps } from '@bettercms-ai/types';
|
|
4
4
|
import React from 'react';
|
package/dist/blocks.js
CHANGED
|
@@ -47,7 +47,13 @@ var PT_TYPES = {
|
|
|
47
47
|
*/
|
|
48
48
|
table: "table",
|
|
49
49
|
/** The annotation carrying a hyperlink's `href`. Spelled `link` because that is the spec's convention. */
|
|
50
|
-
link: "link"
|
|
50
|
+
link: "link",
|
|
51
|
+
/**
|
|
52
|
+
* The annotation carrying a site's OWN inline markup — `<span class>`, `<small>`, `<time
|
|
53
|
+
* datetime>`. Unprefixed, like `table`, because it describes HTML rather than a BetterCMS
|
|
54
|
+
* concept. See `styled.ts` for what it may hold and why the list is that short.
|
|
55
|
+
*/
|
|
56
|
+
styled: "styled"
|
|
51
57
|
};
|
|
52
58
|
var BUILTIN_TABLE_KEY = "builtin:table";
|
|
53
59
|
var STORABLE_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -59,10 +65,71 @@ var STORABLE_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
|
59
65
|
PT_TYPES.mention
|
|
60
66
|
]);
|
|
61
67
|
|
|
68
|
+
// ../richtext/src/styled.ts
|
|
69
|
+
var STYLED_TAGS = ["span", "small", "sup", "sub", "kbd", "abbr", "time"];
|
|
70
|
+
var TAG_SET = new Set(STYLED_TAGS);
|
|
71
|
+
var isStyledTag = (tag) => typeof tag === "string" && TAG_SET.has(tag);
|
|
72
|
+
var ATTR_NAMES = /* @__PURE__ */ new Set(["title", "datetime", "style", "aria-label"]);
|
|
73
|
+
var DATA_ATTR = /^data-[a-z][a-z0-9-]*$/;
|
|
74
|
+
var isStyledAttr = (name) => ATTR_NAMES.has(name) || DATA_ATTR.test(name) && !name.startsWith("data-bcms-");
|
|
75
|
+
var STYLE_PROPS = /* @__PURE__ */ new Set([
|
|
76
|
+
"color",
|
|
77
|
+
"background-color",
|
|
78
|
+
"opacity",
|
|
79
|
+
"font",
|
|
80
|
+
"font-family",
|
|
81
|
+
"font-size",
|
|
82
|
+
"font-style",
|
|
83
|
+
"font-weight",
|
|
84
|
+
"font-variant",
|
|
85
|
+
"font-feature-settings",
|
|
86
|
+
"font-variation-settings",
|
|
87
|
+
"letter-spacing",
|
|
88
|
+
"line-height",
|
|
89
|
+
"word-spacing",
|
|
90
|
+
"white-space",
|
|
91
|
+
"word-break",
|
|
92
|
+
"text-align",
|
|
93
|
+
"text-decoration",
|
|
94
|
+
"text-decoration-color",
|
|
95
|
+
"text-decoration-line",
|
|
96
|
+
"text-decoration-style",
|
|
97
|
+
"text-transform",
|
|
98
|
+
"text-underline-offset",
|
|
99
|
+
"vertical-align"
|
|
100
|
+
]);
|
|
101
|
+
var STYLE_VALUE_BAN = /url\(|expression\(|@import|\\|\/\*|[<>]/i;
|
|
102
|
+
function safeStyle(css) {
|
|
103
|
+
const kept = [];
|
|
104
|
+
for (const declaration of css.split(";")) {
|
|
105
|
+
const at = declaration.indexOf(":");
|
|
106
|
+
if (at < 0) continue;
|
|
107
|
+
const name = declaration.slice(0, at).trim().toLowerCase();
|
|
108
|
+
const value = declaration.slice(at + 1).trim();
|
|
109
|
+
if (!name || !value) continue;
|
|
110
|
+
if (!name.startsWith("--") && !STYLE_PROPS.has(name)) continue;
|
|
111
|
+
if (STYLE_VALUE_BAN.test(value)) continue;
|
|
112
|
+
kept.push(`${name}:${value}`);
|
|
113
|
+
}
|
|
114
|
+
return kept.length ? kept.join(";") : void 0;
|
|
115
|
+
}
|
|
116
|
+
function styledAttrEntries(attrs) {
|
|
117
|
+
if (!attrs || typeof attrs !== "object" || Array.isArray(attrs)) return [];
|
|
118
|
+
const out = [];
|
|
119
|
+
for (const [name, raw] of Object.entries(attrs)) {
|
|
120
|
+
if (!isStyledAttr(name) || typeof raw !== "string") continue;
|
|
121
|
+
const value = name === "style" ? safeStyle(raw) : raw;
|
|
122
|
+
if (value === void 0) continue;
|
|
123
|
+
out.push([name, value]);
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
|
|
62
128
|
// ../richtext/src/href.ts
|
|
63
129
|
var SAFE_HREF_SCHEMES = ["http:", "https:", "mailto:", "tel:"];
|
|
64
130
|
function safeHref(raw) {
|
|
65
131
|
const trimmed = raw.trim();
|
|
132
|
+
if (trimmed.startsWith("#")) return /^#[^\s<>"'`]*$/.test(trimmed) ? trimmed : null;
|
|
66
133
|
if (trimmed.startsWith("/") && !trimmed.startsWith("//")) return trimmed;
|
|
67
134
|
try {
|
|
68
135
|
return SAFE_HREF_SCHEMES.includes(new URL(trimmed).protocol) ? trimmed : null;
|
|
@@ -233,6 +300,18 @@ function readStructuredContent(props) {
|
|
|
233
300
|
// src/blocks/portableText.tsx
|
|
234
301
|
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
235
302
|
var TOKEN = /^[a-z0-9-]+$/i;
|
|
303
|
+
function styleObject(css) {
|
|
304
|
+
const out = {};
|
|
305
|
+
for (const declaration of css.split(";")) {
|
|
306
|
+
const at = declaration.indexOf(":");
|
|
307
|
+
if (at < 0) continue;
|
|
308
|
+
const name = declaration.slice(0, at).trim();
|
|
309
|
+
const value = declaration.slice(at + 1).trim();
|
|
310
|
+
if (!name || !value) continue;
|
|
311
|
+
out[name.startsWith("--") ? name : name.replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = value;
|
|
312
|
+
}
|
|
313
|
+
return out;
|
|
314
|
+
}
|
|
236
315
|
function Bound({
|
|
237
316
|
base,
|
|
238
317
|
nodeKey,
|
|
@@ -303,6 +382,36 @@ function buildComponents() {
|
|
|
303
382
|
const blank = v.target === "_blank";
|
|
304
383
|
return /* @__PURE__ */ jsx2("a", { href, ...blank ? { target: "_blank", rel: "noopener noreferrer" } : {}, children });
|
|
305
384
|
},
|
|
385
|
+
/**
|
|
386
|
+
* The site's own inline markup, rendered as itself — the React half of
|
|
387
|
+
* `portableTextToHtml`'s `styled` mark, and it has to stay the same shape: Astro and the
|
|
388
|
+
* editor canvas render through that serialiser while a Next site renders through this
|
|
389
|
+
* one, and two renderers of one value that disagree is the whole bug class.
|
|
390
|
+
*
|
|
391
|
+
* The tag is re-checked against `STYLED_TAGS` and the attribute bag re-filtered, because
|
|
392
|
+
* a markDef is DATA and arrives from imports, the Management API and AI writes. React
|
|
393
|
+
* escapes the values it writes; the allowlist is what keeps a `href` or an `onClick` out
|
|
394
|
+
* of the props entirely. An unrecognised tag renders its CHILDREN — the words are the
|
|
395
|
+
* author's, the wrapper is only the design.
|
|
396
|
+
*/
|
|
397
|
+
[PT_TYPES.styled]: ({ value, children }) => {
|
|
398
|
+
const v = value ?? {};
|
|
399
|
+
const tag = typeof v.tag === "string" ? v.tag.toLowerCase() : "";
|
|
400
|
+
if (!isStyledTag(tag)) return /* @__PURE__ */ jsx2(Fragment, { children });
|
|
401
|
+
const Tag = tag;
|
|
402
|
+
const className = typeof v.className === "string" && v.className ? v.className : void 0;
|
|
403
|
+
const attrs = styledAttrEntries(v.attrs);
|
|
404
|
+
const style = attrs.find(([name]) => name === "style")?.[1];
|
|
405
|
+
return /* @__PURE__ */ jsx2(
|
|
406
|
+
Tag,
|
|
407
|
+
{
|
|
408
|
+
className,
|
|
409
|
+
...Object.fromEntries(attrs.filter(([name]) => name !== "style")),
|
|
410
|
+
...style ? { style: styleObject(style) } : {},
|
|
411
|
+
children
|
|
412
|
+
}
|
|
413
|
+
);
|
|
414
|
+
},
|
|
306
415
|
highlight: ({ children }) => /* @__PURE__ */ jsx2("mark", { children }),
|
|
307
416
|
"strike-through": ({ children }) => /* @__PURE__ */ jsx2("s", { children }),
|
|
308
417
|
/**
|