@ai-matrx/rich-content 0.2.31 → 0.2.33

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,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.33
4
+
5
+ - The interactive diagram's two PNG exports (`handleExportImage` and the viewport-fallback export) take a picture through Alchemy's `renderElement` (html-to-image: native SVG edges, transforms, modern colours) instead of the html2canvas `captureElement` (AP-2 capture rule: pictures of surfaces use `renderElement`, documents use html2canvas). `table-source` reads the author's edge pipes and a delimiter cell through `@ai-matrx/content-ir/source`.
6
+
7
+ **Consumer action:** none.
8
+
9
+ ## 0.2.32
10
+ ### CopySplitButton: moreOnly
11
+
12
+ - `CopySplitButton` takes `moreOnly`: just the chevron and its menu (same two rows, same "Default" tag, same copy) for a host whose main control is already its own one-click tile — the phone note dock. matrx-frontend `TextCopyChevron` is now this, not a second build.
13
+
3
14
  ## 0.2.31
4
15
 
5
16
  - 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.
@@ -9,10 +9,16 @@ export interface CopySplitButtonProps {
9
9
  size?: "sm" | "xs";
10
10
  /** Mount more copy options (the Alchemy palette) under the two rows; called with null on close. */
11
11
  mountMore?: (element: HTMLElement | null, close: () => void) => void;
12
+ /**
13
+ * Render only the chevron and its menu — for a host whose main control is already its own one-click
14
+ * tile (the phone note dock). Same rows, same "Default" tag, same copy module; the host positions it
15
+ * through `className`.
16
+ */
17
+ moreOnly?: boolean;
12
18
  /** Keep the page's text selection when pressed (the selection toolbar). */
13
19
  preserveSelection?: boolean;
14
20
  disabled?: boolean;
15
21
  className?: string;
16
22
  }
17
- export declare function CopySplitButton({ copy, label, size, mountMore, preserveSelection, disabled, className, }: CopySplitButtonProps): React.ReactElement;
23
+ export declare function CopySplitButton({ copy, label, size, mountMore, moreOnly, preserveSelection, disabled, className, }: CopySplitButtonProps): React.ReactElement;
18
24
  export default CopySplitButton;
@@ -13,6 +13,7 @@ function CopySplitButton({
13
13
  label = "Copy",
14
14
  size = "sm",
15
15
  mountMore,
16
+ moreOnly = false,
16
17
  preserveSelection = false,
17
18
  disabled = false,
18
19
  className
@@ -41,12 +42,13 @@ function CopySplitButton({
41
42
  {
42
43
  "data-copy-split": "",
43
44
  className: cn(
44
- "inline-flex shrink-0 items-center rounded-md text-muted-foreground hover:bg-accent/40",
45
+ "inline-flex shrink-0 items-center rounded-md text-muted-foreground",
46
+ !moreOnly && "hover:bg-accent/40",
45
47
  disabled && "pointer-events-none opacity-50",
46
48
  className
47
49
  ),
48
50
  children: [
49
- /* @__PURE__ */ jsx(
51
+ moreOnly ? null : /* @__PURE__ */ jsx(
50
52
  "button",
51
53
  {
52
54
  type: "button",
@@ -80,8 +82,8 @@ function CopySplitButton({
80
82
  disabled,
81
83
  onMouseDown: keep,
82
84
  className: cn(
83
- "inline-flex w-4 items-center justify-center rounded-r-md hover:bg-accent hover:text-foreground pointer-coarse:h-11 pointer-coarse:w-6",
84
- box
85
+ "inline-flex items-center justify-center hover:bg-accent hover:text-foreground",
86
+ moreOnly ? "h-6 w-6 rounded-md" : cn("w-4 rounded-r-md pointer-coarse:h-11 pointer-coarse:w-6", box)
85
87
  ),
86
88
  children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" })
87
89
  }
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { captureElement } from "@ai-matrx/alchemy/operate/capture";
3
+ import { renderElement } from "@ai-matrx/alchemy/operate/capture";
4
4
  import { downloadFile, downloadUrl } from "@ai-matrx/kit/download";
5
5
  import { Button, Chip } from "@ai-matrx/design-system/controls";
6
6
  import {
@@ -1046,15 +1046,12 @@ const DiagramFlow = ({
1046
1046
  }
1047
1047
  exportContainer.appendChild(cloned);
1048
1048
  document.body.appendChild(exportContainer);
1049
- captureElement(exportContainer, {
1049
+ renderElement(exportContainer, {
1050
1050
  backgroundColor: bgColor,
1051
- scale: 2,
1052
- useCORS: true,
1053
- allowTaint: true,
1051
+ pixelRatio: 2,
1054
1052
  width: imageWidth,
1055
1053
  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")
1054
+ filter: (node) => !(node.classList?.contains("react-flow__controls") || node.classList?.contains("react-flow__minimap") || node.classList?.contains("react-flow__panel"))
1058
1055
  }).then((canvas) => {
1059
1056
  downloadUrl(
1060
1057
  canvas.toDataURL("image/png"),
@@ -2113,11 +2110,9 @@ const InteractiveDiagramBlock = ({
2113
2110
  );
2114
2111
  if (!viewport) return;
2115
2112
  const isDark = document.documentElement.classList.contains("dark") || window.matchMedia("(prefers-color-scheme: dark)").matches;
2116
- captureElement(viewport, {
2113
+ renderElement(viewport, {
2117
2114
  backgroundColor: isDark ? "#111827" : "#ffffff",
2118
- scale: 2,
2119
- useCORS: true,
2120
- safeColors: { dark: isDark }
2115
+ pixelRatio: 2
2121
2116
  }).then((canvas) => {
2122
2117
  downloadUrl(
2123
2118
  canvas.toDataURL("image/png"),
@@ -1,5 +1,5 @@
1
1
  import { lexGfmBlocks } from "./gfm-lexer.js";
2
- import { rowCells, splitRowSegments, unescapeCellPipes } from "@ai-matrx/content-ir/source";
2
+ import { isGfmDelimiterRow, rowCells, splitRowSegments, unescapeCellPipes } from "@ai-matrx/content-ir/source";
3
3
  import { rowCells as rowCells2, splitRowSegments as splitRowSegments2 } from "@ai-matrx/content-ir/source";
4
4
  function backslashRunBefore(text, index) {
5
5
  let run = 0;
@@ -112,7 +112,7 @@ function respliceRow(segs, stored, texts) {
112
112
  }
113
113
  function freshRow(texts, lead = true, trail = true) {
114
114
  const cells = texts.map((text) => freshCell(text).trim());
115
- const needLead = lead || !cells.every((cell) => /^:?-+:?$/.test(cell));
115
+ const needLead = lead || !cells.every((cell) => isGfmDelimiterRow(`|${cell}|`));
116
116
  const needTrail = trail || cells[cells.length - 1] === "" || endsInEscape(cells[cells.length - 1] ?? "");
117
117
  const line = `${needLead ? "| " : ""}${cells.join(" | ")}${needTrail ? " |" : ""}`;
118
118
  return assertRowReadsBack(line, cells);
@@ -140,8 +140,9 @@ function rewriteTableSource(original, grid) {
140
140
  return [freshRow(grid.headers), freshRow(grid.headers.map(() => "---")), ...grid.rows.map((row) => freshRow(row))].join("\n");
141
141
  }
142
142
  const headerLine = lines[headerAt] ?? "";
143
- const lead = headerLine.trimStart().startsWith("|");
144
- const trail = headerLine.trimEnd().endsWith("|");
143
+ const headerSegments = splitRowSegments(headerLine);
144
+ const lead = headerSegments.length > 1 && (headerSegments[0] ?? "").trim() === "";
145
+ const trail = headerSegments.length > 1 && (headerSegments[headerSegments.length - 1] ?? "").trim() === "";
145
146
  const width = rowCells(headerLine).length;
146
147
  const columnsChanged = grid.headers.length !== width;
147
148
  const out = [...lines];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/rich-content",
3
- "version": "0.2.31",
3
+ "version": "0.2.33",
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,21 @@
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
134
  "@ai-matrx/canvas": "latest",
136
- "@ai-matrx/data": "latest",
137
- "@ai-matrx/content-ir-react": "latest",
138
- "@ai-matrx/design-system": "latest",
139
135
  "@ai-matrx/content-ir": "latest",
136
+ "@ai-matrx/content-ir-react": "latest",
137
+ "@ai-matrx/data": "latest",
138
+ "@ai-matrx/agents": "latest",
140
139
  "@ai-matrx/diff": "latest",
141
140
  "@ai-matrx/icons": "latest",
142
- "@ai-matrx/media": "latest",
141
+ "@ai-matrx/design-system": "latest",
143
142
  "@ai-matrx/print": "latest",
143
+ "@ai-matrx/media": "latest",
144
144
  "@ai-matrx/records": "latest",
145
145
  "@ai-matrx/records-ui": "latest",
146
- "@ai-matrx/kit": "latest",
147
- "@ai-matrx/tap-target": "latest"
146
+ "@ai-matrx/tap-target": "latest",
147
+ "@ai-matrx/kit": "latest"
148
148
  },
149
149
  "peerDependencies": {
150
150
  "react": ">=19.0.0",