@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.
Files changed (248) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/LICENSE +1 -1
  3. package/README.md +58 -3
  4. package/dist/display/blocks/csv/CsvBlock.d.ts +10 -0
  5. package/dist/display/blocks/csv/CsvBlock.js +245 -0
  6. package/dist/display/blocks/table/parseMarkdownTable.d.ts +20 -0
  7. package/dist/display/blocks/table/parseMarkdownTable.js +35 -0
  8. package/dist/display/chat-markdown/citations/remarkMatrxCite.d.ts +33 -0
  9. package/dist/display/chat-markdown/citations/remarkMatrxCite.js +71 -0
  10. package/dist/display/chat-markdown/matrx-variables/remarkMatrxVariable.d.ts +29 -0
  11. package/dist/display/chat-markdown/matrx-variables/remarkMatrxVariable.js +71 -0
  12. package/dist/display/chat-markdown/page-break/remarkMatrxPageBreak.d.ts +55 -0
  13. package/dist/display/chat-markdown/page-break/remarkMatrxPageBreak.js +108 -0
  14. package/dist/display/chat-markdown/rehypeSafeRawHtml.d.ts +59 -0
  15. package/dist/display/chat-markdown/rehypeSafeRawHtml.js +140 -0
  16. package/dist/display/markdown-classification/processors/bock-processors/parse-markdown-table.d.ts +14 -0
  17. package/dist/display/markdown-classification/processors/bock-processors/parse-markdown-table.js +171 -0
  18. package/dist/display/markdown-classification/processors/types.d.ts +120 -0
  19. package/dist/display/markdown-classification/processors/types.js +10 -0
  20. package/dist/display/markdown-classification/processors/utils/client-blocks.d.ts +169 -0
  21. package/dist/display/markdown-classification/processors/utils/client-blocks.js +0 -0
  22. package/dist/display/markdown-classification/processors/utils/content-splitter-core.d.ts +223 -0
  23. package/dist/display/markdown-classification/processors/utils/content-splitter-core.js +1791 -0
  24. package/dist/display/markdown-classification/processors/utils/content-splitter-v2.d.ts +15 -0
  25. package/dist/display/markdown-classification/processors/utils/content-splitter-v2.js +19 -0
  26. package/dist/display/markdown-classification/processors/utils/expand-text-blocks.d.ts +9 -0
  27. package/dist/display/markdown-classification/processors/utils/expand-text-blocks.js +36 -0
  28. package/dist/display/markdown-classification/processors/utils/gfm-table-lines.d.ts +5 -0
  29. package/dist/display/markdown-classification/processors/utils/gfm-table-lines.js +40 -0
  30. package/dist/display/markdown-classification/processors/utils/gfm-table-vector-inputs.d.ts +16 -0
  31. package/dist/display/markdown-classification/processors/utils/gfm-table-vector-inputs.js +385 -0
  32. package/dist/display/markdown-classification/processors/utils/gfm-table-vectors.d.ts +43 -0
  33. package/dist/display/markdown-classification/processors/utils/gfm-table-vectors.js +33 -0
  34. package/dist/display/markdown-classification/processors/utils/table-data-parser.d.ts +9 -0
  35. package/dist/display/markdown-classification/processors/utils/table-data-parser.js +30 -0
  36. package/dist/display/markdown-classification/processors/utils/thinking-parser.d.ts +7 -0
  37. package/dist/display/markdown-classification/processors/utils/thinking-parser.js +70 -0
  38. package/dist/display/render-block.d.ts +23 -0
  39. package/dist/display/render-block.js +0 -0
  40. package/dist/display/themes.d.ts +1023 -0
  41. package/dist/display/themes.js +1105 -0
  42. package/dist/display/types.d.ts +32 -0
  43. package/dist/display/types.js +0 -0
  44. package/dist/host/ContentIrHostBoundary.d.ts +8 -0
  45. package/dist/host/ContentIrHostBoundary.js +15 -0
  46. package/dist/host/ErrorActions.d.ts +4 -0
  47. package/dist/host/ErrorActions.js +11 -0
  48. package/dist/host/NestedContent.d.ts +3 -0
  49. package/dist/host/NestedContent.js +12 -0
  50. package/dist/host/ResourcePeek.d.ts +6 -0
  51. package/dist/host/ResourcePeek.js +14 -0
  52. package/dist/host/component-registry.d.ts +2 -0
  53. package/dist/host/component-registry.js +25 -0
  54. package/dist/host/diagnostics.d.ts +2 -0
  55. package/dist/host/diagnostics.js +15 -0
  56. package/dist/host/index.d.ts +3 -0
  57. package/dist/host/index.js +18 -0
  58. package/dist/host/kind-incident.d.ts +3 -0
  59. package/dist/host/kind-incident.js +12 -0
  60. package/dist/host/kind-registry.d.ts +2 -0
  61. package/dist/host/kind-registry.js +35 -0
  62. package/dist/host/people.d.ts +3 -0
  63. package/dist/host/people.js +12 -0
  64. package/dist/host/registry.d.ts +27 -0
  65. package/dist/host/registry.js +55 -0
  66. package/dist/host/splitter-envelopes.d.ts +2 -0
  67. package/dist/host/splitter-envelopes.js +7 -0
  68. package/dist/host/tables.d.ts +6 -0
  69. package/dist/host/tables.js +8 -0
  70. package/dist/host/toast.d.ts +2 -0
  71. package/dist/host/toast.js +57 -0
  72. package/dist/host/types.d.ts +157 -0
  73. package/dist/host/types.js +0 -0
  74. package/dist/host/wikilinks.d.ts +15 -0
  75. package/dist/host/wikilinks.js +23 -0
  76. package/dist/kinds/host/route-env.d.ts +33 -0
  77. package/dist/kinds/host/route-env.js +16 -0
  78. package/dist/kinds/react/KindEscapedNotice.d.ts +14 -0
  79. package/dist/kinds/react/KindEscapedNotice.js +72 -0
  80. package/dist/kinds/react/ProvisionalKindBoundary.d.ts +19 -0
  81. package/dist/kinds/react/ProvisionalKindBoundary.js +17 -0
  82. package/dist/kinds/react/ensure-kind-renderable.d.ts +14 -0
  83. package/dist/kinds/react/ensure-kind-renderable.js +45 -0
  84. package/dist/kinds/react/kind-problems.d.ts +62 -0
  85. package/dist/kinds/react/kind-problems.js +134 -0
  86. package/dist/kinds/react/kind-route.d.ts +23 -0
  87. package/dist/kinds/react/kind-route.js +24 -0
  88. package/dist/kinds/react/loading/infer-loading-slug.d.ts +58 -0
  89. package/dist/kinds/react/loading/infer-loading-slug.js +100 -0
  90. package/dist/kinds/react/loading/kind-loading-components.d.ts +48 -0
  91. package/dist/kinds/react/loading/kind-loading-components.js +447 -0
  92. package/dist/kinds/react/loading/kind-loading-registry.d.ts +21 -0
  93. package/dist/kinds/react/loading/kind-loading-registry.js +62 -0
  94. package/dist/kinds/react/loading/kind-loading-slugs.d.ts +18 -0
  95. package/dist/kinds/react/loading/kind-loading-slugs.js +34 -0
  96. package/dist/kinds/react/loading/kind-loading.types.d.ts +79 -0
  97. package/dist/kinds/react/loading/kind-loading.types.js +31 -0
  98. package/dist/kinds/react/loading/resolve-loading-slug.d.ts +29 -0
  99. package/dist/kinds/react/loading/resolve-loading-slug.js +51 -0
  100. package/dist/kinds/react/partial-kind-route.d.ts +32 -0
  101. package/dist/kinds/react/partial-kind-route.js +27 -0
  102. package/dist/kinds/react/registry-versioned.d.ts +54 -0
  103. package/dist/kinds/react/registry-versioned.js +18 -0
  104. package/dist/kinds/react/route-at-version.d.ts +38 -0
  105. package/dist/kinds/react/route-at-version.js +78 -0
  106. package/dist/kinds/react/slot/KindSlot.d.ts +87 -0
  107. package/dist/kinds/react/slot/KindSlot.js +65 -0
  108. package/dist/kinds/react/use-registry-repaint.d.ts +6 -0
  109. package/dist/kinds/react/use-registry-repaint.js +36 -0
  110. package/dist/kinds/react/useIrNode.d.ts +9 -0
  111. package/dist/kinds/react/useIrNode.js +46 -0
  112. package/dist/kinds/react/useLiveJsonRegion.d.ts +15 -0
  113. package/dist/kinds/react/useLiveJsonRegion.js +84 -0
  114. package/dist/kinds/redux/render-block-envelope.d.ts +14 -0
  115. package/dist/kinds/redux/render-block-envelope.js +26 -0
  116. package/dist/kinds/registry/kind-correctors.d.ts +63 -0
  117. package/dist/kinds/registry/kind-correctors.js +94 -0
  118. package/dist/kinds/registry/legacy-quiz-envelope.d.ts +1 -0
  119. package/dist/kinds/registry/legacy-quiz-envelope.js +1 -0
  120. package/dist/kinds/registry/region-envelope-memo.d.ts +45 -0
  121. package/dist/kinds/registry/region-envelope-memo.js +94 -0
  122. package/dist/kinds/registry/registry-debug.d.ts +38 -0
  123. package/dist/kinds/registry/registry-debug.js +39 -0
  124. package/dist/markdown-core/MarkdownCore.d.ts +6 -0
  125. package/dist/markdown-core/MarkdownCore.js +32 -0
  126. package/dist/markdown-core/MarkdownCoreImpl.d.ts +3 -0
  127. package/dist/markdown-core/MarkdownCoreImpl.js +27 -0
  128. package/dist/markdown-core/MarkdownCoreServer.d.ts +4 -0
  129. package/dist/markdown-core/MarkdownCoreServer.js +26 -0
  130. package/dist/markdown-core/heading-anchors-context.d.ts +2 -0
  131. package/dist/markdown-core/heading-anchors-context.js +11 -0
  132. package/dist/markdown-core/heading-anchors-props.d.ts +2 -0
  133. package/dist/markdown-core/heading-anchors-props.js +6 -0
  134. package/dist/markdown-core/markdown-core-html.d.ts +3 -0
  135. package/dist/markdown-core/markdown-core-html.js +33 -0
  136. package/dist/markdown-core/markdown-core-presets.d.ts +22 -0
  137. package/dist/markdown-core/markdown-core-presets.js +118 -0
  138. package/dist/markdown-core/markdown-core-types.d.ts +43 -0
  139. package/dist/markdown-core/markdown-core-types.js +0 -0
  140. package/dist/markdown-core/math/vendor/micromark-math-math-flow.d.ts +15 -0
  141. package/dist/markdown-core/math/vendor/micromark-math-math-flow.js +143 -0
  142. package/dist/markdown-core/math/vendor/micromark-math-math-text.d.ts +24 -0
  143. package/dist/markdown-core/math/vendor/micromark-math-math-text.js +127 -0
  144. package/dist/markdown-core/math/vendor/micromark-math-syntax.d.ts +30 -0
  145. package/dist/markdown-core/math/vendor/micromark-math-syntax.js +15 -0
  146. package/dist/markdown-core/math/vendor/rehype-katex.d.ts +15 -0
  147. package/dist/markdown-core/math/vendor/rehype-katex.js +83 -0
  148. package/dist/markdown-core/math/vendor/remark-math.d.ts +9 -0
  149. package/dist/markdown-core/math/vendor/remark-math.js +20 -0
  150. package/dist/markdown-core/math-normalizer.d.ts +57 -0
  151. package/dist/markdown-core/math-normalizer.js +315 -0
  152. package/dist/markdown-core/plain-title.d.ts +2 -0
  153. package/dist/markdown-core/plain-title.js +1 -0
  154. package/dist/markdown-core/stream-heal.d.ts +2 -0
  155. package/dist/markdown-core/stream-heal.js +184 -0
  156. package/dist/markdown-core/streaming-context.d.ts +2 -0
  157. package/dist/markdown-core/streaming-context.js +11 -0
  158. package/dist/markdown-core/syntax/callout-types.d.ts +9 -0
  159. package/dist/markdown-core/syntax/callout-types.js +101 -0
  160. package/dist/markdown-core/syntax/containers.d.ts +17 -0
  161. package/dist/markdown-core/syntax/containers.js +353 -0
  162. package/dist/markdown-core/syntax/document-numbering.d.ts +52 -0
  163. package/dist/markdown-core/syntax/document-numbering.js +184 -0
  164. package/dist/markdown-core/syntax/elements/CalloutIcon.d.ts +3 -0
  165. package/dist/markdown-core/syntax/elements/CalloutIcon.js +41 -0
  166. package/dist/markdown-core/syntax/elements/CrossRef.d.ts +12 -0
  167. package/dist/markdown-core/syntax/elements/CrossRef.js +57 -0
  168. package/dist/markdown-core/syntax/elements/CsvFence.d.ts +4 -0
  169. package/dist/markdown-core/syntax/elements/CsvFence.js +18 -0
  170. package/dist/markdown-core/syntax/elements/DetailsElements.d.ts +12 -0
  171. package/dist/markdown-core/syntax/elements/DetailsElements.js +38 -0
  172. package/dist/markdown-core/syntax/elements/DocumentNumbering.d.ts +20 -0
  173. package/dist/markdown-core/syntax/elements/DocumentNumbering.js +37 -0
  174. package/dist/markdown-core/syntax/elements/DocumentPropertiesPanel.d.ts +4 -0
  175. package/dist/markdown-core/syntax/elements/DocumentPropertiesPanel.js +46 -0
  176. package/dist/markdown-core/syntax/elements/InDocAnchor.d.ts +11 -0
  177. package/dist/markdown-core/syntax/elements/InDocAnchor.js +87 -0
  178. package/dist/markdown-core/syntax/elements/MarkdownSourceEdit.d.ts +24 -0
  179. package/dist/markdown-core/syntax/elements/MarkdownSourceEdit.js +28 -0
  180. package/dist/markdown-core/syntax/elements/MatrxTabs.d.ts +10 -0
  181. package/dist/markdown-core/syntax/elements/MatrxTabs.js +59 -0
  182. package/dist/markdown-core/syntax/elements/MentionChip.d.ts +7 -0
  183. package/dist/markdown-core/syntax/elements/MentionChip.js +69 -0
  184. package/dist/markdown-core/syntax/elements/NestedBody.d.ts +3 -0
  185. package/dist/markdown-core/syntax/elements/NestedBody.js +13 -0
  186. package/dist/markdown-core/syntax/elements/TableOfContents.d.ts +5 -0
  187. package/dist/markdown-core/syntax/elements/TableOfContents.js +44 -0
  188. package/dist/markdown-core/syntax/elements/TaskCheckbox.d.ts +7 -0
  189. package/dist/markdown-core/syntax/elements/TaskCheckbox.js +63 -0
  190. package/dist/markdown-core/syntax/elements/WikiEmbed.d.ts +7 -0
  191. package/dist/markdown-core/syntax/elements/WikiEmbed.js +68 -0
  192. package/dist/markdown-core/syntax/elements/WikiLink.d.ts +10 -0
  193. package/dist/markdown-core/syntax/elements/WikiLink.js +114 -0
  194. package/dist/markdown-core/syntax/elements/core-syntax-elements.d.ts +22 -0
  195. package/dist/markdown-core/syntax/elements/core-syntax-elements.js +70 -0
  196. package/dist/markdown-core/syntax/elements/find-in-document.d.ts +3 -0
  197. package/dist/markdown-core/syntax/elements/find-in-document.js +20 -0
  198. package/dist/markdown-core/syntax/elements/wiki-target.d.ts +5 -0
  199. package/dist/markdown-core/syntax/elements/wiki-target.js +8 -0
  200. package/dist/markdown-core/syntax/frontmatter.d.ts +26 -0
  201. package/dist/markdown-core/syntax/frontmatter.js +72 -0
  202. package/dist/markdown-core/syntax/gfm-cell-pipes.d.ts +11 -0
  203. package/dist/markdown-core/syntax/gfm-cell-pipes.js +4 -0
  204. package/dist/markdown-core/syntax/inline-syntax.d.ts +26 -0
  205. package/dist/markdown-core/syntax/inline-syntax.js +163 -0
  206. package/dist/markdown-core/syntax/linear-scan.d.ts +46 -0
  207. package/dist/markdown-core/syntax/linear-scan.js +129 -0
  208. package/dist/markdown-core/syntax/mdast-helpers.d.ts +48 -0
  209. package/dist/markdown-core/syntax/mdast-helpers.js +48 -0
  210. package/dist/markdown-core/syntax/mentions.d.ts +2 -0
  211. package/dist/markdown-core/syntax/mentions.js +42 -0
  212. package/dist/markdown-core/syntax/names.d.ts +6 -0
  213. package/dist/markdown-core/syntax/names.js +18 -0
  214. package/dist/markdown-core/syntax/prepare-syntax-source.d.ts +1 -0
  215. package/dist/markdown-core/syntax/prepare-syntax-source.js +78 -0
  216. package/dist/markdown-core/syntax/rehype-matrx-syntax.d.ts +9 -0
  217. package/dist/markdown-core/syntax/rehype-matrx-syntax.js +180 -0
  218. package/dist/markdown-core/syntax/remark-inline-only.d.ts +23 -0
  219. package/dist/markdown-core/syntax/remark-inline-only.js +25 -0
  220. package/dist/markdown-core/syntax/remark-matrx-syntax.d.ts +7 -0
  221. package/dist/markdown-core/syntax/remark-matrx-syntax.js +351 -0
  222. package/dist/markdown-core/syntax/remark-table-code-pipes.d.ts +7 -0
  223. package/dist/markdown-core/syntax/remark-table-code-pipes.js +23 -0
  224. package/dist/markdown-core/syntax/stream-heal-syntax.d.ts +2 -0
  225. package/dist/markdown-core/syntax/stream-heal-syntax.js +108 -0
  226. package/dist/markdown-core/syntax/task-source.d.ts +10 -0
  227. package/dist/markdown-core/syntax/task-source.js +34 -0
  228. package/dist/markdown-core/syntax/useDocumentProperties.d.ts +3 -0
  229. package/dist/markdown-core/syntax/useDocumentProperties.js +8 -0
  230. package/dist/math-vendor-LICENSE-NOTICE.md +18 -0
  231. package/dist/styles.css +0 -0
  232. package/dist/utils/addressWithoutNavigating.d.ts +68 -0
  233. package/dist/utils/addressWithoutNavigating.js +60 -0
  234. package/dist/utils/client-lazy.d.ts +13 -0
  235. package/dist/utils/client-lazy.js +24 -0
  236. package/dist/utils/decision-options.d.ts +21 -0
  237. package/dist/utils/decision-options.js +32 -0
  238. package/dist/utils/gfm-lexer.d.ts +5 -0
  239. package/dist/utils/gfm-lexer.js +10 -0
  240. package/dist/utils/json.d.ts +63 -0
  241. package/dist/utils/json.js +22 -0
  242. package/dist/utils/plain-title.d.ts +25 -0
  243. package/dist/utils/plain-title.js +51 -0
  244. package/dist/utils/table-source.d.ts +62 -0
  245. package/dist/utils/table-source.js +191 -0
  246. package/dist/utils/youtube.d.ts +52 -0
  247. package/dist/utils/youtube.js +137 -0
  248. package/package.json +132 -5
@@ -0,0 +1,23 @@
1
+ import { unescapeCellPipes } from "./gfm-cell-pipes.js";
2
+ function unescapeCodeIn(node) {
3
+ if (node.type === "inlineCode" && typeof node.value === "string") {
4
+ node.value = unescapeCellPipes(node.value);
5
+ return;
6
+ }
7
+ node.children?.forEach(unescapeCodeIn);
8
+ }
9
+ function visit(node) {
10
+ if (node.type === "tableCell") {
11
+ unescapeCodeIn(node);
12
+ return;
13
+ }
14
+ node.children?.forEach(visit);
15
+ }
16
+ function remarkTableCodePipes() {
17
+ return (tree) => {
18
+ visit(tree);
19
+ };
20
+ }
21
+ export {
22
+ remarkTableCodePipes as default
23
+ };
@@ -0,0 +1,2 @@
1
+ import { type RemendHandler } from "remend";
2
+ export declare const SYNTAX_STREAM_HANDLERS: RemendHandler[];
@@ -0,0 +1,108 @@
1
+ import { isWithinCodeBlock } from "remend";
2
+ import { hasUnclosedFrontmatter } from "./frontmatter.js";
3
+ import { unfinishedHeadingIdHead } from "./linear-scan.js";
4
+ function lastLineStart(text) {
5
+ return text.lastIndexOf("\n") + 1;
6
+ }
7
+ const pendingFrontmatter = {
8
+ name: "matrx-pending-frontmatter",
9
+ priority: 0,
10
+ handle: (text) => {
11
+ if (text === "-" || text === "--" || text === "---" || text === "+" || text === "++" || text === "+++") return "";
12
+ if (!hasUnclosedFrontmatter(text)) return text;
13
+ const [opener, ...rest] = text.split("\n");
14
+ const looks = opener === "+++" ? TOML_LINE : YAML_LINE;
15
+ return rest.every((line, i) => looks.test(line) || i === rest.length - 1 && /^[\w"'-]*$/.test(line)) ? "" : text;
16
+ }
17
+ };
18
+ const YAML_LINE = /^(?:[\w"'][^:\n]*:(?:\s.*)?|\s+.*|-(?:\s.*)?|#.*|)$/;
19
+ const TOML_LINE = /^\s*(?:[\w."-]+\s*=.*|\[.*\]|#.*|)$/;
20
+ const TRAILING_BLOCK_MARKER = /^(?:[ \t]{0,3}:{2,}[^\n]*|[ \t]{0,3}>[ \t]*\[!?[^\]\n]*\]?[+-]?|(?:!!!|\?\?\?\+?)[^\n]*|\*\[[^\n]*)$/;
21
+ const pendingBlockMarker = {
22
+ name: "matrx-pending-block-marker",
23
+ priority: 2,
24
+ handle: (text) => {
25
+ const start = lastLineStart(text);
26
+ const line = text.slice(start);
27
+ if (!line || !TRAILING_BLOCK_MARKER.test(line)) return text;
28
+ if (isWithinCodeBlock(text, start)) return text;
29
+ if (/^[ \t]{0,3}>[ \t]*\[![A-Za-z-]+\][+-]?[ \t]*$/.test(line)) return text;
30
+ return text.slice(0, start);
31
+ }
32
+ };
33
+ const TRAILING_WIKILINK = /(!?)\[\[([^[\]\n]*)\]?$/;
34
+ const TRAILING_TEXT_DIRECTIVE = /:(?:span|mark|kbd|abbr|sup|sub)\[([^\]\n]*)(?:\](\{[^}\n]*)?)?$/;
35
+ const TRAILING_FOOTNOTE_REF = /\[\^[^\]\s]*$/;
36
+ const TRAILING_EMOJI = /(^|[\s(])(:[a-z0-9_+-]+)$/;
37
+ const TRAILING_HIGHLIGHT = /(^|[^=\\])==(?=[^\s=])([^\n=]*)$/;
38
+ const pendingInline = {
39
+ name: "matrx-pending-inline-syntax",
40
+ priority: 3,
41
+ handle: (text) => {
42
+ const start = lastLineStart(text);
43
+ if (isWithinCodeBlock(text, start)) return text;
44
+ let out = text;
45
+ const wiki = TRAILING_WIKILINK.exec(out);
46
+ if (wiki && wiki.index >= start) {
47
+ out = out.slice(0, wiki.index) + (wiki[1] ? "" : wiki[2] ?? "");
48
+ }
49
+ const directive = TRAILING_TEXT_DIRECTIVE.exec(out);
50
+ if (directive && directive.index >= start && (directive[2] !== void 0 || !out.endsWith("]"))) {
51
+ out = out.slice(0, directive.index) + (directive[1] ?? "");
52
+ }
53
+ const fn = TRAILING_FOOTNOTE_REF.exec(out);
54
+ if (fn && fn.index >= start) out = out.slice(0, fn.index);
55
+ const emoji = TRAILING_EMOJI.exec(out);
56
+ if (emoji && emoji.index >= start) out = out.slice(0, emoji.index + (emoji[1] ?? "").length);
57
+ const hl = TRAILING_HIGHLIGHT.exec(out);
58
+ if (hl && hl.index >= start && (hl[2] ?? "").trim() && !/\s$/.test(out)) out = `${out}==`;
59
+ return out;
60
+ }
61
+ };
62
+ const pendingHeadingId = {
63
+ name: "matrx-pending-heading-id",
64
+ priority: -2,
65
+ handle: (text) => {
66
+ const start = lastLineStart(text);
67
+ const head = unfinishedHeadingIdHead(text.slice(start));
68
+ if (head === null || isWithinCodeBlock(text, start)) return text;
69
+ return text.slice(0, start) + head;
70
+ }
71
+ };
72
+ function openDisplayDollar(text) {
73
+ let at = -1;
74
+ let open = -1;
75
+ while ((at = text.indexOf("$$", at + 1)) !== -1) {
76
+ if (isWithinCodeBlock(text, at)) continue;
77
+ open = open === -1 ? at : -1;
78
+ at += 1;
79
+ }
80
+ return open;
81
+ }
82
+ const pendingMath = {
83
+ name: "matrx-pending-math",
84
+ priority: -1,
85
+ handle: (text) => {
86
+ let cut = openDisplayDollar(text);
87
+ for (const [open, close] of [
88
+ ["\\[", "\\]"],
89
+ ["\\(", "\\)"]
90
+ ]) {
91
+ const o = text.lastIndexOf(open);
92
+ if (o !== -1 && text.indexOf(close, o) === -1 && !isWithinCodeBlock(text, o)) {
93
+ cut = cut === -1 ? o : Math.min(cut, o);
94
+ }
95
+ }
96
+ return cut === -1 ? text : text.slice(0, cut);
97
+ }
98
+ };
99
+ const SYNTAX_STREAM_HANDLERS = [
100
+ pendingFrontmatter,
101
+ pendingBlockMarker,
102
+ pendingInline,
103
+ pendingHeadingId,
104
+ pendingMath
105
+ ];
106
+ export {
107
+ SYNTAX_STREAM_HANDLERS
108
+ };
@@ -0,0 +1,10 @@
1
+ export interface TaskToggleRequest {
2
+ /** The item text as rendered. */
3
+ text: string;
4
+ /** Index among the rendered block's tasks — the tiebreaker for duplicates. */
5
+ index: number;
6
+ /** The state the person asked for. */
7
+ checked: boolean;
8
+ }
9
+ /** The new source with that one task toggled, or null when no stored task matches. */
10
+ export declare function toggleTaskInSource(source: string, request: TaskToggleRequest): string | null;
@@ -0,0 +1,34 @@
1
+ import { blockEdit, spliceSave, tokenizeSource } from "@ai-matrx/content-ir/source";
2
+ const TASK_LINE = /^([ \t]*(?:[-*+]|\d+[.)])[ \t]+)\[([ xX])\]([ \t]+|$)(.*)$/;
3
+ function normalize(text) {
4
+ return text.replace(/[*_~`=]|\[\[|\]\]/g, "").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\s+/g, " ").trim().toLowerCase();
5
+ }
6
+ function toggleTaskInSource(source, request) {
7
+ const blocks = tokenizeSource(source);
8
+ const all = [];
9
+ blocks.forEach((block2, blockIndex) => {
10
+ if (block2.kind !== "prose") return;
11
+ let offset = 0;
12
+ for (const line of block2.raw.split("\n")) {
13
+ const m = TASK_LINE.exec(line);
14
+ if (m) {
15
+ all.push({ blockIndex, lineStart: offset, markerOffset: offset + (m[1] ?? "").length + 1, text: m[4] ?? "" });
16
+ }
17
+ offset += line.length + 1;
18
+ }
19
+ });
20
+ if (all.length === 0) return null;
21
+ const wanted = normalize(request.text);
22
+ const matches = all.filter((c) => normalize(c.text) === wanted);
23
+ const pick = matches.length === 1 ? matches[0] : matches.length > 1 ? matches[Math.min(request.index, matches.length - 1)] ?? matches[0] : wanted === "" && all[request.index] ? all[request.index] : void 0;
24
+ if (!pick) return null;
25
+ const block = blocks[pick.blockIndex];
26
+ if (!block) return null;
27
+ const nextRaw = block.raw.slice(0, pick.markerOffset) + (request.checked ? "x" : " ") + block.raw.slice(pick.markerOffset + 1);
28
+ if (nextRaw === block.raw) return source;
29
+ const result = spliceSave(source, [blockEdit(block, nextRaw)], { blocks, requireIntegrity: true });
30
+ return result.text;
31
+ }
32
+ export {
33
+ toggleTaskInSource
34
+ };
@@ -0,0 +1,3 @@
1
+ import { type DocumentPropertiesResult } from "./frontmatter.js";
2
+ /** The React Compiler memoizes this per `source`. */
3
+ export declare function useDocumentProperties(source: string): DocumentPropertiesResult;
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ import { extractFrontmatter } from "./frontmatter.js";
3
+ function useDocumentProperties(source) {
4
+ return extractFrontmatter(source);
5
+ }
6
+ export {
7
+ useDocumentProperties
8
+ };
@@ -0,0 +1,18 @@
1
+ # Vendored math plugins
2
+
3
+ `remark-math`, `rehype-katex` and the syntax half of `micromark-extension-math` are copied here
4
+ verbatim (one import path changed each) under their MIT license:
5
+
6
+ > (The MIT License) Copyright (c) Titus Wormer <tituswormer@gmail.com>. Permission is hereby
7
+ > granted, free of charge, to any person obtaining a copy of this software and associated
8
+ > documentation files (the 'Software'), to deal in the Software without restriction, including
9
+ > without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
+ > and/or sell copies of the Software, and to permit persons to whom the Software is furnished to
11
+ > do so, subject to the following conditions: The above copyright notice and this permission
12
+ > notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS
13
+ > PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND.
14
+
15
+ Why: the upstream packages pin `katex ^0.16.0`, which carries GHSA-238p-pmpm-9mq7 (fixed in
16
+ 0.18.2), so any npm consumer of this package installed a vulnerable KaTeX. Vendored, the only
17
+ KaTeX in the graph is this package's own `katex` dependency (^0.18.9). Drop the vendor copy the
18
+ day upstream depends on katex >= 0.18.2.
File without changes
@@ -0,0 +1,68 @@
1
+ /**
2
+ * addressWithoutNavigating — THE one way to change the address bar without a
3
+ * navigation (lane URL-STATE, 2026-09-24; born in lane PANEL-REMOUNT).
4
+ *
5
+ * Query state (`?view=`, `?panels=`, `?tab=`, a selected row, a filter) is
6
+ * bookkeeping about the page you are on, never a new page. `router.replace` /
7
+ * `router.push` are App Router NAVIGATIONS: each one fetches a fresh RSC
8
+ * payload for the route (a `?_rsc=` request, a server round trip) and, on
9
+ * commit, can remount the page — measured on /data/[tableId]: one `?_rsc=`
10
+ * request per layout switch, and before PANEL-REMOUNT the whole grid re-read.
11
+ *
12
+ * `history.pushState` / `history.replaceState` are patched by Next's app
13
+ * router: called with a state object that does NOT carry `__NA`, the patch
14
+ * copies Next's own tree state onto the new entry and dispatches a `restore`,
15
+ * so `useSearchParams` / `usePathname` hold the new URL with no server round
16
+ * trip and no remount. Back/forward still work (Next owns the entry).
17
+ *
18
+ * 🚨 This module always passes `null`. Passing `window.history.state` (the
19
+ * old habit) forwards Next's `__NA` marker, which SKIPS the patch: the address
20
+ * bar changes while `useSearchParams` keeps the old value.
21
+ *
22
+ * Use the router for a REAL route change (another page), or when the server
23
+ * component of the current route reads the changed param from `searchParams`
24
+ * — only a navigation re-renders server output. Everything else comes here.
25
+ * Guard: eslint `matrx/no-navigation-for-query-state`.
26
+ */
27
+ /**
28
+ * Replace the current history entry's address. No navigation, no RSC fetch.
29
+ *
30
+ * A HASH-ONLY change skips Next's patch on purpose: nothing Next exposes
31
+ * (`usePathname`, `useSearchParams`, `useParams`) reads the hash, yet the
32
+ * patch's `restore` hands every `useSearchParams` reader a new object, so each
33
+ * write re-rendered all of them. A canvas writing its camera to `#cam=` while
34
+ * panning paid that per write — over a second per pointer move on a board of
35
+ * fifteen heavy tiles, so the board froze while panning (2026-10-01). Forwarding
36
+ * the entry's own state keeps Next's `__NA` tree on it (Back still works).
37
+ */
38
+ export declare function replaceAddressWithoutNavigating(href: string | URL): void;
39
+ /** Push a new history entry (Back returns to the old address). No navigation, no RSC fetch. */
40
+ export declare function pushAddressWithoutNavigating(href: string | URL): void;
41
+ /**
42
+ * The current path (and hash) carrying `search` as its query — the usual
43
+ * argument for the two writers above. An empty query drops the `?`.
44
+ */
45
+ export declare function currentPathWithSearch(search: URLSearchParams | string): string;
46
+ /** Just enough of Next's router for the two helpers below (and their tests). */
47
+ export interface AddressRouter {
48
+ push: (href: string, options?: {
49
+ scroll?: boolean;
50
+ }) => void;
51
+ replace: (href: string, options?: {
52
+ scroll?: boolean;
53
+ }) => void;
54
+ }
55
+ /**
56
+ * For a caller that names its page by a literal path (`/tasks?task=…`) but
57
+ * can also render somewhere else (a window panel over another page): on that
58
+ * page the change is query state, so no navigation; anywhere else it is a real
59
+ * route change, so the router carries it exactly as before (`options` are the
60
+ * router's, passed through unchanged).
61
+ */
62
+ export declare function replaceAddressOrNavigate(router: AddressRouter, href: string, options?: {
63
+ scroll?: boolean;
64
+ }): void;
65
+ /** The push twin of {@link replaceAddressOrNavigate}. */
66
+ export declare function pushAddressOrNavigate(router: AddressRouter, href: string, options?: {
67
+ scroll?: boolean;
68
+ }): void;
@@ -0,0 +1,60 @@
1
+ function currentHref() {
2
+ return `${window.location.pathname}${window.location.search}${window.location.hash}`;
3
+ }
4
+ function resolve(href) {
5
+ const url = new URL(String(href), window.location.href);
6
+ if (url.origin !== window.location.origin) {
7
+ throw new Error(
8
+ `addressWithoutNavigating: ${url.origin} is another origin; history writes are same-origin only. Navigate instead.`
9
+ );
10
+ }
11
+ return `${url.pathname}${url.search}${url.hash}`;
12
+ }
13
+ function replaceAddressWithoutNavigating(href) {
14
+ if (typeof window === "undefined") return;
15
+ const next = resolve(href);
16
+ if (next === currentHref()) return;
17
+ const sameDocument = next.split("#")[0] === `${window.location.pathname}${window.location.search}`;
18
+ const state = window.history.state;
19
+ if (sameDocument && isNextEntry(state)) {
20
+ window.history.replaceState(state, "", next);
21
+ return;
22
+ }
23
+ window.history.replaceState(null, "", next);
24
+ }
25
+ function isNextEntry(state) {
26
+ return typeof state === "object" && state !== null && ("__NA" in state || "_N" in state);
27
+ }
28
+ function pushAddressWithoutNavigating(href) {
29
+ if (typeof window === "undefined") return;
30
+ const next = resolve(href);
31
+ if (next === currentHref()) return;
32
+ window.history.pushState(null, "", next);
33
+ }
34
+ function currentPathWithSearch(search) {
35
+ const qs = (typeof search === "string" ? search : search.toString()).replace(
36
+ /^\?/,
37
+ ""
38
+ );
39
+ if (typeof window === "undefined") return qs ? `?${qs}` : "";
40
+ return `${window.location.pathname}${qs ? `?${qs}` : ""}${window.location.hash}`;
41
+ }
42
+ function isCurrentPath(href) {
43
+ const url = new URL(String(href), window.location.href);
44
+ return url.origin === window.location.origin && url.pathname === window.location.pathname;
45
+ }
46
+ function replaceAddressOrNavigate(router, href, options) {
47
+ if (typeof window !== "undefined" && isCurrentPath(href)) replaceAddressWithoutNavigating(href);
48
+ else router.replace(href, options);
49
+ }
50
+ function pushAddressOrNavigate(router, href, options) {
51
+ if (typeof window !== "undefined" && isCurrentPath(href)) pushAddressWithoutNavigating(href);
52
+ else router.push(href, options);
53
+ }
54
+ export {
55
+ currentPathWithSearch,
56
+ pushAddressOrNavigate,
57
+ pushAddressWithoutNavigating,
58
+ replaceAddressOrNavigate,
59
+ replaceAddressWithoutNavigating
60
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `clientLazy` — the package's replacement for `next/dynamic(..., { ssr: false })`: a lazily
3
+ * loaded component that renders only on the client. On the server and during hydration it
4
+ * renders `loading` (hydration-safe through `useSyncExternalStore`); on the client it renders
5
+ * the loaded component inside a Suspense boundary with the same `loading` as the fallback.
6
+ */
7
+ import { type ComponentType } from "react";
8
+ export declare function useIsClient(): boolean;
9
+ export declare function clientLazy<P extends object>(load: () => Promise<{
10
+ default: ComponentType<P>;
11
+ }>, options?: {
12
+ loading?: ComponentType;
13
+ }): ComponentType<P>;
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ import { createElement, lazy, Suspense, useSyncExternalStore } from "react";
3
+ const subscribe = () => () => {
4
+ };
5
+ const onClient = () => true;
6
+ const onServer = () => false;
7
+ function useIsClient() {
8
+ return useSyncExternalStore(subscribe, onClient, onServer);
9
+ }
10
+ function clientLazy(load, options = {}) {
11
+ const Lazy = lazy(load);
12
+ const Loading = options.loading;
13
+ function ClientLazy(props) {
14
+ const client = useIsClient();
15
+ const fallback = Loading ? createElement(Loading) : null;
16
+ if (!client) return fallback;
17
+ return createElement(Suspense, { fallback }, createElement(Lazy, props));
18
+ }
19
+ return ClientLazy;
20
+ }
21
+ export {
22
+ clientLazy,
23
+ useIsClient
24
+ };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The ONE place a `<decision>` body becomes options. Used by the splitter
3
+ * (content-splitter-core), the stream accumulator, and parseDecisionXml — the
4
+ * server twin is aidream matrx_ai/processing/blocks/parsers/decision_parser.py
5
+ * and MUST stay in lockstep with this file.
6
+ *
7
+ * Tolerant of real model output (captured 2026-10-04): extra attributes on
8
+ * <option> (`id` before/after `label`), single or double quotes, indentation
9
+ * and blank lines, and self-describing options with no `label` attribute
10
+ * (`<option id="x">Label text</option>` — the text names the choice).
11
+ * The choice id is the model's `id` attribute when present (unique), else
12
+ * `opt-N`. Only options whose closing tag has arrived count.
13
+ */
14
+ export interface ParsedDecisionOption {
15
+ /** The model's own `id` attribute when present and unique, else `opt-N`. */
16
+ id: string;
17
+ label: string;
18
+ text: string;
19
+ }
20
+ export declare function parseOptionAttributes(attrString: string): Record<string, string>;
21
+ export declare function parseDecisionOptionsFromBody(body: string): ParsedDecisionOption[];
@@ -0,0 +1,32 @@
1
+ const OPTION_RE = /<option\b([^>]*)>([\s\S]*?)<\/option\s*>/gi;
2
+ const ATTR_RE = /([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
3
+ function parseOptionAttributes(attrString) {
4
+ const attrs = {};
5
+ ATTR_RE.lastIndex = 0;
6
+ let m;
7
+ while ((m = ATTR_RE.exec(attrString)) !== null) {
8
+ attrs[m[1]] = m[2] ?? m[3] ?? "";
9
+ }
10
+ return attrs;
11
+ }
12
+ function parseDecisionOptionsFromBody(body) {
13
+ const options = [];
14
+ const seen = /* @__PURE__ */ new Set();
15
+ const re = new RegExp(OPTION_RE.source, OPTION_RE.flags);
16
+ let m;
17
+ while ((m = re.exec(body)) !== null) {
18
+ const attrs = parseOptionAttributes(m[1]);
19
+ const text = m[2].trim();
20
+ const label = (attrs.label ?? attrs.title ?? attrs.name ?? "").trim() || text.split("\n")[0].trim();
21
+ if (label === "") continue;
22
+ const own = (attrs.id ?? "").trim();
23
+ const id = own !== "" && !seen.has(own) ? own : `opt-${options.length}`;
24
+ seen.add(id);
25
+ options.push({ id, label, text });
26
+ }
27
+ return options;
28
+ }
29
+ export {
30
+ parseDecisionOptionsFromBody,
31
+ parseOptionAttributes
32
+ };
@@ -0,0 +1,5 @@
1
+ import { type Token, type Tokens } from "marked";
2
+ export { Lexer, Tokenizer } from "marked";
3
+ export type { Token, Tokens };
4
+ /** GFM block tokens of `text` (each with its exact `raw`), blank-line tokens dropped. */
5
+ export declare function lexGfmBlocks(text: string): Token[];
@@ -0,0 +1,10 @@
1
+ import { marked } from "marked";
2
+ import { Lexer, Tokenizer } from "marked";
3
+ function lexGfmBlocks(text) {
4
+ return marked.lexer(text, { gfm: true }).filter((token) => token.type !== "space");
5
+ }
6
+ export {
7
+ Lexer,
8
+ Tokenizer,
9
+ lexGfmBlocks
10
+ };
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Canonical JSON value types + narrowing guards.
3
+ *
4
+ * ─── Why this exists ───────────────────────────────────────────────────────
5
+ * This repo patches the Supabase-generated `Json` alias to `unknown`
6
+ * (scripts/patch-db-types.sh) so that typed objects are freely assignable INTO
7
+ * JSONB columns on the write side. The cost lands on the READ side: a JSONB
8
+ * column comes back as bare `unknown`, and to do anything with it you must
9
+ * narrow it.
10
+ *
11
+ * The lazy/cheating reaction (the #1 source of `as unknown as` in the codebase)
12
+ * is to cast the WHOLE row — `row as unknown as MyTypedRow` — which re-asserts
13
+ * every already-correctly-typed column just to reach the one `unknown` JSON
14
+ * field. That silently discards real type information and is exactly the
15
+ * "type cheating" this system exists to kill.
16
+ *
17
+ * The honest move is to narrow ONLY the JSON field, and to have a real name for
18
+ * "this is just a JSON object, accept it" that is NOT `any` and NOT a bare
19
+ * `unknown`:
20
+ *
21
+ * import { type JsonObject, isJsonObject } from "@/types/json";
22
+ *
23
+ * // 1. "It's just an object" — type the ONE field, leave the row typed:
24
+ * interface MyRow { id: string; name: string; config: JsonObject }
25
+ *
26
+ * // 2. Narrowing a bare `unknown` DB field honestly (runtime-checked):
27
+ * const cfg = isJsonObject(row.config) ? row.config : undefined;
28
+ * // cfg is JsonObject | undefined — no cast, no `any`, no whole-row nuke.
29
+ *
30
+ * // 3. Reading a leaf — JsonObject values are JsonValue, narrow as needed:
31
+ * const label = typeof cfg?.label === "string" ? cfg.label : null;
32
+ *
33
+ * For a concrete, known shape (an API/DB frame), prefer a Zod parse at the
34
+ * boundary (see TYPESCRIPT_STANDARDS.md §4). These types are for the genuinely
35
+ * open / loosely-shaped JSON the standards doc calls "just an object" — they
36
+ * give it an honest name so nobody reaches for `any` or a whole-row cast.
37
+ */
38
+ export type JsonPrimitive = string | number | boolean | null;
39
+ /**
40
+ * A JSON object. Values are `JsonValue | undefined` so optional keys read
41
+ * cleanly (mirrors Supabase's original generated `Json` object member).
42
+ */
43
+ export interface JsonObject {
44
+ [key: string]: JsonValue | undefined;
45
+ }
46
+ export type JsonArray = JsonValue[];
47
+ export type JsonValue = JsonPrimitive | JsonObject | JsonArray;
48
+ /**
49
+ * Narrow an `unknown` (e.g. a bare JSONB column) to a `JsonObject`.
50
+ * Plain object only — arrays and `null` return false.
51
+ */
52
+ export declare function isJsonObject(value: unknown): value is JsonObject;
53
+ /**
54
+ * Convert a `JsonObject` (values `JsonValue | undefined`) into the strict
55
+ * `Record<string, JsonValue>` shape generated API request bodies demand,
56
+ * dropping `undefined` entries (they don't exist in real JSON — `undefined`
57
+ * only appears via optional-key reads). Cast-free by construction.
58
+ */
59
+ export declare function toJsonRecord(obj: JsonObject): Record<string, JsonValue>;
60
+ /** Narrow an `unknown` to a `JsonArray`. */
61
+ export declare function isJsonArray(value: unknown): value is JsonArray;
62
+ /** Narrow an `unknown` to a JSON primitive (string | number | boolean | null). */
63
+ export declare function isJsonPrimitive(value: unknown): value is JsonPrimitive;
@@ -0,0 +1,22 @@
1
+ function isJsonObject(value) {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3
+ }
4
+ function toJsonRecord(obj) {
5
+ const out = {};
6
+ for (const [key, value] of Object.entries(obj)) {
7
+ if (value !== void 0) out[key] = value;
8
+ }
9
+ return out;
10
+ }
11
+ function isJsonArray(value) {
12
+ return Array.isArray(value);
13
+ }
14
+ function isJsonPrimitive(value) {
15
+ return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
16
+ }
17
+ export {
18
+ isJsonArray,
19
+ isJsonObject,
20
+ isJsonPrimitive,
21
+ toJsonRecord
22
+ };
@@ -0,0 +1,25 @@
1
+ /** Strip markdown syntax from ONE line, keeping its readable text. */
2
+ export declare function plainTextOfMarkdownLine(line: string): string;
3
+ export interface PlainTitleOptions {
4
+ /** Longest title; longer titles are cut at a word boundary with "…". */
5
+ maxLength?: number;
6
+ }
7
+ /**
8
+ * The plain-text title of markdown content: its first heading or line with
9
+ * the syntax removed. Returns "" when the content has no nameable line.
10
+ */
11
+ export declare function plainTitleFromMarkdown(source: string | null | undefined, { maxLength }?: PlainTitleOptions): string;
12
+ /**
13
+ * Display form of a STORED title that may still carry syntax (rows written
14
+ * before the projection existed). Clean titles come back unchanged; a title
15
+ * that projects to nothing keeps its original text rather than going blank.
16
+ */
17
+ export declare function displayTitle(title: string | null | undefined): string;
18
+ /**
19
+ * A read-boundary copy of `row` whose `key` title is in display form.
20
+ * Services that return rows whose title may have been derived from markdown
21
+ * (flashcard sets, assessments, study media) pass them through this, so every
22
+ * list, header, picker and export shows the clean name. Storage is untouched;
23
+ * a person who edits and saves the name saves what they see.
24
+ */
25
+ export declare function withDisplayTitle<T, K extends keyof T>(row: T, key: K): T;
@@ -0,0 +1,51 @@
1
+ import { fenceLineKinds } from "@ai-matrx/content-ir/source";
2
+ import { hasJsonKindKey } from "@ai-matrx/content-ir/surfaces";
3
+ import { kindTextLabel } from "@ai-matrx/content-ir/surfaces";
4
+ function firstContentLine(source) {
5
+ const lines = source.split(/\r?\n/);
6
+ let i = 0;
7
+ if (lines[0]?.trim() === "---") {
8
+ const end = lines.findIndex((l, k) => k > 0 && /^(---|\.\.\.)\s*$/.test(l));
9
+ if (end > 0) i = end + 1;
10
+ }
11
+ const kinds = fenceLineKinds(source);
12
+ for (; i < lines.length; i += 1) {
13
+ const line = lines[i];
14
+ if (kinds[i] !== "prose") continue;
15
+ if (/^\s*([-=*_])(\s*\1){2,}\s*$/.test(line)) continue;
16
+ if (line.trim()) return line;
17
+ }
18
+ return "";
19
+ }
20
+ function plainTextOfMarkdownLine(line) {
21
+ let text = line.replace(/^\s{0,3}#{1,6}(\s+|$)/, "").replace(/^\s*(>\s?)+/, "").replace(/^\s*([-*+]|\d+[.)])\s+/, "").replace(/^\s*\[[ xX]\]\s+/, "").replace(/\s+#+\s*$/, "");
22
+ text = text.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").replace(/\[([^\]]+)\]\[[^\]]*\]/g, "$1").replace(/<(https?:\/\/[^>\s]+)>/g, "$1").replace(/<\/?[A-Za-z][\w:-]*(\s[^<>]*)?\/?>/g, "").replace(/(`+)([\s\S]*?)\1/g, "$2").replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, "$2").replace(/\*(?=\S)([^*]*?\S)\*/g, "$1").replace(/(^|\W)_(?=\S)([^_]*?\S)_(?=\W|$)/g, "$1$2").replace(/~~(?=\S)([\s\S]*?\S)~~/g, "$1").replace(/\\([\\`*_{}#+\-.!>~|])/g, "$1");
23
+ return text.replace(/\s+/g, " ").trim();
24
+ }
25
+ function plainTitleFromMarkdown(source, { maxLength } = {}) {
26
+ const text = source ?? "";
27
+ const holdsKind = hasJsonKindKey(text);
28
+ const title = plainTextOfMarkdownLine(holdsKind ? kindTextLabel(text, 200) : firstContentLine(text)) || // A kind always names its record (an `<artifact>`-wrapped kind used to
29
+ // project to "") — R4, round 6.
30
+ (holdsKind ? kindTextLabel(text, 200) : "");
31
+ if (!maxLength || title.length <= maxLength) return title;
32
+ const cut = title.slice(0, maxLength - 1);
33
+ const space = cut.lastIndexOf(" ");
34
+ return `${(space > maxLength * 0.6 ? cut.slice(0, space) : cut).trimEnd()}\u2026`;
35
+ }
36
+ function displayTitle(title) {
37
+ const raw = title ?? "";
38
+ return plainTitleFromMarkdown(raw) || raw.trim();
39
+ }
40
+ function withDisplayTitle(row, key) {
41
+ const value = row[key];
42
+ if (typeof value !== "string") return row;
43
+ const clean = displayTitle(value);
44
+ return clean === value ? row : { ...row, [key]: clean };
45
+ }
46
+ export {
47
+ displayTitle,
48
+ plainTextOfMarkdownLine,
49
+ plainTitleFromMarkdown,
50
+ withDisplayTitle
51
+ };