@ai-matrx/rich-content 0.2.89 → 0.2.91
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 +12 -0
- package/dist/display/markdown-classification/processors/utils/content-splitter-core.d.ts +1 -9
- package/dist/display/markdown-classification/processors/utils/content-splitter-core.js +40 -7
- package/dist/display/markdown-classification/processors/utils/partial-kind-tail.d.ts +22 -0
- package/dist/display/markdown-classification/processors/utils/partial-kind-tail.js +209 -0
- package/dist/display/themes.js +1 -1
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.91
|
|
4
|
+
|
|
5
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
6
|
+
`git diff npm/rich-content/v0.2.90..npm/rich-content/v0.2.91 -- apps/shared/rich-content`).
|
|
7
|
+
No source changes intended and no consumer action required.
|
|
8
|
+
|
|
9
|
+
## 0.2.90
|
|
10
|
+
|
|
11
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
12
|
+
`git diff npm/rich-content/v0.2.89..npm/rich-content/v0.2.90 -- apps/shared/rich-content`).
|
|
13
|
+
No source changes intended and no consumer action required.
|
|
14
|
+
|
|
3
15
|
## 0.2.89
|
|
4
16
|
|
|
5
17
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
@@ -78,15 +78,7 @@ export interface SplitterBlock {
|
|
|
78
78
|
* Kept as an alias so existing imports continue to compile during migration.
|
|
79
79
|
*/
|
|
80
80
|
export type ContentBlock = SplitterBlock;
|
|
81
|
-
|
|
82
|
-
* Detection is recursive across arrival containers: a complete object that
|
|
83
|
-
* directly declares `__kind` becomes its own JSON/Content-IR block even when
|
|
84
|
-
* Markdown first captured it inside prose, a non-JSON fence, or XML. Every
|
|
85
|
-
* byte around recovered objects stays in document order as its original block
|
|
86
|
-
* type. Artifact wrappers already carry their body envelope for selectors and
|
|
87
|
-
* retain their identity/chrome; they are the one container that must not be
|
|
88
|
-
* exploded into anonymous fragments.
|
|
89
|
-
*/
|
|
81
|
+
export declare function wrapperSourceOf(block: SplitterBlock): string | undefined;
|
|
90
82
|
export declare function recoverEmbeddedKindJsonBlocksWith(input: SplitterBlock[], envelopes: SplitterEnvelopes): SplitterBlock[];
|
|
91
83
|
/**
|
|
92
84
|
* Code-fence languages that promote to a first-class block type (the fence
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
XmlContainerTracker
|
|
44
44
|
} from "@ai-matrx/content-ir/source";
|
|
45
45
|
import { indexOutsideInlineCode } from "@ai-matrx/content-ir/source";
|
|
46
|
+
import { recoverPartialKindTailWith, splitAdjacentJsonRootsWith } from "./partial-kind-tail.js";
|
|
46
47
|
import {
|
|
47
48
|
findBalancedXmlClose,
|
|
48
49
|
initialXmlBalance
|
|
@@ -90,6 +91,15 @@ function isJsonFenceBlock(block) {
|
|
|
90
91
|
function isQuotedSourceFenceBlock(block) {
|
|
91
92
|
return block.type === "code" && block.metadata?.genericXmlContainer !== true && !isJsonFenceLanguage(block.language);
|
|
92
93
|
}
|
|
94
|
+
function withFenceSurface(metadata, language, content, envelopes) {
|
|
95
|
+
if (!language) return metadata;
|
|
96
|
+
const envelope = envelopes.fenceRegion(language, content);
|
|
97
|
+
return envelope ? { ...metadata ?? {}, [IR_ENVELOPE_KEY]: envelope } : metadata;
|
|
98
|
+
}
|
|
99
|
+
const wrapperSources = /* @__PURE__ */ new WeakMap();
|
|
100
|
+
function wrapperSourceOf(block) {
|
|
101
|
+
return wrapperSources.get(block);
|
|
102
|
+
}
|
|
93
103
|
function recoverEmbeddedKindJsonBlocksWith(input, envelopes) {
|
|
94
104
|
const recovered = [];
|
|
95
105
|
for (const block of input) {
|
|
@@ -116,6 +126,8 @@ function recoverEmbeddedKindJsonBlocksWith(input, envelopes) {
|
|
|
116
126
|
recovered.push(block);
|
|
117
127
|
continue;
|
|
118
128
|
}
|
|
129
|
+
const isWrapperSplit = pieces.some((piece) => piece.type === "residual");
|
|
130
|
+
const firstPiece = recovered.length;
|
|
119
131
|
let followsKind = false;
|
|
120
132
|
for (const piece of pieces) {
|
|
121
133
|
if (piece.type === "chrome") continue;
|
|
@@ -162,6 +174,9 @@ function recoverEmbeddedKindJsonBlocksWith(input, envelopes) {
|
|
|
162
174
|
metadata: genericXml ? { genericXmlContainer: true } : void 0
|
|
163
175
|
});
|
|
164
176
|
}
|
|
177
|
+
if (isWrapperSplit) {
|
|
178
|
+
for (const piece of recovered.slice(firstPiece)) wrapperSources.set(piece, block.content);
|
|
179
|
+
}
|
|
165
180
|
}
|
|
166
181
|
return recovered;
|
|
167
182
|
}
|
|
@@ -890,8 +905,11 @@ function extractCodeBlock(language, openTicks, startIndex, lines, fenceChar = "`
|
|
|
890
905
|
{ char: fenceChar, ticks: openTicks, lang: language ?? "" }
|
|
891
906
|
);
|
|
892
907
|
if (!end.closed) {
|
|
908
|
+
const body2 = lines.slice(startIndex);
|
|
909
|
+
const tail = (body2[body2.length - 1] ?? "").trim();
|
|
910
|
+
if (body2.length > 0 && tail && tail.length < openTicks && [...tail].every((c) => c === fenceChar)) body2.pop();
|
|
893
911
|
return {
|
|
894
|
-
content:
|
|
912
|
+
content: body2.join("\n"),
|
|
895
913
|
nextIndex: lines.length + 1,
|
|
896
914
|
metadata: { language },
|
|
897
915
|
closedCleanly: false
|
|
@@ -1288,18 +1306,29 @@ const splitContentIntoBlocksWith = (mdContent, envelopes) => {
|
|
|
1288
1306
|
type: "code",
|
|
1289
1307
|
content: extraction.content,
|
|
1290
1308
|
language: codeCheck.language,
|
|
1291
|
-
metadata:
|
|
1309
|
+
metadata: withFenceSurface(
|
|
1310
|
+
envelopes.withIrEnvelope(
|
|
1311
|
+
extraction.content,
|
|
1312
|
+
codeCheck.meta ? { [FENCE_META_KEY]: codeCheck.meta } : void 0
|
|
1313
|
+
),
|
|
1314
|
+
extraction.closedCleanly ? normalizedLanguage : void 0,
|
|
1292
1315
|
extraction.content,
|
|
1293
|
-
|
|
1316
|
+
envelopes
|
|
1294
1317
|
)
|
|
1295
1318
|
});
|
|
1296
1319
|
}
|
|
1297
1320
|
} else {
|
|
1321
|
+
const codeMetadata = withFenceSurface(
|
|
1322
|
+
codeCheck.meta ? { [FENCE_META_KEY]: codeCheck.meta } : void 0,
|
|
1323
|
+
extraction.closedCleanly ? normalizedLanguage : void 0,
|
|
1324
|
+
extraction.content,
|
|
1325
|
+
envelopes
|
|
1326
|
+
);
|
|
1298
1327
|
blocks.push({
|
|
1299
1328
|
type: "code",
|
|
1300
1329
|
content: extraction.content,
|
|
1301
1330
|
language: codeCheck.language,
|
|
1302
|
-
...
|
|
1331
|
+
...codeMetadata ? { metadata: codeMetadata } : {}
|
|
1303
1332
|
});
|
|
1304
1333
|
}
|
|
1305
1334
|
i = extraction.nextIndex;
|
|
@@ -1341,7 +1370,7 @@ const splitContentIntoBlocksWith = (mdContent, envelopes) => {
|
|
|
1341
1370
|
i,
|
|
1342
1371
|
lines
|
|
1343
1372
|
);
|
|
1344
|
-
let xmlMetadata = extraction.metadata;
|
|
1373
|
+
let xmlMetadata = extraction.metadata ? { rawXml: extraction.content, ...extraction.metadata } : extraction.metadata;
|
|
1345
1374
|
if (xmlMetadata?.isComplete === true) {
|
|
1346
1375
|
const xmlTag = xmlTagNameOf(xmlMatch.matchedTag);
|
|
1347
1376
|
const attributed = xmlMatch.matchedTag !== `<${xmlTag}>`;
|
|
@@ -1776,7 +1805,10 @@ ${extraction.content}
|
|
|
1776
1805
|
if (currentText.trim()) {
|
|
1777
1806
|
blocks.push({ type: "text", content: currentText.trimEnd() });
|
|
1778
1807
|
}
|
|
1779
|
-
return
|
|
1808
|
+
return recoverPartialKindTailWith(
|
|
1809
|
+
recoverEmbeddedKindJsonBlocksWith(splitAdjacentJsonRootsWith(blocks, envelopes), envelopes),
|
|
1810
|
+
envelopes
|
|
1811
|
+
);
|
|
1780
1812
|
};
|
|
1781
1813
|
export {
|
|
1782
1814
|
AUDIO_URL_EXT,
|
|
@@ -1795,5 +1827,6 @@ export {
|
|
|
1795
1827
|
parseXmlAttributes,
|
|
1796
1828
|
recoverEmbeddedKindJsonBlocksWith,
|
|
1797
1829
|
splitContentIntoBlocksWith,
|
|
1798
|
-
startUnrecognizedXmlContainer
|
|
1830
|
+
startUnrecognizedXmlContainer,
|
|
1831
|
+
wrapperSourceOf
|
|
1799
1832
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE OPEN KIND AT THE END OF THE TEXT — the splitter's reading of a text that stops inside a kind.
|
|
3
|
+
*
|
|
4
|
+
* `recoverEmbeddedKindJsonBlocksWith` lifts every COMPLETE kind object out of its container
|
|
5
|
+
* (prose, an XML tag, a reasoning region, a JSON wrapper). A text that ends inside a kind — every
|
|
6
|
+
* frame of a stream, or a truncated answer on reload — holds an object that never closes, so
|
|
7
|
+
* that pass cannot see it and the kind would print as prose, sit inside an XML card, or draw as
|
|
8
|
+
* a raw JSON card (the never-raw law, Arman 2026-09-30). This pass reads the same thing for the
|
|
9
|
+
* open object: the last block is cut where the still-open root object (or array of objects)
|
|
10
|
+
* starts, the moment a `__kind` key is visible in it. Complete elements of an open array of
|
|
11
|
+
* kinds become their own kinds (A6); its array punctuation draws nothing.
|
|
12
|
+
*
|
|
13
|
+
* One reading for both paths: the live stream and a reload of the same text call this the same
|
|
14
|
+
* way, so a stream never draws a layout its own reload does not.
|
|
15
|
+
*/
|
|
16
|
+
import type { SplitterBlock, SplitterEnvelopes } from "./content-splitter-core.js";
|
|
17
|
+
export declare function recoverPartialKindTailWith(blocks: SplitterBlock[], envelopes: SplitterEnvelopes): SplitterBlock[];
|
|
18
|
+
/**
|
|
19
|
+
* Root objects written back to back (`{…}{…}{…}`, a provider concatenating answers): each root is
|
|
20
|
+
* its own JSON region — one block per root, the reading the live stream always drew.
|
|
21
|
+
*/
|
|
22
|
+
export declare function splitAdjacentJsonRootsWith(blocks: SplitterBlock[], envelopes: SplitterEnvelopes): SplitterBlock[];
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bareRegionBreaksIntoProse,
|
|
3
|
+
hasKindKey,
|
|
4
|
+
jsonKindSignal,
|
|
5
|
+
kindObjectProseBreak,
|
|
6
|
+
normalizeRecoveredContainerPiece
|
|
7
|
+
} from "@ai-matrx/content-ir/surfaces";
|
|
8
|
+
import { hasUnclosedBacktickRun } from "@ai-matrx/content-ir/source";
|
|
9
|
+
const unescapeKey = (source) => source.replace(/\\_/g, "_");
|
|
10
|
+
const isKindSource = (source) => hasKindKey(source) || hasKindKey(unescapeKey(source));
|
|
11
|
+
const QUOTE_MARKERS = /\n[ \t]{0,3}(?:> ?)+/g;
|
|
12
|
+
const unquote = (source) => source.replace(QUOTE_MARKERS, "\n");
|
|
13
|
+
const NEVER_CUT = /* @__PURE__ */ new Set(["artifact", "matrx", "matrx_file", "table", "tree", "image", "video", "audio"]);
|
|
14
|
+
const JSON_LANGUAGES = /* @__PURE__ */ new Set(["", "json", "jsonc", "json5"]);
|
|
15
|
+
function scan(text, from) {
|
|
16
|
+
const stack = [];
|
|
17
|
+
let inString = false;
|
|
18
|
+
let escaped = false;
|
|
19
|
+
const isArray = text[from] === "[";
|
|
20
|
+
const elements = [];
|
|
21
|
+
let elementStart = -1;
|
|
22
|
+
for (let i = from; i < text.length; i++) {
|
|
23
|
+
const ch = text[i];
|
|
24
|
+
if (inString) {
|
|
25
|
+
if (escaped) escaped = false;
|
|
26
|
+
else if (ch === "\\") escaped = true;
|
|
27
|
+
else if (ch === '"') inString = false;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (isArray && stack.length === 1 && elementStart === -1 && !/[\s,\]]/.test(ch)) elementStart = i;
|
|
31
|
+
if (ch === '"') inString = true;
|
|
32
|
+
else if (ch === "{" || ch === "[") stack.push(ch);
|
|
33
|
+
else if (ch === "}" || ch === "]") {
|
|
34
|
+
stack.pop();
|
|
35
|
+
if (stack.length === 0) return { closedAt: i + 1 };
|
|
36
|
+
if (isArray && stack.length === 1 && elementStart !== -1) {
|
|
37
|
+
elements.push({ start: elementStart, end: i + 1, complete: true });
|
|
38
|
+
elementStart = -1;
|
|
39
|
+
}
|
|
40
|
+
} else if (isArray && stack.length === 1 && ch === ",") {
|
|
41
|
+
if (elementStart !== -1) elements.push({ start: elementStart, end: i, complete: true });
|
|
42
|
+
elementStart = -1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (isArray && elementStart !== -1) elements.push({ start: elementStart, end: text.length, complete: false });
|
|
46
|
+
return { open: { start: from, elements, isArray } };
|
|
47
|
+
}
|
|
48
|
+
function openRootOf(text, prose, xml) {
|
|
49
|
+
let i = 0;
|
|
50
|
+
while (i < text.length) {
|
|
51
|
+
const ch = text[i];
|
|
52
|
+
if (ch !== "{" && ch !== "[") {
|
|
53
|
+
i++;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (prose) {
|
|
57
|
+
const lineStart = text.lastIndexOf("\n", i - 1) + 1;
|
|
58
|
+
if (hasUnclosedBacktickRun(text.slice(lineStart, i))) {
|
|
59
|
+
i++;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (xml && text.lastIndexOf("<", i) > text.lastIndexOf(">", i)) {
|
|
64
|
+
i++;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (ch === "{" && !/^\{\s*(?:"|\}|$)/.test(unquote(text.slice(i)))) {
|
|
68
|
+
i++;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (ch === "[" && !/^\[\s*(?:\{|$)/.test(text.slice(i))) {
|
|
72
|
+
i++;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const result = scan(text, i);
|
|
76
|
+
if ("open" in result) return result.open;
|
|
77
|
+
i = result.closedAt;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
function isCuttable(block) {
|
|
82
|
+
if (NEVER_CUT.has(block.type)) return false;
|
|
83
|
+
if (block.type === "code") {
|
|
84
|
+
if (block.metadata?.genericXmlContainer === true) return true;
|
|
85
|
+
return JSON_LANGUAGES.has((block.language ?? "").toLowerCase());
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
const TRAILING_JSON_FENCE_OPENER = /\n?[ \t]*(?:`{3,}|~{3,})[ \t]*(?:json5?|jsonc)?[ \t]*\n?[ \t]*$/i;
|
|
90
|
+
function recoverPartialKindTail(blocks, envelopes) {
|
|
91
|
+
const last = blocks[blocks.length - 1];
|
|
92
|
+
if (!last || !isCuttable(last)) return blocks;
|
|
93
|
+
const content = last.content;
|
|
94
|
+
const isJsonCode = last.type === "code" && last.metadata?.genericXmlContainer !== true;
|
|
95
|
+
if (isJsonCode && blocks.length > 1 && content.trim() && /^[\s,\]]+$/.test(content)) return blocks.slice(0, -1);
|
|
96
|
+
if (!content.includes("{")) return blocks;
|
|
97
|
+
const isCodeJson = last.type === "code" && last.metadata?.genericXmlContainer !== true;
|
|
98
|
+
const xml = last.type !== "text" && content.trimStart().startsWith("<");
|
|
99
|
+
const root = openRootOf(content, last.type === "text", xml);
|
|
100
|
+
if (!root) return blocks;
|
|
101
|
+
const lead = content.slice(0, root.start);
|
|
102
|
+
if (isCodeJson && !root.isArray) {
|
|
103
|
+
if (!lead.trim() || !/^[\s,[\]]*$/.test(lead)) return blocks;
|
|
104
|
+
return [
|
|
105
|
+
...blocks.slice(0, -1),
|
|
106
|
+
{ type: "code", content: content.slice(root.start), language: "json", metadata: { isComplete: false } }
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
const rootText = last.type === "text" ? unquote(content.slice(root.start)) : content.slice(root.start);
|
|
110
|
+
if (kindObjectProseBreak(rootText.trimStart()) !== null || bareRegionBreaksIntoProse(rootText)) return blocks;
|
|
111
|
+
const opensLine = !lead || lead.endsWith("\n");
|
|
112
|
+
const bareRegion = last.type === "text" && opensLine && (/^(?:\[\s*)?\{\s*"[^"]*"\s*:/.test(rootText) || /^(?:\[\s*)?\{\s*(?:"[^"]*"?\s*)?$/.test(rootText)) && !bareRegionBreaksIntoProse(rootText);
|
|
113
|
+
const headOf = () => {
|
|
114
|
+
const before2 = lead.trimEnd();
|
|
115
|
+
return before2.trim() ? [{ ...last, content: before2 }] : [];
|
|
116
|
+
};
|
|
117
|
+
const splitAsRegion = () => [
|
|
118
|
+
...blocks.slice(0, -1),
|
|
119
|
+
...headOf(),
|
|
120
|
+
{ type: "code", content: rootText, language: "json", metadata: { isComplete: false } }
|
|
121
|
+
];
|
|
122
|
+
const pieces = [];
|
|
123
|
+
if (root.isArray) {
|
|
124
|
+
for (const element of root.elements) {
|
|
125
|
+
const source = content.slice(element.start, element.end).trim();
|
|
126
|
+
if (!source.startsWith("{") || !isKindSource(source)) {
|
|
127
|
+
if (element.complete) return bareRegion ? splitAsRegion() : blocks;
|
|
128
|
+
if (jsonKindSignal(source) === "not_kind") return bareRegion ? splitAsRegion() : blocks;
|
|
129
|
+
}
|
|
130
|
+
pieces.push(
|
|
131
|
+
element.complete ? { type: "code", content: source, language: "json", metadata: envelopes.withIrEnvelope(source, void 0) } : { type: "code", content: source, language: "json", metadata: { isComplete: false } }
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (pieces.length === 0) return blocks;
|
|
135
|
+
} else {
|
|
136
|
+
const raw = last.type === "text" ? unquote(content.slice(root.start)) : content.slice(root.start);
|
|
137
|
+
if (!isKindSource(raw)) return bareRegion ? splitAsRegion() : blocks;
|
|
138
|
+
const source = /"[^"]*\\_/.test(raw) ? unescapeKey(raw) : raw;
|
|
139
|
+
pieces.push({ type: "code", content: source, language: "json", metadata: { isComplete: false } });
|
|
140
|
+
}
|
|
141
|
+
let before = content.slice(0, root.start);
|
|
142
|
+
if (last.type !== "text") before = before.replace(TRAILING_JSON_FENCE_OPENER, "");
|
|
143
|
+
const container = isCodeJson ? "" : normalizeRecoveredContainerPiece(before, last.type, false);
|
|
144
|
+
const head = container && container.trim() ? [{ ...last, content: last.type === "text" ? container.trimEnd() : container }] : [];
|
|
145
|
+
return withoutArrayChrome([...blocks.slice(0, -1), ...head, ...pieces]);
|
|
146
|
+
}
|
|
147
|
+
function recoverPartialKindTailWith(blocks, envelopes) {
|
|
148
|
+
const out = recoverPartialKindTail(blocks, envelopes);
|
|
149
|
+
const cleaned = withoutArrayChrome(out);
|
|
150
|
+
return cleaned.length === out.length ? out : cleaned;
|
|
151
|
+
}
|
|
152
|
+
const isJsonBlock = (block) => block?.type === "code" && block.language === "json";
|
|
153
|
+
function withoutArrayChrome(blocks) {
|
|
154
|
+
return blocks.filter(
|
|
155
|
+
(block, i) => !((block.type === "text" || isJsonBlock(block)) && /^[\s[\],]+$/.test(block.content) && (isJsonBlock(blocks[i - 1]) || isJsonBlock(blocks[i + 1])))
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
function splitAdjacentJsonRootsWith(blocks, envelopes) {
|
|
159
|
+
let changed = false;
|
|
160
|
+
const out = [];
|
|
161
|
+
for (const block of blocks) {
|
|
162
|
+
const roots = block.type === "text" ? adjacentRoots(block.content) : null;
|
|
163
|
+
if (!roots) {
|
|
164
|
+
out.push(block);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
changed = true;
|
|
168
|
+
roots.forEach((root, n) => {
|
|
169
|
+
const open = n === roots.length - 1 && scan(root, 0) && !("closedAt" in scan(root, 0));
|
|
170
|
+
out.push({
|
|
171
|
+
type: "code",
|
|
172
|
+
content: root,
|
|
173
|
+
language: "json",
|
|
174
|
+
metadata: open ? { isComplete: false } : envelopes.withIrEnvelope(root, void 0)
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return changed ? out : blocks;
|
|
179
|
+
}
|
|
180
|
+
function adjacentRoots(content) {
|
|
181
|
+
const text = content.trim();
|
|
182
|
+
if (!text.startsWith("{")) return null;
|
|
183
|
+
const roots = [];
|
|
184
|
+
let i = 0;
|
|
185
|
+
while (i < text.length) {
|
|
186
|
+
if (text[i] !== "{") return null;
|
|
187
|
+
const result = scan(text, i);
|
|
188
|
+
if (!("closedAt" in result)) {
|
|
189
|
+
if (roots.length === 0) return null;
|
|
190
|
+
roots.push(text.slice(i));
|
|
191
|
+
return roots;
|
|
192
|
+
}
|
|
193
|
+
const root = text.slice(i, result.closedAt);
|
|
194
|
+
try {
|
|
195
|
+
const value = JSON.parse(root);
|
|
196
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
197
|
+
} catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
roots.push(root);
|
|
201
|
+
i = result.closedAt;
|
|
202
|
+
while (i < text.length && /\s/.test(text[i])) i++;
|
|
203
|
+
}
|
|
204
|
+
return roots.length >= 2 ? roots : null;
|
|
205
|
+
}
|
|
206
|
+
export {
|
|
207
|
+
recoverPartialKindTailWith,
|
|
208
|
+
splitAdjacentJsonRootsWith
|
|
209
|
+
};
|
package/dist/display/themes.js
CHANGED
|
@@ -833,7 +833,7 @@ const THEMES = {
|
|
|
833
833
|
headerText: "text-neutral-900 dark:text-neutral-100",
|
|
834
834
|
row: {
|
|
835
835
|
even: "bg-white dark:bg-neutral-800",
|
|
836
|
-
odd: "bg-neutral-50 dark:bg-neutral-
|
|
836
|
+
odd: "bg-neutral-50 dark:bg-neutral-900",
|
|
837
837
|
hover: "hover:bg-neutral-100 dark:hover:bg-neutral-700"
|
|
838
838
|
},
|
|
839
839
|
border: "border-neutral-200 dark:border-neutral-700",
|
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.91",
|
|
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,19 +131,19 @@
|
|
|
131
131
|
"unist-util-visit-parents": "^6.0.2",
|
|
132
132
|
"yaml": "^2.9.1",
|
|
133
133
|
"@ai-matrx/agents": "latest",
|
|
134
|
+
"@ai-matrx/alchemy": "latest",
|
|
134
135
|
"@ai-matrx/content-ir": "latest",
|
|
135
136
|
"@ai-matrx/content-ir-react": "latest",
|
|
136
137
|
"@ai-matrx/data": "latest",
|
|
137
|
-
"@ai-matrx/alchemy": "latest",
|
|
138
138
|
"@ai-matrx/canvas": "latest",
|
|
139
|
-
"@ai-matrx/icons": "latest",
|
|
140
139
|
"@ai-matrx/diff": "latest",
|
|
140
|
+
"@ai-matrx/icons": "latest",
|
|
141
141
|
"@ai-matrx/kit": "latest",
|
|
142
|
-
"@ai-matrx/
|
|
142
|
+
"@ai-matrx/media": "latest",
|
|
143
143
|
"@ai-matrx/print": "latest",
|
|
144
|
-
"@ai-matrx/records-ui": "latest",
|
|
145
144
|
"@ai-matrx/records": "latest",
|
|
146
|
-
"@ai-matrx/
|
|
145
|
+
"@ai-matrx/design-system": "latest",
|
|
146
|
+
"@ai-matrx/records-ui": "latest"
|
|
147
147
|
},
|
|
148
148
|
"peerDependencies": {
|
|
149
149
|
"react": ">=19.0.0",
|
|
@@ -204,5 +204,5 @@
|
|
|
204
204
|
"verify:modules-tarball": "node ./scripts/verify-modules-tarball.mjs",
|
|
205
205
|
"verify:tarball": "node ./scripts/verify-tarball.mjs"
|
|
206
206
|
},
|
|
207
|
-
"gitHead": "
|
|
207
|
+
"gitHead": "aeb33c88d654862267985a12ae7e1880d216bda8"
|
|
208
208
|
}
|