@risali/react 0.2.0 → 0.6.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 +22 -0
- package/dist/blog-jsonld.d.ts +74 -0
- package/dist/blog-jsonld.d.ts.map +1 -0
- package/dist/blog-jsonld.js +111 -0
- package/dist/blog-jsonld.js.map +1 -0
- package/dist/blog-list.d.ts +29 -0
- package/dist/blog-list.d.ts.map +1 -0
- package/dist/blog-list.js +113 -0
- package/dist/blog-list.js.map +1 -0
- package/dist/blog-post.d.ts +40 -0
- package/dist/blog-post.d.ts.map +1 -0
- package/dist/blog-post.js +109 -0
- package/dist/blog-post.js.map +1 -0
- package/dist/blog.d.ts +69 -0
- package/dist/blog.d.ts.map +1 -0
- package/dist/blog.js +79 -0
- package/dist/blog.js.map +1 -0
- package/dist/brand.d.ts +9 -0
- package/dist/brand.d.ts.map +1 -0
- package/dist/brand.js +68 -0
- package/dist/brand.js.map +1 -0
- package/dist/event-marker.d.ts +35 -0
- package/dist/event-marker.d.ts.map +1 -0
- package/dist/event-marker.js +17 -0
- package/dist/event-marker.js.map +1 -0
- package/dist/image.d.ts +34 -34
- package/dist/image.d.ts.map +1 -1
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/page.d.ts +18 -0
- package/dist/page.d.ts.map +1 -0
- package/dist/page.js +41 -0
- package/dist/page.js.map +1 -0
- package/dist/rich-text.d.ts.map +1 -1
- package/dist/rich-text.js +3 -0
- package/dist/rich-text.js.map +1 -1
- package/dist/sanitize.d.ts.map +1 -1
- package/dist/sanitize.js +154 -10
- package/dist/sanitize.js.map +1 -1
- package/dist/sections/blocks.d.ts +10 -0
- package/dist/sections/blocks.d.ts.map +1 -0
- package/dist/sections/blocks.js +164 -0
- package/dist/sections/blocks.js.map +1 -0
- package/dist/sections/index.d.ts +12 -0
- package/dist/sections/index.d.ts.map +1 -0
- package/dist/sections/index.js +104 -0
- package/dist/sections/index.js.map +1 -0
- package/dist/style.d.ts.map +1 -1
- package/dist/style.js +2 -1
- package/dist/style.js.map +1 -1
- package/dist/tracking-component.d.ts +29 -0
- package/dist/tracking-component.d.ts.map +1 -0
- package/dist/tracking-component.js +100 -0
- package/dist/tracking-component.js.map +1 -0
- package/dist/tracking.d.ts +27 -0
- package/dist/tracking.d.ts.map +1 -0
- package/dist/tracking.js +49 -0
- package/dist/tracking.js.map +1 -0
- package/dist/types.d.ts +37 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/sanitize.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
// Minimal HTML sanitiser for Risali rich-text blocks.
|
|
2
2
|
// Pure (no DOM, no external deps) so it runs in RSC / Node / tests alike.
|
|
3
3
|
// Defense-in-depth over the editor-side sanitiser planned for PR 26.
|
|
4
|
+
//
|
|
5
|
+
// Tag set grew in PR 36a (blog rendering): added h2/h3/h4, ul/ol/li,
|
|
6
|
+
// blockquote, pre, code, hr, figure/figcaption, img (with src
|
|
7
|
+
// whitelist), iframe (YouTube/Vimeo embed src whitelist).
|
|
4
8
|
const ALLOWED_TAGS = new Set([
|
|
9
|
+
// Inline (original set)
|
|
5
10
|
"span",
|
|
6
11
|
"strong",
|
|
7
12
|
"b",
|
|
@@ -11,10 +16,29 @@ const ALLOWED_TAGS = new Set([
|
|
|
11
16
|
"br",
|
|
12
17
|
"a",
|
|
13
18
|
"p",
|
|
19
|
+
// Blog block tags (PR 36a)
|
|
20
|
+
"h2",
|
|
21
|
+
"h3",
|
|
22
|
+
"h4",
|
|
23
|
+
"ul",
|
|
24
|
+
"ol",
|
|
25
|
+
"li",
|
|
26
|
+
"blockquote",
|
|
27
|
+
"pre",
|
|
28
|
+
"code",
|
|
29
|
+
"hr",
|
|
30
|
+
"figure",
|
|
31
|
+
"figcaption",
|
|
32
|
+
"img",
|
|
33
|
+
"iframe",
|
|
14
34
|
]);
|
|
15
|
-
const VOID_TAGS = new Set(["br"]);
|
|
35
|
+
const VOID_TAGS = new Set(["br", "hr", "img"]);
|
|
36
|
+
// Self-closing in the source but with allowed attributes — iframe is a
|
|
37
|
+
// special case because we want to render `<iframe src=... allowfullscreen></iframe>`
|
|
38
|
+
// even though it carries no children in practice.
|
|
39
|
+
const FORCED_PAIRED = new Set(["iframe"]);
|
|
16
40
|
const ALLOWED_ATTRS = {
|
|
17
|
-
a: new Set(["href"]),
|
|
41
|
+
a: new Set(["href", "rel"]),
|
|
18
42
|
span: new Set(["style"]),
|
|
19
43
|
p: new Set(["style"]),
|
|
20
44
|
strong: new Set(["style"]),
|
|
@@ -22,12 +46,52 @@ const ALLOWED_ATTRS = {
|
|
|
22
46
|
b: new Set(["style"]),
|
|
23
47
|
i: new Set(["style"]),
|
|
24
48
|
u: new Set(["style"]),
|
|
49
|
+
// Blog tags
|
|
50
|
+
h2: new Set(["id"]),
|
|
51
|
+
h3: new Set(["id"]),
|
|
52
|
+
h4: new Set(["id"]),
|
|
53
|
+
code: new Set(["class"]), // language-<lang> only
|
|
54
|
+
img: new Set(["src", "alt", "width", "height", "loading"]),
|
|
55
|
+
iframe: new Set([
|
|
56
|
+
"src",
|
|
57
|
+
"width",
|
|
58
|
+
"height",
|
|
59
|
+
"allowfullscreen",
|
|
60
|
+
"loading",
|
|
61
|
+
"title",
|
|
62
|
+
]),
|
|
25
63
|
};
|
|
26
|
-
|
|
64
|
+
// href / a tag: http(s), mail/tel, or root-relative path.
|
|
65
|
+
const SAFE_HREF_RE = /^(https?:\/\/|mailto:|tel:|\/[^/])/i;
|
|
66
|
+
// img src: http(s) or root-relative (no protocol-relative // URLs).
|
|
67
|
+
const SAFE_IMG_SRC_RE = /^(https?:\/\/|\/[^/])/i;
|
|
68
|
+
// iframe src: only YouTube + Vimeo embed paths. Blocks everything else
|
|
69
|
+
// including data:, javascript:, and arbitrary 3rd-party embeds. The
|
|
70
|
+
// blog HTML builder (lib/blog/normalize.ts) normalises watch URLs
|
|
71
|
+
// into these embed forms server-side, so by the time we sanitise
|
|
72
|
+
// they always match.
|
|
73
|
+
const SAFE_IFRAME_SRC_RE = /^https?:\/\/(?:www\.)?(?:youtube\.com\/embed\/|youtube-nocookie\.com\/embed\/|player\.vimeo\.com\/video\/)[A-Za-z0-9_-][A-Za-z0-9_\-?&=/.]*$/i;
|
|
74
|
+
// <a rel=...> — whitelist common security/SEO tokens. Anything else
|
|
75
|
+
// (e.g. dns-prefetch via shenanigans) is dropped.
|
|
76
|
+
const ALLOWED_REL = new Set([
|
|
77
|
+
"noopener",
|
|
78
|
+
"noreferrer",
|
|
79
|
+
"nofollow",
|
|
80
|
+
"sponsored",
|
|
81
|
+
"ugc",
|
|
82
|
+
]);
|
|
83
|
+
// <code class="language-xxx">.
|
|
84
|
+
const SAFE_CODE_CLASS_RE = /^language-[A-Za-z0-9_-]{1,30}$/;
|
|
85
|
+
// Numeric width/height for img/iframe.
|
|
86
|
+
const SAFE_NUMERIC_RE = /^\d{1,5}$/;
|
|
87
|
+
// loading="lazy"|"eager".
|
|
88
|
+
const SAFE_LOADING_RE = /^(lazy|eager)$/;
|
|
89
|
+
// id="…" for heading anchors — keep tame.
|
|
90
|
+
const SAFE_ID_RE = /^[A-Za-z][A-Za-z0-9_-]{0,80}$/;
|
|
27
91
|
const STYLE_COLOR_RE = /(?:^|;)\s*color\s*:\s*(#[0-9a-fA-F]{6})\s*(?:;|$)/;
|
|
28
92
|
const STYLE_FONT_SIZE_RE = /(?:^|;)\s*font-size\s*:\s*([0-9]+(?:\.[0-9]+)?)(px|rem|em)\s*(?:;|$)/i;
|
|
29
93
|
const TAG_RE = /<\/?([a-zA-Z][a-zA-Z0-9]*)((?:\s+[^>]*)?)\/?>|<!--[\s\S]*?-->/g;
|
|
30
|
-
const ATTR_RE = /([a-zA-Z][a-zA-Z0-9_-]*)\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'>]+))/g;
|
|
94
|
+
const ATTR_RE = /([a-zA-Z][a-zA-Z0-9_-]*)\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'>]+))|([a-zA-Z][a-zA-Z0-9_-]*)(?=\s|$|\/)/g;
|
|
31
95
|
function sanitiseStyle(raw) {
|
|
32
96
|
const out = [];
|
|
33
97
|
const colorMatch = raw.match(STYLE_COLOR_RE);
|
|
@@ -44,6 +108,13 @@ function sanitiseStyle(raw) {
|
|
|
44
108
|
}
|
|
45
109
|
return out.join(";");
|
|
46
110
|
}
|
|
111
|
+
function sanitiseRel(raw) {
|
|
112
|
+
const tokens = raw
|
|
113
|
+
.split(/\s+/)
|
|
114
|
+
.map((t) => t.toLowerCase())
|
|
115
|
+
.filter((t) => ALLOWED_REL.has(t));
|
|
116
|
+
return Array.from(new Set(tokens)).join(" ");
|
|
117
|
+
}
|
|
47
118
|
function sanitiseAttrs(tag, raw) {
|
|
48
119
|
const allowed = ALLOWED_ATTRS[tag];
|
|
49
120
|
if (!allowed || !raw.trim())
|
|
@@ -52,20 +123,72 @@ function sanitiseAttrs(tag, raw) {
|
|
|
52
123
|
ATTR_RE.lastIndex = 0;
|
|
53
124
|
let match;
|
|
54
125
|
while ((match = ATTR_RE.exec(raw)) !== null) {
|
|
55
|
-
|
|
126
|
+
// Two variants: `name="value"` (groups 1..5) or bare `name` (group 6).
|
|
127
|
+
const name = (match[1] ?? match[6] ?? "").toLowerCase();
|
|
56
128
|
if (!name || !allowed.has(name))
|
|
57
129
|
continue;
|
|
58
|
-
const
|
|
130
|
+
const rawValue = match[3] ?? match[4] ?? match[5] ?? "";
|
|
131
|
+
const isBoolean = !match[1] && !!match[6];
|
|
59
132
|
if (name === "href") {
|
|
60
|
-
if (SAFE_HREF_RE.test(
|
|
61
|
-
parts.push(`href="${escapeAttr(
|
|
133
|
+
if (SAFE_HREF_RE.test(rawValue)) {
|
|
134
|
+
parts.push(`href="${escapeAttr(rawValue)}"`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else if (name === "rel") {
|
|
138
|
+
const safe = sanitiseRel(rawValue);
|
|
139
|
+
if (safe)
|
|
140
|
+
parts.push(`rel="${escapeAttr(safe)}"`);
|
|
141
|
+
}
|
|
142
|
+
else if (name === "src" && tag === "img") {
|
|
143
|
+
if (SAFE_IMG_SRC_RE.test(rawValue)) {
|
|
144
|
+
parts.push(`src="${escapeAttr(rawValue)}"`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else if (name === "src" && tag === "iframe") {
|
|
148
|
+
if (SAFE_IFRAME_SRC_RE.test(rawValue)) {
|
|
149
|
+
parts.push(`src="${escapeAttr(rawValue)}"`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else if (name === "alt") {
|
|
153
|
+
// alt may legitimately be empty (decorative images).
|
|
154
|
+
parts.push(`alt="${escapeAttr(rawValue.slice(0, 500))}"`);
|
|
155
|
+
}
|
|
156
|
+
else if (name === "width" || name === "height") {
|
|
157
|
+
if (SAFE_NUMERIC_RE.test(rawValue)) {
|
|
158
|
+
parts.push(`${name}="${rawValue}"`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else if (name === "loading") {
|
|
162
|
+
if (SAFE_LOADING_RE.test(rawValue)) {
|
|
163
|
+
parts.push(`${name}="${rawValue}"`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else if (name === "allowfullscreen") {
|
|
167
|
+
// Boolean attr — emit canonical form.
|
|
168
|
+
parts.push("allowfullscreen");
|
|
169
|
+
}
|
|
170
|
+
else if (name === "title") {
|
|
171
|
+
parts.push(`title="${escapeAttr(rawValue.slice(0, 200))}"`);
|
|
172
|
+
}
|
|
173
|
+
else if (name === "id") {
|
|
174
|
+
if (SAFE_ID_RE.test(rawValue)) {
|
|
175
|
+
parts.push(`id="${escapeAttr(rawValue)}"`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else if (name === "class" && tag === "code") {
|
|
179
|
+
if (SAFE_CODE_CLASS_RE.test(rawValue)) {
|
|
180
|
+
parts.push(`class="${escapeAttr(rawValue)}"`);
|
|
62
181
|
}
|
|
63
182
|
}
|
|
64
183
|
else if (name === "style") {
|
|
65
|
-
const safe = sanitiseStyle(
|
|
184
|
+
const safe = sanitiseStyle(rawValue);
|
|
66
185
|
if (safe)
|
|
67
186
|
parts.push(`style="${escapeAttr(safe)}"`);
|
|
68
187
|
}
|
|
188
|
+
// Boolean attrs without explicit value (allowfullscreen alone).
|
|
189
|
+
if (isBoolean && name === "allowfullscreen") {
|
|
190
|
+
// Already emitted above.
|
|
191
|
+
}
|
|
69
192
|
}
|
|
70
193
|
return parts.length ? " " + parts.join(" ") : "";
|
|
71
194
|
}
|
|
@@ -82,6 +205,10 @@ export function sanitiseRichText(input) {
|
|
|
82
205
|
const stack = [];
|
|
83
206
|
const pieces = [];
|
|
84
207
|
let lastIndex = 0;
|
|
208
|
+
// Inside <pre><code> we want raw text rendering (no further tag
|
|
209
|
+
// matching). We don't fully suspend the parser — that would break
|
|
210
|
+
// nested allowed tags in rare edge cases — but we leave the
|
|
211
|
+
// children-as-text fallback to the surrounding escape pass.
|
|
85
212
|
TAG_RE.lastIndex = 0;
|
|
86
213
|
let match;
|
|
87
214
|
while ((match = TAG_RE.exec(input)) !== null) {
|
|
@@ -106,11 +233,28 @@ export function sanitiseRichText(input) {
|
|
|
106
233
|
}
|
|
107
234
|
}
|
|
108
235
|
else if (VOID_TAGS.has(tag)) {
|
|
109
|
-
|
|
236
|
+
const attrs = sanitiseAttrs(tag, match[2] || "");
|
|
237
|
+
// img requires a valid src; if sanitiseAttrs dropped it (rejected URL
|
|
238
|
+
// or an XSS payload like `src=x onerror=…`), drop the element entirely
|
|
239
|
+
// — a src-less img is useless and keeps "<img" out of the output.
|
|
240
|
+
// Mirrors the iframe-without-src rule below.
|
|
241
|
+
if (tag === "img" && !/\ssrc=/.test(attrs))
|
|
242
|
+
continue;
|
|
243
|
+
pieces.push(`<${tag}${attrs}/>`);
|
|
110
244
|
}
|
|
111
245
|
else {
|
|
112
246
|
const attrs = sanitiseAttrs(tag, match[2] || "");
|
|
247
|
+
// iframe with no src is useless and could confuse readers; drop.
|
|
248
|
+
if (tag === "iframe" && !/\ssrc=/.test(attrs))
|
|
249
|
+
continue;
|
|
113
250
|
pieces.push(`<${tag}${attrs}>`);
|
|
251
|
+
if (FORCED_PAIRED.has(tag)) {
|
|
252
|
+
// Always close iframe — the source may have written
|
|
253
|
+
// `<iframe ...></iframe>` (good) or `<iframe ... />` (sloppy
|
|
254
|
+
// but accepted here). We push the open tag and rely on the
|
|
255
|
+
// close tag in the source. If none arrives, the trailing
|
|
256
|
+
// stack drain below closes it for us.
|
|
257
|
+
}
|
|
114
258
|
stack.push(tag);
|
|
115
259
|
}
|
|
116
260
|
}
|
package/dist/sanitize.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,0EAA0E;AAC1E,qEAAqE;
|
|
1
|
+
{"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { RisaliBlock } from "../types.js";
|
|
3
|
+
export declare function safeButtonHref(href: unknown): string | null;
|
|
4
|
+
/** Sorts builder children by sparse position; blocks without one go last. */
|
|
5
|
+
export declare function sortByPosition<T extends {
|
|
6
|
+
position?: number | null;
|
|
7
|
+
}>(items: readonly T[]): T[];
|
|
8
|
+
export declare function renderBlock(block: RisaliBlock): ReactNode;
|
|
9
|
+
export declare function renderBlocks(blocks: readonly RisaliBlock[]): ReactNode;
|
|
10
|
+
//# sourceMappingURL=blocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../src/sections/blocks.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAItD,OAAO,KAAK,EAAE,WAAW,EAAuC,MAAM,aAAa,CAAC;AAUpF,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAK3D;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACnE,KAAK,EAAE,SAAS,CAAC,EAAE,GAClB,CAAC,EAAE,CAML;AAmDD,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CA6GzD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,SAAS,CAMtE"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Child-block renderer shared by every builder section (PR 40b).
|
|
2
|
+
//
|
|
3
|
+
// Deliberately SYNC (plain createElement, no async components) so the tree
|
|
4
|
+
// returned by an awaited <RisaliPage> can be rendered with
|
|
5
|
+
// react-dom/server's renderToStaticMarkup in tests. Styles use only inline
|
|
6
|
+
// CSS + the --risali-* CSS variables emitted by <RisaliBrand> — zero
|
|
7
|
+
// dependency on the client site's Tailwind setup.
|
|
8
|
+
import { createElement, Fragment } from "react";
|
|
9
|
+
import { sanitiseRichText } from "../sanitize.js";
|
|
10
|
+
import { applyStyleOverrides } from "../style.js";
|
|
11
|
+
const SAFE_SRC_RE = /^(https?:\/\/|\/)/;
|
|
12
|
+
// Mirror of src/lib/editor/href.ts on the dashboard side — buttons accept
|
|
13
|
+
// only https://, mailto:, tel: and root-relative paths (never "//", http:,
|
|
14
|
+
// javascript:, data:). Keep the two in sync.
|
|
15
|
+
const SAFE_BUTTON_HREF_RE = /^(https:\/\/[^\s]+|mailto:[^\s]+|tel:[+0-9() -]+|\/(?!\/)[^\s]*)$/i;
|
|
16
|
+
const CONTROL_CHARS_RE = /[\u0000-\u001F\u007F]/;
|
|
17
|
+
export function safeButtonHref(href) {
|
|
18
|
+
if (typeof href !== "string" || !href || href.length > 1000)
|
|
19
|
+
return null;
|
|
20
|
+
if (CONTROL_CHARS_RE.test(href))
|
|
21
|
+
return null;
|
|
22
|
+
if (/["'<>`\\]/.test(href))
|
|
23
|
+
return null;
|
|
24
|
+
return SAFE_BUTTON_HREF_RE.test(href) ? href : null;
|
|
25
|
+
}
|
|
26
|
+
/** Sorts builder children by sparse position; blocks without one go last. */
|
|
27
|
+
export function sortByPosition(items) {
|
|
28
|
+
return [...items].sort((a, b) => (typeof a.position === "number" ? a.position : Number.MAX_SAFE_INTEGER) -
|
|
29
|
+
(typeof b.position === "number" ? b.position : Number.MAX_SAFE_INTEGER));
|
|
30
|
+
}
|
|
31
|
+
const HEADING_STYLE = {
|
|
32
|
+
fontFamily: "var(--risali-font-heading, inherit)",
|
|
33
|
+
fontSize: "2em",
|
|
34
|
+
lineHeight: 1.2,
|
|
35
|
+
margin: "0 0 0.5em",
|
|
36
|
+
};
|
|
37
|
+
const TEXT_STYLE = {
|
|
38
|
+
margin: "0 0 1em",
|
|
39
|
+
};
|
|
40
|
+
const IMG_STYLE = {
|
|
41
|
+
maxWidth: "100%",
|
|
42
|
+
height: "auto",
|
|
43
|
+
display: "block",
|
|
44
|
+
borderRadius: "8px",
|
|
45
|
+
};
|
|
46
|
+
const HR_STYLE = {
|
|
47
|
+
border: "none",
|
|
48
|
+
borderTop: "1px solid var(--risali-color-secondary, #e5e7eb)",
|
|
49
|
+
margin: "1.5em 0",
|
|
50
|
+
};
|
|
51
|
+
const BUTTON_BASE_STYLE = {
|
|
52
|
+
display: "inline-block",
|
|
53
|
+
padding: "0.75em 1.5em",
|
|
54
|
+
borderRadius: "8px",
|
|
55
|
+
textDecoration: "none",
|
|
56
|
+
fontWeight: 600,
|
|
57
|
+
cursor: "pointer",
|
|
58
|
+
};
|
|
59
|
+
function buttonStyle(variant) {
|
|
60
|
+
if (variant === "secondary") {
|
|
61
|
+
return {
|
|
62
|
+
...BUTTON_BASE_STYLE,
|
|
63
|
+
border: "2px solid var(--risali-color-primary, #111827)",
|
|
64
|
+
color: "var(--risali-color-primary, #111827)",
|
|
65
|
+
background: "transparent",
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
...BUTTON_BASE_STYLE,
|
|
70
|
+
background: "var(--risali-color-primary, #111827)",
|
|
71
|
+
color: "#ffffff",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function renderBlock(block) {
|
|
75
|
+
const overrides = block.style_overrides;
|
|
76
|
+
switch (block.type) {
|
|
77
|
+
case "text": {
|
|
78
|
+
const value = typeof block.value === "string" ? block.value : "";
|
|
79
|
+
if (block.variant === "heading") {
|
|
80
|
+
return createElement("h2", {
|
|
81
|
+
key: block.key,
|
|
82
|
+
"data-risali-key": block.key,
|
|
83
|
+
style: applyStyleOverrides(HEADING_STYLE, overrides),
|
|
84
|
+
}, value);
|
|
85
|
+
}
|
|
86
|
+
return createElement("p", {
|
|
87
|
+
key: block.key,
|
|
88
|
+
"data-risali-key": block.key,
|
|
89
|
+
style: applyStyleOverrides(TEXT_STYLE, overrides),
|
|
90
|
+
}, value);
|
|
91
|
+
}
|
|
92
|
+
case "rich_text": {
|
|
93
|
+
const raw = typeof block.value === "string" ? block.value : "";
|
|
94
|
+
return createElement("div", {
|
|
95
|
+
key: block.key,
|
|
96
|
+
"data-risali-key": block.key,
|
|
97
|
+
"data-risali-type": "rich_text",
|
|
98
|
+
style: applyStyleOverrides(undefined, overrides),
|
|
99
|
+
dangerouslySetInnerHTML: { __html: sanitiseRichText(raw) },
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
case "image": {
|
|
103
|
+
const value = block.value && typeof block.value === "object"
|
|
104
|
+
? block.value
|
|
105
|
+
: {};
|
|
106
|
+
const src = typeof value.src === "string" && value.src && SAFE_SRC_RE.test(value.src)
|
|
107
|
+
? value.src
|
|
108
|
+
: null;
|
|
109
|
+
if (!src)
|
|
110
|
+
return null;
|
|
111
|
+
return createElement("img", {
|
|
112
|
+
key: block.key,
|
|
113
|
+
"data-risali-key": block.key,
|
|
114
|
+
src,
|
|
115
|
+
alt: typeof value.alt === "string" ? value.alt : "",
|
|
116
|
+
loading: "lazy",
|
|
117
|
+
style: IMG_STYLE,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
case "button": {
|
|
121
|
+
const value = block.value && typeof block.value === "object"
|
|
122
|
+
? block.value
|
|
123
|
+
: {};
|
|
124
|
+
const text = typeof value.text === "string" ? value.text : "";
|
|
125
|
+
if (!text)
|
|
126
|
+
return null;
|
|
127
|
+
const href = safeButtonHref(value.href);
|
|
128
|
+
const style = applyStyleOverrides(buttonStyle(block.variant), overrides);
|
|
129
|
+
// An unsafe/missing href degrades to a non-link span — the text stays
|
|
130
|
+
// visible and editable, the navigation does not happen.
|
|
131
|
+
if (!href) {
|
|
132
|
+
return createElement("span", {
|
|
133
|
+
key: block.key,
|
|
134
|
+
"data-risali-key": block.key,
|
|
135
|
+
"data-risali-type": "button",
|
|
136
|
+
"data-risali-variant": block.variant ?? "primary",
|
|
137
|
+
style,
|
|
138
|
+
}, text);
|
|
139
|
+
}
|
|
140
|
+
return createElement("a", {
|
|
141
|
+
key: block.key,
|
|
142
|
+
"data-risali-key": block.key,
|
|
143
|
+
"data-risali-type": "button",
|
|
144
|
+
"data-risali-variant": block.variant ?? "primary",
|
|
145
|
+
href,
|
|
146
|
+
style,
|
|
147
|
+
}, text);
|
|
148
|
+
}
|
|
149
|
+
case "divider":
|
|
150
|
+
return createElement("hr", {
|
|
151
|
+
key: block.key,
|
|
152
|
+
"data-risali-key": block.key,
|
|
153
|
+
"data-risali-type": "divider",
|
|
154
|
+
style: HR_STYLE,
|
|
155
|
+
});
|
|
156
|
+
default:
|
|
157
|
+
// html blocks (and any future type) are not builder children — skip.
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
export function renderBlocks(blocks) {
|
|
162
|
+
return createElement(Fragment, null, sortByPosition(blocks).map((b) => renderBlock(b)));
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=blocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocks.js","sourceRoot":"","sources":["../../src/sections/blocks.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from "react";
|
|
2
|
+
import type { RisaliBlock, RisaliSection } from "../types.js";
|
|
3
|
+
export type RisaliSectionProps = {
|
|
4
|
+
section: RisaliSection;
|
|
5
|
+
/** Child blocks already sorted by position. */
|
|
6
|
+
blocks: RisaliBlock[];
|
|
7
|
+
};
|
|
8
|
+
export type RisaliSectionComponent = (props: RisaliSectionProps) => ReactNode;
|
|
9
|
+
export declare const RISALI_SECTION_COMPONENTS: Record<string, RisaliSectionComponent>;
|
|
10
|
+
/** Wrapper style for the <section> element from whitelisted settings. */
|
|
11
|
+
export declare function sectionWrapperStyle(section: RisaliSection): CSSProperties;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sections/index.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG9D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,aAAa,CAAC;IACvB,+CAA+C;IAC/C,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,CAAC,KAAK,EAAE,kBAAkB,KAAK,SAAS,CAAC;AAoH9E,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAQ5E,CAAC;AAYF,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,aAAa,CAWzE"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Builder section catalog v1 (PR 40b) — render side of
|
|
2
|
+
// src/lib/sections/catalog.ts in the dashboard repo (gallery/defaults).
|
|
3
|
+
// Keep the `type` keys of the two in sync.
|
|
4
|
+
//
|
|
5
|
+
// Every component is SYNC and content-driven: it lays out the ordered child
|
|
6
|
+
// blocks it receives, nothing more. Signature sections a client repo ships
|
|
7
|
+
// in code are passed to <RisaliPage customSections={{...}}> and take
|
|
8
|
+
// precedence over this registry on a type-key collision.
|
|
9
|
+
import { createElement } from "react";
|
|
10
|
+
import { renderBlock, renderBlocks, sortByPosition } from "./blocks.js";
|
|
11
|
+
const CONTAINER_STYLE = {
|
|
12
|
+
maxWidth: "1100px",
|
|
13
|
+
margin: "0 auto",
|
|
14
|
+
};
|
|
15
|
+
function gridStyle(columns) {
|
|
16
|
+
const n = typeof columns === "number" && [2, 3, 4].includes(columns) ? columns : 3;
|
|
17
|
+
return {
|
|
18
|
+
display: "grid",
|
|
19
|
+
gridTemplateColumns: `repeat(${n}, minmax(0, 1fr))`,
|
|
20
|
+
gap: "24px",
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function HeroSection({ section, blocks }) {
|
|
24
|
+
const centered = section.variant !== "left";
|
|
25
|
+
return createElement("div", {
|
|
26
|
+
style: {
|
|
27
|
+
...CONTAINER_STYLE,
|
|
28
|
+
textAlign: centered ? "center" : "left",
|
|
29
|
+
},
|
|
30
|
+
}, renderBlocks(blocks));
|
|
31
|
+
}
|
|
32
|
+
function TextImageSection({ section, blocks }) {
|
|
33
|
+
const sorted = sortByPosition(blocks);
|
|
34
|
+
const images = sorted.filter((b) => b.type === "image");
|
|
35
|
+
const rest = sorted.filter((b) => b.type !== "image");
|
|
36
|
+
const imageLeft = section.settings?.image_side === "left";
|
|
37
|
+
const textCol = createElement("div", { key: "text" }, rest.map((b) => renderBlock(b)));
|
|
38
|
+
const imageCol = createElement("div", { key: "image" }, images.map((b) => renderBlock(b)));
|
|
39
|
+
return createElement("div", {
|
|
40
|
+
style: {
|
|
41
|
+
...CONTAINER_STYLE,
|
|
42
|
+
display: "grid",
|
|
43
|
+
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
|
|
44
|
+
gap: "48px",
|
|
45
|
+
alignItems: "center",
|
|
46
|
+
},
|
|
47
|
+
}, imageLeft ? [imageCol, textCol] : [textCol, imageCol]);
|
|
48
|
+
}
|
|
49
|
+
// Flow model v1: the first heading spans the full width, every following
|
|
50
|
+
// block is one grid cell. Grouped feature items (icon+title+text as a unit)
|
|
51
|
+
// are a planned follow-up.
|
|
52
|
+
function FeaturesSection({ section, blocks }) {
|
|
53
|
+
const sorted = sortByPosition(blocks);
|
|
54
|
+
const [first, ...rest] = sorted;
|
|
55
|
+
const headingFirst = first && first.type === "text" && first.variant === "heading";
|
|
56
|
+
const headOut = headingFirst ? renderBlock(first) : null;
|
|
57
|
+
const cells = (headingFirst ? rest : sorted).map((b) => createElement("div", { key: b.key }, renderBlock(b)));
|
|
58
|
+
return createElement("div", { style: CONTAINER_STYLE }, headOut, createElement("div", { style: gridStyle(section.settings?.columns) }, cells));
|
|
59
|
+
}
|
|
60
|
+
function GallerySection({ section, blocks }) {
|
|
61
|
+
const sorted = sortByPosition(blocks);
|
|
62
|
+
const images = sorted.filter((b) => b.type === "image");
|
|
63
|
+
const rest = sorted.filter((b) => b.type !== "image");
|
|
64
|
+
return createElement("div", { style: CONTAINER_STYLE }, rest.map((b) => renderBlock(b)), createElement("div", { style: gridStyle(section.settings?.columns) }, images.map((b) => createElement("div", { key: b.key }, renderBlock(b)))));
|
|
65
|
+
}
|
|
66
|
+
function CtaSection({ blocks }) {
|
|
67
|
+
return createElement("div", { style: { ...CONTAINER_STYLE, textAlign: "center" } }, renderBlocks(blocks));
|
|
68
|
+
}
|
|
69
|
+
function ContactSection({ blocks }) {
|
|
70
|
+
return createElement("div", { style: { ...CONTAINER_STYLE, maxWidth: "720px" } }, renderBlocks(blocks));
|
|
71
|
+
}
|
|
72
|
+
function PlainSection({ blocks }) {
|
|
73
|
+
return createElement("div", { style: CONTAINER_STYLE }, renderBlocks(blocks));
|
|
74
|
+
}
|
|
75
|
+
export const RISALI_SECTION_COMPONENTS = {
|
|
76
|
+
hero: HeroSection,
|
|
77
|
+
text_image: TextImageSection,
|
|
78
|
+
features: FeaturesSection,
|
|
79
|
+
gallery: GallerySection,
|
|
80
|
+
cta: CtaSection,
|
|
81
|
+
contact: ContactSection,
|
|
82
|
+
plain: PlainSection,
|
|
83
|
+
};
|
|
84
|
+
// Palette KEY shape — mirrors the content API + dashboard catalog guard.
|
|
85
|
+
// Anything else would be a CSS-variable injection through settings.
|
|
86
|
+
const PALETTE_KEY_RE = /^[a-z][a-z0-9_]{0,40}$/;
|
|
87
|
+
const SPACING_PADDING = {
|
|
88
|
+
compact: "24px 16px",
|
|
89
|
+
normal: "48px 16px",
|
|
90
|
+
spacious: "96px 16px",
|
|
91
|
+
};
|
|
92
|
+
/** Wrapper style for the <section> element from whitelisted settings. */
|
|
93
|
+
export function sectionWrapperStyle(section) {
|
|
94
|
+
const style = {
|
|
95
|
+
padding: SPACING_PADDING[section.settings?.spacing ?? ""] ?? SPACING_PADDING.normal,
|
|
96
|
+
fontFamily: "var(--risali-font-body, inherit)",
|
|
97
|
+
};
|
|
98
|
+
const bg = section.settings?.background;
|
|
99
|
+
if (typeof bg === "string" && PALETTE_KEY_RE.test(bg)) {
|
|
100
|
+
style.backgroundColor = `var(--risali-color-${bg})`;
|
|
101
|
+
}
|
|
102
|
+
return style;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sections/index.tsx"],"names":[],"mappings":"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"}
|
package/dist/style.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAIvD,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAG5D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAIvD,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAG5D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAKzE;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,aAAa,GAAG,SAAS,EAC/B,SAAS,EAAE,oBAAoB,GAAG,IAAI,GAAG,SAAS,GACjD,aAAa,GAAG,SAAS,CAS3B"}
|
package/dist/style.js
CHANGED
|
@@ -7,7 +7,8 @@ export function safeColor(value) {
|
|
|
7
7
|
export function safeFontSizeMultiplier(value) {
|
|
8
8
|
if (typeof value !== "number" || !Number.isFinite(value))
|
|
9
9
|
return undefined;
|
|
10
|
-
|
|
10
|
+
// Mirrors the editor's slider bounds (PR 40a) — values outside are dropped.
|
|
11
|
+
if (value < 0.5 || value > 3)
|
|
11
12
|
return undefined;
|
|
12
13
|
return value;
|
|
13
14
|
}
|
package/dist/style.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.js","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,GAAG,mBAAmB,CAAC;AAEnC,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChD,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3E,IAAI,KAAK,GAAG,GAAG,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,IAA+B,EAC/B,SAAkD;IAElD,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,sBAAsB,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACpE,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,IAAI,GAAkB,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;IAChD,IAAI,KAAK;QAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAC9B,IAAI,IAAI;QAAE,IAAI,CAAC,QAAQ,GAAG,GAAG,IAAI,IAAI,CAAC;IACtC,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
1
|
+
{"version":3,"file":"style.js","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,GAAG,mBAAmB,CAAC;AAEnC,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChD,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3E,4EAA4E;IAC5E,IAAI,KAAK,GAAG,GAAG,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,IAA+B,EAC/B,SAAkD;IAElD,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,sBAAsB,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACpE,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,IAAI,GAAkB,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC;IAChD,IAAI,KAAK;QAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAC9B,IAAI,IAAI;QAAE,IAAI,CAAC,QAAQ,GAAG,GAAG,IAAI,IAAI,CAAC;IACtC,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type GetTrackingConfigOptions } from "./tracking.js";
|
|
2
|
+
export type RisaliTrackingProps = GetTrackingConfigOptions;
|
|
3
|
+
/**
|
|
4
|
+
* Server component that emits Risali-managed tracking tags into <head>.
|
|
5
|
+
* Render once inside the root layout's <head> (Next.js App Router):
|
|
6
|
+
*
|
|
7
|
+
* import { RisaliTracking } from "@risali/react";
|
|
8
|
+
* export default function RootLayout({ children }) {
|
|
9
|
+
* return (
|
|
10
|
+
* <html>
|
|
11
|
+
* <head>
|
|
12
|
+
* <RisaliTracking />
|
|
13
|
+
* </head>
|
|
14
|
+
* <body>{children}</body>
|
|
15
|
+
* </html>
|
|
16
|
+
* );
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* Behaviour mirrors the Lovable /risali.js snippet: every tag ships as a
|
|
20
|
+
* <script type="text/plain" data-risali-consent="analytics|marketing"> so it
|
|
21
|
+
* stays inert until the GDPR banner (rendered by the host or by the Risali
|
|
22
|
+
* snippet itself) flips it to executable JS via gdprApplyConsentToScripts.
|
|
23
|
+
*
|
|
24
|
+
* Pro custom HTML is rendered raw (server-sanitized whitelist already runs at
|
|
25
|
+
* save time). Because Next.js needs dangerouslySetInnerHTML for raw <script>
|
|
26
|
+
* tags inside the React tree, we wrap the entire payload in a single block.
|
|
27
|
+
*/
|
|
28
|
+
export declare function RisaliTracking(props?: RisaliTrackingProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
29
|
+
//# sourceMappingURL=tracking-component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking-component.d.ts","sourceRoot":"","sources":["../src/tracking-component.tsx"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,cAAc,CAAC,KAAK,GAAE,mBAAwB,oDA+JnE"}
|