@charcuterie/ui 4.19.0 → 4.20.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.
Files changed (41) hide show
  1. package/README.md +2 -0
  2. package/dist/htmlEditor/HtmlEditor.d.ts +25 -0
  3. package/dist/htmlEditor/HtmlEditor.d.ts.map +1 -0
  4. package/dist/htmlEditor/HtmlEditor.js +233 -0
  5. package/dist/htmlEditor/HtmlEditor.js.map +1 -0
  6. package/dist/htmlEditor/HtmlView.d.ts +10 -0
  7. package/dist/htmlEditor/HtmlView.d.ts.map +1 -0
  8. package/dist/htmlEditor/HtmlView.js +28 -0
  9. package/dist/htmlEditor/HtmlView.js.map +1 -0
  10. package/dist/htmlEditor/htmlContentStyles.d.ts +3 -0
  11. package/dist/htmlEditor/htmlContentStyles.d.ts.map +1 -0
  12. package/dist/htmlEditor/htmlContentStyles.js +3 -0
  13. package/dist/htmlEditor/htmlContentStyles.js.map +1 -0
  14. package/dist/htmlEditor/index.d.ts +8 -0
  15. package/dist/htmlEditor/index.d.ts.map +1 -0
  16. package/dist/htmlEditor/index.js +4 -0
  17. package/dist/htmlEditor/index.js.map +1 -0
  18. package/dist/htmlEditor/safeHtml.d.ts +12 -0
  19. package/dist/htmlEditor/safeHtml.d.ts.map +1 -0
  20. package/dist/htmlEditor/safeHtml.js +137 -0
  21. package/dist/htmlEditor/safeHtml.js.map +1 -0
  22. package/dist/htmlEditor/view.d.ts +6 -0
  23. package/dist/htmlEditor/view.d.ts.map +1 -0
  24. package/dist/htmlEditor/view.js +3 -0
  25. package/dist/htmlEditor/view.js.map +1 -0
  26. package/package.json +52 -2
  27. package/src/htmlEditor/HtmlEditor.mdx +61 -0
  28. package/src/htmlEditor/HtmlEditor.stories.tsx +192 -0
  29. package/src/htmlEditor/HtmlEditor.test.tsx +308 -0
  30. package/src/htmlEditor/HtmlEditor.tsx +422 -0
  31. package/src/htmlEditor/HtmlView.mdx +37 -0
  32. package/src/htmlEditor/HtmlView.stories.tsx +119 -0
  33. package/src/htmlEditor/HtmlView.test.tsx +88 -0
  34. package/src/htmlEditor/HtmlView.tsx +71 -0
  35. package/src/htmlEditor/htmlContentStyles.ts +3 -0
  36. package/src/htmlEditor/htmlDocument.storyHelpers.ts +7 -0
  37. package/src/htmlEditor/index.ts +7 -0
  38. package/src/htmlEditor/safeHtml.test.ts +123 -0
  39. package/src/htmlEditor/safeHtml.ts +187 -0
  40. package/src/htmlEditor/view.ts +5 -0
  41. package/src/sourceRules.test.ts +22 -0
package/README.md CHANGED
@@ -329,3 +329,5 @@ rename breaks only the rendered page).
329
329
  source and fails on this, so the component M4 adds joins the rule automatically.
330
330
 
331
331
  **`Chart`** provides responsive bar and line plots, negative values, gaps for missing data, and an optional semantic values table. TanStack provides hover/touch tooltips, keyboard navigation, and transitions with reduced-motion support. Set `renderer="portable"` for the lightweight fallback. The `@charcuterie/logic/charts`, `/charts/preact`, and `/charts/react` subpaths expose static and framework-specific surfaces; `/core` remains dependency-free. Apps supply their own series, labels, and semantic colors.
332
+
333
+ **HTML notes** use optional `@charcuterie/ui/html-view` and `/html-editor` entries: `HtmlView` renders a safe document projection and `HtmlEditor` preserves original HTML until a real edit. The existing markdown editors keep their markdown-only storage contracts.
@@ -0,0 +1,25 @@
1
+ import type { ReactNode } from "react";
2
+ import type { SlotProps } from "../slotProps.ts";
3
+ export type HtmlEditorProps = SlotProps & {
4
+ className?: string;
5
+ /** Initial only. Remount with a key to switch documents. */
6
+ defaultValue?: string;
7
+ icons?: Readonly<Record<string, ReactNode>>;
8
+ isDisabled?: boolean;
9
+ isReadOnly?: boolean;
10
+ /** Omit inside a Field, which supplies the real label. */
11
+ label?: string;
12
+ /** Hidden form control; its original bytes survive until an edit. */
13
+ name?: string;
14
+ onChange?: (value: string) => void;
15
+ toolbarLabel?: string;
16
+ };
17
+ /**
18
+ * A rich editor for records already stored as HTML. Sanitization is
19
+ * a projection for display and editing, not a migration: mounting,
20
+ * selecting, focusing and submitting an untouched form never emit
21
+ * normalized HTML. ProseMirror owns document edits and their undo
22
+ * history; external defaults never replace that document.
23
+ */
24
+ export declare const HtmlEditor: ({ className, defaultValue, icons, isDisabled, isReadOnly, label, name, onChange, toolbarLabel, ...slotProps }: HtmlEditorProps) => ReactNode;
25
+ //# sourceMappingURL=HtmlEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HtmlEditor.d.ts","sourceRoot":"","sources":["../../src/htmlEditor/HtmlEditor.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAWtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAOhD,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG;IACxC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAA;IAC3C,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAaD;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,+GAWxB,eAAe,KAAG,SA8VpB,CAAA"}
@@ -0,0 +1,233 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useUniqueId } from "@charcuterie/logic";
3
+ import Image from "@tiptap/extension-image";
4
+ import TextAlign from "@tiptap/extension-text-align";
5
+ import { Color, TextStyle, } from "@tiptap/extension-text-style";
6
+ import { EditorContent, useEditor } from "@tiptap/react";
7
+ import StarterKit from "@tiptap/starter-kit";
8
+ import { useEffect, useRef, useState } from "react";
9
+ import { Button } from "../Button/Button.js";
10
+ import { Dialog } from "../Dialog/Dialog.js";
11
+ import { Field } from "../Field/Field.js";
12
+ import { FOCUS_RING_CLASS } from "../intentStyles.js";
13
+ import { toSafeImageUrl, toSafeLinkUrl, } from "../safeUrls.js";
14
+ import { Toolbar } from "../Toolbar/Toolbar.js";
15
+ import { toClassName } from "../toClassName.js";
16
+ import { HTML_CONTENT_CLASS } from "./htmlContentStyles.js";
17
+ import { toSafeHtml, toSafeHtmlColour } from "./safeHtml.js";
18
+ const EDITOR_CLASS = toClassName(HTML_CONTENT_CLASS, "min-h-40 rounded-md border border-border-default bg-surface-sunken px-3 py-2", FOCUS_RING_CLASS);
19
+ const INPUT_CLASS = toClassName("w-full min-w-0 rounded-md border border-border-default bg-surface-sunken p-2 text-content-primary", FOCUS_RING_CLASS);
20
+ /**
21
+ * A rich editor for records already stored as HTML. Sanitization is
22
+ * a projection for display and editing, not a migration: mounting,
23
+ * selecting, focusing and submitting an untouched form never emit
24
+ * normalized HTML. ProseMirror owns document edits and their undo
25
+ * history; external defaults never replace that document.
26
+ */
27
+ export const HtmlEditor = ({ className, defaultValue = "", icons, isDisabled = false, isReadOnly = false, label, name, onChange, toolbarLabel = "HTML formatting", ...slotProps }) => {
28
+ const generatedId = useUniqueId("html-editor");
29
+ const dialogInputRef = useRef(null);
30
+ const id = slotProps.id ?? generatedId;
31
+ const [initialValue] = useState(defaultValue);
32
+ const [formValue, setFormValue] = useState(initialValue);
33
+ const [dialog, setDialog] = useState(null);
34
+ const [error, setError] = useState();
35
+ const editor = useEditor({
36
+ content: toSafeHtml(initialValue),
37
+ editable: !isDisabled && !isReadOnly,
38
+ immediatelyRender: false,
39
+ shouldRerenderOnTransaction: true,
40
+ extensions: [
41
+ StarterKit.configure({
42
+ link: {
43
+ openOnClick: false,
44
+ autolink: false,
45
+ isAllowedUri: (url) => toSafeLinkUrl(url) !== undefined,
46
+ },
47
+ }),
48
+ Image.configure({ allowBase64: true }),
49
+ TextStyle,
50
+ Color,
51
+ TextAlign.configure({
52
+ types: ["heading", "paragraph"],
53
+ alignments: [
54
+ "start",
55
+ "end",
56
+ "left",
57
+ "right",
58
+ "center",
59
+ "justify",
60
+ ],
61
+ }),
62
+ ],
63
+ editorProps: {
64
+ attributes: {
65
+ id,
66
+ role: "textbox",
67
+ "aria-multiline": "true",
68
+ "aria-label": label ?? "",
69
+ "aria-labelledby": label === undefined && slotProps.id
70
+ ? `${slotProps.id}-label`
71
+ : "",
72
+ "aria-describedby": slotProps["aria-describedby"] ?? "",
73
+ "aria-invalid": String(slotProps["aria-invalid"] ?? false),
74
+ "aria-required": String(slotProps["aria-required"] ??
75
+ slotProps.required ??
76
+ false),
77
+ "aria-disabled": String(isDisabled),
78
+ "aria-readonly": String(isReadOnly),
79
+ tabindex: String(isDisabled ? -1 : 0),
80
+ class: EDITOR_CLASS,
81
+ },
82
+ transformPastedHTML: toSafeHtml,
83
+ },
84
+ onUpdate: ({ editor: changedEditor, transaction, }) => {
85
+ if (!transaction.docChanged)
86
+ return;
87
+ const next = toSafeHtml(changedEditor.getHTML());
88
+ setFormValue(next);
89
+ onChange?.(next);
90
+ },
91
+ }, []);
92
+ useEffect(() => {
93
+ editor?.setEditable(!isDisabled && !isReadOnly, false);
94
+ }, [editor, isDisabled, isReadOnly]);
95
+ const hasDisabledActions = isDisabled || isReadOnly || !editor;
96
+ const openDialog = (next) => {
97
+ setError(undefined);
98
+ setDialog(next);
99
+ };
100
+ const toggle = (key, title, onSelect, isPressed) => ({
101
+ key,
102
+ type: "control",
103
+ element: (_jsx(Button, { appearance: "ghost", "aria-pressed": isPressed, iconStart: icons?.[key], isDisabled: hasDisabledActions, onClick: onSelect, children: title })),
104
+ });
105
+ const action = (key, title, onSelect) => ({
106
+ key,
107
+ label: title,
108
+ icon: icons?.[key],
109
+ isDisabled: hasDisabledActions,
110
+ onSelect,
111
+ });
112
+ const closeDialog = () => setDialog(null);
113
+ return (_jsxs("div", { className: toClassName("min-w-0 space-y-2", className), children: [_jsx(Toolbar, { label: toolbarLabel, overflow: "panel", overflowIcon: icons?.overflow, items: [
114
+ toggle("bold", "Bold", () => {
115
+ editor?.chain().focus().toggleBold().run();
116
+ }, editor?.isActive("bold") ?? false),
117
+ toggle("italic", "Italic", () => {
118
+ editor?.chain().focus().toggleItalic().run();
119
+ }, editor?.isActive("italic") ?? false),
120
+ toggle("underline", "Underline", () => {
121
+ editor
122
+ ?.chain()
123
+ .focus()
124
+ .toggleUnderline()
125
+ .run();
126
+ }, editor?.isActive("underline") ?? false),
127
+ toggle("bulletList", "Bullet list", () => {
128
+ editor
129
+ ?.chain()
130
+ .focus()
131
+ .toggleBulletList()
132
+ .run();
133
+ }, editor?.isActive("bulletList") ?? false),
134
+ toggle("orderedList", "Numbered list", () => {
135
+ editor
136
+ ?.chain()
137
+ .focus()
138
+ .toggleOrderedList()
139
+ .run();
140
+ }, editor?.isActive("orderedList") ?? false),
141
+ action("alignStart", "Align start", () => {
142
+ editor
143
+ ?.chain()
144
+ .focus()
145
+ .setTextAlign("start")
146
+ .run();
147
+ }),
148
+ action("alignCenter", "Align centre", () => {
149
+ editor
150
+ ?.chain()
151
+ .focus()
152
+ .setTextAlign("center")
153
+ .run();
154
+ }),
155
+ action("alignEnd", "Align end", () => {
156
+ editor
157
+ ?.chain()
158
+ .focus()
159
+ .setTextAlign("end")
160
+ .run();
161
+ }),
162
+ action("link", "Edit link", () => openDialog("link")),
163
+ action("unlink", "Remove link", () => {
164
+ editor
165
+ ?.chain()
166
+ .focus()
167
+ .extendMarkRange("link")
168
+ .unsetLink()
169
+ .run();
170
+ }),
171
+ action("colour", "Text colour", () => openDialog("colour")),
172
+ action("image", "Insert image", () => openDialog("image")),
173
+ action("undo", "Undo", () => {
174
+ editor?.chain().focus().undo().run();
175
+ }),
176
+ action("redo", "Redo", () => {
177
+ editor?.chain().focus().redo().run();
178
+ }),
179
+ ] }), _jsx(EditorContent, { editor: editor }), name !== undefined && (_jsx("input", { disabled: isDisabled, name: name, type: "hidden", value: formValue })), _jsx(Dialog, { initialFocus: dialogInputRef, heading: dialog === "image"
180
+ ? "Insert image"
181
+ : dialog === "colour"
182
+ ? "Text colour"
183
+ : "Edit link", isVisible: dialog !== null, onClose: closeDialog, children: _jsxs("form", { className: "space-y-4", onSubmit: (event) => {
184
+ event.preventDefault();
185
+ // A portalled formatting form must never submit its record form.
186
+ event.stopPropagation();
187
+ if (!editor || hasDisabledActions)
188
+ return;
189
+ const data = new FormData(event.currentTarget);
190
+ const value = String(data.get("value") ?? "").trim();
191
+ if (dialog === "colour") {
192
+ const colour = toSafeHtmlColour(value);
193
+ if (!colour) {
194
+ setError("Enter a colour name, hex, RGB or HSL value.");
195
+ return;
196
+ }
197
+ editor.chain().focus().setColor(colour).run();
198
+ }
199
+ else if (dialog === "image") {
200
+ const src = toSafeImageUrl(value);
201
+ if (!src) {
202
+ setError("Enter a safe image URL.");
203
+ return;
204
+ }
205
+ editor
206
+ .chain()
207
+ .focus()
208
+ .setImage({
209
+ src,
210
+ alt: String(data.get("alt") ?? ""),
211
+ })
212
+ .run();
213
+ }
214
+ else {
215
+ const href = toSafeLinkUrl(value);
216
+ if (!href) {
217
+ setError("Enter a safe link URL.");
218
+ return;
219
+ }
220
+ editor
221
+ .chain()
222
+ .focus()
223
+ .extendMarkRange("link")
224
+ .setLink({ href })
225
+ .run();
226
+ }
227
+ closeDialog();
228
+ }, children: [_jsx(Field, { error: error, label: dialog === "colour" ? "Colour" : "URL", isRequired: true, children: _jsx("input", { className: INPUT_CLASS, defaultValue: dialog === "link"
229
+ ? String(editor?.getAttributes("link").href ??
230
+ "")
231
+ : "", name: "value", ref: dialogInputRef, required: true }) }), dialog === "image" && (_jsx(Field, { label: "Image description", children: _jsx("input", { className: INPUT_CLASS, name: "alt" }) })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { isDisabled: hasDisabledActions, type: "submit", children: "Apply" }), _jsx(Button, { appearance: "outline", onClick: closeDialog, children: "Cancel" })] })] }, dialog) })] }));
232
+ };
233
+ //# sourceMappingURL=HtmlEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HtmlEditor.js","sourceRoot":"","sources":["../../src/htmlEditor/HtmlEditor.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from "react";
2
+ export type HtmlViewProps = {
3
+ className?: string;
4
+ /** The readable document's accessible name. */
5
+ label: string;
6
+ value: string;
7
+ };
8
+ /** HTML is a distinct stored format. No editor and no innerHTML. */
9
+ export declare const HtmlView: ({ className, label, value, }: HtmlViewProps) => ReactNode;
10
+ //# sourceMappingURL=HtmlView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HtmlView.d.ts","sourceRoot":"","sources":["../../src/htmlEditor/HtmlView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AASrD,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AA2CD,oEAAoE;AACpE,eAAO,MAAM,QAAQ,GAAI,8BAItB,aAAa,KAAG,SAQlB,CAAA"}
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createElement } from "react";
3
+ import { TextLink } from "../TextLink/TextLink.js";
4
+ import { toClassName } from "../toClassName.js";
5
+ import { HTML_CONTENT_CLASS } from "./htmlContentStyles.js";
6
+ import { toHtmlContent } from "./safeHtml.js";
7
+ const toStyle = (value) => Object.fromEntries((value ?? "")
8
+ .split(";")
9
+ .filter(Boolean)
10
+ .map((declaration) => {
11
+ const [name, content] = declaration.split(":");
12
+ return [
13
+ name === "text-align" ? "textAlign" : name,
14
+ content,
15
+ ];
16
+ }));
17
+ const renderNode = (node, key) => {
18
+ if ("text" in node)
19
+ return node.text;
20
+ const { style, ...attributes } = node.attributes;
21
+ const children = node.children.map(renderNode);
22
+ if (node.tag === "a" && attributes.href !== undefined)
23
+ return (_jsx(TextLink, { href: attributes.href, style: toStyle(style), title: attributes.title, children: children }, key));
24
+ return createElement(node.tag, { ...attributes, key, style: toStyle(style) }, children.length ? children : undefined);
25
+ };
26
+ /** HTML is a distinct stored format. No editor and no innerHTML. */
27
+ export const HtmlView = ({ className, label, value, }) => (_jsx("div", { "aria-label": label, className: toClassName(HTML_CONTENT_CLASS, className), role: "document", children: toHtmlContent(value).map(renderNode) }));
28
+ //# sourceMappingURL=HtmlView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HtmlView.js","sourceRoot":"","sources":["../../src/htmlEditor/HtmlView.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAErC,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAA0B,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAE3D,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAS7C,MAAM,OAAO,GAAG,CACd,KAAyB,EACV,EAAE,CACjB,MAAM,CAAC,WAAW,CAChB,CAAC,KAAK,IAAI,EAAE,CAAC;KACV,KAAK,CAAC,GAAG,CAAC;KACV,MAAM,CAAC,OAAO,CAAC;KACf,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE;IACnB,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC9C,OAAO;QACL,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;QAC1C,OAAO;KACR,CAAA;AACH,CAAC,CAAC,CACL,CAAA;AAEH,MAAM,UAAU,GAAG,CACjB,IAAqB,EACrB,GAAW,EACA,EAAE;IACb,IAAI,MAAM,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,IAAI,CAAA;IACpC,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,EAAE,GAAG,IAAI,CAAC,UAAU,CAAA;IAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IAC9C,IAAI,IAAI,CAAC,GAAG,KAAK,GAAG,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS;QACnD,OAAO,CACL,KAAC,QAAQ,IACP,IAAI,EAAE,UAAU,CAAC,IAAI,EAErB,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,EACrB,KAAK,EAAE,UAAU,CAAC,KAAK,YAEtB,QAAQ,IAJJ,GAAG,CAKC,CACZ,CAAA;IACH,OAAO,aAAa,CAClB,IAAI,CAAC,GAAG,EACR,EAAE,GAAG,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,EAC7C,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CACvC,CAAA;AACH,CAAC,CAAA;AAED,oEAAoE;AACpE,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EACvB,SAAS,EACT,KAAK,EACL,KAAK,GACS,EAAa,EAAE,CAAC,CAC9B,4BACc,KAAK,EACjB,SAAS,EAAE,WAAW,CAAC,kBAAkB,EAAE,SAAS,CAAC,EACrD,IAAI,EAAC,UAAU,YAEd,aAAa,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,GACjC,CACP,CAAA"}
@@ -0,0 +1,3 @@
1
+ /** Identical document paint in the reader and the editing surface. */
2
+ export declare const HTML_CONTENT_CLASS = "min-w-0 wrap-anywhere text-content-primary [&_p]:my-2 [&_div]:my-2 [&_ul]:my-2 [&_ul]:list-disc [&_ul]:ps-6 [&_ol]:my-2 [&_ol]:list-decimal [&_ol]:ps-6 [&_li]:my-1 [&_blockquote]:my-3 [&_blockquote]:border-s-2 [&_blockquote]:border-border-default [&_blockquote]:ps-4 [&_blockquote]:text-content-secondary [&_pre]:whitespace-pre-wrap [&_pre]:rounded-md [&_pre]:bg-surface-raised [&_pre]:p-3 [&_code]:font-mono [&_h1]:my-3 [&_h1]:text-2xl [&_h1]:font-bold [&_h2]:my-3 [&_h2]:text-xl [&_h2]:font-bold [&_h3]:my-3 [&_h3]:text-lg [&_h3]:font-bold [&_h4]:my-2 [&_h4]:font-semibold [&_h5]:my-2 [&_h5]:font-semibold [&_h6]:my-2 [&_h6]:font-semibold [&_a]:text-intent-accent-content [&_a]:underline [&_img]:max-w-full [&_img]:h-auto [&_img]:rounded-md [&_hr]:my-3 [&_hr]:border-border-default";
3
+ //# sourceMappingURL=htmlContentStyles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"htmlContentStyles.d.ts","sourceRoot":"","sources":["../../src/htmlEditor/htmlContentStyles.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,oxBACovB,CAAA"}
@@ -0,0 +1,3 @@
1
+ /** Identical document paint in the reader and the editing surface. */
2
+ export const HTML_CONTENT_CLASS = "min-w-0 wrap-anywhere text-content-primary [&_p]:my-2 [&_div]:my-2 [&_ul]:my-2 [&_ul]:list-disc [&_ul]:ps-6 [&_ol]:my-2 [&_ol]:list-decimal [&_ol]:ps-6 [&_li]:my-1 [&_blockquote]:my-3 [&_blockquote]:border-s-2 [&_blockquote]:border-border-default [&_blockquote]:ps-4 [&_blockquote]:text-content-secondary [&_pre]:whitespace-pre-wrap [&_pre]:rounded-md [&_pre]:bg-surface-raised [&_pre]:p-3 [&_code]:font-mono [&_h1]:my-3 [&_h1]:text-2xl [&_h1]:font-bold [&_h2]:my-3 [&_h2]:text-xl [&_h2]:font-bold [&_h3]:my-3 [&_h3]:text-lg [&_h3]:font-bold [&_h4]:my-2 [&_h4]:font-semibold [&_h5]:my-2 [&_h5]:font-semibold [&_h6]:my-2 [&_h6]:font-semibold [&_a]:text-intent-accent-content [&_a]:underline [&_img]:max-w-full [&_img]:h-auto [&_img]:rounded-md [&_hr]:my-3 [&_hr]:border-border-default";
3
+ //# sourceMappingURL=htmlContentStyles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"htmlContentStyles.js","sourceRoot":"","sources":["../../src/htmlEditor/htmlContentStyles.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAC7B,ixBAAixB,CAAA"}
@@ -0,0 +1,8 @@
1
+ /** Opt-in rich HTML surfaces; no HTML conversion in markdown components. */
2
+ export type { HtmlEditorProps } from "./HtmlEditor.tsx";
3
+ export { HtmlEditor } from "./HtmlEditor.tsx";
4
+ export type { HtmlViewProps } from "./HtmlView.tsx";
5
+ export { HtmlView } from "./HtmlView.tsx";
6
+ export type { HtmlContentNode } from "./safeHtml.ts";
7
+ export { toHtmlContent, toSafeHtml } from "./safeHtml.ts";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/htmlEditor/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,4 @@
1
+ export { HtmlEditor } from "./HtmlEditor.js";
2
+ export { HtmlView } from "./HtmlView.js";
3
+ export { toHtmlContent, toSafeHtml } from "./safeHtml.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/htmlEditor/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAkB,CAAA;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAgB,CAAA;AAEzC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,12 @@
1
+ export type HtmlContentNode = {
2
+ text: string;
3
+ } | {
4
+ attributes: Record<string, string>;
5
+ children: HtmlContentNode[];
6
+ tag: string;
7
+ };
8
+ export declare const toSafeHtmlColour: (value: string) => string | undefined;
9
+ export declare const toHtmlContent: (value: string) => HtmlContentNode[];
10
+ /** A display/edit projection, never an instruction to rewrite stored HTML. */
11
+ export declare const toSafeHtml: (value: string) => string;
12
+ //# sourceMappingURL=safeHtml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safeHtml.d.ts","sourceRoot":"","sources":["../../src/htmlEditor/safeHtml.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IACE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AA+CL,eAAO,MAAM,gBAAgB,GAC3B,OAAO,MAAM,KACZ,MAAM,GAAG,SAOX,CAAA;AAoFD,eAAO,MAAM,aAAa,GACxB,OAAO,MAAM,KACZ,eAAe,EAC4B,CAAA;AA2B9C,8EAA8E;AAC9E,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,MACG,CAAA"}
@@ -0,0 +1,137 @@
1
+ import { parseFragment } from "parse5";
2
+ import { toSafeImageUrl, toSafeLinkUrl, } from "../safeUrls.js";
3
+ // One format policy for rendering, initial editor content, paste,
4
+ // and serialized edits. Parsing HTML5 before filtering also decodes
5
+ // character references in URLs; an encoded scheme cannot evade the
6
+ // shared URL guard. Nothing from the source becomes a React prop.
7
+ const TAGS = new Set([
8
+ "p",
9
+ "div",
10
+ "br",
11
+ "strong",
12
+ "b",
13
+ "em",
14
+ "i",
15
+ "u",
16
+ "s",
17
+ "strike",
18
+ "span",
19
+ "a",
20
+ "img",
21
+ "ul",
22
+ "ol",
23
+ "li",
24
+ "blockquote",
25
+ "h1",
26
+ "h2",
27
+ "h3",
28
+ "h4",
29
+ "h5",
30
+ "h6",
31
+ "pre",
32
+ "code",
33
+ "hr",
34
+ ]);
35
+ const DROP_CONTENT = new Set([
36
+ "script",
37
+ "style",
38
+ "template",
39
+ "iframe",
40
+ "object",
41
+ "embed",
42
+ "svg",
43
+ "math",
44
+ "noscript",
45
+ ]);
46
+ const VOID_TAGS = new Set(["br", "hr", "img"]);
47
+ export const toSafeHtmlColour = (value) => {
48
+ const colour = value.trim().toLowerCase();
49
+ return /^(#[\da-f]{3}|#[\da-f]{4}|#[\da-f]{6}|#[\da-f]{8}|[a-z]+|(?:rgb|rgba|hsl|hsla)\([\d.,%\s+-]+\))$/.test(colour)
50
+ ? colour
51
+ : undefined;
52
+ };
53
+ const toSafeStyle = (value) => value
54
+ .split(";")
55
+ .flatMap((declaration) => {
56
+ const [rawName, ...parts] = declaration.split(":");
57
+ const name = rawName?.trim().toLowerCase();
58
+ const content = parts.join(":").trim().toLowerCase();
59
+ if (name === "text-align" &&
60
+ /^(start|end|left|right|center|justify)$/.test(content)) {
61
+ return [`text-align:${content}`];
62
+ }
63
+ if (name === "color" &&
64
+ toSafeHtmlColour(content) !== undefined) {
65
+ return [`color:${content}`];
66
+ }
67
+ return [];
68
+ })
69
+ .join(";");
70
+ const toSafeAttributes = (tag, attributes) => {
71
+ const source = Object.fromEntries(attributes
72
+ .filter((attribute) => !attribute.namespace)
73
+ .map(({ name, value }) => [name, value]));
74
+ const result = {};
75
+ const style = toSafeStyle(source.style ?? "");
76
+ if (style !== "")
77
+ result.style = style;
78
+ if (tag === "a") {
79
+ const href = toSafeLinkUrl(source.href ?? "");
80
+ if (source.href !== undefined && href !== undefined)
81
+ result.href = href;
82
+ if (source.title)
83
+ result.title = source.title;
84
+ // Source target/rel attributes never reach the page.
85
+ result.rel = "noopener noreferrer";
86
+ }
87
+ if (tag === "img") {
88
+ const src = toSafeImageUrl(source.src ?? "");
89
+ if (src)
90
+ result.src = src;
91
+ result.alt = source.alt ?? "";
92
+ if (source.title)
93
+ result.title = source.title;
94
+ }
95
+ if (tag === "ol" && /^\d{1,6}$/.test(source.start ?? ""))
96
+ result.start = source.start ?? "1";
97
+ return result;
98
+ };
99
+ const toSafeNodes = (nodes) => nodes.flatMap((node) => {
100
+ if (node.nodeName === "#text" && "value" in node)
101
+ return [{ text: node.value }];
102
+ if (!("tagName" in node))
103
+ return [];
104
+ if (DROP_CONTENT.has(node.tagName) ||
105
+ node.namespaceURI !== "http://www.w3.org/1999/xhtml")
106
+ return [];
107
+ const children = toSafeNodes(node.childNodes);
108
+ if (!TAGS.has(node.tagName))
109
+ return children;
110
+ const attributes = toSafeAttributes(node.tagName, node.attrs);
111
+ // A rejected image is absent, rather than a broken image or an
112
+ // empty src that makes another request for the document itself.
113
+ if (node.tagName === "img" && !attributes.src)
114
+ return [];
115
+ return [{ attributes, children, tag: node.tagName }];
116
+ });
117
+ export const toHtmlContent = (value) => toSafeNodes(parseFragment(value).childNodes);
118
+ const escapeHtml = (value) => value.replace(/[&<>"]/g, (character) => ({
119
+ "&": "&amp;",
120
+ "<": "&lt;",
121
+ ">": "&gt;",
122
+ '"': "&quot;",
123
+ })[character] ?? character);
124
+ const serialize = (node) => {
125
+ if ("text" in node)
126
+ return escapeHtml(node.text);
127
+ const attributes = Object.entries(node.attributes)
128
+ .map(([name, value]) => ` ${name}="${escapeHtml(value)}"`)
129
+ .join("");
130
+ const start = `<${node.tag}${attributes}>`;
131
+ return VOID_TAGS.has(node.tag)
132
+ ? start
133
+ : `${start}${node.children.map(serialize).join("")}</${node.tag}>`;
134
+ };
135
+ /** A display/edit projection, never an instruction to rewrite stored HTML. */
136
+ export const toSafeHtml = (value) => toHtmlContent(value).map(serialize).join("");
137
+ //# sourceMappingURL=safeHtml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safeHtml.js","sourceRoot":"","sources":["../../src/htmlEditor/safeHtml.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /** Read HTML without importing or installing the rich editor core. */
2
+ export type { HtmlViewProps } from "./HtmlView.tsx";
3
+ export { HtmlView } from "./HtmlView.tsx";
4
+ export type { HtmlContentNode } from "./safeHtml.ts";
5
+ export { toHtmlContent, toSafeHtml } from "./safeHtml.ts";
6
+ //# sourceMappingURL=view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../src/htmlEditor/view.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,3 @@
1
+ export { HtmlView } from "./HtmlView.js";
2
+ export { toHtmlContent, toSafeHtml } from "./safeHtml.js";
3
+ //# sourceMappingURL=view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.js","sourceRoot":"","sources":["../../src/htmlEditor/view.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAgB,CAAA;AAEzC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA"}