@ai-matrx/rich-content 0.2.7 → 0.2.9
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 +18 -0
- package/dist/code-block/CodeBlock.js +3 -8
- package/dist/display/blocks/chart/ChartBlock.js +1 -1
- package/dist/display/blocks/common/BlockHeaderWrapper.js +2 -9
- package/dist/display/blocks/csv/CsvBlock.js +1 -1
- package/dist/display/blocks/diagram/InteractiveDiagramBlock.js +29 -72
- package/dist/display/blocks/diff/DiffBlock.js +1 -1
- package/dist/display/blocks/generic/StructuredValueTabs.js +1 -1
- package/dist/display/blocks/images/ImageBlock.js +9 -27
- package/dist/display/blocks/json/JsonBlock.js +1 -1
- package/dist/display/blocks/json/json-tabular-utils.js +2 -9
- package/dist/display/blocks/links/LinkComponent.js +3 -7
- package/dist/display/blocks/markdown-preview/MarkdownPreviewBlock.js +1 -1
- package/dist/display/blocks/svg/SvgBlock.js +3 -8
- package/dist/display/blocks/table/StreamingTableRenderer.js +9 -29
- package/dist/display/blocks/toml/TomlBlock.js +1 -1
- package/dist/display/blocks/tree/TreeBlock.js +1 -1
- package/dist/display/blocks/xml/XmlBlock.js +1 -1
- package/dist/display/blocks/yaml/YamlBlock.js +1 -1
- package/dist/display/chat-markdown/InlineCodeSnippet.js +1 -1
- package/dist/display/chat-markdown/internal-handlers/PlainTextFallback.js +2 -2
- package/dist/display/tables/MarkdownTable.js +9 -29
- package/dist/display/tables/TableWithSeparatedControls.js +9 -29
- package/dist/mermaid/export.d.ts +1 -0
- package/dist/mermaid/export.js +5 -8
- package/dist/mermaid/print-render.js +11 -11
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.9
|
|
4
|
+
|
|
5
|
+
AP-2 (2026-10-07) — the kit and Alchemy doors are the only doors in this package (aidream guard `apps/shared/scripts/check-shared-doors.mjs`). New dependency `@ai-matrx/alchemy`; `html2canvas` dependency removed. Every table/code/SVG/JSON/mermaid/diagram download saves through `@ai-matrx/kit/download` (`downloadFile` / `downloadUrl`) — the tables' "Download Again" toast action now really downloads again (it clicked a revoked URL before). Link/URL/mermaid copies go through `@ai-matrx/kit/clipboard` `copyText` (copied state from its boolean; `copyMermaidSource`/`copyMermaidSvg` throw when refused, as before); ImageBlock's image copy uses `useClipboard().copyImage`. The diagram PNG export captures through `@ai-matrx/alchemy/operate/capture` `captureElement` with `safeColors` (the local getComputedStyle patch is gone). No consumer action.
|
|
6
|
+
|
|
7
|
+
## 0.2.8
|
|
8
|
+
### Unreleased
|
|
9
|
+
|
|
10
|
+
### Shared fence parsing
|
|
11
|
+
|
|
12
|
+
- The plain-text fallback and Mermaid print reader use Content IR's canonical
|
|
13
|
+
fence rules instead of private regex copies.
|
|
14
|
+
- Mermaid print preserves unfinished bodies and dedenting, ignores malformed
|
|
15
|
+
fence info, and does not render diagrams nested inside another code block.
|
|
16
|
+
|
|
17
|
+
### Consumer action
|
|
18
|
+
|
|
19
|
+
- None required. Public imports and host setup are unchanged.
|
|
20
|
+
|
|
3
21
|
## 0.2.7
|
|
4
22
|
|
|
5
23
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { downloadFile } from "@ai-matrx/kit/download";
|
|
3
4
|
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
5
|
import { useRef, useState, useEffect } from "react";
|
|
5
6
|
import { KindDataGate } from "../host/app-bindings.js";
|
|
@@ -178,21 +179,15 @@ const CodeBlockBody = ({
|
|
|
178
179
|
});
|
|
179
180
|
textToCopy = paddedLines.join("\n");
|
|
180
181
|
}
|
|
181
|
-
await copyText(textToCopy);
|
|
182
|
+
if (!await copyText(textToCopy)) return;
|
|
182
183
|
setIsCopied(true);
|
|
183
184
|
setTimeout(() => setIsCopied(false), 2e3);
|
|
184
185
|
};
|
|
185
186
|
const handleDownload = (e) => {
|
|
186
187
|
e.stopPropagation();
|
|
187
|
-
const blob = new Blob([code], { type: "text/plain" });
|
|
188
|
-
const url = window.URL.createObjectURL(blob);
|
|
189
|
-
const a = document.createElement("a");
|
|
190
|
-
a.href = url;
|
|
191
188
|
const titleFile = fence.title && /^[\w./-]+\.\w+$/.test(fence.title) ? fence.title.split("/").pop() : void 0;
|
|
192
189
|
const ext = codeLanguageToExtension(rawLanguage || "txt");
|
|
193
|
-
|
|
194
|
-
a.click();
|
|
195
|
-
window.URL.revokeObjectURL(url);
|
|
190
|
+
downloadFile(titleFile ?? `code${ext}`, code, "text/plain");
|
|
196
191
|
};
|
|
197
192
|
const toggleLineNumbers = (e) => {
|
|
198
193
|
e.stopPropagation();
|
|
@@ -38,7 +38,7 @@ const ChartBlock = ({ content = "", isStreamActive = false, className }) => {
|
|
|
38
38
|
const paneLayout = chartPaneLayout(presentation, spec?.type ?? "");
|
|
39
39
|
const handleCopy = async () => {
|
|
40
40
|
try {
|
|
41
|
-
await copyText(source);
|
|
41
|
+
if (!await copyText(source)) return;
|
|
42
42
|
setCopied(true);
|
|
43
43
|
setTimeout(() => setCopied(false), 1500);
|
|
44
44
|
} catch {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { downloadFile } from "@ai-matrx/kit/download";
|
|
3
4
|
import { useState, useRef, useEffect } from "react";
|
|
4
5
|
import {
|
|
5
6
|
Maximize2,
|
|
@@ -93,15 +94,7 @@ const BlockHeaderWrapper = ({
|
|
|
93
94
|
};
|
|
94
95
|
const handleExport = () => {
|
|
95
96
|
if (exportData === void 0) return;
|
|
96
|
-
|
|
97
|
-
type: "application/json"
|
|
98
|
-
});
|
|
99
|
-
const url = URL.createObjectURL(blob);
|
|
100
|
-
const a = document.createElement("a");
|
|
101
|
-
a.href = url;
|
|
102
|
-
a.download = `${exportFilename}.json`;
|
|
103
|
-
a.click();
|
|
104
|
-
URL.revokeObjectURL(url);
|
|
97
|
+
downloadFile(`${exportFilename}.json`, JSON.stringify(exportData, null, 2), "application/json");
|
|
105
98
|
};
|
|
106
99
|
const handleImportClick = () => fileInputRef.current?.click();
|
|
107
100
|
const handleFileChange = (e) => {
|
|
@@ -86,7 +86,7 @@ const CsvBlock = ({
|
|
|
86
86
|
const headers = data[0] || [];
|
|
87
87
|
const bodyRows = data.slice(1);
|
|
88
88
|
const handleCopy = async () => {
|
|
89
|
-
await copyText(content);
|
|
89
|
+
if (!await copyText(content)) return;
|
|
90
90
|
setCopied(true);
|
|
91
91
|
setTimeout(() => setCopied(false), 2e3);
|
|
92
92
|
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { captureElement } from "@ai-matrx/alchemy/operate/capture";
|
|
4
|
+
import { downloadFile, downloadUrl } from "@ai-matrx/kit/download";
|
|
3
5
|
import { Button, Chip } from "@ai-matrx/design-system/controls";
|
|
4
6
|
import {
|
|
5
7
|
useState,
|
|
@@ -121,7 +123,6 @@ import {
|
|
|
121
123
|
DropdownMenuSeparator,
|
|
122
124
|
DropdownMenuTrigger
|
|
123
125
|
} from "@ai-matrx/design-system";
|
|
124
|
-
const UNSAFE_COLOR_RE = /\b(oklch|oklab|lab|lch|color)\s*\(/i;
|
|
125
126
|
function backgroundVariantFromDiagram(diagram) {
|
|
126
127
|
if (diagram.renderHints?.background === "lines")
|
|
127
128
|
return BackgroundVariant.Lines;
|
|
@@ -129,42 +130,6 @@ function backgroundVariantFromDiagram(diagram) {
|
|
|
129
130
|
return BackgroundVariant.Cross;
|
|
130
131
|
return BackgroundVariant.Dots;
|
|
131
132
|
}
|
|
132
|
-
function safenColor(value, prop, isDark) {
|
|
133
|
-
if (!UNSAFE_COLOR_RE.test(value)) return value;
|
|
134
|
-
const lp = prop.toLowerCase();
|
|
135
|
-
if (lp.includes("background")) return isDark ? "#1f2937" : "#ffffff";
|
|
136
|
-
if (lp === "color") return isDark ? "#f3f4f6" : "#111827";
|
|
137
|
-
if (lp.includes("border") || lp.includes("outline"))
|
|
138
|
-
return isDark ? "#4b5563" : "#d1d5db";
|
|
139
|
-
if (lp === "fill" || lp === "stroke") return isDark ? "#9ca3af" : "#374151";
|
|
140
|
-
return isDark ? "#6b7280" : "#6b7280";
|
|
141
|
-
}
|
|
142
|
-
function patchComputedStyleForCapture(isDark) {
|
|
143
|
-
const original = window.getComputedStyle.bind(window);
|
|
144
|
-
window.getComputedStyle = function(elt, pseudo) {
|
|
145
|
-
const computed = original(elt, pseudo);
|
|
146
|
-
return new Proxy(computed, {
|
|
147
|
-
get(target, prop) {
|
|
148
|
-
const value = target[prop];
|
|
149
|
-
if (typeof prop === "string" && typeof value === "string" && value && UNSAFE_COLOR_RE.test(value)) {
|
|
150
|
-
return safenColor(value, prop, isDark);
|
|
151
|
-
}
|
|
152
|
-
if (prop === "getPropertyValue") {
|
|
153
|
-
return (p) => {
|
|
154
|
-
const raw = target.getPropertyValue(p);
|
|
155
|
-
return raw && UNSAFE_COLOR_RE.test(raw) ? safenColor(raw, p, isDark) : raw;
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
if (typeof value === "function")
|
|
159
|
-
return value.bind(target);
|
|
160
|
-
return value;
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
};
|
|
164
|
-
return () => {
|
|
165
|
-
window.getComputedStyle = original;
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
133
|
const PedigreeNode = ({
|
|
169
134
|
data,
|
|
170
135
|
selected
|
|
@@ -1081,22 +1046,20 @@ const DiagramFlow = ({
|
|
|
1081
1046
|
}
|
|
1082
1047
|
exportContainer.appendChild(cloned);
|
|
1083
1048
|
document.body.appendChild(exportContainer);
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
ignoreElements: (el) => el.classList.contains("react-flow__controls") || el.classList.contains("react-flow__minimap") || el.classList.contains("react-flow__panel")
|
|
1094
|
-
}).finally(restore);
|
|
1049
|
+
captureElement(exportContainer, {
|
|
1050
|
+
backgroundColor: bgColor,
|
|
1051
|
+
scale: 2,
|
|
1052
|
+
useCORS: true,
|
|
1053
|
+
allowTaint: true,
|
|
1054
|
+
width: imageWidth,
|
|
1055
|
+
height: imageHeight,
|
|
1056
|
+
safeColors: { dark: isDark },
|
|
1057
|
+
ignoreElements: (el) => el.classList.contains("react-flow__controls") || el.classList.contains("react-flow__minimap") || el.classList.contains("react-flow__panel")
|
|
1095
1058
|
}).then((canvas) => {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1059
|
+
downloadUrl(
|
|
1060
|
+
canvas.toDataURL("image/png"),
|
|
1061
|
+
`${diagram.title.replace(/[^a-z0-9]/gi, "_").toLowerCase()}_diagram${isDark ? "_dark" : "_light"}.png`
|
|
1062
|
+
);
|
|
1100
1063
|
}).catch((err) => console.warn("Diagram export failed:", err)).finally(() => document.body.removeChild(exportContainer));
|
|
1101
1064
|
}, [getNodes, diagram.title, onExportImage]);
|
|
1102
1065
|
const measuredWidth = useStore((s) => s.width);
|
|
@@ -2133,15 +2096,11 @@ const InteractiveDiagramBlock = ({
|
|
|
2133
2096
|
triggerPrint
|
|
2134
2097
|
} = usePrintOptions(diagramPrinter, diagram);
|
|
2135
2098
|
const exportDiagramJSON = useCallback(() => {
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
a.href = url;
|
|
2142
|
-
a.download = `${diagram.title.replace(/[^a-z0-9]/gi, "_").toLowerCase()}.json`;
|
|
2143
|
-
a.click();
|
|
2144
|
-
URL.revokeObjectURL(url);
|
|
2099
|
+
downloadFile(
|
|
2100
|
+
`${diagram.title.replace(/[^a-z0-9]/gi, "_").toLowerCase()}.json`,
|
|
2101
|
+
JSON.stringify({ diagram }, null, 2),
|
|
2102
|
+
"application/json"
|
|
2103
|
+
);
|
|
2145
2104
|
}, [diagram]);
|
|
2146
2105
|
const toggleEditing = () => {
|
|
2147
2106
|
const next = !editing;
|
|
@@ -2154,18 +2113,16 @@ const InteractiveDiagramBlock = ({
|
|
|
2154
2113
|
);
|
|
2155
2114
|
if (!viewport) return;
|
|
2156
2115
|
const isDark = document.documentElement.classList.contains("dark") || window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
useCORS: true
|
|
2163
|
-
}).finally(restore);
|
|
2116
|
+
captureElement(viewport, {
|
|
2117
|
+
backgroundColor: isDark ? "#111827" : "#ffffff",
|
|
2118
|
+
scale: 2,
|
|
2119
|
+
useCORS: true,
|
|
2120
|
+
safeColors: { dark: isDark }
|
|
2164
2121
|
}).then((canvas) => {
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2122
|
+
downloadUrl(
|
|
2123
|
+
canvas.toDataURL("image/png"),
|
|
2124
|
+
`${diagram.title.replace(/[^a-z0-9]/gi, "_").toLowerCase()}_diagram.png`
|
|
2125
|
+
);
|
|
2169
2126
|
}).catch((err) => console.warn("Image export failed:", err));
|
|
2170
2127
|
}, [diagram.title]);
|
|
2171
2128
|
const renderLegend = () => {
|
|
@@ -85,7 +85,7 @@ const DiffSpecBlock = ({ content = "", isStreamActive = false, className }) => {
|
|
|
85
85
|
});
|
|
86
86
|
const handleCopy = async () => {
|
|
87
87
|
try {
|
|
88
|
-
await copyText(content.trim());
|
|
88
|
+
if (!await copyText(content.trim())) return;
|
|
89
89
|
setCopied(true);
|
|
90
90
|
setTimeout(() => setCopied(false), 1500);
|
|
91
91
|
} catch {
|
|
@@ -47,7 +47,7 @@ function StructuredValueTabs({
|
|
|
47
47
|
const json = value !== void 0 && value !== null ? JSON.stringify(value, null, 2) : raw ?? "";
|
|
48
48
|
const copy = async () => {
|
|
49
49
|
try {
|
|
50
|
-
await copyText(json);
|
|
50
|
+
if (!await copyText(json)) return;
|
|
51
51
|
setCopied(true);
|
|
52
52
|
setTimeout(() => setCopied(false), 1500);
|
|
53
53
|
} catch {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { copyText, useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
+
import { downloadFile } from "@ai-matrx/kit/download";
|
|
3
5
|
import { useState, useRef, useEffect } from "react";
|
|
4
6
|
import {
|
|
5
7
|
DownloadIcon,
|
|
@@ -46,6 +48,7 @@ const ImageBlockImpl = ({ src: srcProp, alt = "Image" }) => {
|
|
|
46
48
|
const editableFileId = ourFile?.fileId ?? null;
|
|
47
49
|
const [feedback, setFeedback] = useState("none");
|
|
48
50
|
const [showCopySuccess, setShowCopySuccess] = useState(false);
|
|
51
|
+
const { copyImage } = useClipboard();
|
|
49
52
|
const [showShareModal, setShowShareModal] = useState(false);
|
|
50
53
|
const [showExpandedView, setShowExpandedView] = useState(false);
|
|
51
54
|
const [zoomLevel, setZoomLevel] = useState(1);
|
|
@@ -55,45 +58,24 @@ const ImageBlockImpl = ({ src: srcProp, alt = "Image" }) => {
|
|
|
55
58
|
try {
|
|
56
59
|
const response = await fetch(src);
|
|
57
60
|
const blob = await response.blob();
|
|
58
|
-
|
|
59
|
-
const link = document.createElement("a");
|
|
60
|
-
link.href = url;
|
|
61
|
-
link.download = src.split("/").pop() || "image";
|
|
62
|
-
document.body.appendChild(link);
|
|
63
|
-
link.click();
|
|
64
|
-
document.body.removeChild(link);
|
|
65
|
-
window.URL.revokeObjectURL(url);
|
|
61
|
+
downloadFile(src.split("/").pop() || "image", blob, blob.type);
|
|
66
62
|
} catch (err) {
|
|
67
63
|
console.error("Failed to download image:", err);
|
|
68
64
|
}
|
|
69
65
|
};
|
|
70
66
|
const handleCopyUrl = (e) => {
|
|
71
67
|
e?.preventDefault();
|
|
72
|
-
|
|
68
|
+
void copyText(src).then((landed) => {
|
|
69
|
+
if (!landed) return;
|
|
73
70
|
setShowCopySuccess(true);
|
|
74
71
|
setTimeout(() => setShowCopySuccess(false), 2e3);
|
|
75
|
-
}).catch((err) => {
|
|
76
|
-
console.error("Failed to copy URL:", err);
|
|
77
72
|
});
|
|
78
73
|
};
|
|
79
74
|
const handleCopyImage = async (e) => {
|
|
80
75
|
e?.preventDefault();
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (navigator.clipboard && navigator.clipboard.write) {
|
|
85
|
-
await navigator.clipboard.write([
|
|
86
|
-
new ClipboardItem({
|
|
87
|
-
[blob.type]: blob
|
|
88
|
-
})
|
|
89
|
-
]);
|
|
90
|
-
setShowCopySuccess(true);
|
|
91
|
-
setTimeout(() => setShowCopySuccess(false), 2e3);
|
|
92
|
-
} else {
|
|
93
|
-
console.error("Clipboard API not supported");
|
|
94
|
-
}
|
|
95
|
-
} catch (err) {
|
|
96
|
-
console.error("Failed to copy image to clipboard:", err);
|
|
76
|
+
if (await copyImage(src)) {
|
|
77
|
+
setShowCopySuccess(true);
|
|
78
|
+
setTimeout(() => setShowCopySuccess(false), 2e3);
|
|
97
79
|
}
|
|
98
80
|
};
|
|
99
81
|
const handleShare = async (e) => {
|
|
@@ -489,7 +489,7 @@ const JsonViewHeader = ({
|
|
|
489
489
|
const [copied, setCopied] = useState(false);
|
|
490
490
|
const handleCopy = async () => {
|
|
491
491
|
try {
|
|
492
|
-
await copyText(content);
|
|
492
|
+
if (!await copyText(content)) return;
|
|
493
493
|
setCopied(true);
|
|
494
494
|
toast.success("Copied JSON to clipboard");
|
|
495
495
|
setTimeout(() => setCopied(false), 1500);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { downloadFile } from "@ai-matrx/kit/download";
|
|
1
2
|
import { announceBareHost, hostCapability } from "../../../host/registry.js";
|
|
2
3
|
function parseJsonSafe(text) {
|
|
3
4
|
if (!text || !text.trim()) {
|
|
@@ -89,15 +90,7 @@ function rowsToNdjson(rows) {
|
|
|
89
90
|
return rows.map((r) => JSON.stringify(r)).join("\n");
|
|
90
91
|
}
|
|
91
92
|
function downloadText(filename, content, mime = "text/plain") {
|
|
92
|
-
|
|
93
|
-
const url = window.URL.createObjectURL(blob);
|
|
94
|
-
const a = document.createElement("a");
|
|
95
|
-
a.href = url;
|
|
96
|
-
a.download = filename;
|
|
97
|
-
document.body.appendChild(a);
|
|
98
|
-
a.click();
|
|
99
|
-
document.body.removeChild(a);
|
|
100
|
-
window.URL.revokeObjectURL(url);
|
|
93
|
+
downloadFile(filename, content, `${mime};charset=utf-8`);
|
|
101
94
|
}
|
|
102
95
|
async function rowsToXlsx(rows, columns, filename) {
|
|
103
96
|
const impl = hostCapability("app")?.rowsToXlsx;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { copyText } from "@ai-matrx/kit/clipboard";
|
|
3
4
|
import React, { useState, useRef, useEffect, useCallback, useId, memo } from "react";
|
|
4
5
|
import { createPortal } from "react-dom";
|
|
5
6
|
import { cn } from "@ai-matrx/design-system";
|
|
@@ -164,15 +165,10 @@ const ValidLinkComponentCore = ({
|
|
|
164
165
|
clearOpenTimer();
|
|
165
166
|
}, [clearOpenTimer]);
|
|
166
167
|
const handleCopyLink = useCallback(() => {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
navigator.clipboard.writeText(finalHref).then(() => {
|
|
168
|
+
void copyText(finalHref).then((landed) => {
|
|
169
|
+
if (!landed) return;
|
|
172
170
|
setCopied(true);
|
|
173
171
|
setTimeout(() => setCopied(false), 2e3);
|
|
174
|
-
}).catch((err) => {
|
|
175
|
-
console.error("Failed to copy link:", err);
|
|
176
172
|
});
|
|
177
173
|
}, [finalHref]);
|
|
178
174
|
const handleOpenInNewTab = useCallback(
|
|
@@ -24,7 +24,7 @@ const MarkdownPreviewBlock = ({
|
|
|
24
24
|
const [mode, setMode] = useState("preview");
|
|
25
25
|
const [copied, setCopied] = useState(false);
|
|
26
26
|
const handleCopy = async () => {
|
|
27
|
-
await copyText(content);
|
|
27
|
+
if (!await copyText(content)) return;
|
|
28
28
|
setCopied(true);
|
|
29
29
|
setTimeout(() => setCopied(false), 2e3);
|
|
30
30
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { downloadFile } from "@ai-matrx/kit/download";
|
|
3
4
|
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
5
|
import { useEffect, useMemo, useState } from "react";
|
|
5
6
|
import { createPortal } from "react-dom";
|
|
@@ -40,7 +41,7 @@ const SvgBlock = ({ content = "", isStreamActive = false, className }) => {
|
|
|
40
41
|
const [fullscreen, setFullscreen] = useState(false);
|
|
41
42
|
const handleCopy = async () => {
|
|
42
43
|
try {
|
|
43
|
-
await copyText(svg);
|
|
44
|
+
if (!await copyText(svg)) return;
|
|
44
45
|
setCopied(true);
|
|
45
46
|
setTimeout(() => setCopied(false), 1500);
|
|
46
47
|
} catch {
|
|
@@ -49,13 +50,7 @@ const SvgBlock = ({ content = "", isStreamActive = false, className }) => {
|
|
|
49
50
|
};
|
|
50
51
|
const handleDownload = () => {
|
|
51
52
|
try {
|
|
52
|
-
|
|
53
|
-
const url = URL.createObjectURL(blob);
|
|
54
|
-
const a = document.createElement("a");
|
|
55
|
-
a.href = url;
|
|
56
|
-
a.download = `${(title ?? "illustration").replace(/[^\w-]+/g, "-").toLowerCase()}.svg`;
|
|
57
|
-
a.click();
|
|
58
|
-
URL.revokeObjectURL(url);
|
|
53
|
+
downloadFile(`${(title ?? "illustration").replace(/[^\w-]+/g, "-").toLowerCase()}.svg`, svg, "image/svg+xml");
|
|
59
54
|
} catch {
|
|
60
55
|
toast.error("Couldn't download the SVG");
|
|
61
56
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { downloadFile } from "@ai-matrx/kit/download";
|
|
3
4
|
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
5
|
import { toast as copyToast } from "../../../host/toast.js";
|
|
5
6
|
import { rewriteTableSource } from "../../../utils/table-source.js";
|
|
@@ -268,9 +269,9 @@ const StreamingTableRendererCore = ({
|
|
|
268
269
|
};
|
|
269
270
|
const copyJsonToClipboard = async () => {
|
|
270
271
|
try {
|
|
271
|
-
await copyText(
|
|
272
|
+
if (!await copyText(
|
|
272
273
|
JSON.stringify(tableData.normalizedData, null, 2)
|
|
273
|
-
);
|
|
274
|
+
)) return;
|
|
274
275
|
toast.success("JSON copied to clipboard");
|
|
275
276
|
} catch (err) {
|
|
276
277
|
toast.error(err instanceof Error ? err.message : "Failed to copy JSON");
|
|
@@ -300,17 +301,12 @@ const StreamingTableRendererCore = ({
|
|
|
300
301
|
}).join(",")
|
|
301
302
|
)
|
|
302
303
|
].join("\n");
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
const link = document.createElement("a");
|
|
306
|
-
link.href = url;
|
|
307
|
-
link.download = "table_data.csv";
|
|
308
|
-
link.click();
|
|
309
|
-
URL.revokeObjectURL(url);
|
|
304
|
+
const saveCsv = () => downloadFile("table_data.csv", csvContent, "text/csv;charset=utf-8");
|
|
305
|
+
saveCsv();
|
|
310
306
|
toast.success("Table exported to CSV", {
|
|
311
307
|
action: {
|
|
312
308
|
label: "Download Again",
|
|
313
|
-
onClick:
|
|
309
|
+
onClick: saveCsv,
|
|
314
310
|
className: "font-medium"
|
|
315
311
|
}
|
|
316
312
|
});
|
|
@@ -324,28 +320,12 @@ const StreamingTableRendererCore = ({
|
|
|
324
320
|
try {
|
|
325
321
|
const markdownContent = content || "";
|
|
326
322
|
const fileName = headers && headers[0] ? `${headers[0].replace(/[^a-z0-9]/gi, "_").toLowerCase()}.md` : "table_data.md";
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
});
|
|
330
|
-
const url = URL.createObjectURL(blob);
|
|
331
|
-
const link = document.createElement("a");
|
|
332
|
-
link.href = url;
|
|
333
|
-
link.download = fileName;
|
|
334
|
-
document.body.appendChild(link);
|
|
335
|
-
link.click();
|
|
336
|
-
document.body.removeChild(link);
|
|
337
|
-
URL.revokeObjectURL(url);
|
|
323
|
+
const saveMarkdown = () => downloadFile(fileName, markdownContent, "text/markdown;charset=utf-8");
|
|
324
|
+
saveMarkdown();
|
|
338
325
|
toast.success("Markdown file downloaded", {
|
|
339
326
|
action: {
|
|
340
327
|
label: "Download Again",
|
|
341
|
-
onClick:
|
|
342
|
-
const newLink = document.createElement("a");
|
|
343
|
-
newLink.href = url;
|
|
344
|
-
newLink.download = fileName;
|
|
345
|
-
document.body.appendChild(newLink);
|
|
346
|
-
newLink.click();
|
|
347
|
-
document.body.removeChild(newLink);
|
|
348
|
-
},
|
|
328
|
+
onClick: saveMarkdown,
|
|
349
329
|
className: "font-medium"
|
|
350
330
|
}
|
|
351
331
|
});
|
|
@@ -75,7 +75,7 @@ const TomlBlock = ({ content, className }) => {
|
|
|
75
75
|
);
|
|
76
76
|
const lines = parseTomlLines(content);
|
|
77
77
|
const handleCopy = async () => {
|
|
78
|
-
await copyText(content);
|
|
78
|
+
if (!await copyText(content)) return;
|
|
79
79
|
setCopied(true);
|
|
80
80
|
setTimeout(() => setCopied(false), 2e3);
|
|
81
81
|
};
|
|
@@ -94,7 +94,7 @@ const TreeBlock = ({ content, className }) => {
|
|
|
94
94
|
const lines = parseTreeLines(content);
|
|
95
95
|
const wrap = treeWrapsLines(useCanvasPresentation());
|
|
96
96
|
const handleCopy = async () => {
|
|
97
|
-
await copyText(content);
|
|
97
|
+
if (!await copyText(content)) return;
|
|
98
98
|
setCopied(true);
|
|
99
99
|
setTimeout(() => setCopied(false), 2e3);
|
|
100
100
|
};
|
|
@@ -38,7 +38,7 @@ const XmlBlock = ({
|
|
|
38
38
|
const tokens = tokenizeXml(content);
|
|
39
39
|
const isStreaming = useMarkdownStreaming();
|
|
40
40
|
const handleCopy = async () => {
|
|
41
|
-
await copyText(content);
|
|
41
|
+
if (!await copyText(content)) return;
|
|
42
42
|
setCopied(true);
|
|
43
43
|
setTimeout(() => setCopied(false), 2e3);
|
|
44
44
|
};
|
|
@@ -81,7 +81,7 @@ const YamlBlock = ({ content, className }) => {
|
|
|
81
81
|
const [collapsed, setCollapsed] = useState(/* @__PURE__ */ new Set());
|
|
82
82
|
const lines = parseYamlLines(content);
|
|
83
83
|
const handleCopy = async () => {
|
|
84
|
-
await copyText(content);
|
|
84
|
+
if (!await copyText(content)) return;
|
|
85
85
|
setCopied(true);
|
|
86
86
|
setTimeout(() => setCopied(false), 2e3);
|
|
87
87
|
};
|
|
@@ -91,7 +91,7 @@ const InlineCodeSnippet = ({
|
|
|
91
91
|
const langColor = language ? LANGUAGE_COLORS[language] : void 0;
|
|
92
92
|
const handleCopy = async () => {
|
|
93
93
|
try {
|
|
94
|
-
await copyText(code);
|
|
94
|
+
if (!await copyText(code)) return;
|
|
95
95
|
setCopied(true);
|
|
96
96
|
setTimeout(() => setCopied(false), 2e3);
|
|
97
97
|
} catch {
|
|
@@ -3,6 +3,7 @@ import { Suspense, lazy } from "react";
|
|
|
3
3
|
import { cn } from "@ai-matrx/design-system";
|
|
4
4
|
import { MarkdownErrorBoundary } from "./MarkdownErrorBoundary.js";
|
|
5
5
|
import { findEmbeddedKindJsonRegions } from "@ai-matrx/content-ir/surfaces";
|
|
6
|
+
import { fenceOpenerOf } from "@ai-matrx/content-ir/source";
|
|
6
7
|
import {
|
|
7
8
|
firstKindSlug,
|
|
8
9
|
hasKindKey,
|
|
@@ -15,12 +16,11 @@ import { humanizeKind } from "@ai-matrx/content-ir/surfaces";
|
|
|
15
16
|
const KindInstanceRender = lazy(
|
|
16
17
|
() => import("../../../host/app-bindings.js").then((m) => ({ default: m.KindInstanceRender }))
|
|
17
18
|
);
|
|
18
|
-
const FENCE_LINE = /^[ \t]*(`{3,}|~{3,})[^\n`]*$/gm;
|
|
19
19
|
function isKindObject(value) {
|
|
20
20
|
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.__kind === "string";
|
|
21
21
|
}
|
|
22
22
|
function textPieces(text) {
|
|
23
|
-
const cleaned = text.
|
|
23
|
+
const cleaned = text.split("\n").map((line) => fenceOpenerOf(line) ? "" : line).join("\n");
|
|
24
24
|
if (!hasKindKey(cleaned)) return cleaned.trim() ? [{ type: "text", text: cleaned }] : [];
|
|
25
25
|
const keyAt = cleaned.search(/"__kind"\s*:/);
|
|
26
26
|
const objectAt = cleaned.lastIndexOf("{", keyAt);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { downloadFile } from "@ai-matrx/kit/download";
|
|
3
4
|
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
5
|
import { toast as copyToast } from "../../host/toast.js";
|
|
5
6
|
import { rewriteTableSource } from "../../utils/table-source.js";
|
|
@@ -269,9 +270,9 @@ const MarkdownTable = ({
|
|
|
269
270
|
};
|
|
270
271
|
const copyJsonToClipboard = async () => {
|
|
271
272
|
try {
|
|
272
|
-
await copyText(
|
|
273
|
+
if (!await copyText(
|
|
273
274
|
JSON.stringify(internalTableData.normalizedData, null, 2)
|
|
274
|
-
);
|
|
275
|
+
)) return;
|
|
275
276
|
toast.success("JSON copied to clipboard");
|
|
276
277
|
} catch (err) {
|
|
277
278
|
toast.error(err instanceof Error ? err.message : "Failed to copy JSON");
|
|
@@ -299,17 +300,12 @@ const MarkdownTable = ({
|
|
|
299
300
|
}).join(",")
|
|
300
301
|
)
|
|
301
302
|
].join("\n");
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
const link = document.createElement("a");
|
|
305
|
-
link.href = url;
|
|
306
|
-
link.download = "table_data.csv";
|
|
307
|
-
link.click();
|
|
308
|
-
URL.revokeObjectURL(url);
|
|
303
|
+
const saveCsv = () => downloadFile("table_data.csv", csvContent, "text/csv;charset=utf-8");
|
|
304
|
+
saveCsv();
|
|
309
305
|
toast.success("Table exported to CSV", {
|
|
310
306
|
action: {
|
|
311
307
|
label: "Download Again",
|
|
312
|
-
onClick:
|
|
308
|
+
onClick: saveCsv,
|
|
313
309
|
className: "font-medium"
|
|
314
310
|
}
|
|
315
311
|
});
|
|
@@ -321,28 +317,12 @@ const MarkdownTable = ({
|
|
|
321
317
|
try {
|
|
322
318
|
const markdownContent = content || "";
|
|
323
319
|
const fileName = internalTableData.headers && internalTableData.headers[0] ? `${internalTableData.headers[0].replace(/[^a-z0-9]/gi, "_").toLowerCase()}.md` : "table_data.md";
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
});
|
|
327
|
-
const url = URL.createObjectURL(blob);
|
|
328
|
-
const link = document.createElement("a");
|
|
329
|
-
link.href = url;
|
|
330
|
-
link.download = fileName;
|
|
331
|
-
document.body.appendChild(link);
|
|
332
|
-
link.click();
|
|
333
|
-
document.body.removeChild(link);
|
|
334
|
-
URL.revokeObjectURL(url);
|
|
320
|
+
const saveMarkdown = () => downloadFile(fileName, markdownContent, "text/markdown;charset=utf-8");
|
|
321
|
+
saveMarkdown();
|
|
335
322
|
toast.success("Markdown file downloaded", {
|
|
336
323
|
action: {
|
|
337
324
|
label: "Download Again",
|
|
338
|
-
onClick:
|
|
339
|
-
const newLink = document.createElement("a");
|
|
340
|
-
newLink.href = url;
|
|
341
|
-
newLink.download = fileName;
|
|
342
|
-
document.body.appendChild(newLink);
|
|
343
|
-
newLink.click();
|
|
344
|
-
document.body.removeChild(newLink);
|
|
345
|
-
},
|
|
325
|
+
onClick: saveMarkdown,
|
|
346
326
|
className: "font-medium"
|
|
347
327
|
}
|
|
348
328
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { downloadFile } from "@ai-matrx/kit/download";
|
|
3
4
|
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
5
|
import { toast as copyToast } from "../../host/toast.js";
|
|
5
6
|
import { rewriteTableSource } from "../../utils/table-source.js";
|
|
@@ -86,9 +87,9 @@ const TableControls = ({
|
|
|
86
87
|
}, [generateMarkdownTable, toast]);
|
|
87
88
|
const copyJsonToClipboard = useCallback(async () => {
|
|
88
89
|
try {
|
|
89
|
-
await copyText(
|
|
90
|
+
if (!await copyText(
|
|
90
91
|
JSON.stringify(debouncedTableData.normalizedData, null, 2)
|
|
91
|
-
);
|
|
92
|
+
)) return;
|
|
92
93
|
toast.success("JSON copied to clipboard");
|
|
93
94
|
} catch (err) {
|
|
94
95
|
toast.error(err instanceof Error ? err.message : "Failed to copy JSON");
|
|
@@ -116,17 +117,12 @@ const TableControls = ({
|
|
|
116
117
|
}).join(",")
|
|
117
118
|
)
|
|
118
119
|
].join("\n");
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
const link = document.createElement("a");
|
|
122
|
-
link.href = url;
|
|
123
|
-
link.download = "table_data.csv";
|
|
124
|
-
link.click();
|
|
125
|
-
URL.revokeObjectURL(url);
|
|
120
|
+
const saveCsv = () => downloadFile("table_data.csv", csvContent, "text/csv;charset=utf-8");
|
|
121
|
+
saveCsv();
|
|
126
122
|
toast.success("Table exported to CSV", {
|
|
127
123
|
action: {
|
|
128
124
|
label: "Download Again",
|
|
129
|
-
onClick:
|
|
125
|
+
onClick: saveCsv,
|
|
130
126
|
className: "font-medium"
|
|
131
127
|
}
|
|
132
128
|
});
|
|
@@ -138,28 +134,12 @@ const TableControls = ({
|
|
|
138
134
|
try {
|
|
139
135
|
const markdownContent = content || "";
|
|
140
136
|
const fileName = tableData.headers && tableData.headers[0] ? `${tableData.headers[0].replace(/[^a-z0-9]/gi, "_").toLowerCase()}.md` : "table_data.md";
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
});
|
|
144
|
-
const url = URL.createObjectURL(blob);
|
|
145
|
-
const link = document.createElement("a");
|
|
146
|
-
link.href = url;
|
|
147
|
-
link.download = fileName;
|
|
148
|
-
document.body.appendChild(link);
|
|
149
|
-
link.click();
|
|
150
|
-
document.body.removeChild(link);
|
|
151
|
-
URL.revokeObjectURL(url);
|
|
137
|
+
const saveMarkdown = () => downloadFile(fileName, markdownContent, "text/markdown;charset=utf-8");
|
|
138
|
+
saveMarkdown();
|
|
152
139
|
toast.success("Markdown file downloaded", {
|
|
153
140
|
action: {
|
|
154
141
|
label: "Download Again",
|
|
155
|
-
onClick:
|
|
156
|
-
const newLink = document.createElement("a");
|
|
157
|
-
newLink.href = url;
|
|
158
|
-
newLink.download = fileName;
|
|
159
|
-
document.body.appendChild(newLink);
|
|
160
|
-
newLink.click();
|
|
161
|
-
document.body.removeChild(newLink);
|
|
162
|
-
},
|
|
142
|
+
onClick: saveMarkdown,
|
|
163
143
|
className: "font-medium"
|
|
164
144
|
}
|
|
165
145
|
});
|
package/dist/mermaid/export.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { NormalizedFile } from "@ai-matrx/media/files/engine/handler/types";
|
|
2
|
+
/** Throws when the browser refused the copy — callers say so. */
|
|
2
3
|
export declare function copyMermaidSource(source: string): Promise<void>;
|
|
3
4
|
/** Copies the SVG markup as text — the universally pasteable form. */
|
|
4
5
|
export declare function copyMermaidSvg(svg: string): Promise<void>;
|
package/dist/mermaid/export.js
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { copyText } from "@ai-matrx/kit/clipboard";
|
|
3
|
+
import { downloadFile } from "@ai-matrx/kit/download";
|
|
2
4
|
import { fileHandler } from "@ai-matrx/media/files/engine/handler/handler";
|
|
3
5
|
function svgBlob(svg) {
|
|
4
6
|
return new Blob([svg], { type: "image/svg+xml;charset=utf-8" });
|
|
5
7
|
}
|
|
6
8
|
function triggerDownload(blob, fileName) {
|
|
7
|
-
|
|
8
|
-
const a = document.createElement("a");
|
|
9
|
-
a.href = url;
|
|
10
|
-
a.download = fileName;
|
|
11
|
-
a.click();
|
|
12
|
-
URL.revokeObjectURL(url);
|
|
9
|
+
downloadFile(fileName, blob, blob.type);
|
|
13
10
|
}
|
|
14
11
|
function safeFileName(title, extension) {
|
|
15
12
|
const base = (title || "diagram").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
|
|
16
13
|
return `${base || "diagram"}.${extension}`;
|
|
17
14
|
}
|
|
18
15
|
async function copyMermaidSource(source) {
|
|
19
|
-
await
|
|
16
|
+
if (!await copyText(source)) throw new Error("The browser refused the clipboard.");
|
|
20
17
|
}
|
|
21
18
|
async function copyMermaidSvg(svg) {
|
|
22
|
-
await
|
|
19
|
+
if (!await copyText(svg)) throw new Error("The browser refused the clipboard.");
|
|
23
20
|
}
|
|
24
21
|
function downloadMermaidSvg(svg, title) {
|
|
25
22
|
triggerDownload(svgBlob(svg), safeFileName(title, "svg"));
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { renderMermaid } from "./runtime.js";
|
|
2
|
+
import { closeFence, fenceOpenerOf } from "@ai-matrx/content-ir/source";
|
|
2
3
|
const PRINT_OPTIONS = { theme: "default", look: "classic", layout: "dagre" };
|
|
3
4
|
function mermaidFenceSources(markdown) {
|
|
4
5
|
const out = [];
|
|
5
6
|
const lines = markdown.split("\n");
|
|
7
|
+
const fenceLines = { count: lines.length, line: (index) => lines[index] ?? null };
|
|
6
8
|
for (let i = 0; i < lines.length; i++) {
|
|
7
|
-
const
|
|
8
|
-
if (!
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
for (; j < lines.length; j++) {
|
|
14
|
-
const close = /^\s*(`{3,}|~{3,})\s*$/.exec(lines[j]);
|
|
15
|
-
if (close && close[1][0] === marker[0] && close[1].length >= marker.length) break;
|
|
16
|
-
body.push(lines[j]);
|
|
9
|
+
const opener = fenceOpenerOf(lines[i]);
|
|
10
|
+
if (!opener) continue;
|
|
11
|
+
const end = closeFence(fenceLines, i, opener);
|
|
12
|
+
if (opener.lang !== "mermaid") {
|
|
13
|
+
i = end.line;
|
|
14
|
+
continue;
|
|
17
15
|
}
|
|
16
|
+
const indent = lines[i].length - lines[i].trimStart().length;
|
|
17
|
+
const body = lines.slice(i + 1, end.closed ? end.line : end.line + 1);
|
|
18
18
|
out.push(body.map((l) => l.slice(Math.min(indent, l.length - l.trimStart().length))).join("\n"));
|
|
19
|
-
i =
|
|
19
|
+
i = end.line;
|
|
20
20
|
}
|
|
21
21
|
return out;
|
|
22
22
|
}
|
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.9",
|
|
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",
|
|
@@ -85,7 +85,6 @@
|
|
|
85
85
|
"hast-util-sanitize": "^5.0.2",
|
|
86
86
|
"hast-util-to-string": "^3.0.1",
|
|
87
87
|
"hast-util-to-text": "^4.0.2",
|
|
88
|
-
"html2canvas": "^1.4.1",
|
|
89
88
|
"katex": "^0.18.9",
|
|
90
89
|
"lucide-react": "^1.48.0",
|
|
91
90
|
"marked": "^18.0.14",
|
|
@@ -116,19 +115,20 @@
|
|
|
116
115
|
"unist-util-visit": "^5.1.0",
|
|
117
116
|
"unist-util-visit-parents": "^6.0.2",
|
|
118
117
|
"yaml": "^2.9.1",
|
|
118
|
+
"@ai-matrx/agents": "latest",
|
|
119
|
+
"@ai-matrx/alchemy": "latest",
|
|
119
120
|
"@ai-matrx/canvas": "latest",
|
|
120
121
|
"@ai-matrx/content-ir-react": "latest",
|
|
122
|
+
"@ai-matrx/content-ir": "latest",
|
|
121
123
|
"@ai-matrx/data": "latest",
|
|
122
124
|
"@ai-matrx/design-system": "latest",
|
|
123
|
-
"@ai-matrx/content-ir": "latest",
|
|
124
|
-
"@ai-matrx/agents": "latest",
|
|
125
125
|
"@ai-matrx/diff": "latest",
|
|
126
126
|
"@ai-matrx/icons": "latest",
|
|
127
|
-
"@ai-matrx/kit": "latest",
|
|
128
127
|
"@ai-matrx/print": "latest",
|
|
129
128
|
"@ai-matrx/records": "latest",
|
|
130
129
|
"@ai-matrx/tap-target": "latest",
|
|
131
|
-
"@ai-matrx/media": "latest"
|
|
130
|
+
"@ai-matrx/media": "latest",
|
|
131
|
+
"@ai-matrx/kit": "latest"
|
|
132
132
|
},
|
|
133
133
|
"peerDependencies": {
|
|
134
134
|
"react": ">=19.0.0",
|