@ai-matrx/rich-content 0.2.27 → 0.2.29
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 +15 -0
- package/dist/copy/CopySplitButton.js +1 -1
- package/dist/display/chat-markdown/ConfigurableMarkdownContent.js +8 -0
- package/dist/display/chat-markdown/block-registry/BlockRenderer.js +9 -1
- package/dist/markdown-core/markdown-core-presets.js +2 -1
- package/dist/markdown-core/rehype-drop-scripts.d.ts +7 -0
- package/dist/markdown-core/rehype-drop-scripts.js +18 -0
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.29
|
|
4
|
+
|
|
5
|
+
- The split Copy's popover sizes to the Alchemy palette it holds (it clipped "Download" at 240px).
|
|
6
|
+
|
|
7
|
+
### Consumer action
|
|
8
|
+
|
|
9
|
+
- None required.
|
|
10
|
+
|
|
11
|
+
## 0.2.28
|
|
12
|
+
|
|
13
|
+
- `BlockRenderer` and `ConfigurableMarkdownContent` suspend on the lazy host like the other entry points (plain text meanwhile); a test lists every gated entry point.
|
|
14
|
+
- The "message" markdown preset drops `<script>` elements and inline `on*` handlers after `rehype-raw`, so an answer never hands React a script tag.
|
|
15
|
+
|
|
16
|
+
**Consumer action:** none.
|
|
17
|
+
|
|
3
18
|
## 0.2.27
|
|
4
19
|
|
|
5
20
|
- ONE copy truth (Arman, 2026-10-04: "one click to get either the markdown version or the no-markup version, everywhere"). `copyRichContent(md, "default")` now writes EXACTLY the person's flavor (`copy.default_flavor`, default markdown) — the same bytes as that flavor's menu row; it no longer adds an HTML flavor (formatted copies live in the Alchemy palette). Every flavor is clean: `<artifact …>` wrappers are unwrapped to their body (`cleanCopyMarkdown`, `richCopyPlainText`).
|
|
@@ -90,7 +90,7 @@ function CopySplitButton({
|
|
|
90
90
|
PopoverContent,
|
|
91
91
|
{
|
|
92
92
|
align: "start",
|
|
93
|
-
className: "w-60 p-1",
|
|
93
|
+
className: "w-auto min-w-60 max-w-[min(24rem,calc(100vw-16px))] p-1",
|
|
94
94
|
onOpenAutoFocus: preserveSelection ? (event) => event.preventDefault() : void 0,
|
|
95
95
|
"data-copy-split-menu": "",
|
|
96
96
|
children: [
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
|
+
import { RichContentHostGate } from "../../host/RichContentHostGate.js";
|
|
4
5
|
import MarkdownCore from "../../markdown-core/MarkdownCore.js";
|
|
5
6
|
import { cn } from "@ai-matrx/design-system";
|
|
6
7
|
import { Checkbox } from "@ai-matrx/design-system";
|
|
@@ -123,6 +124,13 @@ function resolveConfig(partial) {
|
|
|
123
124
|
}
|
|
124
125
|
const getDirectionClasses = (direction) => direction === "rtl" ? "text-right rtl" : "text-left ltr";
|
|
125
126
|
const ConfigurableMarkdownContent = (props) => /* @__PURE__ */ jsx(
|
|
127
|
+
RichContentHostGate,
|
|
128
|
+
{
|
|
129
|
+
fallback: /* @__PURE__ */ jsx("div", { "data-rich-content-host-loading": true, style: { whiteSpace: "pre-wrap" }, children: props.content }),
|
|
130
|
+
children: /* @__PURE__ */ jsx(ConfigurableMarkdownContentKindGated, { ...props })
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
const ConfigurableMarkdownContentKindGated = (props) => /* @__PURE__ */ jsx(
|
|
126
134
|
KindTextGate,
|
|
127
135
|
{
|
|
128
136
|
component: "ConfigurableMarkdownContent",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { durableRecordId } from "@ai-matrx/kit/ids";
|
|
4
4
|
import React, { useCallback } from "react";
|
|
5
|
+
import { RichContentHostGate } from "../../../host/RichContentHostGate.js";
|
|
5
6
|
import { BlockComponents, LoadingComponents } from "./BlockComponentRegistry.js";
|
|
6
7
|
import { resolveArtifactDef } from "../../../host/app-bindings.js";
|
|
7
8
|
import {
|
|
@@ -262,7 +263,14 @@ const PendingStructuredBlock = ({
|
|
|
262
263
|
value: envelope.root.value ?? null
|
|
263
264
|
});
|
|
264
265
|
};
|
|
265
|
-
const BlockRenderer = (
|
|
266
|
+
const BlockRenderer = (props) => /* @__PURE__ */ jsx(
|
|
267
|
+
RichContentHostGate,
|
|
268
|
+
{
|
|
269
|
+
fallback: typeof props.block.content === "string" ? /* @__PURE__ */ jsx("div", { "data-rich-content-host-loading": true, style: { whiteSpace: "pre-wrap" }, children: props.block.content }) : null,
|
|
270
|
+
children: /* @__PURE__ */ jsx(BlockRendererGated, { ...props })
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
const BlockRendererGated = ({
|
|
266
274
|
requestId,
|
|
267
275
|
block: inputBlock,
|
|
268
276
|
index,
|
|
@@ -5,6 +5,7 @@ import remarkBreaks from "remark-breaks";
|
|
|
5
5
|
import remarkMath from "./math/vendor/remark-math.js";
|
|
6
6
|
import rehypeKatex from "./math/vendor/rehype-katex.js";
|
|
7
7
|
import rehypeRaw from "rehype-raw";
|
|
8
|
+
import rehypeDropScripts from "./rehype-drop-scripts.js";
|
|
8
9
|
import remarkMatrxVariable from "../display/chat-markdown/matrx-variables/remarkMatrxVariable.js";
|
|
9
10
|
import remarkMatrxCite from "../display/chat-markdown/citations/remarkMatrxCite.js";
|
|
10
11
|
import rehypeSafeRawHtml from "../display/chat-markdown/rehypeSafeRawHtml.js";
|
|
@@ -88,7 +89,7 @@ const MARKDOWN_PRESETS = {
|
|
|
88
89
|
},
|
|
89
90
|
message: {
|
|
90
91
|
remark: [GFM, MATH, remarkBreaks, ...SYNTAX_PARSE, PAGE_BREAK, ...SYNTAX_TRANSFORM],
|
|
91
|
-
rehype: [rehypeRaw, ...SYNTAX_REHYPE, KATEX],
|
|
92
|
+
rehype: [rehypeRaw, rehypeDropScripts, ...SYNTAX_REHYPE, KATEX],
|
|
92
93
|
math: true
|
|
93
94
|
}
|
|
94
95
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Root } from "hast";
|
|
2
|
+
/**
|
|
3
|
+
* After `rehype-raw` the "message" preset holds whatever HTML the text carried — `<script>` too. React
|
|
4
|
+
* refuses to render a script element ("Encountered a script tag while rendering React component") and
|
|
5
|
+
* an answer must never carry one, so scripts (and inline event handlers) are removed from the tree.
|
|
6
|
+
*/
|
|
7
|
+
export default function rehypeDropScripts(): (tree: Root) => void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function rehypeDropScripts() {
|
|
2
|
+
return (tree) => {
|
|
3
|
+
const walk = (node) => {
|
|
4
|
+
node.children = node.children.filter((child) => !(child.type === "element" && child.tagName === "script"));
|
|
5
|
+
for (const child of node.children) {
|
|
6
|
+
if (child.type !== "element") continue;
|
|
7
|
+
for (const key of Object.keys(child.properties ?? {})) {
|
|
8
|
+
if (/^on[A-Za-z]/.test(key)) delete child.properties[key];
|
|
9
|
+
}
|
|
10
|
+
walk(child);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
walk(tree);
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
rehypeDropScripts as default
|
|
18
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/rich-content",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.29",
|
|
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",
|
|
@@ -131,20 +131,20 @@
|
|
|
131
131
|
"unist-util-visit-parents": "^6.0.2",
|
|
132
132
|
"yaml": "^2.9.1",
|
|
133
133
|
"@ai-matrx/agents": "latest",
|
|
134
|
-
"@ai-matrx/canvas": "latest",
|
|
135
|
-
"@ai-matrx/content-ir": "latest",
|
|
136
134
|
"@ai-matrx/alchemy": "latest",
|
|
135
|
+
"@ai-matrx/content-ir": "latest",
|
|
137
136
|
"@ai-matrx/data": "latest",
|
|
138
|
-
"@ai-matrx/
|
|
137
|
+
"@ai-matrx/design-system": "latest",
|
|
138
|
+
"@ai-matrx/canvas": "latest",
|
|
139
139
|
"@ai-matrx/diff": "latest",
|
|
140
|
-
"@ai-matrx/
|
|
140
|
+
"@ai-matrx/content-ir-react": "latest",
|
|
141
141
|
"@ai-matrx/icons": "latest",
|
|
142
|
-
"@ai-matrx/
|
|
143
|
-
"@ai-matrx/print": "latest",
|
|
142
|
+
"@ai-matrx/kit": "latest",
|
|
144
143
|
"@ai-matrx/records": "latest",
|
|
144
|
+
"@ai-matrx/media": "latest",
|
|
145
145
|
"@ai-matrx/records-ui": "latest",
|
|
146
146
|
"@ai-matrx/tap-target": "latest",
|
|
147
|
-
"@ai-matrx/
|
|
147
|
+
"@ai-matrx/print": "latest"
|
|
148
148
|
},
|
|
149
149
|
"peerDependencies": {
|
|
150
150
|
"react": ">=19.0.0",
|