@ai-matrx/rich-content 0.2.29 → 0.2.31
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 +16 -0
- package/dist/display/blocks/chart/table-chart.js +2 -27
- package/dist/display/blocks/csv/CsvBlock.d.ts +2 -0
- package/dist/display/blocks/csv/CsvBlock.js +6 -35
- package/dist/display/blocks/json/JsonBlock.js +1 -2
- package/dist/display/blocks/table/StreamingTableRenderer.js +2 -2
- package/dist/display/tables/MarkdownTable.js +2 -2
- package/dist/display/tables/TableWithSeparatedControls.js +2 -2
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.31
|
|
4
|
+
|
|
5
|
+
- The CSV block and the chart's `parseDelimitedTable` read through THE one reader, kit `parseDelimited` (AP-2). The block's own reader split on `"\n"` before parsing, so a quoted newline — which its own `toDelimited` writer emits after an edit — tore one cell into two rows. Needs kit 0.31.0.
|
|
6
|
+
|
|
7
|
+
**Consumer action:** none.
|
|
8
|
+
|
|
9
|
+
## 0.2.30
|
|
10
|
+
|
|
11
|
+
- One copy, one toast (AP-2): the JSON block's Copy and the three table "Copy JSON" actions passed no
|
|
12
|
+
success words to their notifying `useClipboard` hook, so the kit's "Text copied to clipboard!" fired
|
|
13
|
+
and the component then toasted its own. The words now ride the copy; the extra toast is gone.
|
|
14
|
+
|
|
15
|
+
### Consumer action
|
|
16
|
+
|
|
17
|
+
- None required.
|
|
18
|
+
|
|
3
19
|
## 0.2.29
|
|
4
20
|
|
|
5
21
|
- The split Copy's popover sizes to the Alchemy palette it holds (it clipped "Download" at 240px).
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CHART_PALETTE } from "./chart-spec.js";
|
|
2
|
+
import { parseDelimited } from "@ai-matrx/kit/delimited";
|
|
2
3
|
const SUFFIX = { k: 1e3, m: 1e6, b: 1e9, bn: 1e9, t: 1e12 };
|
|
3
4
|
function parseCellNumber(raw) {
|
|
4
5
|
if (typeof raw === "number") return Number.isFinite(raw) ? raw : null;
|
|
@@ -190,33 +191,7 @@ function parseDelimitedTable(text) {
|
|
|
190
191
|
const firstLine = src.split("\n", 1)[0];
|
|
191
192
|
const delimiter = firstLine.includes(" ") ? " " : firstLine.includes(",") ? "," : firstLine.includes(";") ? ";" : null;
|
|
192
193
|
if (!delimiter) return null;
|
|
193
|
-
const
|
|
194
|
-
let row = [];
|
|
195
|
-
let cell = "";
|
|
196
|
-
let quoted = false;
|
|
197
|
-
for (let i = 0; i < src.length; i++) {
|
|
198
|
-
const ch = src[i];
|
|
199
|
-
if (quoted) {
|
|
200
|
-
if (ch === '"') {
|
|
201
|
-
if (src[i + 1] === '"') {
|
|
202
|
-
cell += '"';
|
|
203
|
-
i++;
|
|
204
|
-
} else quoted = false;
|
|
205
|
-
} else cell += ch;
|
|
206
|
-
} else if (ch === '"' && cell === "") quoted = true;
|
|
207
|
-
else if (ch === delimiter) {
|
|
208
|
-
row.push(cell);
|
|
209
|
-
cell = "";
|
|
210
|
-
} else if (ch === "\n") {
|
|
211
|
-
row.push(cell);
|
|
212
|
-
records.push(row);
|
|
213
|
-
row = [];
|
|
214
|
-
cell = "";
|
|
215
|
-
} else cell += ch;
|
|
216
|
-
}
|
|
217
|
-
row.push(cell);
|
|
218
|
-
records.push(row);
|
|
219
|
-
const cleaned = records.filter((r) => r.some((c) => c.trim() !== ""));
|
|
194
|
+
const cleaned = parseDelimited(src, { delimiter, skipEmptyLines: "greedy" }).data;
|
|
220
195
|
if (cleaned.length < 2) return null;
|
|
221
196
|
const [headers, ...rows] = cleaned;
|
|
222
197
|
if (headers.length < 2) return null;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
/** THE one reader (kit `parseDelimited`): quoted delimiters, doubled quotes and newlines inside quotes. */
|
|
3
|
+
export declare function parseCsv(content: string, delimiter: string): string[][];
|
|
2
4
|
interface CsvBlockProps {
|
|
3
5
|
content: string;
|
|
4
6
|
delimiter?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useClipboard } from "@ai-matrx/kit/clipboard";
|
|
4
|
-
import { toDelimited } from "@ai-matrx/kit/delimited";
|
|
4
|
+
import { parseDelimited, toDelimited } from "@ai-matrx/kit/delimited";
|
|
5
5
|
import { useState, useEffect } from "react";
|
|
6
6
|
import { cn } from "@ai-matrx/design-system";
|
|
7
7
|
import {
|
|
@@ -19,39 +19,9 @@ import { toast } from "../../../host/toast.js";
|
|
|
19
19
|
import { useOpenSaveToTable } from "../../../host/tables.js";
|
|
20
20
|
import { Button } from "@ai-matrx/design-system/controls";
|
|
21
21
|
function parseCsv(content, delimiter) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (!line.trim()) continue;
|
|
26
|
-
const cells = [];
|
|
27
|
-
let current = "";
|
|
28
|
-
let inQuotes = false;
|
|
29
|
-
for (let i = 0; i < line.length; i++) {
|
|
30
|
-
const ch = line[i];
|
|
31
|
-
if (inQuotes) {
|
|
32
|
-
if (ch === '"' && line[i + 1] === '"') {
|
|
33
|
-
current += '"';
|
|
34
|
-
i++;
|
|
35
|
-
} else if (ch === '"') {
|
|
36
|
-
inQuotes = false;
|
|
37
|
-
} else {
|
|
38
|
-
current += ch;
|
|
39
|
-
}
|
|
40
|
-
} else {
|
|
41
|
-
if (ch === '"') {
|
|
42
|
-
inQuotes = true;
|
|
43
|
-
} else if (ch === delimiter) {
|
|
44
|
-
cells.push(current.trim());
|
|
45
|
-
current = "";
|
|
46
|
-
} else {
|
|
47
|
-
current += ch;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
cells.push(current.trim());
|
|
52
|
-
rows.push(cells);
|
|
53
|
-
}
|
|
54
|
-
return rows;
|
|
22
|
+
return parseDelimited(content, { delimiter, skipEmptyLines: "greedy" }).data.map(
|
|
23
|
+
(row) => row.map((cell) => cell.trim())
|
|
24
|
+
);
|
|
55
25
|
}
|
|
56
26
|
function rowsToDelimitedContent(rows, delimiter) {
|
|
57
27
|
return toDelimited(rows, { delimiter, spreadsheetSafe: false });
|
|
@@ -240,5 +210,6 @@ const CsvBlock = ({
|
|
|
240
210
|
};
|
|
241
211
|
var CsvBlock_default = CsvBlock;
|
|
242
212
|
export {
|
|
243
|
-
CsvBlock_default as default
|
|
213
|
+
CsvBlock_default as default,
|
|
214
|
+
parseCsv
|
|
244
215
|
};
|
|
@@ -488,9 +488,8 @@ const JsonViewHeader = ({
|
|
|
488
488
|
const kebabRef = useRef(null);
|
|
489
489
|
const [copied, setCopied] = useState(false);
|
|
490
490
|
const handleCopy = async () => {
|
|
491
|
-
if (!await copyText(content)) return;
|
|
491
|
+
if (!await copyText(content, "Copied JSON to clipboard")) return;
|
|
492
492
|
setCopied(true);
|
|
493
|
-
toast.success("Copied JSON to clipboard");
|
|
494
493
|
setTimeout(() => setCopied(false), 1500);
|
|
495
494
|
};
|
|
496
495
|
const fullMenuItems = [
|
|
@@ -272,9 +272,9 @@ const StreamingTableRendererCore = ({
|
|
|
272
272
|
};
|
|
273
273
|
const copyJsonToClipboard = async () => {
|
|
274
274
|
if (!await copyText(
|
|
275
|
-
JSON.stringify(tableData.normalizedData, null, 2)
|
|
275
|
+
JSON.stringify(tableData.normalizedData, null, 2),
|
|
276
|
+
"JSON copied to clipboard"
|
|
276
277
|
)) return;
|
|
277
|
-
toast.success("JSON copied to clipboard");
|
|
278
278
|
};
|
|
279
279
|
const copyMarkdownToClipboard = async () => {
|
|
280
280
|
if (content) {
|
|
@@ -273,9 +273,9 @@ const MarkdownTable = ({
|
|
|
273
273
|
};
|
|
274
274
|
const copyJsonToClipboard = async () => {
|
|
275
275
|
if (!await copyText(
|
|
276
|
-
JSON.stringify(internalTableData.normalizedData, null, 2)
|
|
276
|
+
JSON.stringify(internalTableData.normalizedData, null, 2),
|
|
277
|
+
"JSON copied to clipboard"
|
|
277
278
|
)) return;
|
|
278
|
-
toast.success("JSON copied to clipboard");
|
|
279
279
|
};
|
|
280
280
|
const copyMarkdownToClipboard = async () => {
|
|
281
281
|
if (content) {
|
|
@@ -90,9 +90,9 @@ const TableControls = ({
|
|
|
90
90
|
}, [generateMarkdownTable, toast]);
|
|
91
91
|
const copyJsonToClipboard = useCallback(async () => {
|
|
92
92
|
if (!await copyText(
|
|
93
|
-
JSON.stringify(debouncedTableData.normalizedData, null, 2)
|
|
93
|
+
JSON.stringify(debouncedTableData.normalizedData, null, 2),
|
|
94
|
+
"JSON copied to clipboard"
|
|
94
95
|
)) return;
|
|
95
|
-
toast.success("JSON copied to clipboard");
|
|
96
96
|
}, [debouncedTableData.normalizedData, toast]);
|
|
97
97
|
const copyMarkdownToClipboard = useCallback(async () => {
|
|
98
98
|
if (content) {
|
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.31",
|
|
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",
|
|
@@ -132,19 +132,19 @@
|
|
|
132
132
|
"yaml": "^2.9.1",
|
|
133
133
|
"@ai-matrx/agents": "latest",
|
|
134
134
|
"@ai-matrx/alchemy": "latest",
|
|
135
|
-
"@ai-matrx/
|
|
135
|
+
"@ai-matrx/canvas": "latest",
|
|
136
136
|
"@ai-matrx/data": "latest",
|
|
137
|
+
"@ai-matrx/content-ir-react": "latest",
|
|
137
138
|
"@ai-matrx/design-system": "latest",
|
|
138
|
-
"@ai-matrx/
|
|
139
|
+
"@ai-matrx/content-ir": "latest",
|
|
139
140
|
"@ai-matrx/diff": "latest",
|
|
140
|
-
"@ai-matrx/content-ir-react": "latest",
|
|
141
141
|
"@ai-matrx/icons": "latest",
|
|
142
|
-
"@ai-matrx/kit": "latest",
|
|
143
|
-
"@ai-matrx/records": "latest",
|
|
144
142
|
"@ai-matrx/media": "latest",
|
|
143
|
+
"@ai-matrx/print": "latest",
|
|
144
|
+
"@ai-matrx/records": "latest",
|
|
145
145
|
"@ai-matrx/records-ui": "latest",
|
|
146
|
-
"@ai-matrx/
|
|
147
|
-
"@ai-matrx/
|
|
146
|
+
"@ai-matrx/kit": "latest",
|
|
147
|
+
"@ai-matrx/tap-target": "latest"
|
|
148
148
|
},
|
|
149
149
|
"peerDependencies": {
|
|
150
150
|
"react": ">=19.0.0",
|