@ai-matrx/rich-content 0.2.28 → 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 CHANGED
@@ -1,5 +1,29 @@
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
+
19
+ ## 0.2.29
20
+
21
+ - The split Copy's popover sizes to the Alchemy palette it holds (it clipped "Download" at 240px).
22
+
23
+ ### Consumer action
24
+
25
+ - None required.
26
+
3
27
  ## 0.2.28
4
28
 
5
29
  - `BlockRenderer` and `ConfigurableMarkdownContent` suspend on the lazy host like the other entry points (plain text meanwhile); a test lists every gated entry point.
@@ -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,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 records = [];
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
- const rows = [];
23
- const lines = content.split("\n");
24
- for (const line of lines) {
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.28",
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",
@@ -133,18 +133,18 @@
133
133
  "@ai-matrx/agents": "latest",
134
134
  "@ai-matrx/alchemy": "latest",
135
135
  "@ai-matrx/canvas": "latest",
136
- "@ai-matrx/content-ir-react": "latest",
137
136
  "@ai-matrx/data": "latest",
137
+ "@ai-matrx/content-ir-react": "latest",
138
+ "@ai-matrx/design-system": "latest",
138
139
  "@ai-matrx/content-ir": "latest",
139
140
  "@ai-matrx/diff": "latest",
140
141
  "@ai-matrx/icons": "latest",
141
- "@ai-matrx/kit": "latest",
142
142
  "@ai-matrx/media": "latest",
143
- "@ai-matrx/records": "latest",
144
143
  "@ai-matrx/print": "latest",
144
+ "@ai-matrx/records": "latest",
145
145
  "@ai-matrx/records-ui": "latest",
146
- "@ai-matrx/tap-target": "latest",
147
- "@ai-matrx/design-system": "latest"
146
+ "@ai-matrx/kit": "latest",
147
+ "@ai-matrx/tap-target": "latest"
148
148
  },
149
149
  "peerDependencies": {
150
150
  "react": ">=19.0.0",