@ai-matrx/rich-content 0.0.0-stage → 0.2.2

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 (247) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/LICENSE +21 -0
  3. package/README.md +61 -2
  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/styles.css +0 -0
  231. package/dist/utils/addressWithoutNavigating.d.ts +68 -0
  232. package/dist/utils/addressWithoutNavigating.js +60 -0
  233. package/dist/utils/client-lazy.d.ts +13 -0
  234. package/dist/utils/client-lazy.js +24 -0
  235. package/dist/utils/decision-options.d.ts +21 -0
  236. package/dist/utils/decision-options.js +32 -0
  237. package/dist/utils/gfm-lexer.d.ts +5 -0
  238. package/dist/utils/gfm-lexer.js +10 -0
  239. package/dist/utils/json.d.ts +63 -0
  240. package/dist/utils/json.js +22 -0
  241. package/dist/utils/plain-title.d.ts +25 -0
  242. package/dist/utils/plain-title.js +51 -0
  243. package/dist/utils/table-source.d.ts +62 -0
  244. package/dist/utils/table-source.js +191 -0
  245. package/dist/utils/youtube.d.ts +52 -0
  246. package/dist/utils/youtube.js +137 -0
  247. package/package.json +134 -4
@@ -0,0 +1,62 @@
1
+ export { rowCells, splitRowSegments } from "@ai-matrx/content-ir/source";
2
+ /**
3
+ * A cell the person typed, made safe for one GFM cell: line breaks become `<br>`
4
+ * and every `|` that would be a boundary (an EVEN run of backslashes before it,
5
+ * zero included) gets one more backslash. A pipe already escaped is left alone.
6
+ */
7
+ export declare function freshCell(text: string): string;
8
+ /** A table edit the writer could not make read back as the intended grid. */
9
+ export declare class TableWriteRefused extends Error {
10
+ readonly row: string;
11
+ constructor(row: string, detail: string);
12
+ }
13
+ /**
14
+ * The WHOLE written table must read back as one table — judged by `marked`
15
+ * (a GFM parser that does not use this module's splitter): one table, one row
16
+ * per written data line, and every cell equal to what the line stores. A row
17
+ * can split correctly and still END the table — a pipe-less row starting `- `
18
+ * is a list item, not a row (verify-RC-B4 R5-1) — so a per-row check is not
19
+ * enough.
20
+ *
21
+ * With `original`, the stored table sets the floor instead: lines the stored
22
+ * table itself never read as rows (a stray `- | x` line after it, an indented
23
+ * line read as code) may stay outside the written table, but the write must
24
+ * never end the table EARLIER than the stored one did, and every row it does
25
+ * read must hold exactly the stored cells.
26
+ */
27
+ export declare function assertTableReadsBack(tableText: string, original?: string): void;
28
+ /**
29
+ * The stored row with ONLY the edited cells rewritten. A row with MORE stored
30
+ * cells than the header keeps the extra ones verbatim (renderers ignore them;
31
+ * they are still the author's bytes); a SHORT row gains a segment only for a
32
+ * cell the person actually filled. A row without an edge pipe gets one when
33
+ * its first or last cell is empty — GFM would otherwise read that empty cell
34
+ * as the edge and shift the row. The result is read back through the splitter
35
+ * and REFUSED (TableWriteRefused) unless it holds exactly the intended cells.
36
+ * Null only when there are no stored segments or the stored texts do not line
37
+ * up with the new ones.
38
+ */
39
+ export declare function respliceRow(segs: unknown[] | null, stored: unknown[] | null, texts: readonly string[]): string | null;
40
+ /** A row the table never stored, in the table's pipe style (edge pipes added where an empty edge cell needs one). */
41
+ export declare function freshRow(texts: readonly string[], lead?: boolean, trail?: boolean): string;
42
+ export interface TableGrid {
43
+ headers: readonly string[];
44
+ rows: ReadonlyArray<readonly string[]>;
45
+ }
46
+ /**
47
+ * The stored table source with the edited grid spliced in — THE writer for
48
+ * every table edit path (see header). `original` is the table's markdown as
49
+ * stored; `grid` is the edited headers and rows as the ONE parser produced
50
+ * them (cell text trimmed, `\|` kept). No edit returns `original` exactly.
51
+ * A row added or removed changes only its own line; a column added or
52
+ * removed rewrites the rows (and the delimiter row) it touches.
53
+ */
54
+ /**
55
+ * Which of a table's data lines are ROWS — THE rule the parser (parseMarkdownTable)
56
+ * and the writer share, so they can never disagree about row count: every
57
+ * non-blank line after the delimiter is a row (an all-empty `| | |` included, as
58
+ * GFM shows it); only a trailing line with no cells at all (a lone `|` still
59
+ * arriving) is not.
60
+ */
61
+ export declare function dataRowIndexes(lines: readonly string[], candidates: readonly number[]): number[];
62
+ export declare function rewriteTableSource(original: string, grid: TableGrid): string;
@@ -0,0 +1,191 @@
1
+ import { lexGfmBlocks } from "./gfm-lexer.js";
2
+ import { rowCells, splitRowSegments, unescapeCellPipes } from "@ai-matrx/content-ir/source";
3
+ import { rowCells as rowCells2, splitRowSegments as splitRowSegments2 } from "@ai-matrx/content-ir/source";
4
+ function backslashRunBefore(text, index) {
5
+ let run = 0;
6
+ for (let i = index - 1; i >= 0 && text[i] === "\\"; i -= 1) run += 1;
7
+ return run;
8
+ }
9
+ function freshCell(text) {
10
+ const flat = text.replace(/(?: {2,}|\\)?\r?\n/g, "<br>");
11
+ let out = "";
12
+ for (let i = 0; i < flat.length; i += 1) {
13
+ if (flat[i] === "|" && backslashRunBefore(flat, i) % 2 === 0) out += "\\";
14
+ out += flat[i];
15
+ }
16
+ return out;
17
+ }
18
+ function endsInEscape(text) {
19
+ return backslashRunBefore(text, text.length) % 2 === 1;
20
+ }
21
+ class TableWriteRefused extends Error {
22
+ constructor(row, detail) {
23
+ super(`The table could not be written without changing other cells (${detail}). Your edit is kept \u2014 nothing was saved.`);
24
+ this.row = row;
25
+ this.name = "TableWriteRefused";
26
+ }
27
+ row;
28
+ }
29
+ function readTable(text) {
30
+ const tokens = lexGfmBlocks(text);
31
+ const first = tokens[0];
32
+ return { tokens: tokens.map((token) => token.type), table: first?.type === "table" ? first : null };
33
+ }
34
+ function strayLines(original) {
35
+ const text = original.replace(/^\n+|\n+$/g, "");
36
+ const data = text.split("\n").filter((line) => line.trim()).length - 2;
37
+ return Math.max(0, data - (readTable(text).table?.rows.length ?? 0));
38
+ }
39
+ function assertTableReadsBack(tableText, original) {
40
+ const text = tableText.replace(/^\n+|\n+$/g, "");
41
+ if (original !== void 0 && !readTable(original.replace(/^\n+|\n+$/g, "")).table) return;
42
+ const { tokens, table: parsed } = readTable(text);
43
+ const lines = text.split("\n").filter((line) => line.trim());
44
+ const dataLines = lines.slice(2);
45
+ const floor = dataLines.length - (original === void 0 ? 0 : strayLines(original));
46
+ if (!parsed || tokens.length !== 1 && parsed.rows.length >= dataLines.length) {
47
+ throw new TableWriteRefused(text, `it would read as ${tokens.join(" + ") || "nothing"}, not one table`);
48
+ }
49
+ if (parsed.rows.length < floor) {
50
+ throw new TableWriteRefused(
51
+ text,
52
+ `it would read as ${tokens.join(" + ")} \u2014 ${parsed.rows.length} of ${dataLines.length} rows would stay in the table`
53
+ );
54
+ }
55
+ const width = parsed.header.length;
56
+ const same = (line, cells) => {
57
+ const written = rowCells(line);
58
+ return cells.every((cell, i) => cell.text === unescapeCellPipes(written[i] ?? ""));
59
+ };
60
+ if (rowCells(lines[0] ?? "").length !== width || !same(lines[0] ?? "", parsed.header)) {
61
+ throw new TableWriteRefused(text, "the header would read back differently");
62
+ }
63
+ parsed.rows.forEach((cells, r) => {
64
+ const line = dataLines[r] ?? "";
65
+ if (!same(line, cells)) throw new TableWriteRefused(line, `row ${r + 1} would read back differently`);
66
+ });
67
+ }
68
+ function assertRowReadsBack(line, intended) {
69
+ const got = rowCells(line);
70
+ if (got.length !== intended.length || got.some((cell, i) => cell !== intended[i])) {
71
+ throw new TableWriteRefused(line, `expected ${JSON.stringify(intended)}, would read ${JSON.stringify(got)}`);
72
+ }
73
+ return line;
74
+ }
75
+ function rewriteSegment(seg, text) {
76
+ const lead = /^\s*/.exec(seg)?.[0] ?? "";
77
+ const core = seg.slice(lead.length).trimEnd();
78
+ const trail = seg.slice(lead.length + core.length);
79
+ const fresh = freshCell(text).trim();
80
+ const pad = Math.max(trail.length > 0 || endsInEscape(fresh) ? 1 : 0, core.length + trail.length - fresh.length);
81
+ return `${lead}${fresh}${" ".repeat(pad)}`;
82
+ }
83
+ function respliceRow(segs, stored, texts) {
84
+ if (!segs || !stored || stored.length !== texts.length || !segs.every((seg) => typeof seg === "string")) return null;
85
+ const parts = segs;
86
+ const original = rowCells(parts.join("|"));
87
+ const first = parts.length > 1 && (parts[0] ?? "").trim() === "" ? 1 : 0;
88
+ const closed = parts.length > 1 && (parts[parts.length - 1] ?? "").trim() === "";
89
+ const end = closed ? parts.length - 1 : parts.length;
90
+ const cells = parts.slice(first, end);
91
+ const edited = /* @__PURE__ */ new Set();
92
+ texts.forEach((text, index) => {
93
+ if (stored[index] === text) return;
94
+ edited.add(index);
95
+ if (index < cells.length) cells[index] = rewriteSegment(cells[index] ?? "", text);
96
+ else {
97
+ while (cells.length < index) cells.push(" ");
98
+ cells.push(` ${freshCell(text).trim()} `);
99
+ }
100
+ });
101
+ if (!edited.size) return parts.join("|");
102
+ const lastIndex = cells.length - 1;
103
+ const needLead = !first && (edited.has(0) || (cells[0] ?? "").trim() === "");
104
+ const needTrail = !closed && lastIndex >= 0 && (cells[lastIndex] ?? "").trim() === "";
105
+ if (needLead && !/^\s/.test(cells[0] ?? "")) cells[0] = ` ${cells[0] ?? ""}`;
106
+ if (needTrail && cells[lastIndex] === "") cells[lastIndex] = " ";
107
+ const edge = first ? [parts[0] ?? ""] : needLead ? [""] : [];
108
+ const tail = closed ? [parts[parts.length - 1] ?? ""] : needTrail || cells.length > end - first ? [""] : [];
109
+ const line = [...edge, ...cells, ...tail].join("|");
110
+ const intended = cells.map((cell, i) => edited.has(i) ? freshCell(texts[i] ?? "").trim() : original[i] ?? cell.trim());
111
+ return assertRowReadsBack(line, intended);
112
+ }
113
+ function freshRow(texts, lead = true, trail = true) {
114
+ const cells = texts.map((text) => freshCell(text).trim());
115
+ const needLead = lead || !cells.every((cell) => /^:?-+:?$/.test(cell));
116
+ const needTrail = trail || cells[cells.length - 1] === "" || endsInEscape(cells[cells.length - 1] ?? "");
117
+ const line = `${needLead ? "| " : ""}${cells.join(" | ")}${needTrail ? " |" : ""}`;
118
+ return assertRowReadsBack(line, cells);
119
+ }
120
+ function rewriteLine(line, texts, lead, trail) {
121
+ const stored = rowCells(line);
122
+ if (texts.length < stored.length && stored.slice(texts.length).some((cell) => cell !== "")) {
123
+ return freshRow(texts, lead, trail);
124
+ }
125
+ const padded = texts.length > stored.length ? [...stored, ...Array(texts.length - stored.length).fill("")] : stored;
126
+ const aligned = texts.length < stored.length ? texts.concat(stored.slice(texts.length)) : texts;
127
+ return respliceRow(splitRowSegments(line), padded, aligned) ?? freshRow(texts, lead, trail);
128
+ }
129
+ function dataRowIndexes(lines, candidates) {
130
+ const last = candidates[candidates.length - 1];
131
+ if (last !== void 0 && rowCells(lines[last] ?? "").length === 0) return candidates.slice(0, -1);
132
+ return [...candidates];
133
+ }
134
+ function rewriteTableSource(original, grid) {
135
+ const lines = original.split("\n");
136
+ const used = lines.map((line, index) => line.trim() ? index : -1).filter((index) => index >= 0);
137
+ const headerAt = used[0];
138
+ const delimAt = used[1];
139
+ if (headerAt === void 0 || delimAt === void 0) {
140
+ return [freshRow(grid.headers), freshRow(grid.headers.map(() => "---")), ...grid.rows.map((row) => freshRow(row))].join("\n");
141
+ }
142
+ const headerLine = lines[headerAt] ?? "";
143
+ const lead = headerLine.trimStart().startsWith("|");
144
+ const trail = headerLine.trimEnd().endsWith("|");
145
+ const width = rowCells(headerLine).length;
146
+ const columnsChanged = grid.headers.length !== width;
147
+ const out = [...lines];
148
+ out[headerAt] = rewriteLine(headerLine, grid.headers, lead, trail);
149
+ if (columnsChanged) {
150
+ out[delimAt] = freshRow(grid.headers.map(() => "---"), lead, trail);
151
+ }
152
+ const dataAt = dataRowIndexes(lines, used.slice(2));
153
+ if (grid.rows.length === dataAt.length) {
154
+ grid.rows.forEach((row, i) => {
155
+ const at = dataAt[i];
156
+ out[at] = rewriteLine(lines[at] ?? "", row, lead, trail);
157
+ });
158
+ const written2 = out.join("\n");
159
+ if (written2 !== original) assertTableReadsBack(written2, original);
160
+ return written2;
161
+ }
162
+ const key = (cells) => JSON.stringify(cells);
163
+ const storedKeys = dataAt.map((index) => key(rowCells(lines[index] ?? "")));
164
+ let cursor = 0;
165
+ const rebuilt = grid.rows.map((row) => {
166
+ const k = key(row);
167
+ for (let j = cursor; j < storedKeys.length; j += 1) {
168
+ if (storedKeys[j] === k) {
169
+ cursor = j + 1;
170
+ return lines[dataAt[j]] ?? "";
171
+ }
172
+ }
173
+ return freshRow(row, lead, trail);
174
+ });
175
+ const firstData = dataAt[0] ?? delimAt + 1;
176
+ const lastData = dataAt.length ? dataAt[dataAt.length - 1] : delimAt;
177
+ const written = [...out.slice(0, firstData), ...rebuilt, ...out.slice(lastData + 1)].join("\n");
178
+ assertTableReadsBack(written, original);
179
+ return written;
180
+ }
181
+ export {
182
+ TableWriteRefused,
183
+ assertTableReadsBack,
184
+ dataRowIndexes,
185
+ freshCell,
186
+ freshRow,
187
+ respliceRow,
188
+ rewriteTableSource,
189
+ rowCells2 as rowCells,
190
+ splitRowSegments2 as splitRowSegments
191
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Shared YouTube URL primitives.
3
+ *
4
+ * Single source of truth for turning any YouTube URL (watch / share / embed /
5
+ * shorts / live) into a video id + start offset, building a privacy-enhanced
6
+ * embed URL, and deriving a poster thumbnail without an API call.
7
+ *
8
+ * Consumed by:
9
+ * - the markdown splitter's `detectYoutubeMarkdown` (auto-playable YouTube
10
+ * links in any rendered content)
11
+ * - `features/files/blocks/youtube/YouTubeEmbed` (the one embed component)
12
+ * - `features/research/.../mediaEmbed` (delegates its `youtubeId` here)
13
+ *
14
+ * Do NOT re-implement YouTube id extraction anywhere else — extend this file.
15
+ */
16
+ /**
17
+ * Parse a YouTube time token into whole seconds.
18
+ * Accepts plain seconds ("15", "15s") and the `1h2m3s` form ("1m30s", "90").
19
+ * Returns undefined when the token is absent or unparseable.
20
+ */
21
+ export declare function parseYouTubeStart(token: string | null | undefined): number | undefined;
22
+ export interface ParsedYouTube {
23
+ /** The 11-ish char video id. */
24
+ videoId: string;
25
+ /** Start offset in whole seconds, when the URL carried `t` / `start`. */
26
+ start?: number;
27
+ }
28
+ /**
29
+ * Extract `{ videoId, start }` from any YouTube URL, or null when the URL is
30
+ * not a recognizable YouTube video link.
31
+ */
32
+ export declare function parseYouTubeUrl(url: string): ParsedYouTube | null;
33
+ /** Just the video id, or null — back-compat helper for callers that only need it. */
34
+ export declare function youtubeId(url: string): string | null;
35
+ /**
36
+ * True when the URL points at a YouTube channel or profile page — not an
37
+ * embeddable video. These must open on YouTube; they cannot be iframe-embedded.
38
+ */
39
+ export declare function isYouTubeChannelUrl(url: string): boolean;
40
+ /** A readable channel/handle label parsed from a YouTube channel URL. */
41
+ export declare function youTubeChannelLabel(url: string): string;
42
+ /** True when a thumbnail URL points at YouTube's image host (img.youtube.com / i.ytimg.com). */
43
+ export declare function isYouTubeThumbnailUrl(url: string): boolean;
44
+ /** Privacy-enhanced (`youtube-nocookie.com`) embed URL for a video id. */
45
+ export declare function youTubeEmbedUrl(videoId: string, opts?: {
46
+ start?: number;
47
+ autoplay?: boolean;
48
+ }): string;
49
+ /** Canonical watch URL for a video id (used for durable round-trip + "open on YouTube"). */
50
+ export declare function youTubeWatchUrl(videoId: string, start?: number): string;
51
+ /** A poster thumbnail derived without any API call. `maxres` falls back to `hq`. */
52
+ export declare function youTubeThumbnail(videoId: string, quality?: "maxres" | "hq" | "mq" | "sd"): string;
@@ -0,0 +1,137 @@
1
+ function isYouTubeId(id) {
2
+ return !!id && /^[A-Za-z0-9_-]{6,}$/.test(id);
3
+ }
4
+ function parseYouTubeStart(token) {
5
+ if (!token) return void 0;
6
+ const raw = token.trim();
7
+ if (!raw) return void 0;
8
+ if (/^\d+$/.test(raw)) {
9
+ const n = parseInt(raw, 10);
10
+ return Number.isFinite(n) && n > 0 ? n : void 0;
11
+ }
12
+ const m = raw.match(/^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/i);
13
+ if (m && (m[1] || m[2] || m[3])) {
14
+ const h = parseInt(m[1] ?? "0", 10);
15
+ const min = parseInt(m[2] ?? "0", 10);
16
+ const s = parseInt(m[3] ?? "0", 10);
17
+ const total = h * 3600 + min * 60 + s;
18
+ return total > 0 ? total : void 0;
19
+ }
20
+ return void 0;
21
+ }
22
+ function parseYouTubeUrl(url) {
23
+ let u;
24
+ try {
25
+ u = new URL(url.trim());
26
+ } catch {
27
+ return null;
28
+ }
29
+ const host = u.hostname.replace(/^www\./, "").toLowerCase();
30
+ let videoId = null;
31
+ if (host === "youtu.be") {
32
+ videoId = u.pathname.split("/").filter(Boolean)[0] ?? null;
33
+ } else if (host.endsWith("youtube.com") || host === "youtube-nocookie.com") {
34
+ if (u.pathname === "/watch") {
35
+ videoId = u.searchParams.get("v");
36
+ } else {
37
+ const m = u.pathname.match(/^\/(?:embed|shorts|v|live)\/([^/?#]+)/);
38
+ videoId = m ? m[1] : null;
39
+ }
40
+ }
41
+ if (!isYouTubeId(videoId)) return null;
42
+ const hashT = u.hash.match(/[#&]t=([^&]+)/)?.[1];
43
+ const start = parseYouTubeStart(u.searchParams.get("start")) ?? parseYouTubeStart(u.searchParams.get("t")) ?? parseYouTubeStart(hashT);
44
+ return start !== void 0 ? { videoId, start } : { videoId };
45
+ }
46
+ function youtubeId(url) {
47
+ return parseYouTubeUrl(url)?.videoId ?? null;
48
+ }
49
+ const YOUTUBE_NON_CHANNEL_SEGMENTS = /* @__PURE__ */ new Set([
50
+ "watch",
51
+ "embed",
52
+ "shorts",
53
+ "v",
54
+ "live",
55
+ "results",
56
+ "feed",
57
+ "playlist",
58
+ "account",
59
+ "about",
60
+ "trends",
61
+ "gaming",
62
+ "music",
63
+ "news",
64
+ "movies",
65
+ "premium",
66
+ "redirect",
67
+ "attribution_link"
68
+ ]);
69
+ function isYouTubeChannelUrl(url) {
70
+ if (parseYouTubeUrl(url)) return false;
71
+ try {
72
+ const u = new URL(url.trim());
73
+ const host = u.hostname.replace(/^www\./, "").toLowerCase();
74
+ if (host === "youtu.be") return false;
75
+ if (!host.endsWith("youtube.com") && host !== "youtube-nocookie.com") {
76
+ return false;
77
+ }
78
+ const segments = u.pathname.split("/").filter(Boolean);
79
+ if (segments.length === 0) return true;
80
+ const first = segments[0].toLowerCase();
81
+ if (YOUTUBE_NON_CHANNEL_SEGMENTS.has(first)) return false;
82
+ if (first === "user" || first === "channel" || first === "c") return true;
83
+ if (first.startsWith("@")) return true;
84
+ return segments.length === 1;
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
89
+ function youTubeChannelLabel(url) {
90
+ try {
91
+ const u = new URL(url.trim());
92
+ const segments = u.pathname.split("/").filter(Boolean);
93
+ if (segments.length === 0) return "YouTube";
94
+ const first = segments[0];
95
+ const firstLower = first.toLowerCase();
96
+ if (first.startsWith("@")) return first;
97
+ if (firstLower === "user" || firstLower === "channel" || firstLower === "c") {
98
+ return segments[1] ?? first;
99
+ }
100
+ return first;
101
+ } catch {
102
+ return "YouTube channel";
103
+ }
104
+ }
105
+ function isYouTubeThumbnailUrl(url) {
106
+ try {
107
+ const host = new URL(url).hostname.replace(/^www\./, "").toLowerCase();
108
+ return host === "img.youtube.com" || host === "i.ytimg.com";
109
+ } catch {
110
+ return false;
111
+ }
112
+ }
113
+ function youTubeEmbedUrl(videoId, opts = {}) {
114
+ const params = new URLSearchParams({ rel: "0" });
115
+ if (opts.autoplay) params.set("autoplay", "1");
116
+ if (opts.start && opts.start > 0) params.set("start", String(opts.start));
117
+ return `https://www.youtube-nocookie.com/embed/${videoId}?${params.toString()}`;
118
+ }
119
+ function youTubeWatchUrl(videoId, start) {
120
+ const base = `https://www.youtube.com/watch?v=${videoId}`;
121
+ return start && start > 0 ? `${base}&t=${start}s` : base;
122
+ }
123
+ function youTubeThumbnail(videoId, quality = "hq") {
124
+ const file = quality === "maxres" ? "maxresdefault" : quality === "mq" ? "mqdefault" : quality === "sd" ? "sddefault" : "hqdefault";
125
+ return `https://i.ytimg.com/vi/${videoId}/${file}.jpg`;
126
+ }
127
+ export {
128
+ isYouTubeChannelUrl,
129
+ isYouTubeThumbnailUrl,
130
+ parseYouTubeStart,
131
+ parseYouTubeUrl,
132
+ youTubeChannelLabel,
133
+ youTubeEmbedUrl,
134
+ youTubeThumbnail,
135
+ youTubeWatchUrl,
136
+ youtubeId
137
+ };
package/package.json CHANGED
@@ -1,6 +1,136 @@
1
1
  {
2
2
  "name": "@ai-matrx/rich-content",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.2",
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
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/AI-Matrix-Engine/aidream.git",
10
+ "directory": "apps/shared/rich-content"
11
+ },
12
+ "homepage": "https://github.com/AI-Matrix-Engine/aidream/tree/main/apps/shared/rich-content#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/AI-Matrix-Engine/aidream/issues"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE",
20
+ "CHANGELOG.md"
21
+ ],
22
+ "sideEffects": [
23
+ "*.css"
24
+ ],
25
+ "exports": {
26
+ "./host": {
27
+ "types": "./dist/host/index.d.ts",
28
+ "import": "./dist/host/index.js"
29
+ },
30
+ "./host/*": {
31
+ "types": "./dist/host/*.d.ts",
32
+ "import": "./dist/host/*.js"
33
+ },
34
+ "./markdown-core/*": {
35
+ "types": "./dist/markdown-core/*.d.ts",
36
+ "import": "./dist/markdown-core/*.js"
37
+ },
38
+ "./display/*": {
39
+ "types": "./dist/display/*.d.ts",
40
+ "import": "./dist/display/*.js"
41
+ },
42
+ "./kinds/*": {
43
+ "types": "./dist/kinds/*.d.ts",
44
+ "import": "./dist/kinds/*.js"
45
+ },
46
+ "./utils/*": {
47
+ "types": "./dist/utils/*.d.ts",
48
+ "import": "./dist/utils/*.js"
49
+ },
50
+ "./styles.css": "./dist/styles.css",
51
+ "./package.json": "./package.json"
52
+ },
53
+ "dependencies": {
54
+ "@types/hast": "^3.0.4",
55
+ "@types/mdast": "^4.0.4",
56
+ "@types/unist": "^3.0.3",
57
+ "hast-util-from-html": "^2.0.3",
58
+ "hast-util-from-html-isomorphic": "^2.0.0",
59
+ "hast-util-sanitize": "^5.0.2",
60
+ "hast-util-to-string": "^3.0.1",
61
+ "hast-util-to-text": "^4.0.2",
62
+ "katex": "^0.18.9",
63
+ "lucide-react": "^1.48.0",
64
+ "marked": "^18.0.14",
65
+ "mdast-util-math": "^3.0.0",
66
+ "micromark-factory-space": "^2.1.0",
67
+ "micromark-util-character": "^2.1.1",
68
+ "react-markdown": "^10.1.0",
69
+ "rehype-raw": "^7.0.0",
70
+ "rehype-slug": "^6.0.0",
71
+ "remark-breaks": "^4.0.0",
72
+ "remark-definition-list": "^2.0.0",
73
+ "remark-directive": "^4.0.0",
74
+ "remark-frontmatter": "^5.0.0",
75
+ "remark-gemoji": "^8.0.0",
76
+ "remark-gfm": "^4.0.1",
77
+ "remark-parse": "^11.0.0",
78
+ "remark-rehype": "^11.1.2",
79
+ "remend": "1.3.1",
80
+ "server-only": "^0.0.1",
81
+ "smol-toml": "^1.9.0",
82
+ "unified": "^11.0.5",
83
+ "unist-util-visit": "^5.1.0",
84
+ "unist-util-visit-parents": "^6.0.2",
85
+ "yaml": "^2.9.1",
86
+ "@ai-matrx/agents": "latest",
87
+ "@ai-matrx/content-ir": "latest",
88
+ "@ai-matrx/content-ir-react": "latest",
89
+ "@ai-matrx/kit": "latest",
90
+ "@ai-matrx/media": "latest",
91
+ "@ai-matrx/design-system": "latest",
92
+ "@ai-matrx/print": "latest",
93
+ "@ai-matrx/records": "latest"
94
+ },
95
+ "peerDependencies": {
96
+ "react": ">=19.0.0",
97
+ "react-dom": ">=19.0.0"
98
+ },
99
+ "devDependencies": {
100
+ "@testing-library/react": "^16.3.3",
101
+ "@types/jest": "^30.0.0",
102
+ "@types/katex": "^0.16.7",
103
+ "@types/node": "^26.6.2",
104
+ "@types/react": "19.3.0",
105
+ "@types/react-dom": "19.3.0",
106
+ "@typescript/native": "npm:typescript@^7.0.2",
107
+ "jest": "^30.5.2",
108
+ "jest-environment-jsdom": "^30.5.2",
109
+ "jsdom": "^30.1.1",
110
+ "publint": "^0.3.24",
111
+ "react": "^19.3.0",
112
+ "react-dom": "^19.3.0",
113
+ "ts-jest": "^29.4.14",
114
+ "tsup": "^8.5.1",
115
+ "tsx": "^4.23.15",
116
+ "typescript": "^6.0.3"
117
+ },
118
+ "publishConfig": {
119
+ "access": "public",
120
+ "registry": "https://registry.npmjs.org/"
121
+ },
122
+ "engines": {
123
+ "node": ">=20"
124
+ },
125
+ "scripts": {
126
+ "build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsup && node ./scripts/copy-css.mjs && node ./scripts/tsc-own.mjs -p tsconfig.build.json && node ./scripts/finish-build.mjs",
127
+ "check:package": "pnpm build && publint && pnpm verify:modules-tarball && pnpm verify:tarball && pnpm check:host-boundary",
128
+ "check:host-boundary": "node ./scripts/check-host-boundary.mjs && node ./scripts/check-host-boundary.mjs --self-test",
129
+ "deps:write": "node ./scripts/finish-build.mjs --write-deps",
130
+ "typecheck": "node ./scripts/tsc-own.mjs -p tsconfig.json",
131
+ "test": "pnpm test:linear-time && jest -c jest.config.mjs --testPathPatterns='^(?!.*regex-linear-time\\.guard\\.test\\.ts$).*'",
132
+ "test:linear-time": "jest -c jest.config.mjs src/markdown-core/__tests__/regex-linear-time.guard.test.ts --runInBand",
133
+ "verify:modules-tarball": "node ./scripts/verify-modules-tarball.mjs",
134
+ "verify:tarball": "node ./scripts/verify-tarball.mjs"
135
+ }
136
+ }