basalt-ui 1.10.0 → 1.12.0

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 (273) hide show
  1. package/AGENTS.md +21 -21
  2. package/CHANGELOG.md +25 -0
  3. package/README.md +36 -27
  4. package/agent/rules/basalt-content.md +16 -3
  5. package/dist/agent/adapter.d.ts +146 -0
  6. package/dist/agent/adapter.d.ts.map +1 -0
  7. package/dist/agent/adapter.js +473 -0
  8. package/dist/agent/adapter.test.d.ts +2 -0
  9. package/dist/agent/adapter.test.d.ts.map +1 -0
  10. package/dist/agent/ai-sdk-transport.d.ts +3 -52
  11. package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
  12. package/dist/agent/ai-sdk-transport.js +108 -24
  13. package/dist/agent/ai-sdk-transport.test.d.ts +2 -0
  14. package/dist/agent/ai-sdk-transport.test.d.ts.map +1 -0
  15. package/dist/agent/coalesce.d.ts +42 -0
  16. package/dist/agent/coalesce.d.ts.map +1 -0
  17. package/dist/agent/coalesce.js +137 -0
  18. package/dist/agent/coalesce.test.d.ts +2 -0
  19. package/dist/agent/coalesce.test.d.ts.map +1 -0
  20. package/dist/agent/foreign.d.ts +97 -0
  21. package/dist/agent/foreign.d.ts.map +1 -0
  22. package/dist/agent/foreign.js +18 -0
  23. package/dist/agent/foreign.test.d.ts +2 -0
  24. package/dist/agent/foreign.test.d.ts.map +1 -0
  25. package/dist/agent/history.d.ts +2 -0
  26. package/dist/agent/history.d.ts.map +1 -1
  27. package/dist/agent/id.d.ts +65 -0
  28. package/dist/agent/id.d.ts.map +1 -0
  29. package/dist/agent/id.js +30 -0
  30. package/dist/agent/id.test.d.ts +2 -0
  31. package/dist/agent/id.test.d.ts.map +1 -0
  32. package/dist/agent/index.d.ts +11 -4
  33. package/dist/agent/index.d.ts.map +1 -1
  34. package/dist/agent/index.js +18 -3
  35. package/dist/agent/merge.d.ts +8 -0
  36. package/dist/agent/merge.d.ts.map +1 -0
  37. package/dist/agent/merge.js +35 -0
  38. package/dist/agent/merge.test.d.ts +2 -0
  39. package/dist/agent/merge.test.d.ts.map +1 -0
  40. package/dist/agent/part-list.d.ts +49 -34
  41. package/dist/agent/part-list.d.ts.map +1 -1
  42. package/dist/agent/part-list.js +53 -7
  43. package/dist/agent/part-list.test.d.ts +2 -0
  44. package/dist/agent/part-list.test.d.ts.map +1 -0
  45. package/dist/agent/parts.d.ts +135 -20
  46. package/dist/agent/parts.d.ts.map +1 -1
  47. package/dist/agent/parts.js +138 -11
  48. package/dist/agent/parts.test.d.ts +2 -0
  49. package/dist/agent/parts.test.d.ts.map +1 -0
  50. package/dist/agent/thread.d.ts +14 -0
  51. package/dist/agent/thread.d.ts.map +1 -1
  52. package/dist/agent/thread.js +8 -2
  53. package/dist/agent/thread.test.d.ts +2 -0
  54. package/dist/agent/thread.test.d.ts.map +1 -0
  55. package/dist/agent/transport.d.ts +39 -3
  56. package/dist/agent/transport.d.ts.map +1 -1
  57. package/dist/agent/transport.js +20 -17
  58. package/dist/agent/transport.test.d.ts +2 -0
  59. package/dist/agent/transport.test.d.ts.map +1 -0
  60. package/dist/agent/use-agent-stream.d.ts +2 -1
  61. package/dist/agent/use-agent-stream.d.ts.map +1 -1
  62. package/dist/agent/use-agent-stream.js +5 -2
  63. package/dist/agent/use-agent-stream.test.d.ts +2 -0
  64. package/dist/agent/use-agent-stream.test.d.ts.map +1 -0
  65. package/dist/agent/use-agent-thread-runs.d.ts +9 -3
  66. package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
  67. package/dist/agent/use-agent-thread-runs.foreign.test.d.ts +2 -0
  68. package/dist/agent/use-agent-thread-runs.foreign.test.d.ts.map +1 -0
  69. package/dist/agent/use-agent-thread-runs.js +75 -23
  70. package/dist/agent/use-agent-thread-runs.resume.test.d.ts +2 -0
  71. package/dist/agent/use-agent-thread-runs.resume.test.d.ts.map +1 -0
  72. package/dist/agent/use-agent-thread-runs.test.d.ts +2 -0
  73. package/dist/agent/use-agent-thread-runs.test.d.ts.map +1 -0
  74. package/dist/agent/use-agent-thread-runs.wedge.test.d.ts +2 -0
  75. package/dist/agent/use-agent-thread-runs.wedge.test.d.ts.map +1 -0
  76. package/dist/agent-chat/composer.d.ts +130 -8
  77. package/dist/agent-chat/composer.d.ts.map +1 -1
  78. package/dist/agent-chat/composer.js +189 -23
  79. package/dist/agent-chat/composer.test.d.ts +2 -0
  80. package/dist/agent-chat/composer.test.d.ts.map +1 -0
  81. package/dist/agent-chat/index.d.ts +3 -1
  82. package/dist/agent-chat/index.d.ts.map +1 -1
  83. package/dist/agent-chat/index.js +2 -0
  84. package/dist/agent-chat/thread-detail-panel.d.ts.map +1 -1
  85. package/dist/agent-chat/thread-detail-panel.js +1 -1
  86. package/dist/agent-chat/thread-message.d.ts +24 -7
  87. package/dist/agent-chat/thread-message.d.ts.map +1 -1
  88. package/dist/agent-chat/thread-message.js +115 -45
  89. package/dist/agent-chat/thread-message.test.d.ts +2 -0
  90. package/dist/agent-chat/thread-message.test.d.ts.map +1 -0
  91. package/dist/agent-chat/thread-workspace.js +1 -1
  92. package/dist/agent-chat/tool-chip.d.ts +21 -0
  93. package/dist/agent-chat/tool-chip.d.ts.map +1 -0
  94. package/dist/agent-chat/tool-chip.js +120 -0
  95. package/dist/agent-chat/tool-chip.test.d.ts +2 -0
  96. package/dist/agent-chat/tool-chip.test.d.ts.map +1 -0
  97. package/dist/charts/primitives/ChartFrame.test.d.ts +2 -0
  98. package/dist/charts/primitives/ChartFrame.test.d.ts.map +1 -0
  99. package/dist/charts/utils/domain.test.d.ts +2 -0
  100. package/dist/charts/utils/domain.test.d.ts.map +1 -0
  101. package/dist/cli/check-theme.test.d.ts +2 -0
  102. package/dist/cli/check-theme.test.d.ts.map +1 -0
  103. package/dist/cli/help.test.d.ts +2 -0
  104. package/dist/cli/help.test.d.ts.map +1 -0
  105. package/dist/cli/index.d.ts.map +1 -1
  106. package/dist/cli/init-sync.test.d.ts +2 -0
  107. package/dist/cli/init-sync.test.d.ts.map +1 -0
  108. package/dist/cli/placement-engine.test.d.ts +2 -0
  109. package/dist/cli/placement-engine.test.d.ts.map +1 -0
  110. package/dist/cli/tarball-parity.test.d.ts +2 -0
  111. package/dist/cli/tarball-parity.test.d.ts.map +1 -0
  112. package/dist/cli/template-format.test.d.ts +2 -0
  113. package/dist/cli/template-format.test.d.ts.map +1 -0
  114. package/dist/cli/template-ledger.test.d.ts +2 -0
  115. package/dist/cli/template-ledger.test.d.ts.map +1 -0
  116. package/dist/cli/tokens-css.test.d.ts +2 -0
  117. package/dist/cli/tokens-css.test.d.ts.map +1 -0
  118. package/dist/content/article-actions.test.d.ts +2 -0
  119. package/dist/content/article-actions.test.d.ts.map +1 -0
  120. package/dist/content/article-model.test.d.ts +2 -0
  121. package/dist/content/article-model.test.d.ts.map +1 -0
  122. package/dist/content/block-split.test.d.ts +2 -0
  123. package/dist/content/block-split.test.d.ts.map +1 -0
  124. package/dist/content/fence-block.d.ts +37 -10
  125. package/dist/content/fence-block.d.ts.map +1 -1
  126. package/dist/content/fence-block.js +80 -14
  127. package/dist/content/heading-components.d.ts.map +1 -1
  128. package/dist/content/heading-components.js +7 -4
  129. package/dist/content/heading-components.test.d.ts +2 -0
  130. package/dist/content/heading-components.test.d.ts.map +1 -0
  131. package/dist/content/index.d.ts +6 -3
  132. package/dist/content/index.d.ts.map +1 -1
  133. package/dist/content/index.js +5 -1
  134. package/dist/content/markdown.d.ts +101 -14
  135. package/dist/content/markdown.d.ts.map +1 -1
  136. package/dist/content/markdown.js +141 -28
  137. package/dist/content/markdown.test.d.ts +2 -0
  138. package/dist/content/markdown.test.d.ts.map +1 -0
  139. package/dist/content/prose.module.css.test.d.ts +2 -0
  140. package/dist/content/prose.module.css.test.d.ts.map +1 -0
  141. package/dist/content/sanitize.d.ts +170 -0
  142. package/dist/content/sanitize.d.ts.map +1 -0
  143. package/dist/content/sanitize.js +178 -0
  144. package/dist/content/sanitize.test.d.ts +2 -0
  145. package/dist/content/sanitize.test.d.ts.map +1 -0
  146. package/dist/content/slug.test.d.ts +2 -0
  147. package/dist/content/slug.test.d.ts.map +1 -0
  148. package/dist/dashboard/stat-card.test.d.ts +2 -0
  149. package/dist/dashboard/stat-card.test.d.ts.map +1 -0
  150. package/dist/guard/check-source.test.d.ts +2 -0
  151. package/dist/guard/check-source.test.d.ts.map +1 -0
  152. package/dist/guard/guard-hook.test.d.ts +2 -0
  153. package/dist/guard/guard-hook.test.d.ts.map +1 -0
  154. package/dist/guard/index.d.ts.map +1 -1
  155. package/dist/guard/index.js +1 -3
  156. package/dist/index.d.ts +4 -4
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +12 -2
  159. package/dist/provider/index.test.d.ts +2 -0
  160. package/dist/provider/index.test.d.ts.map +1 -0
  161. package/dist/provider/lab-theme.test.d.ts +2 -0
  162. package/dist/provider/lab-theme.test.d.ts.map +1 -0
  163. package/dist/router-tanstack/multi-search-param-store.test.d.ts +2 -0
  164. package/dist/router-tanstack/multi-search-param-store.test.d.ts.map +1 -0
  165. package/dist/styles.floor.test.d.ts +2 -0
  166. package/dist/styles.floor.test.d.ts.map +1 -0
  167. package/dist/surfaces.d.ts +5 -5
  168. package/dist/surfaces.d.ts.map +1 -1
  169. package/dist/surfaces.js +13 -4
  170. package/dist/theme/border-coverage.test.d.ts +18 -0
  171. package/dist/theme/border-coverage.test.d.ts.map +1 -0
  172. package/dist/theme/contrast.test.d.ts +2 -0
  173. package/dist/theme/contrast.test.d.ts.map +1 -0
  174. package/dist/theme/density-option.test.d.ts +2 -0
  175. package/dist/theme/density-option.test.d.ts.map +1 -0
  176. package/dist/theme/density-relations.test.d.ts +2 -0
  177. package/dist/theme/density-relations.test.d.ts.map +1 -0
  178. package/dist/theme/derive-theme.test.d.ts +2 -0
  179. package/dist/theme/derive-theme.test.d.ts.map +1 -0
  180. package/dist/theme/fonts-option.test.d.ts +2 -0
  181. package/dist/theme/fonts-option.test.d.ts.map +1 -0
  182. package/dist/theme/radius-option.test.d.ts +2 -0
  183. package/dist/theme/radius-option.test.d.ts.map +1 -0
  184. package/dist/theme/radius.test.d.ts +2 -0
  185. package/dist/theme/radius.test.d.ts.map +1 -0
  186. package/dist/theme/shadow-surfaces.test.d.ts +26 -0
  187. package/dist/theme/shadow-surfaces.test.d.ts.map +1 -0
  188. package/dist/theme/spacing.test.d.ts +2 -0
  189. package/dist/theme/spacing.test.d.ts.map +1 -0
  190. package/dist/theme/use-basalt-spacing.test.d.ts +2 -0
  191. package/dist/theme/use-basalt-spacing.test.d.ts.map +1 -0
  192. package/dist/theme-lab/derive-controls.test.d.ts +2 -0
  193. package/dist/theme-lab/derive-controls.test.d.ts.map +1 -0
  194. package/dist/theme-lab/derive-state.test.d.ts +2 -0
  195. package/dist/theme-lab/derive-state.test.d.ts.map +1 -0
  196. package/dist/theme-lab/theme-lab.test.d.ts +2 -0
  197. package/dist/theme-lab/theme-lab.test.d.ts.map +1 -0
  198. package/dist/tokens/build-fonts-css.test.d.ts +2 -0
  199. package/dist/tokens/build-fonts-css.test.d.ts.map +1 -0
  200. package/dist/tokens/build-radius-css.test.d.ts +2 -0
  201. package/dist/tokens/build-radius-css.test.d.ts.map +1 -0
  202. package/dist/tokens/density.test.d.ts +2 -0
  203. package/dist/tokens/density.test.d.ts.map +1 -0
  204. package/dist/tokens/derive-radius.test.d.ts +2 -0
  205. package/dist/tokens/derive-radius.test.d.ts.map +1 -0
  206. package/dist/tokens/derive.test.d.ts +2 -0
  207. package/dist/tokens/derive.test.d.ts.map +1 -0
  208. package/dist/tokens/palette.test.d.ts +2 -0
  209. package/dist/tokens/palette.test.d.ts.map +1 -0
  210. package/dist/vite-color-mix.test.d.ts +2 -0
  211. package/dist/vite-color-mix.test.d.ts.map +1 -0
  212. package/dist/vite.test.d.ts +2 -0
  213. package/dist/vite.test.d.ts.map +1 -0
  214. package/llms.txt +7 -7
  215. package/package.json +6 -1
  216. package/src/agent/adapter.test.ts +1403 -0
  217. package/src/agent/adapter.ts +887 -0
  218. package/src/agent/ai-sdk-transport.test.ts +380 -55
  219. package/src/agent/ai-sdk-transport.ts +206 -53
  220. package/src/agent/coalesce.test.ts +387 -0
  221. package/src/agent/coalesce.ts +229 -0
  222. package/src/agent/foreign.test.ts +51 -0
  223. package/src/agent/foreign.ts +140 -0
  224. package/src/agent/history.ts +2 -0
  225. package/src/agent/id.test.ts +75 -0
  226. package/src/agent/id.ts +91 -0
  227. package/src/agent/index.ts +34 -3
  228. package/src/agent/merge.test.ts +206 -0
  229. package/src/agent/merge.ts +114 -0
  230. package/src/agent/part-list.test.tsx +112 -0
  231. package/src/agent/part-list.tsx +108 -35
  232. package/src/agent/parts.test.ts +516 -57
  233. package/src/agent/parts.ts +323 -32
  234. package/src/agent/thread.test.ts +75 -2
  235. package/src/agent/thread.ts +21 -1
  236. package/src/agent/transport.test.ts +70 -0
  237. package/src/agent/transport.ts +72 -20
  238. package/src/agent/use-agent-stream.test.tsx +53 -19
  239. package/src/agent/use-agent-stream.ts +13 -3
  240. package/src/agent/use-agent-thread-runs.foreign.test.tsx +138 -0
  241. package/src/agent/use-agent-thread-runs.resume.test.tsx +44 -11
  242. package/src/agent/use-agent-thread-runs.test.tsx +418 -22
  243. package/src/agent/use-agent-thread-runs.ts +170 -26
  244. package/src/agent/use-agent-thread-runs.wedge.test.tsx +15 -7
  245. package/src/agent-chat/composer.test.tsx +924 -0
  246. package/src/agent-chat/composer.tsx +521 -52
  247. package/src/agent-chat/index.ts +5 -1
  248. package/src/agent-chat/thread-detail-panel.tsx +3 -1
  249. package/src/agent-chat/thread-message.test.tsx +351 -0
  250. package/src/agent-chat/thread-message.tsx +262 -64
  251. package/src/agent-chat/thread-workspace.tsx +1 -1
  252. package/src/agent-chat/tool-chip.test.tsx +139 -0
  253. package/src/agent-chat/tool-chip.tsx +219 -0
  254. package/src/cli/index.ts +0 -1
  255. package/src/cli/template-format.test.ts +2 -2
  256. package/src/content/fence-block.tsx +242 -15
  257. package/src/content/heading-components.test.tsx +65 -0
  258. package/src/content/heading-components.tsx +26 -5
  259. package/src/content/index.ts +19 -3
  260. package/src/content/markdown.test.tsx +603 -0
  261. package/src/content/markdown.tsx +355 -35
  262. package/src/content/sanitize.test.ts +659 -0
  263. package/src/content/sanitize.ts +510 -0
  264. package/src/dashboard/stat-card.test.tsx +6 -1
  265. package/src/guard/check-source.test.ts +8 -4
  266. package/src/guard/index.ts +1 -3
  267. package/src/index.ts +15 -1
  268. package/src/surfaces.ts +13 -4
  269. package/src/theme/contrast.test.ts +1 -4
  270. package/src/theme/density-relations.test.ts +4 -2
  271. package/src/theme/fonts-option.test.ts +7 -1
  272. package/src/theme/shadow-surfaces.test.ts +12 -6
  273. package/src/theme/use-basalt-spacing.test.tsx +2 -1
@@ -1,19 +1,46 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Element } from 'hast';
3
+ export type FenceRenderContext = {
4
+ /** The fence body, without the surrounding fence markers. */
5
+ readonly code: string;
6
+ /** The info string's first word (` ```ts title="x" ` → `'ts'`), absent on a bare fence. */
7
+ readonly language?: string;
8
+ /** Raw meta string after the language on the opening fence line (`'title="x"'` above). */
9
+ readonly meta?: string;
10
+ /** Parsed `title="…"` out of `meta` — the filename tab `CodeBlock` renders. */
11
+ readonly title?: string;
12
+ /** `false` only for the in-flight tail block of a streaming message. Monotone: never true→false. */
13
+ readonly settled: boolean;
14
+ };
1
15
  /**
2
- * FenceBlock — renders a fenced code block's hast `pre` node as `CodeBlock` or `MermaidDiagram`
3
- * (docs/CONTENT-SPEC.md §2 decision 4 / §6). Shared by every `pre` override `./markdown` builds
4
- * (one per streamed block, each with its own `settled` flag).
16
+ * `undefined` DECLINES — the fence falls back to the default `CodeBlock` rendering, no different
17
+ * from an unclaimed language. `null` is NOT a decline; it is a deliberate "render nothing" result
18
+ * and is used as-is. A renderer that THROWS is also contained and falls back to `CodeBlock` (see the
19
+ * module doc above) — no renderer needs its own try/parse/fallback scaffolding to survive malformed,
20
+ * model-authored fence bodies.
21
+ */
22
+ export type FenceRenderer = (ctx: FenceRenderContext) => ReactNode;
23
+ /** Fence renderers keyed by language. */
24
+ export type FenceRenderers = Readonly<Record<string, FenceRenderer>>;
25
+ /**
26
+ * Wraps a renderer that is only worth running on finished text: renders the plain `CodeBlock`
27
+ * while the fence is still streaming, then `render` once its block settles. The built-in `mermaid`
28
+ * entry IS this — a consumer's own heavyweight fence (a chart spec, a diagram DSL) wants the same
29
+ * deal.
5
30
  *
6
- * `mermaid` fences render as a plain `CodeBlock` while `settled` is `false` (the in-flight
7
- * streaming tail) and upgrade to `MermaidDiagram` once the fence's block settles — no flicker, no
8
- * wasted parse pass on a still-changing diagram. Every other fenced language hides its copy
9
- * action while unsettled for the same reason (copying a still-streaming block is misleading).
31
+ * @example
32
+ * import { settledOnly } from 'basalt-ui/content'
10
33
  *
11
- * Not part of the public surface.
34
+ * <Markdown fenceRenderers={{ vega: settledOnly(({ code }) => <VegaChart spec={code} />) }}>
12
35
  */
13
- import type { Element } from 'hast';
36
+ export declare function settledOnly(render: FenceRenderer): FenceRenderer;
37
+ /** basalt's own fence renderers. A consumer entry for the same key wins over these. */
38
+ export declare const BUILT_IN_FENCE_RENDERERS: FenceRenderers;
14
39
  export type FenceBlockProps = {
15
40
  readonly node?: Element;
16
41
  readonly settled: boolean;
42
+ /** Consumer overrides, consulted BEFORE `BUILT_IN_FENCE_RENDERERS`. */
43
+ readonly renderers?: FenceRenderers;
17
44
  };
18
- export declare function FenceBlock({ node, settled }: FenceBlockProps): import("react").JSX.Element | null;
45
+ export declare function FenceBlock({ node, settled, renderers }: FenceBlockProps): import("react").JSX.Element | null;
19
46
  //# sourceMappingURL=fence-block.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fence-block.d.ts","sourceRoot":"","sources":["../../src/content/fence-block.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAKnC,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B,CAAA;AAED,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,eAAe,sCAgB5D"}
1
+ {"version":3,"file":"fence-block.d.ts","sourceRoot":"","sources":["../../src/content/fence-block.tsx"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAOnC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,oGAAoG;IACpG,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,kBAAkB,KAAK,SAAS,CAAA;AAElE,yCAAyC;AACzC,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAA;AAYpE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAEhE;AAED,uFAAuF;AACvF,eAAO,MAAM,wBAAwB,EAAE,cAEtC,CAAA;AAiBD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,uEAAuE;IACvE,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAA;CACpC,CAAA;AA2HD,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,eAAe,sCA2BvE"}
@@ -1,23 +1,89 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
+ import { Component, useRef } from "react";
2
3
  import { CodeBlock } from "./code-block.js";
3
4
  import { extractFenceInfo } from "./markdown-hast.js";
4
5
  import { MermaidDiagram } from "./mermaid.js";
5
- function FenceBlock({ node, settled }) {
6
- const fence = node ? extractFenceInfo(node) : void 0;
7
- if (!fence) return null;
8
- if (fence.language === "mermaid" && settled) {
9
- return /* @__PURE__ */ jsx(MermaidDiagram, { code: fence.code });
6
+ const defaultFenceRenderer = ({ code, language, title, settled }) => /* @__PURE__ */ jsx(
7
+ CodeBlock,
8
+ {
9
+ code,
10
+ showCopy: settled,
11
+ ...language !== void 0 && { language },
12
+ ...title !== void 0 && { title }
10
13
  }
11
- return /* @__PURE__ */ jsx(
12
- CodeBlock,
13
- {
14
- code: fence.code,
15
- showCopy: settled,
16
- ...fence.language !== void 0 && { language: fence.language },
17
- ...fence.title !== void 0 && { title: fence.title }
18
- }
14
+ );
15
+ function settledOnly(render) {
16
+ return (ctx) => ctx.settled ? render(ctx) : defaultFenceRenderer(ctx);
17
+ }
18
+ const BUILT_IN_FENCE_RENDERERS = {
19
+ mermaid: settledOnly(({ code }) => /* @__PURE__ */ jsx(MermaidDiagram, { code }))
20
+ };
21
+ function extractFenceMeta(preNode) {
22
+ const codeNode = preNode.children.find(
23
+ (child) => child.type === "element" && child.tagName === "code"
19
24
  );
25
+ const data = codeNode?.data;
26
+ return data?.meta;
27
+ }
28
+ function lookupFenceRenderer(registry, language) {
29
+ if (registry === void 0 || !Object.hasOwn(registry, language)) return void 0;
30
+ const candidate = registry[language];
31
+ return typeof candidate === "function" ? candidate : void 0;
32
+ }
33
+ function warnFenceRendererThrew(warned, error) {
34
+ if (warned.current) return;
35
+ warned.current = true;
36
+ if (process.env["NODE_ENV"] !== "production") {
37
+ console.warn(
38
+ "[basalt] FenceBlock: a fence renderer threw \u2014 falling back to the default CodeBlock rendering.",
39
+ error
40
+ );
41
+ }
42
+ }
43
+ function renderFenceSafely(render, ctx, warned) {
44
+ let rendered;
45
+ try {
46
+ rendered = render(ctx);
47
+ } catch (error) {
48
+ warnFenceRendererThrew(warned, error);
49
+ return defaultFenceRenderer(ctx);
50
+ }
51
+ return rendered === void 0 ? defaultFenceRenderer(ctx) : rendered;
52
+ }
53
+ class FenceRenderBoundary extends Component {
54
+ constructor(props) {
55
+ super(props);
56
+ this.state = { hasError: false };
57
+ }
58
+ static getDerivedStateFromError() {
59
+ return { hasError: true };
60
+ }
61
+ componentDidCatch(error) {
62
+ warnFenceRendererThrew(this.props.warned, error);
63
+ }
64
+ render() {
65
+ return this.state.hasError ? defaultFenceRenderer(this.props.ctx) : this.props.children;
66
+ }
67
+ }
68
+ function FenceBlock({ node, settled, renderers }) {
69
+ const warned = useRef(false);
70
+ const fence = node ? extractFenceInfo(node) : void 0;
71
+ if (!fence) return null;
72
+ const meta = node ? extractFenceMeta(node) : void 0;
73
+ const ctx = {
74
+ code: fence.code,
75
+ settled,
76
+ ...fence.language !== void 0 && { language: fence.language },
77
+ ...fence.title !== void 0 && { title: fence.title },
78
+ ...meta !== void 0 && { meta }
79
+ };
80
+ const language = fence.language;
81
+ const render = language === void 0 ? void 0 : lookupFenceRenderer(renderers, language) ?? lookupFenceRenderer(BUILT_IN_FENCE_RENDERERS, language);
82
+ const active = render ?? defaultFenceRenderer;
83
+ return /* @__PURE__ */ jsx(FenceRenderBoundary, { ctx, warned, children: renderFenceSafely(active, ctx, warned) }, ctx.code);
20
84
  }
21
85
  export {
22
- FenceBlock
86
+ BUILT_IN_FENCE_RENDERERS,
87
+ FenceBlock,
88
+ settledOnly
23
89
  };
@@ -1 +1 @@
1
- {"version":3,"file":"heading-components.d.ts","sourceRoot":"","sources":["../../src/content/heading-components.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,gBAAgB,CAAA;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAqCzC,yFAAyF;AACzF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CASjF"}
1
+ {"version":3,"file":"heading-components.d.ts","sourceRoot":"","sources":["../../src/content/heading-components.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,gBAAgB,CAAA;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AA8CzC,yFAAyF;AACzF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CASjF"}
@@ -18,11 +18,14 @@ function headingText(node) {
18
18
  }
19
19
  function createHeadingRenderer(level, tracker) {
20
20
  const Tag = HEADING_TAGS[level];
21
- return function HeadingRenderer({ children }) {
22
- const id = tracker.slug(headingText(children));
23
- return /* @__PURE__ */ jsxs(Tag, { id, children: [
21
+ return function HeadingRenderer({ id, children, node: _node, ...rest }) {
22
+ if (id !== void 0) {
23
+ return /* @__PURE__ */ jsx(Tag, { id, ...rest, children });
24
+ }
25
+ const slug = tracker.slug(headingText(children));
26
+ return /* @__PURE__ */ jsxs(Tag, { id: slug, ...rest, children: [
24
27
  children,
25
- /* @__PURE__ */ jsx(HeadingAnchor, { id })
28
+ /* @__PURE__ */ jsx(HeadingAnchor, { id: slug })
26
29
  ] });
27
30
  };
28
31
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=heading-components.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heading-components.test.d.ts","sourceRoot":"","sources":["../../src/content/heading-components.test.tsx"],"names":[],"mappings":""}
@@ -4,7 +4,8 @@
4
4
  * Mantine-coupled (like `./forms`/`./notifications`/`./data`): prose typography, a shiki-backed
5
5
  * code block (optional peer, lazy singleton), a semantic callout, a scroll-spy TOC rail, a
6
6
  * scroll-driven reading-progress bar, a streaming-aware `Markdown` renderer (react-markdown +
7
- * remark-gfm, optional peers), `MermaidDiagram` (beautiful-mermaid, optional peer), the MDX
7
+ * remark-gfm + rehype-sanitize + remend, all optional peers) with its fence-renderer registry,
8
+ * `MermaidDiagram` (beautiful-mermaid, optional peer), the MDX
8
9
  * component map, the docs-page frame (`ArticleLayout`), overview cards (`ArticleCard`/
9
10
  * `ArticleGrid`), and the contextual-help drawer (`GuideLink`/`GuideDrawer`). See
10
11
  * `agent/rules/basalt-content.md` for the full doctrine + the content-collections recipe.
@@ -23,8 +24,10 @@ export { headingSlug, readingTime, SlugTracker } from './slug.js';
23
24
  export type { ReadingTime } from './slug.js';
24
25
  export { HeadingAnchor } from './heading-anchor.js';
25
26
  export type { HeadingAnchorProps } from './heading-anchor.js';
26
- export { Markdown } from './markdown.js';
27
- export type { MarkdownComponents, MarkdownProps } from './markdown.js';
27
+ export { Markdown, settledOnly } from './markdown.js';
28
+ export type { FenceRenderContext, FenceRenderer, FenceRenderers, MarkdownComponents, MarkdownContentTrust, MarkdownProps, } from './markdown.js';
29
+ export { BASALT_SANITIZE_SCHEMA, mergeSanitizeSchema } from './sanitize.js';
30
+ export type { SanitizePropertyDefinition, SanitizeSchema, SanitizeSchemaExtension, SanitizeSchemaInput, } from './sanitize.js';
28
31
  export { MermaidDiagram } from './mermaid.js';
29
32
  export type { MermaidDiagramProps } from './mermaid.js';
30
33
  export { blockSplit } from './block-split.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/content/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAE1D,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AACvC,YAAY,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAE1D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAE9D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAEzC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,YAAY,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE1C,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAC1D,YAAY,EAAE,0BAA0B,EAAE,MAAM,OAAO,CAAA;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE/F,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAExE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACjF,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEhF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,YAAY,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAEhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAEjE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAChD,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/content/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAE1D,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AACvC,YAAY,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAE1D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAE9D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAC9D,YAAY,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAEzC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAClD,YAAY,EACV,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,GACd,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AACxE,YAAY,EACV,0BAA0B,EAC1B,cAAc,EACd,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,YAAY,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE1C,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAC1D,YAAY,EAAE,0BAA0B,EAAE,MAAM,OAAO,CAAA;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE/F,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAExE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACjF,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEhF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpD,YAAY,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAEhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAEjE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAChD,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA"}
@@ -5,7 +5,8 @@ import { TableOfContents } from "./toc.js";
5
5
  import { ReadingProgress } from "./reading-progress.js";
6
6
  import { headingSlug, readingTime, SlugTracker } from "./slug.js";
7
7
  import { HeadingAnchor } from "./heading-anchor.js";
8
- import { Markdown } from "./markdown.js";
8
+ import { Markdown, settledOnly } from "./markdown.js";
9
+ import { BASALT_SANITIZE_SCHEMA, mergeSanitizeSchema } from "./sanitize.js";
9
10
  import { MermaidDiagram } from "./mermaid.js";
10
11
  import { blockSplit } from "./block-split.js";
11
12
  import { createMdxComponents, mdxComponents } from "./mdx.js";
@@ -20,6 +21,7 @@ export {
20
21
  ArticleFilterBar,
21
22
  ArticleGrid,
22
23
  ArticleLayout,
24
+ BASALT_SANITIZE_SCHEMA,
23
25
  Callout,
24
26
  CodeBlock,
25
27
  GuideDrawer,
@@ -37,7 +39,9 @@ export {
37
39
  formatArticleDate,
38
40
  headingSlug,
39
41
  mdxComponents,
42
+ mergeSanitizeSchema,
40
43
  readingTime,
44
+ settledOnly,
41
45
  sortArticles,
42
46
  toArticleActions
43
47
  };
@@ -5,15 +5,26 @@
5
5
  * `agent/**` deliberately ships no renderer of its own, since `agent/** -> content` is
6
6
  * lint-blocked by design.
7
7
  *
8
- * LAZY-LOADED: react-markdown and remark-gfm are OPTIONAL peers, loaded via `React.lazy` + dynamic
9
- * `import()` (mirrors `agent/streaming-markdown.tsx` and `./highlighter`) — importing
10
- * `basalt-ui/content` never eagerly resolves them. Without them installed, `Markdown` renders the
11
- * raw string as plain text.
8
+ * LAZY-LOADED: react-markdown, remark-gfm, rehype-sanitize and remend are all OPTIONAL peers,
9
+ * loaded via `React.lazy` / dynamic `import()` (mirrors `agent/streaming-markdown.tsx` and
10
+ * `./highlighter`) — importing `basalt-ui/content` never eagerly resolves any of them. Without
11
+ * react-markdown or remark-gfm, `Markdown` renders the raw string as plain text; without
12
+ * rehype-sanitize the sanitize pass is skipped (dev-warned once); without remend the streaming
13
+ * tail renders unrepaired.
14
+ *
15
+ * SANITIZATION: basalt appends its own `rehype-sanitize` pass AFTER the consumer's `rehypePlugins`
16
+ * — plugin order in unified is registration order, so last is the only position nothing can
17
+ * outrun. `sanitizeSchema` is an ADDITIONS-only extension merged over `BASALT_SANITIZE_SCHEMA`
18
+ * (see `./sanitize`); removing a default allowance is deliberately not expressible.
19
+ *
20
+ * TRUST vs STREAMING: `streaming` is a rendering mode (the text is still arriving); `contentTrust`
21
+ * is the security input (where the text came from). They are independent — a FINISHED agent message
22
+ * is settled AND untrusted. Only `contentTrust` selects the image allowlist default.
12
23
  *
13
24
  * Default element overrides:
14
25
  * - `h1`..`h6` — slugged + hover-anchored (`./heading-components`).
15
- * - fenced code (`pre`) — `CodeBlock`, or `MermaidDiagram` for a settled ` ```mermaid ` fence
16
- * (`./fence-block`).
26
+ * - fenced code (`pre`) — the fence-renderer registry (`./fence-block`): `fenceRenderers[lang]`,
27
+ * then the built-ins (a settled ` ```mermaid ` fence → `MermaidDiagram`), then `CodeBlock`.
17
28
  * - `blockquote` — a GFM alert marker (`[!NOTE]` etc.) renders as `Callout`; otherwise passes
18
29
  * through (`./callout-alert`).
19
30
  * - `a`/`img` — hardened via `allowedLinkPrefixes`/`allowedImagePrefixes`/`defaultOrigin`
@@ -32,12 +43,23 @@
32
43
  * import { Markdown } from 'basalt-ui/content'
33
44
  *
34
45
  * <Markdown>{articleMarkdown}</Markdown>
35
- * <Markdown density="chat" streaming allowedLinkPrefixes={['https://']}>{streamedText}</Markdown>
46
+ * <Markdown density="chat" streaming contentTrust="untrusted">{streamedText}</Markdown>
47
+ * <Markdown fenceRenderers={{ vega: settledOnly(({ code }) => <VegaChart spec={code} />) }}>
36
48
  */
37
49
  import type { CSSProperties, JSX } from 'react';
38
50
  import type { Components } from 'react-markdown';
51
+ import type { FenceRenderers } from './fence-block.js';
39
52
  import type { ProseDensity } from './prose.js';
53
+ import type { SanitizeSchemaExtension, SanitizeSchemaInput } from './sanitize.js';
40
54
  export type MarkdownComponents = Components;
55
+ export { settledOnly } from './fence-block.js';
56
+ export type { FenceRenderContext, FenceRenderer, FenceRenderers } from './fence-block.js';
57
+ /**
58
+ * Provenance of a `Markdown` body — the ONLY input to the image-allowlist default. Deliberately not
59
+ * a boolean: `contentTrust="untrusted"` says at the call site what it means, where `untrusted` /
60
+ * `!trusted` would not.
61
+ */
62
+ export type MarkdownContentTrust = 'trusted' | 'untrusted';
41
63
  export type MarkdownProps = {
42
64
  /** The markdown string to render. Safe to update on every character during streaming. */
43
65
  readonly children: string;
@@ -45,24 +67,89 @@ export type MarkdownProps = {
45
67
  readonly density?: ProseDensity;
46
68
  /** `Prose`'s measure cap. Defaults to `true` for `'article'` density, `false` for `'chat'`. */
47
69
  readonly measure?: boolean;
48
- /** AI-stream mode — block-split + memoize + `remend`-repair the in-flight tail. Default `false`. */
70
+ /** AI-stream mode — block-split + memoize + `remend`-repair the in-flight tail. Default `false`.
71
+ * Purely a RENDERING mode: it says the text is still arriving, NOT that it is untrusted. Use
72
+ * `contentTrust` for the security policy. */
49
73
  readonly streaming?: boolean;
50
- /** Per-element overrides merged OVER the defaults (only the keys you supply are replaced). */
74
+ /**
75
+ * Where `children` came from — the input to the image policy, independent of `streaming`.
76
+ * `'untrusted'` (model-generated or third-party markdown) drops the `allowedImagePrefixes`
77
+ * default to same-origin `['/']`; `'trusted'` (your own authored content) keeps
78
+ * `['https://', '/']`. An explicit `allowedImagePrefixes` always wins over both.
79
+ *
80
+ * Default: `'untrusted'` when `streaming` is set, `'trusted'` otherwise. That default only
81
+ * preserves the pre-1.12 fail-safe — it is NOT the policy. Settledness has nothing to do with
82
+ * trust: a FINISHED agent message is still model-generated, so any surface rendering agent text
83
+ * must pin `contentTrust="untrusted"` explicitly rather than lean on `streaming`.
84
+ */
85
+ readonly contentTrust?: MarkdownContentTrust;
86
+ /**
87
+ * Per-element overrides merged OVER the defaults (only the keys you supply are replaced).
88
+ *
89
+ * A CUSTOM tag admitted via `sanitizeSchema.tagNames` (e.g. `'x-callout'`) cannot be given an
90
+ * override here: react-markdown's `Components` type is a mapped type over `JSX.IntrinsicElements`,
91
+ * which only has entries for real HTML/SVG tags — a made-up tag name is not a valid key of it, so
92
+ * there is no override slot for it to occupy. Style such a tag with CSS (element/attribute
93
+ * selectors) or a wrapping component instead of trying to intercept it here.
94
+ */
51
95
  readonly components?: MarkdownComponents;
96
+ /**
97
+ * Fence renderers keyed by language, consulted BEFORE the built-in mermaid/`CodeBlock` dispatch.
98
+ * Wrap a heavyweight renderer in `settledOnly` to keep it off the still-streaming tail. Pass a
99
+ * referentially stable object — it participates in the streaming memoization.
100
+ */
101
+ readonly fenceRenderers?: FenceRenderers;
102
+ /**
103
+ * ADDITIONS merged into `BASALT_SANITIZE_SCHEMA` over rehype-sanitize's `defaultSchema`.
104
+ * Removals are not expressible — a schema handed to `rehype-sanitize` directly would replace the
105
+ * defaults wholesale (hast-util-sanitize merges with a shallow top-level spread), which is
106
+ * exactly the footgun this shape removes.
107
+ */
108
+ readonly sanitizeSchema?: SanitizeSchemaExtension;
52
109
  /** Allowed `href` prefixes. Default `['https://', 'mailto:', '#', '/']`. */
53
110
  readonly allowedLinkPrefixes?: readonly string[];
54
- /** Allowed `img src` prefixes. Default `['https://', '/']`; in `streaming` mode `['/']`
55
- * (same-origin only) — auto-fetched images are the classic prompt-injection exfiltration
56
- * channel, so remote origins must be opted in explicitly for model-generated content. */
111
+ /** Allowed `img src` prefixes. Default `['https://', '/']` for trusted content, `['/']`
112
+ * (same-origin only) when `contentTrust` resolves to `'untrusted'` — auto-fetched images are the
113
+ * classic prompt-injection exfiltration channel, so remote origins must be opted in explicitly
114
+ * for model-generated content. */
57
115
  readonly allowedImagePrefixes?: readonly string[];
58
116
  /** Resolves a relative `href`/`src` to an absolute URL before the allowlist check's result is used. */
59
117
  readonly defaultOrigin?: string;
60
118
  /** Escape hatch — appended after `remark-gfm`. */
61
119
  readonly remarkPlugins?: readonly unknown[];
62
- /** Escape hatch — appended after react-markdown's defaults. */
120
+ /**
121
+ * Escape hatch — appended after react-markdown's defaults, and BEFORE basalt's sanitize pass
122
+ * (which always runs last, so nothing supplied here can outrun it).
123
+ *
124
+ * react-markdown does NOT parse raw HTML in the markdown SOURCE by default (no `rehype-raw` is
125
+ * included here or by react-markdown itself) — a literal `<script>...</script>` typed into
126
+ * `children` never becomes a hast element; it stays inert text and the sanitize pass never even
127
+ * sees it. Concretely: a test that types a raw `<script>` tag into `children` and asserts it never
128
+ * renders is NOT exercising the sanitize pass — it would pass identically with the pass removed
129
+ * entirely, because the string was never a node the sanitizer could act on in the first place. The
130
+ * sanitize pass only ever sees elements produced by remark/rehype PLUGINS (`remarkPlugins`,
131
+ * `rehypePlugins`, or `rehype-raw` if a consumer adds it here) — that is the surface worth testing
132
+ * against unsafe markup, not raw source text.
133
+ */
63
134
  readonly rehypePlugins?: readonly unknown[];
64
135
  readonly className?: string;
65
136
  readonly style?: CSSProperties;
66
137
  };
67
- export declare function Markdown({ children, density, measure, streaming, components, allowedLinkPrefixes, allowedImagePrefixes, defaultOrigin, remarkPlugins, rehypePlugins, className, style, }: MarkdownProps): JSX.Element;
138
+ /** The resolved sanitize peer: its plugin plus the `defaultSchema` it re-exports. */
139
+ type MarkdownSanitizer = {
140
+ readonly plugin: unknown;
141
+ readonly baseSchema: SanitizeSchemaInput;
142
+ };
143
+ /**
144
+ * Consumer plugins first, basalt's sanitize pass LAST — "everything after rehype-sanitize could be
145
+ * unsafe", so last is the only position nothing supplied through the escape hatch can outrun.
146
+ *
147
+ * Exported for the ordering test only; `./index.ts` deliberately does not re-export it.
148
+ */
149
+ export declare function composeRehypePlugins({ sanitizer, consumerPlugins, sanitizeSchema, }: {
150
+ readonly sanitizer: MarkdownSanitizer | null;
151
+ readonly consumerPlugins?: readonly unknown[];
152
+ readonly sanitizeSchema?: SanitizeSchemaExtension;
153
+ }): readonly unknown[];
154
+ export declare function Markdown({ children, density, measure, streaming, contentTrust, components, fenceRenderers, sanitizeSchema, allowedLinkPrefixes, allowedImagePrefixes, defaultOrigin, remarkPlugins, rehypePlugins, className, style, }: MarkdownProps): JSX.Element;
68
155
  //# sourceMappingURL=markdown.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/content/markdown.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAG/C,OAAO,KAAK,EACV,UAAU,EAIX,MAAM,gBAAgB,CAAA;AAMvB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAO3C,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAA;AAE3C,MAAM,MAAM,aAAa,GAAG;IAC1B,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,yEAAyE;IACzE,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAA;IAC/B,+FAA+F;IAC/F,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B,oGAAoG;IACpG,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B,8FAA8F;IAC9F,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAA;IACxC,4EAA4E;IAC5E,QAAQ,CAAC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAChD;;6FAEyF;IACzF,QAAQ,CAAC,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACjD,uGAAuG;IACvG,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,kDAAkD;IAClD,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IAC3C,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAA;CAC/B,CAAA;AAyND,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,OAAmB,EACnB,OAAO,EACP,SAAiB,EACjB,UAAU,EACV,mBAA2C,EAC3C,oBAAoF,EACpF,aAAa,EACb,aAAa,EACb,aAAa,EACb,SAAS,EACT,KAAK,GACN,EAAE,aAAa,eA+Bf"}
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/content/markdown.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAE/C,OAAO,KAAK,EACV,UAAU,EAIX,MAAM,gBAAgB,CAAA;AAGvB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAInD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C,OAAO,KAAK,EAAkB,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAO9F,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAA;AAK3C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEtF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,WAAW,CAAA;AAE1D,MAAM,MAAM,aAAa,GAAG;IAC1B,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,yEAAyE;IACzE,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAA;IAC/B,+FAA+F;IAC/F,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B;;iDAE6C;IAC7C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,oBAAoB,CAAA;IAC5C;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAA;IACxC;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,uBAAuB,CAAA;IACjD,4EAA4E;IAC5E,QAAQ,CAAC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAChD;;;sCAGkC;IAClC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACjD,uGAAuG;IACvG,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;IAC/B,kDAAkD;IAClD,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IAC3C;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAA;CAC/B,CAAA;AAqED,qFAAqF;AACrF,KAAK,iBAAiB,GAAG;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAA;CACzC,CAAA;AA8ED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,eAAe,EACf,cAAc,GACf,EAAE;IACD,QAAQ,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC5C,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IAC7C,QAAQ,CAAC,cAAc,CAAC,EAAE,uBAAuB,CAAA;CAClD,GAAG,SAAS,OAAO,EAAE,CAKrB;AA2RD,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,OAAmB,EACnB,OAAO,EACP,SAAiB,EAIjB,YAAkD,EAClD,UAAU,EACV,cAAc,EACd,cAAc,EACd,mBAA2C,EAC3C,oBAA0D,EAC1D,aAAa,EACb,aAAa,EACb,aAAa,EACb,SAAS,EACT,KAAK,GACN,EAAE,aAAa,eAiCf"}