@aglyn/besigner-ui 1.0.0-beta.207 → 1.0.0-beta.209

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/besigner-ui",
3
- "version": "1.0.0-beta.207",
3
+ "version": "1.0.0-beta.209",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,22 +25,22 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/aglyn": "1.0.0-beta.207",
29
- "@aglyn/aglyn-markdown-editor": "1.0.0-beta.207",
30
- "@aglyn/aglyn-node-renderer": "1.0.0-beta.207",
31
- "@aglyn/besigner": "1.0.0-beta.207",
32
- "@aglyn/shared-data-enums": "1.0.0-beta.207",
33
- "@aglyn/shared-data-mdi": "1.0.0-beta.207",
34
- "@aglyn/shared-data-types": "1.0.0-beta.207",
35
- "@aglyn/shared-svg-icons": "1.0.0-beta.207",
36
- "@aglyn/shared-ui-json-editor": "1.0.0-beta.207",
37
- "@aglyn/shared-ui-jsx": "1.0.0-beta.207",
38
- "@aglyn/shared-ui-jsx-forms": "1.0.0-beta.207",
39
- "@aglyn/shared-ui-snackstack": "1.0.0-beta.207",
40
- "@aglyn/shared-ui-theme": "1.0.0-beta.207",
41
- "@aglyn/shared-util-dom": "1.0.0-beta.207",
42
- "@aglyn/shared-util-tools": "1.0.0-beta.207",
43
- "@aglyn/shared-util-vendor": "1.0.0-beta.207",
28
+ "@aglyn/aglyn": "1.0.0-beta.209",
29
+ "@aglyn/aglyn-markdown-editor": "1.0.0-beta.209",
30
+ "@aglyn/aglyn-node-renderer": "1.0.0-beta.209",
31
+ "@aglyn/besigner": "1.0.0-beta.209",
32
+ "@aglyn/shared-data-enums": "1.0.0-beta.209",
33
+ "@aglyn/shared-data-mdi": "1.0.0-beta.209",
34
+ "@aglyn/shared-data-types": "1.0.0-beta.209",
35
+ "@aglyn/shared-svg-icons": "1.0.0-beta.209",
36
+ "@aglyn/shared-ui-json-editor": "1.0.0-beta.209",
37
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.209",
38
+ "@aglyn/shared-ui-jsx-forms": "1.0.0-beta.209",
39
+ "@aglyn/shared-ui-snackstack": "1.0.0-beta.209",
40
+ "@aglyn/shared-ui-theme": "1.0.0-beta.209",
41
+ "@aglyn/shared-util-dom": "1.0.0-beta.209",
42
+ "@aglyn/shared-util-tools": "1.0.0-beta.209",
43
+ "@aglyn/shared-util-vendor": "1.0.0-beta.209",
44
44
  "@dnd-kit/core": "^6.3.1",
45
45
  "@dnd-kit/utilities": "^3.2.2",
46
46
  "@react-aria/utils": "^3.34.1",
@@ -59,7 +59,7 @@ import { escapeHtml } from "@aglyn/shared-util-tools/escape-html";
59
59
  'br',
60
60
  'span'
61
61
  ]);
62
- const SAFE_HREF = /^(https?:\/\/|mailto:|tel:|\/)/i;
62
+ const SAFE_HREF = /^(https?:\/\/|mailto:|tel:|sms:|\/)/i;
63
63
  /**
64
64
  * Allowlist HTML sanitizer for inline rich text: keeps basic formatting
65
65
  * tags, strips every attribute except a safe `href` on links (which gains
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/besigner/feature/designer/src/lib/utils/sanitize-rich-text.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// The one escaper (AGL-2706). The local `escapeAttribute` this replaces\n// omitted `'`, which single-quoted attributes delimit just as double quotes\n// do — the omission was survivable only because every value here lands in a\n// double-quoted slot, and a rule that holds by where its callers happen to\n// put it is the kind that stops holding quietly. `escapeText` below stays\n// local: it is the TEXT half, and a text node has no quote to break.\nimport { escapeHtml } from '@aglyn/shared-util-tools/escape-html'\n\n/** Tags the inline rich-text editor may persist (AGL-54). */\nconst ALLOWED_TAGS = new Set([\n 'b',\n 'strong',\n 'i',\n 'em',\n 'u',\n 'a',\n 'ul',\n 'ol',\n 'li',\n 'br',\n 'p',\n 'div',\n 'span',\n])\n\n/**\n * The subset a PHRASING-ONLY surface may persist (AGL-2557) — the label of a\n * Button, a Screen Link or an Accordion Summary, which ships inside a\n * `<button>` or an `<a>`.\n *\n * Neither element may contain a block or a second control. A `<div>` here is\n * not merely untidy: the parser closes the control on that start tag and\n * promotes the rest to a sibling, which reaches the browser as a React\n * hydration mismatch on the published page. The renderer enforces the same\n * narrowing on the way out (`INLINE_AUTHOR_HTML_ELEMENTS`); this end of the\n * round trip means what is STORED is what will be drawn, so the Attributes\n * panel and the plain-text fallback describe the same element as the canvas.\n *\n * `br` stays: it is phrasing content, and Shift+Enter is the one line break\n * these surfaces still offer.\n */\nconst PHRASING_TAGS = new Set(['b', 'strong', 'i', 'em', 'u', 'br', 'span'])\n\n/** How much markup one commit may keep — see {@link PHRASING_TAGS}. */\nexport interface SanitizeRichTextOptions {\n phrasingOnly?: boolean\n}\n\nconst SAFE_HREF = /^(https?:\\/\\/|mailto:|tel:|\\/)/i\n\n/**\n * Allowlist HTML sanitizer for inline rich text: keeps basic formatting\n * tags, strips every attribute except a safe `href` on links (which gains\n * `rel=\"noopener noreferrer\"`), and unwraps disallowed elements to their\n * text content (so nothing script-capable survives). Runs at commit time in\n * the editor (browser DOM available).\n *\n * `options.phrasingOnly` narrows the allowlist for a surface that ships\n * inside a control (AGL-2557); omitted, every existing caller is unchanged.\n */\nexport function sanitizeRichText(\n html: string,\n options?: SanitizeRichTextOptions,\n): string {\n if (typeof document === 'undefined') return ''\n const allowed = options?.phrasingOnly ? PHRASING_TAGS : ALLOWED_TAGS\n const template = document.createElement('template')\n template.innerHTML = html\n\n const sanitizeNode = (node: Node): string => {\n if (node.nodeType === Node.TEXT_NODE) {\n return escapeText(node.textContent ?? '')\n }\n if (node.nodeType !== Node.ELEMENT_NODE) return ''\n const element = node as Element\n const tag = element.tagName.toLowerCase()\n const inner = Array.from(element.childNodes).map(sanitizeNode).join('')\n if (!allowed.has(tag)) return inner\n // A bare `span` is INERT and is unwrapped (AGL-2486). Every attribute is\n // stripped here, so a surviving `<span>` carries no styling, no class and\n // no binding — it renders exactly as its own text. Keeping it would be\n // dead weight with a cost: `html` is what marks a node as \"formatted\", so\n // an inert wrapper left behind by a contentEditable merge would pin the\n // Attributes Text field read-only forever over markup that does nothing.\n // Measured on test-org: deleting the line break out of\n // `About <div>us</div>` left Chrome with `About<span>us</span>`.\n if (tag === 'span') return inner\n if (tag === 'br') return '<br>'\n if (tag === 'a') {\n const href = element.getAttribute('href') ?? ''\n if (!SAFE_HREF.test(href)) return inner\n return `<a href=\"${escapeHtml(href)}\" rel=\"noopener noreferrer\">${inner}</a>`\n }\n return `<${tag}>${inner}</${tag}>`\n }\n\n return Array.from(template.content.childNodes).map(sanitizeNode).join('')\n}\n\nfunction escapeText(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\n/**\n * Tags that end the line they are on. `br` is the explicit one; the rest are\n * the block-level members of {@link ALLOWED_TAGS}, which a contentEditable\n * produces on its own — pressing Enter in the rich surface forks the DOM\n * into `div`s, and that fork IS the author's line break.\n */\nconst LINE_BREAKING_TAGS = new Set(['BR', 'DIV', 'P', 'LI', 'UL', 'OL'])\n\n/**\n * Plain-text projection of markup, for the `children` fallback prop.\n *\n * Line breaks SURVIVE (AGL-2486). A node that carries one stores\n * `html: \"Your entire web <div>presence. </div>\"` beside\n * `children: \"Your entire web presence. \"`. The canvas renders `html` and\n * shows two lines; the Attributes panel renders `children`. Projected with\n * `template.content.textContent` the panel shows one, because `textContent`\n * concatenates across every element boundary — a `<div>` fork and a `<br>`\n * both vanish into nothing.\n *\n * That is a real break silently dropped by the projection, not the soft wrap\n * a bounding box misreports. `children` is not a display detail either: it is\n * what every\n * plain renderer, the SSR fallback and the panel field all read, so the\n * break was being lost to all three.\n *\n * Only the trailing newline is trimmed — a block element closes at the end\n * of the string and that boundary separates nothing.\n */\nexport function richTextToPlain(html: string): string {\n if (typeof document === 'undefined') return html\n const template = document.createElement('template')\n template.innerHTML = html\n const parts: string[] = []\n const endsOpen = () =>\n parts.length > 0 && !parts[parts.length - 1]!.endsWith('\\n')\n\n const walk = (parent: Node): void => {\n for (const child of Array.from(parent.childNodes)) {\n if (child.nodeType === 3 /* TEXT_NODE */) {\n parts.push(child.nodeValue ?? '')\n continue\n }\n if (child.nodeType !== 1 /* ELEMENT_NODE */) continue\n const element = child as Element\n if (element.tagName === 'BR') {\n parts.push('\\n')\n continue\n }\n const breaks = LINE_BREAKING_TAGS.has(element.tagName)\n // A block that FOLLOWS content starts its own line; one that opens the\n // string, or already sits after a break, does not add an empty one.\n if (breaks && endsOpen()) parts.push('\\n')\n walk(element)\n if (breaks && endsOpen()) parts.push('\\n')\n }\n }\n\n walk(template.content)\n return parts.join('').replace(/\\n+$/, '')\n}\n"],"names":["escapeHtml","ALLOWED_TAGS","Set","PHRASING_TAGS","SAFE_HREF","sanitizeRichText","html","options","document","allowed","phrasingOnly","template","createElement","innerHTML","sanitizeNode","node","nodeType","Node","TEXT_NODE","escapeText","textContent","ELEMENT_NODE","element","tag","tagName","toLowerCase","inner","Array","from","childNodes","map","join","has","href","getAttribute","test","content","text","replace","LINE_BREAKING_TAGS","richTextToPlain","parts","endsOpen","length","endsWith","walk","parent","child","push","nodeValue","breaks"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../../libs/besigner/feature/designer/src/lib/utils/sanitize-rich-text.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// The one escaper (AGL-2706). The local `escapeAttribute` this replaces\n// omitted `'`, which single-quoted attributes delimit just as double quotes\n// do — the omission was survivable only because every value here lands in a\n// double-quoted slot, and a rule that holds by where its callers happen to\n// put it is the kind that stops holding quietly. `escapeText` below stays\n// local: it is the TEXT half, and a text node has no quote to break.\nimport { escapeHtml } from '@aglyn/shared-util-tools/escape-html'\n\n/** Tags the inline rich-text editor may persist (AGL-54). */\nconst ALLOWED_TAGS = new Set([\n 'b',\n 'strong',\n 'i',\n 'em',\n 'u',\n 'a',\n 'ul',\n 'ol',\n 'li',\n 'br',\n 'p',\n 'div',\n 'span',\n])\n\n/**\n * The subset a PHRASING-ONLY surface may persist (AGL-2557) — the label of a\n * Button, a Screen Link or an Accordion Summary, which ships inside a\n * `<button>` or an `<a>`.\n *\n * Neither element may contain a block or a second control. A `<div>` here is\n * not merely untidy: the parser closes the control on that start tag and\n * promotes the rest to a sibling, which reaches the browser as a React\n * hydration mismatch on the published page. The renderer enforces the same\n * narrowing on the way out (`INLINE_AUTHOR_HTML_ELEMENTS`); this end of the\n * round trip means what is STORED is what will be drawn, so the Attributes\n * panel and the plain-text fallback describe the same element as the canvas.\n *\n * `br` stays: it is phrasing content, and Shift+Enter is the one line break\n * these surfaces still offer.\n */\nconst PHRASING_TAGS = new Set(['b', 'strong', 'i', 'em', 'u', 'br', 'span'])\n\n/** How much markup one commit may keep — see {@link PHRASING_TAGS}. */\nexport interface SanitizeRichTextOptions {\n phrasingOnly?: boolean\n}\n\nconst SAFE_HREF = /^(https?:\\/\\/|mailto:|tel:|sms:|\\/)/i\n\n/**\n * Allowlist HTML sanitizer for inline rich text: keeps basic formatting\n * tags, strips every attribute except a safe `href` on links (which gains\n * `rel=\"noopener noreferrer\"`), and unwraps disallowed elements to their\n * text content (so nothing script-capable survives). Runs at commit time in\n * the editor (browser DOM available).\n *\n * `options.phrasingOnly` narrows the allowlist for a surface that ships\n * inside a control (AGL-2557); omitted, every existing caller is unchanged.\n */\nexport function sanitizeRichText(\n html: string,\n options?: SanitizeRichTextOptions,\n): string {\n if (typeof document === 'undefined') return ''\n const allowed = options?.phrasingOnly ? PHRASING_TAGS : ALLOWED_TAGS\n const template = document.createElement('template')\n template.innerHTML = html\n\n const sanitizeNode = (node: Node): string => {\n if (node.nodeType === Node.TEXT_NODE) {\n return escapeText(node.textContent ?? '')\n }\n if (node.nodeType !== Node.ELEMENT_NODE) return ''\n const element = node as Element\n const tag = element.tagName.toLowerCase()\n const inner = Array.from(element.childNodes).map(sanitizeNode).join('')\n if (!allowed.has(tag)) return inner\n // A bare `span` is INERT and is unwrapped (AGL-2486). Every attribute is\n // stripped here, so a surviving `<span>` carries no styling, no class and\n // no binding — it renders exactly as its own text. Keeping it would be\n // dead weight with a cost: `html` is what marks a node as \"formatted\", so\n // an inert wrapper left behind by a contentEditable merge would pin the\n // Attributes Text field read-only forever over markup that does nothing.\n // Measured on test-org: deleting the line break out of\n // `About <div>us</div>` left Chrome with `About<span>us</span>`.\n if (tag === 'span') return inner\n if (tag === 'br') return '<br>'\n if (tag === 'a') {\n const href = element.getAttribute('href') ?? ''\n if (!SAFE_HREF.test(href)) return inner\n return `<a href=\"${escapeHtml(href)}\" rel=\"noopener noreferrer\">${inner}</a>`\n }\n return `<${tag}>${inner}</${tag}>`\n }\n\n return Array.from(template.content.childNodes).map(sanitizeNode).join('')\n}\n\nfunction escapeText(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\n/**\n * Tags that end the line they are on. `br` is the explicit one; the rest are\n * the block-level members of {@link ALLOWED_TAGS}, which a contentEditable\n * produces on its own — pressing Enter in the rich surface forks the DOM\n * into `div`s, and that fork IS the author's line break.\n */\nconst LINE_BREAKING_TAGS = new Set(['BR', 'DIV', 'P', 'LI', 'UL', 'OL'])\n\n/**\n * Plain-text projection of markup, for the `children` fallback prop.\n *\n * Line breaks SURVIVE (AGL-2486). A node that carries one stores\n * `html: \"Your entire web <div>presence. </div>\"` beside\n * `children: \"Your entire web presence. \"`. The canvas renders `html` and\n * shows two lines; the Attributes panel renders `children`. Projected with\n * `template.content.textContent` the panel shows one, because `textContent`\n * concatenates across every element boundary — a `<div>` fork and a `<br>`\n * both vanish into nothing.\n *\n * That is a real break silently dropped by the projection, not the soft wrap\n * a bounding box misreports. `children` is not a display detail either: it is\n * what every\n * plain renderer, the SSR fallback and the panel field all read, so the\n * break was being lost to all three.\n *\n * Only the trailing newline is trimmed — a block element closes at the end\n * of the string and that boundary separates nothing.\n */\nexport function richTextToPlain(html: string): string {\n if (typeof document === 'undefined') return html\n const template = document.createElement('template')\n template.innerHTML = html\n const parts: string[] = []\n const endsOpen = () =>\n parts.length > 0 && !parts[parts.length - 1]!.endsWith('\\n')\n\n const walk = (parent: Node): void => {\n for (const child of Array.from(parent.childNodes)) {\n if (child.nodeType === 3 /* TEXT_NODE */) {\n parts.push(child.nodeValue ?? '')\n continue\n }\n if (child.nodeType !== 1 /* ELEMENT_NODE */) continue\n const element = child as Element\n if (element.tagName === 'BR') {\n parts.push('\\n')\n continue\n }\n const breaks = LINE_BREAKING_TAGS.has(element.tagName)\n // A block that FOLLOWS content starts its own line; one that opens the\n // string, or already sits after a break, does not add an empty one.\n if (breaks && endsOpen()) parts.push('\\n')\n walk(element)\n if (breaks && endsOpen()) parts.push('\\n')\n }\n }\n\n walk(template.content)\n return parts.join('').replace(/\\n+$/, '')\n}\n"],"names":["escapeHtml","ALLOWED_TAGS","Set","PHRASING_TAGS","SAFE_HREF","sanitizeRichText","html","options","document","allowed","phrasingOnly","template","createElement","innerHTML","sanitizeNode","node","nodeType","Node","TEXT_NODE","escapeText","textContent","ELEMENT_NODE","element","tag","tagName","toLowerCase","inner","Array","from","childNodes","map","join","has","href","getAttribute","test","content","text","replace","LINE_BREAKING_TAGS","richTextToPlain","parts","endsOpen","length","endsWith","walk","parent","child","push","nodeValue","breaks"],"mappings":"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"}