@ai-matrx/rich-content 0.0.0 → 0.2.3
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 +80 -0
- package/LICENSE +1 -1
- package/README.md +58 -3
- package/dist/display/blocks/csv/CsvBlock.d.ts +10 -0
- package/dist/display/blocks/csv/CsvBlock.js +245 -0
- package/dist/display/blocks/table/parseMarkdownTable.d.ts +20 -0
- package/dist/display/blocks/table/parseMarkdownTable.js +35 -0
- package/dist/display/chat-markdown/citations/remarkMatrxCite.d.ts +33 -0
- package/dist/display/chat-markdown/citations/remarkMatrxCite.js +71 -0
- package/dist/display/chat-markdown/matrx-variables/remarkMatrxVariable.d.ts +29 -0
- package/dist/display/chat-markdown/matrx-variables/remarkMatrxVariable.js +71 -0
- package/dist/display/chat-markdown/page-break/remarkMatrxPageBreak.d.ts +55 -0
- package/dist/display/chat-markdown/page-break/remarkMatrxPageBreak.js +108 -0
- package/dist/display/chat-markdown/rehypeSafeRawHtml.d.ts +59 -0
- package/dist/display/chat-markdown/rehypeSafeRawHtml.js +140 -0
- package/dist/display/markdown-classification/processors/bock-processors/parse-markdown-table.d.ts +14 -0
- package/dist/display/markdown-classification/processors/bock-processors/parse-markdown-table.js +171 -0
- package/dist/display/markdown-classification/processors/types.d.ts +120 -0
- package/dist/display/markdown-classification/processors/types.js +10 -0
- package/dist/display/markdown-classification/processors/utils/client-blocks.d.ts +169 -0
- package/dist/display/markdown-classification/processors/utils/client-blocks.js +0 -0
- package/dist/display/markdown-classification/processors/utils/content-splitter-core.d.ts +223 -0
- package/dist/display/markdown-classification/processors/utils/content-splitter-core.js +1791 -0
- package/dist/display/markdown-classification/processors/utils/content-splitter-v2.d.ts +15 -0
- package/dist/display/markdown-classification/processors/utils/content-splitter-v2.js +19 -0
- package/dist/display/markdown-classification/processors/utils/expand-text-blocks.d.ts +9 -0
- package/dist/display/markdown-classification/processors/utils/expand-text-blocks.js +36 -0
- package/dist/display/markdown-classification/processors/utils/gfm-table-lines.d.ts +5 -0
- package/dist/display/markdown-classification/processors/utils/gfm-table-lines.js +40 -0
- package/dist/display/markdown-classification/processors/utils/gfm-table-vector-inputs.d.ts +16 -0
- package/dist/display/markdown-classification/processors/utils/gfm-table-vector-inputs.js +385 -0
- package/dist/display/markdown-classification/processors/utils/gfm-table-vectors.d.ts +43 -0
- package/dist/display/markdown-classification/processors/utils/gfm-table-vectors.js +33 -0
- package/dist/display/markdown-classification/processors/utils/table-data-parser.d.ts +9 -0
- package/dist/display/markdown-classification/processors/utils/table-data-parser.js +30 -0
- package/dist/display/markdown-classification/processors/utils/thinking-parser.d.ts +7 -0
- package/dist/display/markdown-classification/processors/utils/thinking-parser.js +70 -0
- package/dist/display/render-block.d.ts +23 -0
- package/dist/display/render-block.js +0 -0
- package/dist/display/themes.d.ts +1023 -0
- package/dist/display/themes.js +1105 -0
- package/dist/display/types.d.ts +32 -0
- package/dist/display/types.js +0 -0
- package/dist/host/ContentIrHostBoundary.d.ts +8 -0
- package/dist/host/ContentIrHostBoundary.js +15 -0
- package/dist/host/ErrorActions.d.ts +4 -0
- package/dist/host/ErrorActions.js +11 -0
- package/dist/host/NestedContent.d.ts +3 -0
- package/dist/host/NestedContent.js +12 -0
- package/dist/host/ResourcePeek.d.ts +6 -0
- package/dist/host/ResourcePeek.js +14 -0
- package/dist/host/component-registry.d.ts +2 -0
- package/dist/host/component-registry.js +25 -0
- package/dist/host/diagnostics.d.ts +2 -0
- package/dist/host/diagnostics.js +15 -0
- package/dist/host/index.d.ts +3 -0
- package/dist/host/index.js +18 -0
- package/dist/host/kind-incident.d.ts +3 -0
- package/dist/host/kind-incident.js +12 -0
- package/dist/host/kind-registry.d.ts +2 -0
- package/dist/host/kind-registry.js +35 -0
- package/dist/host/people.d.ts +3 -0
- package/dist/host/people.js +12 -0
- package/dist/host/registry.d.ts +27 -0
- package/dist/host/registry.js +55 -0
- package/dist/host/splitter-envelopes.d.ts +2 -0
- package/dist/host/splitter-envelopes.js +7 -0
- package/dist/host/tables.d.ts +6 -0
- package/dist/host/tables.js +8 -0
- package/dist/host/toast.d.ts +2 -0
- package/dist/host/toast.js +57 -0
- package/dist/host/types.d.ts +157 -0
- package/dist/host/types.js +0 -0
- package/dist/host/wikilinks.d.ts +15 -0
- package/dist/host/wikilinks.js +23 -0
- package/dist/kinds/host/route-env.d.ts +33 -0
- package/dist/kinds/host/route-env.js +16 -0
- package/dist/kinds/react/KindEscapedNotice.d.ts +14 -0
- package/dist/kinds/react/KindEscapedNotice.js +72 -0
- package/dist/kinds/react/ProvisionalKindBoundary.d.ts +19 -0
- package/dist/kinds/react/ProvisionalKindBoundary.js +17 -0
- package/dist/kinds/react/ensure-kind-renderable.d.ts +14 -0
- package/dist/kinds/react/ensure-kind-renderable.js +45 -0
- package/dist/kinds/react/kind-problems.d.ts +62 -0
- package/dist/kinds/react/kind-problems.js +134 -0
- package/dist/kinds/react/kind-route.d.ts +23 -0
- package/dist/kinds/react/kind-route.js +24 -0
- package/dist/kinds/react/loading/infer-loading-slug.d.ts +58 -0
- package/dist/kinds/react/loading/infer-loading-slug.js +100 -0
- package/dist/kinds/react/loading/kind-loading-components.d.ts +48 -0
- package/dist/kinds/react/loading/kind-loading-components.js +447 -0
- package/dist/kinds/react/loading/kind-loading-registry.d.ts +21 -0
- package/dist/kinds/react/loading/kind-loading-registry.js +62 -0
- package/dist/kinds/react/loading/kind-loading-slugs.d.ts +18 -0
- package/dist/kinds/react/loading/kind-loading-slugs.js +34 -0
- package/dist/kinds/react/loading/kind-loading.types.d.ts +79 -0
- package/dist/kinds/react/loading/kind-loading.types.js +31 -0
- package/dist/kinds/react/loading/resolve-loading-slug.d.ts +29 -0
- package/dist/kinds/react/loading/resolve-loading-slug.js +51 -0
- package/dist/kinds/react/partial-kind-route.d.ts +32 -0
- package/dist/kinds/react/partial-kind-route.js +27 -0
- package/dist/kinds/react/registry-versioned.d.ts +54 -0
- package/dist/kinds/react/registry-versioned.js +18 -0
- package/dist/kinds/react/route-at-version.d.ts +38 -0
- package/dist/kinds/react/route-at-version.js +78 -0
- package/dist/kinds/react/slot/KindSlot.d.ts +87 -0
- package/dist/kinds/react/slot/KindSlot.js +65 -0
- package/dist/kinds/react/use-registry-repaint.d.ts +6 -0
- package/dist/kinds/react/use-registry-repaint.js +36 -0
- package/dist/kinds/react/useIrNode.d.ts +9 -0
- package/dist/kinds/react/useIrNode.js +46 -0
- package/dist/kinds/react/useLiveJsonRegion.d.ts +15 -0
- package/dist/kinds/react/useLiveJsonRegion.js +84 -0
- package/dist/kinds/redux/render-block-envelope.d.ts +14 -0
- package/dist/kinds/redux/render-block-envelope.js +26 -0
- package/dist/kinds/registry/kind-correctors.d.ts +63 -0
- package/dist/kinds/registry/kind-correctors.js +94 -0
- package/dist/kinds/registry/legacy-quiz-envelope.d.ts +1 -0
- package/dist/kinds/registry/legacy-quiz-envelope.js +1 -0
- package/dist/kinds/registry/region-envelope-memo.d.ts +45 -0
- package/dist/kinds/registry/region-envelope-memo.js +94 -0
- package/dist/kinds/registry/registry-debug.d.ts +38 -0
- package/dist/kinds/registry/registry-debug.js +39 -0
- package/dist/markdown-core/MarkdownCore.d.ts +6 -0
- package/dist/markdown-core/MarkdownCore.js +32 -0
- package/dist/markdown-core/MarkdownCoreImpl.d.ts +3 -0
- package/dist/markdown-core/MarkdownCoreImpl.js +27 -0
- package/dist/markdown-core/MarkdownCoreServer.d.ts +4 -0
- package/dist/markdown-core/MarkdownCoreServer.js +26 -0
- package/dist/markdown-core/heading-anchors-context.d.ts +2 -0
- package/dist/markdown-core/heading-anchors-context.js +11 -0
- package/dist/markdown-core/heading-anchors-props.d.ts +2 -0
- package/dist/markdown-core/heading-anchors-props.js +6 -0
- package/dist/markdown-core/markdown-core-html.d.ts +3 -0
- package/dist/markdown-core/markdown-core-html.js +33 -0
- package/dist/markdown-core/markdown-core-presets.d.ts +22 -0
- package/dist/markdown-core/markdown-core-presets.js +118 -0
- package/dist/markdown-core/markdown-core-types.d.ts +43 -0
- package/dist/markdown-core/markdown-core-types.js +0 -0
- package/dist/markdown-core/math/vendor/micromark-math-math-flow.d.ts +15 -0
- package/dist/markdown-core/math/vendor/micromark-math-math-flow.js +143 -0
- package/dist/markdown-core/math/vendor/micromark-math-math-text.d.ts +24 -0
- package/dist/markdown-core/math/vendor/micromark-math-math-text.js +127 -0
- package/dist/markdown-core/math/vendor/micromark-math-syntax.d.ts +30 -0
- package/dist/markdown-core/math/vendor/micromark-math-syntax.js +15 -0
- package/dist/markdown-core/math/vendor/rehype-katex.d.ts +15 -0
- package/dist/markdown-core/math/vendor/rehype-katex.js +83 -0
- package/dist/markdown-core/math/vendor/remark-math.d.ts +9 -0
- package/dist/markdown-core/math/vendor/remark-math.js +20 -0
- package/dist/markdown-core/math-normalizer.d.ts +57 -0
- package/dist/markdown-core/math-normalizer.js +315 -0
- package/dist/markdown-core/plain-title.d.ts +2 -0
- package/dist/markdown-core/plain-title.js +1 -0
- package/dist/markdown-core/stream-heal.d.ts +2 -0
- package/dist/markdown-core/stream-heal.js +184 -0
- package/dist/markdown-core/streaming-context.d.ts +2 -0
- package/dist/markdown-core/streaming-context.js +11 -0
- package/dist/markdown-core/syntax/callout-types.d.ts +9 -0
- package/dist/markdown-core/syntax/callout-types.js +101 -0
- package/dist/markdown-core/syntax/containers.d.ts +17 -0
- package/dist/markdown-core/syntax/containers.js +353 -0
- package/dist/markdown-core/syntax/document-numbering.d.ts +52 -0
- package/dist/markdown-core/syntax/document-numbering.js +184 -0
- package/dist/markdown-core/syntax/elements/CalloutIcon.d.ts +3 -0
- package/dist/markdown-core/syntax/elements/CalloutIcon.js +41 -0
- package/dist/markdown-core/syntax/elements/CrossRef.d.ts +12 -0
- package/dist/markdown-core/syntax/elements/CrossRef.js +57 -0
- package/dist/markdown-core/syntax/elements/CsvFence.d.ts +4 -0
- package/dist/markdown-core/syntax/elements/CsvFence.js +18 -0
- package/dist/markdown-core/syntax/elements/DetailsElements.d.ts +12 -0
- package/dist/markdown-core/syntax/elements/DetailsElements.js +38 -0
- package/dist/markdown-core/syntax/elements/DocumentNumbering.d.ts +20 -0
- package/dist/markdown-core/syntax/elements/DocumentNumbering.js +37 -0
- package/dist/markdown-core/syntax/elements/DocumentPropertiesPanel.d.ts +4 -0
- package/dist/markdown-core/syntax/elements/DocumentPropertiesPanel.js +46 -0
- package/dist/markdown-core/syntax/elements/InDocAnchor.d.ts +11 -0
- package/dist/markdown-core/syntax/elements/InDocAnchor.js +87 -0
- package/dist/markdown-core/syntax/elements/MarkdownSourceEdit.d.ts +24 -0
- package/dist/markdown-core/syntax/elements/MarkdownSourceEdit.js +28 -0
- package/dist/markdown-core/syntax/elements/MatrxTabs.d.ts +10 -0
- package/dist/markdown-core/syntax/elements/MatrxTabs.js +59 -0
- package/dist/markdown-core/syntax/elements/MentionChip.d.ts +7 -0
- package/dist/markdown-core/syntax/elements/MentionChip.js +69 -0
- package/dist/markdown-core/syntax/elements/NestedBody.d.ts +3 -0
- package/dist/markdown-core/syntax/elements/NestedBody.js +13 -0
- package/dist/markdown-core/syntax/elements/TableOfContents.d.ts +5 -0
- package/dist/markdown-core/syntax/elements/TableOfContents.js +44 -0
- package/dist/markdown-core/syntax/elements/TaskCheckbox.d.ts +7 -0
- package/dist/markdown-core/syntax/elements/TaskCheckbox.js +63 -0
- package/dist/markdown-core/syntax/elements/WikiEmbed.d.ts +7 -0
- package/dist/markdown-core/syntax/elements/WikiEmbed.js +68 -0
- package/dist/markdown-core/syntax/elements/WikiLink.d.ts +10 -0
- package/dist/markdown-core/syntax/elements/WikiLink.js +114 -0
- package/dist/markdown-core/syntax/elements/core-syntax-elements.d.ts +22 -0
- package/dist/markdown-core/syntax/elements/core-syntax-elements.js +70 -0
- package/dist/markdown-core/syntax/elements/find-in-document.d.ts +3 -0
- package/dist/markdown-core/syntax/elements/find-in-document.js +20 -0
- package/dist/markdown-core/syntax/elements/wiki-target.d.ts +5 -0
- package/dist/markdown-core/syntax/elements/wiki-target.js +8 -0
- package/dist/markdown-core/syntax/frontmatter.d.ts +26 -0
- package/dist/markdown-core/syntax/frontmatter.js +72 -0
- package/dist/markdown-core/syntax/gfm-cell-pipes.d.ts +11 -0
- package/dist/markdown-core/syntax/gfm-cell-pipes.js +4 -0
- package/dist/markdown-core/syntax/inline-syntax.d.ts +26 -0
- package/dist/markdown-core/syntax/inline-syntax.js +163 -0
- package/dist/markdown-core/syntax/linear-scan.d.ts +46 -0
- package/dist/markdown-core/syntax/linear-scan.js +129 -0
- package/dist/markdown-core/syntax/mdast-helpers.d.ts +48 -0
- package/dist/markdown-core/syntax/mdast-helpers.js +48 -0
- package/dist/markdown-core/syntax/mentions.d.ts +2 -0
- package/dist/markdown-core/syntax/mentions.js +42 -0
- package/dist/markdown-core/syntax/names.d.ts +6 -0
- package/dist/markdown-core/syntax/names.js +18 -0
- package/dist/markdown-core/syntax/prepare-syntax-source.d.ts +1 -0
- package/dist/markdown-core/syntax/prepare-syntax-source.js +78 -0
- package/dist/markdown-core/syntax/rehype-matrx-syntax.d.ts +9 -0
- package/dist/markdown-core/syntax/rehype-matrx-syntax.js +180 -0
- package/dist/markdown-core/syntax/remark-inline-only.d.ts +23 -0
- package/dist/markdown-core/syntax/remark-inline-only.js +25 -0
- package/dist/markdown-core/syntax/remark-matrx-syntax.d.ts +7 -0
- package/dist/markdown-core/syntax/remark-matrx-syntax.js +351 -0
- package/dist/markdown-core/syntax/remark-table-code-pipes.d.ts +7 -0
- package/dist/markdown-core/syntax/remark-table-code-pipes.js +23 -0
- package/dist/markdown-core/syntax/stream-heal-syntax.d.ts +2 -0
- package/dist/markdown-core/syntax/stream-heal-syntax.js +108 -0
- package/dist/markdown-core/syntax/task-source.d.ts +10 -0
- package/dist/markdown-core/syntax/task-source.js +34 -0
- package/dist/markdown-core/syntax/useDocumentProperties.d.ts +3 -0
- package/dist/markdown-core/syntax/useDocumentProperties.js +8 -0
- package/dist/math-vendor-LICENSE-NOTICE.md +18 -0
- package/dist/styles.css +0 -0
- package/dist/utils/addressWithoutNavigating.d.ts +68 -0
- package/dist/utils/addressWithoutNavigating.js +60 -0
- package/dist/utils/client-lazy.d.ts +13 -0
- package/dist/utils/client-lazy.js +24 -0
- package/dist/utils/decision-options.d.ts +21 -0
- package/dist/utils/decision-options.js +32 -0
- package/dist/utils/gfm-lexer.d.ts +5 -0
- package/dist/utils/gfm-lexer.js +10 -0
- package/dist/utils/json.d.ts +63 -0
- package/dist/utils/json.js +22 -0
- package/dist/utils/plain-title.d.ts +25 -0
- package/dist/utils/plain-title.js +51 -0
- package/dist/utils/table-source.d.ts +62 -0
- package/dist/utils/table-source.js +191 -0
- package/dist/utils/youtube.d.ts +52 -0
- package/dist/utils/youtube.js +137 -0
- package/package.json +132 -5
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.3
|
|
4
|
+
|
|
5
|
+
The packaged artifact now includes the MIT attribution notice for the vendored math plugins.
|
|
6
|
+
|
|
7
|
+
### Consumer action
|
|
8
|
+
|
|
9
|
+
None. Math rendering and public imports are unchanged.
|
|
10
|
+
|
|
11
|
+
## 0.2.2
|
|
12
|
+
|
|
13
|
+
`remark-math`, `rehype-katex` and the syntax half of `micromark-extension-math` are vendored
|
|
14
|
+
(`markdown-core/math/vendor/`, MIT, notice beside them): upstream pins `katex ^0.16`, which
|
|
15
|
+
carries GHSA-238p-pmpm-9mq7 (fixed in 0.18.2), so every npm consumer installed a vulnerable KaTeX
|
|
16
|
+
and the publish audit refused 0.2.1. The only KaTeX in a consumer graph is now this package's
|
|
17
|
+
`katex ^0.18.9` (packed consumer: `npm audit` 0 vulnerabilities; the render canary still draws
|
|
18
|
+
math). Behavior unchanged.
|
|
19
|
+
|
|
20
|
+
## 0.2.1
|
|
21
|
+
|
|
22
|
+
The adversarial linear-time guard now runs alone before the parallel Jest suite, preserving its
|
|
23
|
+
strict budget while removing unrelated worker contention from its measurement.
|
|
24
|
+
|
|
25
|
+
## 0.2.0
|
|
26
|
+
|
|
27
|
+
### The kind runtime (`kinds/*`)
|
|
28
|
+
|
|
29
|
+
Moved from matrx-frontend `features/content-ir` (the app's bindings over content-ir-react): the
|
|
30
|
+
kind route and partial/provisional route, route-at-version, registry repaint, ensure-renderable,
|
|
31
|
+
the loading-skeleton registry and slug inference, kind problems, escaped-kind notice, `KindSlot`,
|
|
32
|
+
`useIrNode`, `useLiveJsonRegion`, the region-envelope memo, kind correctors, render-block envelope
|
|
33
|
+
helpers (with the inbound-envelope sanitizer that lived in the chat package), and the route env.
|
|
34
|
+
|
|
35
|
+
New host capabilities: `kindRegistry`, `componentRegistry` (content-ir-react's resolver plus
|
|
36
|
+
`wasRefused` / `debugSnapshot`), `contentIrHost`, `reportKindComponentIncident`. A bare host knows
|
|
37
|
+
no kinds (announced) and resolves no kind components beyond the compiled floor.
|
|
38
|
+
`registerKindCorrector(kind, fn)` replaces the hard-wired CRM `draft_critique` corrector (the host
|
|
39
|
+
registers its own kinds' correctors).
|
|
40
|
+
|
|
41
|
+
`tsx` is a devDependency (the regex linear-time guard runs its worker through it; CI had none).
|
|
42
|
+
|
|
43
|
+
### Consumer action
|
|
44
|
+
|
|
45
|
+
- matrx-frontend: register `kindRegistry`, `componentRegistry`, `contentIrHost`,
|
|
46
|
+
`reportKindComponentIncident` and the `draft_critique` corrector in the host setup; import
|
|
47
|
+
`@ai-matrx/rich-content/kinds/...` for the moved modules.
|
|
48
|
+
|
|
49
|
+
## 0.1.1
|
|
50
|
+
|
|
51
|
+
Same package contents as 0.1.0; the release carries the conformant packed-tarball canary
|
|
52
|
+
(`scripts/verify-tarball.mjs`) the sibling-graph publish gate requires (the 0.1.0 tag predates it).
|
|
53
|
+
|
|
54
|
+
## 0.1.0
|
|
55
|
+
|
|
56
|
+
First release — the formatted-text engine leaves matrx-frontend (Arman, 2026-10-06: chat depends
|
|
57
|
+
on the engine directly, as a package). This release carries:
|
|
58
|
+
|
|
59
|
+
- `markdown-core/*` — the one markdown renderer: presets, the syntax layer (wikilinks, embeds,
|
|
60
|
+
mentions, footnotes, task lists, directives, document numbering, CSV fences), streaming heal,
|
|
61
|
+
the server renderer and the HTML projection. Moved verbatim from `components/markdown-core`.
|
|
62
|
+
- `display/markdown-classification/processors/utils/*` — the block splitter (core + client
|
|
63
|
+
entry), GFM table lines and vectors, thinking parser, client blocks; `display/render-block`;
|
|
64
|
+
the remark/rehype plugins (cite, variable, page break, safe raw HTML); the CSV block; the
|
|
65
|
+
markdown table parser.
|
|
66
|
+
- `utils/*` — plain titles, decision options, JSON helpers, address-without-navigating, YouTube
|
|
67
|
+
URLs, the GFM table source and lexer, `clientLazy` (replaces `next/dynamic` `ssr:false`).
|
|
68
|
+
- `host` — `configureRichContent` with the capabilities in the README; every default announces
|
|
69
|
+
itself.
|
|
70
|
+
|
|
71
|
+
Changes from the app copies: `next/dynamic` → `clientLazy`; app imports → host capabilities
|
|
72
|
+
(toast, error sink, error actions, nested content, wikilinks, people, record peek, splitter
|
|
73
|
+
envelopes, save-to-table); the wikilink "create" reads the organization on the host side
|
|
74
|
+
(the Redux store read is gone). `remend` is pinned at 1.3.1 (1.4.0 trims trailing whitespace in
|
|
75
|
+
held-back streaming text; the heal tests encode 1.3.1's behavior).
|
|
76
|
+
|
|
77
|
+
### Consumer action
|
|
78
|
+
|
|
79
|
+
- matrx-frontend: call `configureRichContent` at the root (see README) and import from
|
|
80
|
+
`@ai-matrx/rich-content/<domain>/<module>`.
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,7 +1,62 @@
|
|
|
1
1
|
# @ai-matrx/rich-content
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The AI Matrx formatted-text engine as portable React: the one markdown renderer (GFM, math,
|
|
4
|
+
directives, wikilinks, mentions, footnotes, streaming heal), the block splitter (static and
|
|
5
|
+
streaming), and — as the extraction completes — the block renderer and generic blocks,
|
|
6
|
+
`<RichContent>` levels, the rich-document action registry and menus, copy, and the selection
|
|
7
|
+
toolbar. The editor is the sibling package `@ai-matrx/rich-editor`.
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
## Install
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @ai-matrx/rich-content
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Peers: `react`, `react-dom` (>= 19). Tailwind consumers add the package's `dist` to their
|
|
16
|
+
content sources (`@source "../node_modules/@ai-matrx/rich-content/dist";`).
|
|
17
|
+
|
|
18
|
+
## Use
|
|
19
|
+
|
|
20
|
+
Import modules by path — one specifier is one module, so client/server boundaries and lazy
|
|
21
|
+
chunks stay intact:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import MarkdownCore from "@ai-matrx/rich-content/markdown-core/MarkdownCore";
|
|
25
|
+
import { splitContentIntoBlocksV2 } from "@ai-matrx/rich-content/display/markdown-classification/processors/utils/content-splitter-v2";
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Host
|
|
29
|
+
|
|
30
|
+
The engine renders with no host at all. Every capability it can ask for has a default that
|
|
31
|
+
announces itself once on the console and marks degraded elements `data-rich-content-host="bare"`.
|
|
32
|
+
Configure once at the app root:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { configureRichContent } from "@ai-matrx/rich-content/host";
|
|
36
|
+
|
|
37
|
+
configureRichContent({
|
|
38
|
+
toast, // your toast (structural: success/error/info/warning/loading/dismiss)
|
|
39
|
+
captureError, // your error sink
|
|
40
|
+
ErrorActions, // extra actions beside an error message
|
|
41
|
+
NestedContent, // nested markdown renderer (standard level)
|
|
42
|
+
wikilinks, // { resolve, create, loadBody } against your records
|
|
43
|
+
resolvePerson, // @mentions
|
|
44
|
+
ResourcePeek, // your record peek
|
|
45
|
+
splitterEnvelopes, // content-IR envelope hooks for complete regions
|
|
46
|
+
useOpenSaveToTable, // "save these rows as a table"
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| Capability | Default on a bare host |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `toast` | the console |
|
|
53
|
+
| `captureError` | `console.error` |
|
|
54
|
+
| `ErrorActions` | none (the message always shows) |
|
|
55
|
+
| `NestedContent` | the source as plain text |
|
|
56
|
+
| `wikilinks` | every link reads "unavailable" |
|
|
57
|
+
| `resolvePerson` | the mention label as written |
|
|
58
|
+
| `ResourcePeek` | the chip is not clickable |
|
|
59
|
+
| `splitterEnvelopes` | regions carry no `metadata.__ir` |
|
|
60
|
+
| `useOpenSaveToTable` | no "save as table" button |
|
|
61
|
+
|
|
62
|
+
MIT licensed.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface CsvBlockProps {
|
|
3
|
+
content: string;
|
|
4
|
+
delimiter?: string;
|
|
5
|
+
className?: string;
|
|
6
|
+
/** Fires when the user commits an inline cell edit — inner markdown body only (no fence). */
|
|
7
|
+
onInnerContentChange?: (inner: string) => void;
|
|
8
|
+
}
|
|
9
|
+
declare const CsvBlock: React.FC<CsvBlockProps>;
|
|
10
|
+
export default CsvBlock;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import { cn } from "@ai-matrx/design-system";
|
|
5
|
+
import {
|
|
6
|
+
Copy,
|
|
7
|
+
Check,
|
|
8
|
+
ArrowUpDown,
|
|
9
|
+
ArrowUp,
|
|
10
|
+
ArrowDown,
|
|
11
|
+
Pencil,
|
|
12
|
+
X,
|
|
13
|
+
Check as CheckIcon,
|
|
14
|
+
Table2
|
|
15
|
+
} from "lucide-react";
|
|
16
|
+
import { toast } from "../../../host/toast.js";
|
|
17
|
+
import { useOpenSaveToTable } from "../../../host/tables.js";
|
|
18
|
+
import { Button } from "@ai-matrx/design-system/controls";
|
|
19
|
+
function parseCsv(content, delimiter) {
|
|
20
|
+
const rows = [];
|
|
21
|
+
const lines = content.split("\n");
|
|
22
|
+
for (const line of lines) {
|
|
23
|
+
if (!line.trim()) continue;
|
|
24
|
+
const cells = [];
|
|
25
|
+
let current = "";
|
|
26
|
+
let inQuotes = false;
|
|
27
|
+
for (let i = 0; i < line.length; i++) {
|
|
28
|
+
const ch = line[i];
|
|
29
|
+
if (inQuotes) {
|
|
30
|
+
if (ch === '"' && line[i + 1] === '"') {
|
|
31
|
+
current += '"';
|
|
32
|
+
i++;
|
|
33
|
+
} else if (ch === '"') {
|
|
34
|
+
inQuotes = false;
|
|
35
|
+
} else {
|
|
36
|
+
current += ch;
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
if (ch === '"') {
|
|
40
|
+
inQuotes = true;
|
|
41
|
+
} else if (ch === delimiter) {
|
|
42
|
+
cells.push(current.trim());
|
|
43
|
+
current = "";
|
|
44
|
+
} else {
|
|
45
|
+
current += ch;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
cells.push(current.trim());
|
|
50
|
+
rows.push(cells);
|
|
51
|
+
}
|
|
52
|
+
return rows;
|
|
53
|
+
}
|
|
54
|
+
function rowsToDelimitedContent(rows, delimiter) {
|
|
55
|
+
return rows.map(
|
|
56
|
+
(row) => row.map((cell) => {
|
|
57
|
+
const needsQuote = cell.includes(delimiter) || cell.includes('"') || cell.includes("\n") || cell.includes("\r");
|
|
58
|
+
const escaped = cell.replace(/"/g, '""');
|
|
59
|
+
return needsQuote ? `"${escaped}"` : escaped;
|
|
60
|
+
}).join(delimiter)
|
|
61
|
+
).join("\n");
|
|
62
|
+
}
|
|
63
|
+
const CsvBlock = ({
|
|
64
|
+
content,
|
|
65
|
+
delimiter = ",",
|
|
66
|
+
className,
|
|
67
|
+
onInnerContentChange
|
|
68
|
+
}) => {
|
|
69
|
+
const [copied, setCopied] = useState(false);
|
|
70
|
+
const [sortCol, setSortCol] = useState(null);
|
|
71
|
+
const [sortDir, setSortDir] = useState(null);
|
|
72
|
+
const [editCell, setEditCell] = useState(
|
|
73
|
+
null
|
|
74
|
+
);
|
|
75
|
+
const [editValue, setEditValue] = useState("");
|
|
76
|
+
const [data, setData] = useState(() => parseCsv(content, delimiter));
|
|
77
|
+
const openSaveToTable = useOpenSaveToTable();
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
setData(parseCsv(content, delimiter));
|
|
80
|
+
setEditCell(null);
|
|
81
|
+
}, [content, delimiter]);
|
|
82
|
+
const headers = data[0] || [];
|
|
83
|
+
const bodyRows = data.slice(1);
|
|
84
|
+
const handleCopy = async () => {
|
|
85
|
+
await navigator.clipboard.writeText(content);
|
|
86
|
+
setCopied(true);
|
|
87
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
88
|
+
};
|
|
89
|
+
const handleSaveAsTable = () => {
|
|
90
|
+
if (headers.length === 0 || bodyRows.length === 0) {
|
|
91
|
+
toast.error("No CSV rows to save.");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
openSaveToTable?.({ grid: { headers, rows: bodyRows } });
|
|
95
|
+
};
|
|
96
|
+
const handleSort = (colIdx) => {
|
|
97
|
+
if (sortCol === colIdx) {
|
|
98
|
+
if (sortDir === "asc") setSortDir("desc");
|
|
99
|
+
else if (sortDir === "desc") {
|
|
100
|
+
setSortCol(null);
|
|
101
|
+
setSortDir(null);
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
setSortCol(colIdx);
|
|
105
|
+
setSortDir("asc");
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const sortedRows = [...bodyRows];
|
|
109
|
+
if (sortCol !== null && sortDir) {
|
|
110
|
+
sortedRows.sort((a, b) => {
|
|
111
|
+
const aVal = a[sortCol] ?? "";
|
|
112
|
+
const bVal = b[sortCol] ?? "";
|
|
113
|
+
const aNum = Number(aVal);
|
|
114
|
+
const bNum = Number(bVal);
|
|
115
|
+
if (!isNaN(aNum) && !isNaN(bNum)) {
|
|
116
|
+
return sortDir === "asc" ? aNum - bNum : bNum - aNum;
|
|
117
|
+
}
|
|
118
|
+
return sortDir === "asc" ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
const startEdit = (rowIdx, colIdx) => {
|
|
122
|
+
setEditCell({ row: rowIdx, col: colIdx });
|
|
123
|
+
setEditValue(bodyRows[rowIdx]?.[colIdx] ?? "");
|
|
124
|
+
};
|
|
125
|
+
const commitEdit = () => {
|
|
126
|
+
if (!editCell) return;
|
|
127
|
+
const newData = [...data];
|
|
128
|
+
const actualRow = editCell.row + 1;
|
|
129
|
+
if (!newData[actualRow]) return;
|
|
130
|
+
newData[actualRow] = [...newData[actualRow]];
|
|
131
|
+
newData[actualRow][editCell.col] = editValue;
|
|
132
|
+
setData(newData);
|
|
133
|
+
setEditCell(null);
|
|
134
|
+
const inner = rowsToDelimitedContent(newData, delimiter);
|
|
135
|
+
onInnerContentChange?.(inner);
|
|
136
|
+
};
|
|
137
|
+
const cancelEdit = () => {
|
|
138
|
+
setEditCell(null);
|
|
139
|
+
};
|
|
140
|
+
const getSortIcon = (colIdx) => {
|
|
141
|
+
if (sortCol !== colIdx)
|
|
142
|
+
return /* @__PURE__ */ jsx(ArrowUpDown, { className: "w-3 h-3 opacity-0 group-hover/th:opacity-50" });
|
|
143
|
+
if (sortDir === "asc") return /* @__PURE__ */ jsx(ArrowUp, { className: "w-3 h-3 text-primary" });
|
|
144
|
+
return /* @__PURE__ */ jsx(ArrowDown, { className: "w-3 h-3 text-primary" });
|
|
145
|
+
};
|
|
146
|
+
return /* @__PURE__ */ jsxs(
|
|
147
|
+
"div",
|
|
148
|
+
{
|
|
149
|
+
className: cn(
|
|
150
|
+
"my-3 rounded-lg border border-border bg-card overflow-hidden",
|
|
151
|
+
className
|
|
152
|
+
),
|
|
153
|
+
children: [
|
|
154
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-1.5 border-b border-border/50 bg-muted/30", children: [
|
|
155
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
156
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-mono font-semibold text-green-600 dark:text-green-400", children: "CSV" }),
|
|
157
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
158
|
+
bodyRows.length,
|
|
159
|
+
" rows x ",
|
|
160
|
+
headers.length,
|
|
161
|
+
" cols"
|
|
162
|
+
] })
|
|
163
|
+
] }),
|
|
164
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
165
|
+
openSaveToTable ? /* @__PURE__ */ jsx(Button, { variant: "quiet", icon: /* @__PURE__ */ jsx(Table2, {}), onClick: handleSaveAsTable, title: "Save to a table", children: /* @__PURE__ */ jsx("span", { children: "Save to a table" }) }) : null,
|
|
166
|
+
/* @__PURE__ */ jsx(Button, { variant: "quiet", icon: copied ? /* @__PURE__ */ jsx(Check, {}) : /* @__PURE__ */ jsx(Copy, {}), glyphTone: copied ? "success" : void 0, onClick: handleCopy, "aria-label": "Copy" })
|
|
167
|
+
] })
|
|
168
|
+
] }),
|
|
169
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm border-collapse", children: [
|
|
170
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "bg-muted/50", children: [
|
|
171
|
+
/* @__PURE__ */ jsx("th", { className: "px-1 py-1 text-center text-xs text-muted-foreground font-mono border-r border-border/30 w-8", children: "#" }),
|
|
172
|
+
headers.map((h, colIdx) => /* @__PURE__ */ jsx(
|
|
173
|
+
"th",
|
|
174
|
+
{
|
|
175
|
+
onClick: () => handleSort(colIdx),
|
|
176
|
+
className: "group/th px-3 py-1.5 text-left text-xs font-semibold text-foreground border-r border-border/30 last:border-r-0 cursor-pointer hover:bg-muted/70 transition-colors select-none",
|
|
177
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
178
|
+
/* @__PURE__ */ jsx("span", { children: h }),
|
|
179
|
+
getSortIcon(colIdx)
|
|
180
|
+
] })
|
|
181
|
+
},
|
|
182
|
+
colIdx
|
|
183
|
+
))
|
|
184
|
+
] }) }),
|
|
185
|
+
/* @__PURE__ */ jsx("tbody", { children: sortedRows.map((row, rowIdx) => /* @__PURE__ */ jsxs(
|
|
186
|
+
"tr",
|
|
187
|
+
{
|
|
188
|
+
className: "border-t border-border/20 hover:bg-muted/20 transition-colors",
|
|
189
|
+
children: [
|
|
190
|
+
/* @__PURE__ */ jsx("td", { className: "px-1 py-1 text-center text-xs text-muted-foreground font-mono border-r border-border/30", children: rowIdx + 1 }),
|
|
191
|
+
headers.map((_, colIdx) => {
|
|
192
|
+
const isEditing = editCell?.row === rowIdx && editCell?.col === colIdx;
|
|
193
|
+
const cellValue = row[colIdx] ?? "";
|
|
194
|
+
return /* @__PURE__ */ jsx(
|
|
195
|
+
"td",
|
|
196
|
+
{
|
|
197
|
+
className: "group/cell px-3 py-1.5 text-foreground border-r border-border/30 last:border-r-0 relative",
|
|
198
|
+
onDoubleClick: () => startEdit(rowIdx, colIdx),
|
|
199
|
+
children: isEditing ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
200
|
+
/* @__PURE__ */ jsx(
|
|
201
|
+
"input",
|
|
202
|
+
{
|
|
203
|
+
type: "text",
|
|
204
|
+
value: editValue,
|
|
205
|
+
onChange: (e) => setEditValue(e.target.value),
|
|
206
|
+
onKeyDown: (e) => {
|
|
207
|
+
if (e.key === "Enter") commitEdit();
|
|
208
|
+
if (e.key === "Escape") cancelEdit();
|
|
209
|
+
},
|
|
210
|
+
className: "w-full px-1 py-0 text-sm bg-background border border-primary rounded focus:outline-none",
|
|
211
|
+
style: { fontSize: "16px" },
|
|
212
|
+
autoFocus: true
|
|
213
|
+
}
|
|
214
|
+
),
|
|
215
|
+
/* @__PURE__ */ jsx(Button, { variant: "quiet", icon: /* @__PURE__ */ jsx(CheckIcon, {}), onClick: commitEdit, "aria-label": "Done" }),
|
|
216
|
+
/* @__PURE__ */ jsx(Button, { variant: "quiet", icon: /* @__PURE__ */ jsx(X, {}), removes: true, onClick: cancelEdit, "aria-label": "Close" })
|
|
217
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
218
|
+
/* @__PURE__ */ jsx(
|
|
219
|
+
"span",
|
|
220
|
+
{
|
|
221
|
+
className: cn(
|
|
222
|
+
!isNaN(Number(cellValue)) && cellValue !== "" && "text-purple-600 dark:text-purple-400 font-mono tabular-nums"
|
|
223
|
+
),
|
|
224
|
+
children: cellValue
|
|
225
|
+
}
|
|
226
|
+
),
|
|
227
|
+
/* @__PURE__ */ jsx(Button, { variant: "quiet", icon: /* @__PURE__ */ jsx(Pencil, {}), onClick: () => startEdit(rowIdx, colIdx), "aria-label": "Edit", className: "absolute right-1 top-1/2 opacity-0 hover:!opacity-100" })
|
|
228
|
+
] })
|
|
229
|
+
},
|
|
230
|
+
colIdx
|
|
231
|
+
);
|
|
232
|
+
})
|
|
233
|
+
]
|
|
234
|
+
},
|
|
235
|
+
rowIdx
|
|
236
|
+
)) })
|
|
237
|
+
] }) })
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
);
|
|
241
|
+
};
|
|
242
|
+
var CsvBlock_default = CsvBlock;
|
|
243
|
+
export {
|
|
244
|
+
CsvBlock_default as default
|
|
245
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* parseMarkdownTable — the ONE markdown-table parser.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from StreamingTableRenderer so the renderer AND the artifact
|
|
5
|
+
* "Convert to table" path parse identically (no forked regex — the doctrine
|
|
6
|
+
* primitive). Returns null for anything that isn't a well-formed markdown table
|
|
7
|
+
* (header row + separator row + data rows); tolerant of streaming (0 data rows
|
|
8
|
+
* yet) and of leading/trailing pipes.
|
|
9
|
+
*/
|
|
10
|
+
export interface ParsedTable {
|
|
11
|
+
headers: string[];
|
|
12
|
+
rows: string[][];
|
|
13
|
+
/** Rows keyed by markdown-stripped header — the shape DB save / JSON export want. */
|
|
14
|
+
normalizedData: Array<{
|
|
15
|
+
[key: string]: string;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
/** Strip inline markdown from a header so it forms a clean object key. */
|
|
19
|
+
export declare function cleanTableHeaderKey(header: string): string;
|
|
20
|
+
export declare function parseMarkdownTable(content: string): ParsedTable | null;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { unwrapCodeSpans } from "@ai-matrx/content-ir/source";
|
|
2
|
+
import { dataRowIndexes, rowCells } from "../../../utils/table-source.js";
|
|
3
|
+
import { isGfmDelimiterRow } from "../../markdown-classification/processors/utils/gfm-table-lines.js";
|
|
4
|
+
function cleanTableHeaderKey(header) {
|
|
5
|
+
return unwrapCodeSpans(header).replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/(?<![A-Za-z0-9])_([^_\n]+?)_(?![A-Za-z0-9])/g, "$1").replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1|$2").trim();
|
|
6
|
+
}
|
|
7
|
+
function parseMarkdownTable(content) {
|
|
8
|
+
try {
|
|
9
|
+
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
10
|
+
if (lines.length < 2) return null;
|
|
11
|
+
const headerLine = lines[0];
|
|
12
|
+
const separatorLine = lines[1];
|
|
13
|
+
if (!isGfmDelimiterRow(separatorLine)) return null;
|
|
14
|
+
const parseRow = rowCells;
|
|
15
|
+
const headers = parseRow(headerLine);
|
|
16
|
+
if (headers.length === 0) return null;
|
|
17
|
+
const dataLines = lines.slice(2);
|
|
18
|
+
const validRows = dataRowIndexes(dataLines, dataLines.map((_line, index) => index)).map((index) => parseRow(dataLines[index] ?? ""));
|
|
19
|
+
const normalizedData = validRows.map((row) => {
|
|
20
|
+
const rowData = {};
|
|
21
|
+
headers.forEach((header, index) => {
|
|
22
|
+
rowData[cleanTableHeaderKey(header)] = index < row.length ? row[index] : "";
|
|
23
|
+
});
|
|
24
|
+
return rowData;
|
|
25
|
+
});
|
|
26
|
+
return { headers, rows: validRows, normalizedData };
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error("[parseMarkdownTable] Parse error:", error);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
cleanTableHeaderKey,
|
|
34
|
+
parseMarkdownTable
|
|
35
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remark plugin: recognizes inline citation markers `<matrxcite n="3" />`
|
|
3
|
+
* inside markdown TEXT nodes and emits inline `matrx-cite` custom elements so
|
|
4
|
+
* `react-markdown` renders them via the `components` map
|
|
5
|
+
* (→ `CitationMarkerInline`).
|
|
6
|
+
*
|
|
7
|
+
* Why text nodes (mirroring `remarkMatrxVariable`), not raw HTML / the block
|
|
8
|
+
* splitter: `preprocessContent` in BasicMarkdownContent escapes every
|
|
9
|
+
* non-allow-listed tag to `<matrxcite …>`, and CommonMark decodes the
|
|
10
|
+
* entities back to literal `<matrxcite n="3" />` inside text nodes — so the
|
|
11
|
+
* marker arrives here as plain text, safely inside the paragraph flow. The
|
|
12
|
+
* splitter path (audiocite-style) would make each marker its OWN vertically
|
|
13
|
+
* stacked block and re-break citation-split sentences mid-line — the exact
|
|
14
|
+
* regression fixed on 2026-07-16.
|
|
15
|
+
*
|
|
16
|
+
* Design rules (same as remarkMatrxVariable):
|
|
17
|
+
* - Runs at render time on the mdast AST — source strings are never mutated.
|
|
18
|
+
* - Only transforms `text` nodes; code spans/fences keep the literal text.
|
|
19
|
+
* - The source number lives on the DOM as `data-n`.
|
|
20
|
+
*
|
|
21
|
+
* Markers are inserted into display text by
|
|
22
|
+
* `features/agents/redux/execution-system/messages/message-citations.ts`
|
|
23
|
+
* (`insertCitationMarkers`) — this plugin is the render-side half.
|
|
24
|
+
*/
|
|
25
|
+
interface MdastNode {
|
|
26
|
+
type: string;
|
|
27
|
+
value?: string;
|
|
28
|
+
children?: MdastNode[];
|
|
29
|
+
data?: Record<string, unknown>;
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
export default function remarkMatrxCite(): (tree: MdastNode) => void;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const CITE_PATTERN = /<matrxcite\s+n="(\d+)"\s*\/>/g;
|
|
2
|
+
function expandTextNode(value) {
|
|
3
|
+
CITE_PATTERN.lastIndex = 0;
|
|
4
|
+
const parts = [];
|
|
5
|
+
let lastIdx = 0;
|
|
6
|
+
let match;
|
|
7
|
+
let matched = false;
|
|
8
|
+
while ((match = CITE_PATTERN.exec(value)) !== null) {
|
|
9
|
+
matched = true;
|
|
10
|
+
const start = match.index;
|
|
11
|
+
const end = start + match[0].length;
|
|
12
|
+
const n = match[1];
|
|
13
|
+
if (start > lastIdx) {
|
|
14
|
+
parts.push({ type: "text", value: value.slice(lastIdx, start) });
|
|
15
|
+
}
|
|
16
|
+
parts.push({
|
|
17
|
+
type: "matrxCite",
|
|
18
|
+
data: {
|
|
19
|
+
hName: "matrx-cite",
|
|
20
|
+
hProperties: { "data-n": n }
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
lastIdx = end;
|
|
24
|
+
}
|
|
25
|
+
if (!matched) {
|
|
26
|
+
return [{ type: "text", value }];
|
|
27
|
+
}
|
|
28
|
+
if (lastIdx < value.length) {
|
|
29
|
+
parts.push({ type: "text", value: value.slice(lastIdx) });
|
|
30
|
+
}
|
|
31
|
+
return parts;
|
|
32
|
+
}
|
|
33
|
+
const SKIP_TYPES = /* @__PURE__ */ new Set([
|
|
34
|
+
"code",
|
|
35
|
+
"inlineCode",
|
|
36
|
+
"html",
|
|
37
|
+
"yaml",
|
|
38
|
+
"toml",
|
|
39
|
+
"math",
|
|
40
|
+
"inlineMath"
|
|
41
|
+
]);
|
|
42
|
+
function walk(node) {
|
|
43
|
+
if (!node || SKIP_TYPES.has(node.type)) return;
|
|
44
|
+
const children = node.children;
|
|
45
|
+
if (!Array.isArray(children) || children.length === 0) return;
|
|
46
|
+
const next = [];
|
|
47
|
+
let mutated = false;
|
|
48
|
+
for (const child of children) {
|
|
49
|
+
if (child?.type === "text" && typeof child.value === "string" && child.value.includes("<matrxcite")) {
|
|
50
|
+
const expanded = expandTextNode(child.value);
|
|
51
|
+
if (expanded.length !== 1 || expanded[0].type !== "text") {
|
|
52
|
+
mutated = true;
|
|
53
|
+
}
|
|
54
|
+
next.push(...expanded);
|
|
55
|
+
} else {
|
|
56
|
+
next.push(child);
|
|
57
|
+
walk(child);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (mutated) {
|
|
61
|
+
node.children = next;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function remarkMatrxCite() {
|
|
65
|
+
return (tree) => {
|
|
66
|
+
walk(tree);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
remarkMatrxCite as default
|
|
71
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remark plugin: recognizes `{{variable_name}}` tokens inside markdown text
|
|
3
|
+
* and emits inline `matrx-variable` custom elements so `react-markdown` can
|
|
4
|
+
* render them via the `components` map.
|
|
5
|
+
*
|
|
6
|
+
* Design rules:
|
|
7
|
+
* - Runs at render time on the mdast AST — the source `content` string is
|
|
8
|
+
* never modified. Redis/Redux/DB copies of the message are untouched.
|
|
9
|
+
* - Only transforms `text` nodes. `inlineCode`, `code` blocks, `link`, etc.
|
|
10
|
+
* are left alone, so `` `{{literal}}` `` and ```` ```js\n{{foo}}\n``` ````
|
|
11
|
+
* stay as plain text. Inside links/headings/emphasis the pattern still
|
|
12
|
+
* applies because those wrap `text` children.
|
|
13
|
+
* - Pattern requires a closed `}}`. Partial tokens during streaming (e.g.
|
|
14
|
+
* `{{user_na`) stay plain text until the closing braces arrive.
|
|
15
|
+
* - Emits a custom element `matrx-variable` with `data-name="..."` so the
|
|
16
|
+
* renderer can look up the raw snake_case key.
|
|
17
|
+
*
|
|
18
|
+
* Pattern: `{{identifier}}` where identifier starts with a letter or underscore,
|
|
19
|
+
* followed by letters, digits, underscores, or dots.
|
|
20
|
+
*/
|
|
21
|
+
interface MdastNode {
|
|
22
|
+
type: string;
|
|
23
|
+
value?: string;
|
|
24
|
+
children?: MdastNode[];
|
|
25
|
+
data?: Record<string, unknown>;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
export default function remarkMatrxVariable(): (tree: MdastNode) => void;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const VARIABLE_PATTERN = /\{\{([a-zA-Z_][a-zA-Z0-9_.]*)\}\}/g;
|
|
2
|
+
function expandTextNode(value) {
|
|
3
|
+
VARIABLE_PATTERN.lastIndex = 0;
|
|
4
|
+
const parts = [];
|
|
5
|
+
let lastIdx = 0;
|
|
6
|
+
let match;
|
|
7
|
+
let matched = false;
|
|
8
|
+
while ((match = VARIABLE_PATTERN.exec(value)) !== null) {
|
|
9
|
+
matched = true;
|
|
10
|
+
const start = match.index;
|
|
11
|
+
const end = start + match[0].length;
|
|
12
|
+
const name = match[1];
|
|
13
|
+
if (start > lastIdx) {
|
|
14
|
+
parts.push({ type: "text", value: value.slice(lastIdx, start) });
|
|
15
|
+
}
|
|
16
|
+
parts.push({
|
|
17
|
+
type: "matrxVariable",
|
|
18
|
+
data: {
|
|
19
|
+
hName: "matrx-variable",
|
|
20
|
+
hProperties: { "data-name": name }
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
lastIdx = end;
|
|
24
|
+
}
|
|
25
|
+
if (!matched) {
|
|
26
|
+
return [{ type: "text", value }];
|
|
27
|
+
}
|
|
28
|
+
if (lastIdx < value.length) {
|
|
29
|
+
parts.push({ type: "text", value: value.slice(lastIdx) });
|
|
30
|
+
}
|
|
31
|
+
return parts;
|
|
32
|
+
}
|
|
33
|
+
const SKIP_TYPES = /* @__PURE__ */ new Set([
|
|
34
|
+
"code",
|
|
35
|
+
"inlineCode",
|
|
36
|
+
"html",
|
|
37
|
+
"yaml",
|
|
38
|
+
"toml",
|
|
39
|
+
"math",
|
|
40
|
+
"inlineMath"
|
|
41
|
+
]);
|
|
42
|
+
function walk(node) {
|
|
43
|
+
if (!node || SKIP_TYPES.has(node.type)) return;
|
|
44
|
+
const children = node.children;
|
|
45
|
+
if (!Array.isArray(children) || children.length === 0) return;
|
|
46
|
+
const next = [];
|
|
47
|
+
let mutated = false;
|
|
48
|
+
for (const child of children) {
|
|
49
|
+
if (child?.type === "text" && typeof child.value === "string" && child.value.includes("{{")) {
|
|
50
|
+
const expanded = expandTextNode(child.value);
|
|
51
|
+
if (expanded.length !== 1 || expanded[0].type !== "text") {
|
|
52
|
+
mutated = true;
|
|
53
|
+
}
|
|
54
|
+
next.push(...expanded);
|
|
55
|
+
} else {
|
|
56
|
+
next.push(child);
|
|
57
|
+
walk(child);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (mutated) {
|
|
61
|
+
node.children = next;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function remarkMatrxVariable() {
|
|
65
|
+
return (tree) => {
|
|
66
|
+
walk(tree);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
remarkMatrxVariable as default
|
|
71
|
+
};
|