@ai-matrx/rich-content 0.2.56 → 0.2.57

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,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.57
4
+ ### parse5 and the YAML parser leave the first screen
5
+
6
+ - The raw-HTML tag allow-list lives alone in `./display/chat-markdown/raw-html-tags`
7
+ (`rehypeSafeRawHtml` re-exports it). The content splitter, table vectors and prose prep read only the
8
+ list, and importing it from `rehypeSafeRawHtml` dragged hast-util-from-html + parse5 into every chat.
9
+ - Front matter: the YAML and TOML parsers load the first time a document has front matter.
10
+ `extractFrontmatter` returns `pending: true` (empty properties) until `loadFrontmatterParsers()`
11
+ resolves; `useDocumentProperties` re-reads by itself. `splitFrontmatter` / `hasUnclosedFrontmatter`
12
+ are unchanged. Chat's first screen: about −88 kB gzip with both.
13
+
14
+ Consumer action: none (a direct caller of `extractFrontmatter` that needs the parsed properties at
15
+ once awaits `loadFrontmatterParsers()` first).
16
+
17
+ ### tap-target comes from design-system
18
+
19
+ - Imports `@ai-matrx/design-system/tap-target` (and `/tap-target/buttons`, `tap-target.css`) instead of the
20
+ retired `@ai-matrx/tap-target` (Arman, 2026-10-08: tap-target merges into design-system); the
21
+ `@ai-matrx/tap-target` dependency is dropped.
22
+
23
+ Consumer action: none.
24
+
3
25
  ## 0.2.56 — the Alchemy palette loads on first use
4
26
 
5
27
  - THE PAY-ONLY-FOR-WHAT-YOU-USE LAW: `rich-document/variants/ActionBar` lazy-loads `AlchemyDocumentMenu`
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Tags a model may emit as raw HTML that we render. Kept in sync with the
3
+ * pre-escape gate in BasicMarkdownContent (`preprocessContent`) — a tag left
4
+ * un-escaped there must be allowed here, or it renders as literal text.
5
+ * Deliberately excludes `div` (layout container — Case 4) and any scripting or
6
+ * embedding tags (`script`, `style`, `iframe`, `object`, `embed`, `form`…).
7
+ */
8
+ export declare const ALLOWED_RAW_HTML_TAGS: ReadonlySet<string>;
@@ -0,0 +1,57 @@
1
+ const ALLOWED_RAW_HTML_TAGS = /* @__PURE__ */ new Set([
2
+ // media
3
+ "img",
4
+ // links + breaks + rules
5
+ "a",
6
+ "br",
7
+ "hr",
8
+ // inline text semantics
9
+ "span",
10
+ "p",
11
+ "strong",
12
+ "b",
13
+ "em",
14
+ "i",
15
+ "u",
16
+ "s",
17
+ "del",
18
+ "ins",
19
+ "sup",
20
+ "sub",
21
+ "mark",
22
+ "kbd",
23
+ "abbr",
24
+ "small",
25
+ "code",
26
+ "pre",
27
+ // lists + quotes + headings
28
+ "ul",
29
+ "ol",
30
+ "li",
31
+ "blockquote",
32
+ "h1",
33
+ "h2",
34
+ "h3",
35
+ "h4",
36
+ "h5",
37
+ "h6",
38
+ // tables
39
+ "table",
40
+ "thead",
41
+ "tbody",
42
+ "tfoot",
43
+ "tr",
44
+ "th",
45
+ "td",
46
+ "caption",
47
+ "colgroup",
48
+ "col",
49
+ "figure",
50
+ "figcaption",
51
+ // collapsible sections (styled by the core's details/summary elements)
52
+ "details",
53
+ "summary"
54
+ ]);
55
+ export {
56
+ ALLOWED_RAW_HTML_TAGS
57
+ };
@@ -28,6 +28,8 @@
28
28
  */
29
29
  import { type Schema } from "hast-util-sanitize";
30
30
  import type { Root } from "hast";
31
+ import { ALLOWED_RAW_HTML_TAGS } from "./raw-html-tags.js";
32
+ export { ALLOWED_RAW_HTML_TAGS };
31
33
  declare module "hast" {
32
34
  interface Data {
33
35
  /**
@@ -41,14 +43,6 @@ declare module "hast" {
41
43
  matrxRawHtmlTable?: boolean;
42
44
  }
43
45
  }
44
- /**
45
- * Tags a model may emit as raw HTML that we render. Kept in sync with the
46
- * pre-escape gate in BasicMarkdownContent (`preprocessContent`) — a tag left
47
- * un-escaped there must be allowed here, or it renders as literal text.
48
- * Deliberately excludes `div` (layout container — Case 4) and any scripting or
49
- * embedding tags (`script`, `style`, `iframe`, `object`, `embed`, `form`…).
50
- */
51
- export declare const ALLOWED_RAW_HTML_TAGS: ReadonlySet<string>;
52
46
  /**
53
47
  * Sanitization schema for raw-HTML subtrees. Any tag not listed is dropped, any
54
48
  * attribute not listed is stripped (so `onerror`, `onclick`, inline `style`,
@@ -1,63 +1,10 @@
1
1
  import { visit } from "unist-util-visit";
2
2
  import { fromHtml } from "hast-util-from-html";
3
3
  import { sanitize } from "hast-util-sanitize";
4
+ import { ALLOWED_RAW_HTML_TAGS } from "./raw-html-tags.js";
4
5
  function isRawNode(node) {
5
6
  return typeof node === "object" && node !== null && node.type === "raw" && typeof node.value === "string";
6
7
  }
7
- const ALLOWED_RAW_HTML_TAGS = /* @__PURE__ */ new Set([
8
- // media
9
- "img",
10
- // links + breaks + rules
11
- "a",
12
- "br",
13
- "hr",
14
- // inline text semantics
15
- "span",
16
- "p",
17
- "strong",
18
- "b",
19
- "em",
20
- "i",
21
- "u",
22
- "s",
23
- "del",
24
- "ins",
25
- "sup",
26
- "sub",
27
- "mark",
28
- "kbd",
29
- "abbr",
30
- "small",
31
- "code",
32
- "pre",
33
- // lists + quotes + headings
34
- "ul",
35
- "ol",
36
- "li",
37
- "blockquote",
38
- "h1",
39
- "h2",
40
- "h3",
41
- "h4",
42
- "h5",
43
- "h6",
44
- // tables
45
- "table",
46
- "thead",
47
- "tbody",
48
- "tfoot",
49
- "tr",
50
- "th",
51
- "td",
52
- "caption",
53
- "colgroup",
54
- "col",
55
- "figure",
56
- "figcaption",
57
- // collapsible sections (styled by the core's details/summary elements)
58
- "details",
59
- "summary"
60
- ]);
61
8
  const RAW_HTML_SCHEMA = {
62
9
  tagNames: Array.from(ALLOWED_RAW_HTML_TAGS),
63
10
  attributes: {
@@ -32,7 +32,7 @@ import {
32
32
  } from "@ai-matrx/content-ir/surfaces";
33
33
  import { unwrapKindImageAlt } from "@ai-matrx/content-ir/surfaces";
34
34
  import { IR_ENVELOPE_KEY } from "@ai-matrx/content-ir";
35
- import { ALLOWED_RAW_HTML_TAGS } from "../../../chat-markdown/rehypeSafeRawHtml.js";
35
+ import { ALLOWED_RAW_HTML_TAGS } from "../../../chat-markdown/raw-html-tags.js";
36
36
  import { isPageBreakLine } from "@ai-matrx/print/directives";
37
37
  import { readXmlTag } from "@ai-matrx/content-ir/source";
38
38
  import { FENCE_META_KEY, splitFenceInfo } from "@ai-matrx/content-ir/source";
@@ -4,7 +4,7 @@ import { parseMarkdownTable } from "../../../blocks/table/parseMarkdownTable.js"
4
4
  import { splitContentIntoBlocksV2 } from "./content-splitter-v2.js";
5
5
  import { findTableEnd, tableStartsAt } from "./gfm-table-lines.js";
6
6
  import { isHtmlBlockTagName } from "@ai-matrx/content-ir/source";
7
- import { ALLOWED_RAW_HTML_TAGS } from "../../../chat-markdown/rehypeSafeRawHtml.js";
7
+ import { ALLOWED_RAW_HTML_TAGS } from "../../../chat-markdown/raw-html-tags.js";
8
8
  import { CELL_INPUTS, DOCUMENT_INPUTS, HTML_TAG_NAME_INPUTS, ROW_INPUTS, TABLE_END_INPUTS, TABLE_INPUTS, TABLE_OPEN_INPUTS, TABLE_START_INPUTS } from "./gfm-table-vector-inputs.js";
9
9
  function computeGfmTableVectors() {
10
10
  return {
@@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from "react";
4
4
  import {
5
5
  ChevronLeftTapButton,
6
6
  ChevronRightTapButton
7
- } from "@ai-matrx/tap-target/buttons";
7
+ } from "@ai-matrx/design-system/tap-target/buttons";
8
8
  import { cn } from "@ai-matrx/design-system";
9
9
  const EDGE_TOLERANCE_PX = 2;
10
10
  function MarkdownTableScrollArea({
@@ -1,4 +1,4 @@
1
- import { ALLOWED_RAW_HTML_TAGS } from "../../display/chat-markdown/rehypeSafeRawHtml.js";
1
+ import { ALLOWED_RAW_HTML_TAGS } from "../../display/chat-markdown/raw-html-tags.js";
2
2
  import { splitFrontmatter } from "../../markdown-core/syntax/frontmatter.js";
3
3
  import { fenceLineKinds, lineIndent, opensStrippedHtmlBlock } from "@ai-matrx/content-ir/source";
4
4
  import { fromMarkdown } from "mdast-util-from-markdown";
@@ -1,3 +1,9 @@
1
+ type FrontmatterParsers = {
2
+ yaml: (text: string) => unknown;
3
+ toml: (text: string) => unknown;
4
+ };
5
+ /** Load the YAML + TOML parsers (once). `extractFrontmatter` answers fully after this resolves. */
6
+ export declare function loadFrontmatterParsers(): Promise<FrontmatterParsers>;
1
7
  export type FrontmatterFormat = "yaml" | "toml";
2
8
  export interface FrontmatterSplit {
3
9
  format: FrontmatterFormat;
@@ -21,6 +27,12 @@ export interface DocumentPropertiesResult {
21
27
  raw: string | null;
22
28
  /** A sentence saying why the front matter could not be read, or null. */
23
29
  error: string | null;
30
+ /**
31
+ * True while the parsers are still loading (the first document with front matter): `properties`
32
+ * is `{}` until `loadFrontmatterParsers()` resolves; ask again then. `useDocumentProperties` does.
33
+ */
34
+ pending?: boolean;
24
35
  }
25
36
  /** Read a document's front matter as properties. Never throws. */
26
37
  export declare function extractFrontmatter(source: string): DocumentPropertiesResult;
38
+ export {};
@@ -1,5 +1,11 @@
1
- import { parse as parseYaml } from "yaml";
2
- import { parse as parseToml } from "smol-toml";
1
+ let parsers = null;
2
+ let parsersLoading = null;
3
+ function loadFrontmatterParsers() {
4
+ return parsersLoading ??= Promise.all([import("yaml"), import("smol-toml")]).then(([yaml, toml]) => {
5
+ parsers = { yaml: yaml.parse, toml: toml.parse };
6
+ return parsers;
7
+ });
8
+ }
3
9
  const OPEN = { "---": "yaml", "+++": "toml" };
4
10
  function splitFrontmatter(source) {
5
11
  if (!source) return null;
@@ -36,8 +42,12 @@ const EMPTY = { properties: {}, format: null, raw: null, error: null };
36
42
  function extractFrontmatter(source) {
37
43
  const split = splitFrontmatter(source);
38
44
  if (!split) return EMPTY;
45
+ if (!parsers) {
46
+ void loadFrontmatterParsers();
47
+ return { properties: {}, format: split.format, raw: split.raw, error: null, pending: true };
48
+ }
39
49
  try {
40
- const parsed = split.format === "yaml" ? parseYaml(split.inner) : parseToml(split.inner);
50
+ const parsed = split.format === "yaml" ? parsers.yaml(split.inner) : parsers.toml(split.inner);
41
51
  if (parsed === null || parsed === void 0) {
42
52
  return { properties: {}, format: split.format, raw: split.raw, error: null };
43
53
  }
@@ -68,5 +78,6 @@ function extractFrontmatter(source) {
68
78
  export {
69
79
  extractFrontmatter,
70
80
  hasUnclosedFrontmatter,
81
+ loadFrontmatterParsers,
71
82
  splitFrontmatter
72
83
  };
@@ -1,3 +1,3 @@
1
1
  import { type DocumentPropertiesResult } from "./frontmatter.js";
2
- /** The React Compiler memoizes this per `source`. */
2
+ /** The React Compiler memoizes this per `source` (and parser readiness). */
3
3
  export declare function useDocumentProperties(source: string): DocumentPropertiesResult;
@@ -1,8 +1,23 @@
1
1
  "use client";
2
- import { extractFrontmatter } from "./frontmatter.js";
3
- function useDocumentProperties(source) {
2
+ import { useEffect, useState } from "react";
3
+ import { extractFrontmatter, loadFrontmatterParsers } from "./frontmatter.js";
4
+ function readProperties(source, _parsersReady) {
4
5
  return extractFrontmatter(source);
5
6
  }
7
+ function useDocumentProperties(source) {
8
+ const [parsersReady, setParsersReady] = useState(false);
9
+ const result = readProperties(source, parsersReady);
10
+ const pending = result.pending === true;
11
+ useEffect(() => {
12
+ if (!pending) return;
13
+ let live = true;
14
+ void loadFrontmatterParsers().then(() => live && setParsersReady(true));
15
+ return () => {
16
+ live = false;
17
+ };
18
+ }, [pending]);
19
+ return result;
20
+ }
6
21
  export {
7
22
  useDocumentProperties
8
23
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/rich-content",
3
- "version": "0.2.56",
3
+ "version": "0.2.57",
4
4
  "description": "The AI Matrx formatted-text engine: the one markdown renderer, streaming markdown, the block splitter and renderer, the generic blocks, <RichContent> levels, the rich-document action registry and menus, copy, and the selection toolbar — as portable React.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -130,21 +130,20 @@
130
130
  "unist-util-visit": "^5.1.0",
131
131
  "unist-util-visit-parents": "^6.0.2",
132
132
  "yaml": "^2.9.1",
133
- "@ai-matrx/agents": "latest",
134
133
  "@ai-matrx/alchemy": "latest",
135
- "@ai-matrx/canvas": "latest",
134
+ "@ai-matrx/agents": "latest",
136
135
  "@ai-matrx/content-ir": "latest",
137
136
  "@ai-matrx/content-ir-react": "latest",
138
137
  "@ai-matrx/data": "latest",
139
- "@ai-matrx/design-system": "latest",
138
+ "@ai-matrx/canvas": "latest",
140
139
  "@ai-matrx/diff": "latest",
141
140
  "@ai-matrx/icons": "latest",
142
- "@ai-matrx/kit": "latest",
143
- "@ai-matrx/media": "latest",
144
141
  "@ai-matrx/print": "latest",
145
142
  "@ai-matrx/records": "latest",
146
143
  "@ai-matrx/records-ui": "latest",
147
- "@ai-matrx/tap-target": "latest"
144
+ "@ai-matrx/design-system": "latest",
145
+ "@ai-matrx/kit": "latest",
146
+ "@ai-matrx/media": "latest"
148
147
  },
149
148
  "peerDependencies": {
150
149
  "react": ">=19.0.0",