@flamingo-stack/openframe-frontend-core 0.0.488 → 0.0.490-1506.3944.1

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 (311) hide show
  1. package/dist/chat-protocol/decode.d.ts +58 -0
  2. package/dist/chat-protocol/decode.d.ts.map +1 -0
  3. package/dist/chat-protocol/encode.d.ts +44 -0
  4. package/dist/chat-protocol/encode.d.ts.map +1 -0
  5. package/dist/chat-protocol/env-flag.d.ts +35 -0
  6. package/dist/chat-protocol/env-flag.d.ts.map +1 -0
  7. package/dist/chat-protocol/events.d.ts +196 -0
  8. package/dist/chat-protocol/events.d.ts.map +1 -0
  9. package/dist/chat-protocol/frames.d.ts +213 -0
  10. package/dist/chat-protocol/frames.d.ts.map +1 -0
  11. package/dist/chat-protocol/index.cjs +494 -0
  12. package/dist/chat-protocol/index.cjs.map +1 -0
  13. package/dist/chat-protocol/index.d.ts +18 -0
  14. package/dist/chat-protocol/index.d.ts.map +1 -0
  15. package/dist/chat-protocol/index.js +478 -0
  16. package/dist/chat-protocol/index.js.map +1 -0
  17. package/dist/chat-protocol/ip-normalize.d.ts +44 -0
  18. package/dist/chat-protocol/ip-normalize.d.ts.map +1 -0
  19. package/dist/chat-protocol/nats-decoder.d.ts +26 -0
  20. package/dist/chat-protocol/nats-decoder.d.ts.map +1 -0
  21. package/dist/{chunk-AJNDDXKC.js → chunk-4NWARV5I.js} +3 -3
  22. package/dist/{chunk-GQX6LFFA.cjs → chunk-5CJR2GZ4.cjs} +42 -42
  23. package/dist/{chunk-GQX6LFFA.cjs.map → chunk-5CJR2GZ4.cjs.map} +1 -1
  24. package/dist/{chunk-EUYIFALO.cjs → chunk-5EYRXSY5.cjs} +14 -14
  25. package/dist/{chunk-EUYIFALO.cjs.map → chunk-5EYRXSY5.cjs.map} +1 -1
  26. package/dist/{chunk-5RXKOGH6.cjs → chunk-5LMGFI4J.cjs} +7011 -4777
  27. package/dist/chunk-5LMGFI4J.cjs.map +1 -0
  28. package/dist/{chunk-KFQXO7QB.js → chunk-6GT557KN.js} +1 -1
  29. package/dist/chunk-6GT557KN.js.map +1 -0
  30. package/dist/{chunk-77DFPD35.js → chunk-BE6DK7QY.js} +12267 -10033
  31. package/dist/chunk-BE6DK7QY.js.map +1 -0
  32. package/dist/{chunk-A7IPON2V.cjs → chunk-BIKOJ4FH.cjs} +5 -5
  33. package/dist/{chunk-A7IPON2V.cjs.map → chunk-BIKOJ4FH.cjs.map} +1 -1
  34. package/dist/{chunk-HQYJINFV.js → chunk-CORIBKHG.js} +4 -4
  35. package/dist/{chunk-YEEHB4T6.cjs → chunk-CSYSYJCF.cjs} +40 -40
  36. package/dist/{chunk-YEEHB4T6.cjs.map → chunk-CSYSYJCF.cjs.map} +1 -1
  37. package/dist/{chunk-SIKCXKGB.cjs → chunk-DSDZ5T4P.cjs} +7 -7
  38. package/dist/{chunk-SIKCXKGB.cjs.map → chunk-DSDZ5T4P.cjs.map} +1 -1
  39. package/dist/{chunk-PNXNWGBY.js → chunk-GTWO5NAY.js} +2 -2
  40. package/dist/{chunk-SCCTJ2UI.js → chunk-HSACATCD.js} +4 -4
  41. package/dist/{chunk-PECGZWQR.cjs → chunk-KP5KRRD6.cjs} +3 -3
  42. package/dist/{chunk-PECGZWQR.cjs.map → chunk-KP5KRRD6.cjs.map} +1 -1
  43. package/dist/{chunk-2B6773QM.cjs → chunk-LILQJ3UZ.cjs} +1 -1
  44. package/dist/chunk-LILQJ3UZ.cjs.map +1 -0
  45. package/dist/{chunk-RZSA6MZL.cjs → chunk-NWX7MMF5.cjs} +15 -15
  46. package/dist/{chunk-RZSA6MZL.cjs.map → chunk-NWX7MMF5.cjs.map} +1 -1
  47. package/dist/{chunk-V3CNNMY3.cjs → chunk-P4WTZU6L.cjs} +18 -18
  48. package/dist/{chunk-V3CNNMY3.cjs.map → chunk-P4WTZU6L.cjs.map} +1 -1
  49. package/dist/{chunk-KLYHZRAD.js → chunk-PB3BTZDU.js} +2 -2
  50. package/dist/{chunk-FZOBQG6M.cjs → chunk-Q6E7VJ4W.cjs} +37 -36
  51. package/dist/chunk-Q6E7VJ4W.cjs.map +1 -0
  52. package/dist/{chunk-EYEIVYYF.cjs → chunk-QSCHCZL3.cjs} +7 -7
  53. package/dist/{chunk-EYEIVYYF.cjs.map → chunk-QSCHCZL3.cjs.map} +1 -1
  54. package/dist/{chunk-YJSKQ2ZV.js → chunk-RQLBHKXN.js} +3 -3
  55. package/dist/{chunk-CWQACI4Z.js → chunk-RW2LPL72.js} +2 -2
  56. package/dist/{chunk-DX6AOHNU.cjs → chunk-S2NHJDKQ.cjs} +77 -77
  57. package/dist/{chunk-DX6AOHNU.cjs.map → chunk-S2NHJDKQ.cjs.map} +1 -1
  58. package/dist/{chunk-AHDJMEEN.js → chunk-T2EGSZFN.js} +9 -8
  59. package/dist/chunk-T2EGSZFN.js.map +1 -0
  60. package/dist/{chunk-S52LJNMU.js → chunk-TKN5667N.js} +13 -13
  61. package/dist/chunk-TKN5667N.js.map +1 -0
  62. package/dist/{chunk-3BFX6HJM.js → chunk-UUYRJPGJ.js} +6 -6
  63. package/dist/{chunk-F6BYUO5U.js → chunk-VHG6AX5V.js} +3 -3
  64. package/dist/{chunk-FOQV6RCO.cjs → chunk-VQ523ORK.cjs} +29 -29
  65. package/dist/{chunk-FOQV6RCO.cjs.map → chunk-VQ523ORK.cjs.map} +1 -1
  66. package/dist/{chunk-PXF5J24T.js → chunk-XQ2B74SI.js} +2 -2
  67. package/dist/{chunk-H7JX2BG7.cjs → chunk-YHOIAA2E.cjs} +76 -76
  68. package/dist/chunk-YHOIAA2E.cjs.map +1 -0
  69. package/dist/{chunk-LLWBPPZ4.js → chunk-YZ2OIYQ3.js} +6 -6
  70. package/dist/components/case-studies/index.cjs +10 -10
  71. package/dist/components/case-studies/index.js +4 -4
  72. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  73. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  74. package/dist/components/chat/hooks/index.d.ts +0 -1
  75. package/dist/components/chat/hooks/index.d.ts.map +1 -1
  76. package/dist/components/chat/hooks/use-chat-history-hydration.d.ts +47 -0
  77. package/dist/components/chat/hooks/use-chat-history-hydration.d.ts.map +1 -0
  78. package/dist/components/chat/hooks/use-chat.d.ts +1 -0
  79. package/dist/components/chat/hooks/use-chat.d.ts.map +1 -1
  80. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +15 -48
  81. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  82. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +10 -53
  83. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  84. package/dist/components/chat/index.cjs +12 -4
  85. package/dist/components/chat/index.cjs.map +1 -1
  86. package/dist/components/chat/index.d.ts +1 -0
  87. package/dist/components/chat/index.d.ts.map +1 -1
  88. package/dist/components/chat/index.js +23 -15
  89. package/dist/components/chat/stream/chat-dialog-store.d.ts +184 -0
  90. package/dist/components/chat/stream/chat-dialog-store.d.ts.map +1 -0
  91. package/dist/components/chat/stream/chat-stream-reducer.d.ts +367 -0
  92. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -0
  93. package/dist/components/chat/stream/delta-batcher.d.ts +51 -0
  94. package/dist/components/chat/stream/delta-batcher.d.ts.map +1 -0
  95. package/dist/components/chat/stream/index.d.ts +17 -0
  96. package/dist/components/chat/stream/index.d.ts.map +1 -0
  97. package/dist/components/chat/stream/message-mutations.d.ts +77 -0
  98. package/dist/components/chat/stream/message-mutations.d.ts.map +1 -0
  99. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts +22 -0
  100. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts.map +1 -0
  101. package/dist/components/chat/types/api.types.d.ts +1 -81
  102. package/dist/components/chat/types/api.types.d.ts.map +1 -1
  103. package/dist/components/chat/types/processing.types.d.ts +2 -90
  104. package/dist/components/chat/types/processing.types.d.ts.map +1 -1
  105. package/dist/components/chat/types/unified-chat-state.types.d.ts +5 -0
  106. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  107. package/dist/components/chat/utils/chat-conversation-storage.d.ts +34 -0
  108. package/dist/components/chat/utils/chat-conversation-storage.d.ts.map +1 -0
  109. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts +34 -6
  110. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
  111. package/dist/components/chat/utils/index.d.ts +2 -3
  112. package/dist/components/chat/utils/index.d.ts.map +1 -1
  113. package/dist/components/chat/utils/process-historical-messages.d.ts +59 -11
  114. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  115. package/dist/components/contact/index.cjs +5 -5
  116. package/dist/components/contact/index.js +4 -4
  117. package/dist/components/docs/index.cjs +7 -7
  118. package/dist/components/docs/index.js +6 -6
  119. package/dist/components/embeds/index.cjs +5 -5
  120. package/dist/components/embeds/index.js +4 -4
  121. package/dist/components/faq/index.cjs +6 -6
  122. package/dist/components/faq/index.js +5 -5
  123. package/dist/components/features/index.cjs +4 -4
  124. package/dist/components/features/index.js +3 -3
  125. package/dist/components/help-center-pages/index.cjs +23 -23
  126. package/dist/components/help-center-pages/index.js +14 -14
  127. package/dist/components/index.cjs +182 -140
  128. package/dist/components/index.cjs.map +1 -1
  129. package/dist/components/index.js +66 -24
  130. package/dist/components/index.js.map +1 -1
  131. package/dist/components/navigation/index.cjs +4 -4
  132. package/dist/components/navigation/index.js +3 -3
  133. package/dist/components/onboarding-guides/index.cjs +8 -8
  134. package/dist/components/onboarding-guides/index.js +7 -7
  135. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
  136. package/dist/components/related-content/index.cjs +6 -6
  137. package/dist/components/related-content/index.js +5 -5
  138. package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
  139. package/dist/components/tickets/index.cjs +7 -7
  140. package/dist/components/tickets/index.js +6 -6
  141. package/dist/components/ui/index.cjs +46 -4
  142. package/dist/components/ui/index.cjs.map +1 -1
  143. package/dist/components/ui/index.d.ts +1 -2
  144. package/dist/components/ui/index.d.ts.map +1 -1
  145. package/dist/components/ui/index.js +57 -15
  146. package/dist/components/ui/markdown/base-components.d.ts +51 -0
  147. package/dist/components/ui/markdown/base-components.d.ts.map +1 -0
  148. package/dist/components/ui/markdown/engine.d.ts +99 -0
  149. package/dist/components/ui/markdown/engine.d.ts.map +1 -0
  150. package/dist/components/ui/markdown/heading-ids.d.ts +65 -0
  151. package/dist/components/ui/markdown/heading-ids.d.ts.map +1 -0
  152. package/dist/components/ui/markdown/index.d.ts +18 -0
  153. package/dist/components/ui/markdown/index.d.ts.map +1 -0
  154. package/dist/components/ui/markdown/mermaid-diagram.d.ts +79 -0
  155. package/dist/components/ui/markdown/mermaid-diagram.d.ts.map +1 -0
  156. package/dist/components/ui/markdown/rich/embed-overrides.d.ts +14 -0
  157. package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +1 -0
  158. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts +40 -0
  159. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts.map +1 -0
  160. package/dist/components/ui/markdown/rich/shortcodes.d.ts +8 -0
  161. package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +1 -0
  162. package/dist/components/ui/markdown/sanitize.d.ts +153 -0
  163. package/dist/components/ui/markdown/sanitize.d.ts.map +1 -0
  164. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts +23 -0
  165. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts.map +1 -0
  166. package/dist/components/ui/markdown/streaming.d.ts +78 -0
  167. package/dist/components/ui/markdown/streaming.d.ts.map +1 -0
  168. package/dist/components/ui/markdown/text-size.d.ts +34 -0
  169. package/dist/components/ui/markdown/text-size.d.ts.map +1 -0
  170. package/dist/contexts/chat-runtime-context.d.ts +8 -0
  171. package/dist/contexts/chat-runtime-context.d.ts.map +1 -1
  172. package/dist/contexts/index.cjs +2 -2
  173. package/dist/contexts/index.js +1 -1
  174. package/dist/hooks/index.cjs +3 -3
  175. package/dist/hooks/index.js +2 -2
  176. package/dist/index.cjs +62 -4
  177. package/dist/index.cjs.map +1 -1
  178. package/dist/index.js +73 -15
  179. package/dist/utils/index.cjs +169 -40
  180. package/dist/utils/index.cjs.map +1 -1
  181. package/dist/utils/index.d.ts +1 -0
  182. package/dist/utils/index.d.ts.map +1 -1
  183. package/dist/utils/index.js +162 -41
  184. package/dist/utils/index.js.map +1 -1
  185. package/dist/utils/markdown-fences.d.ts +42 -0
  186. package/dist/utils/markdown-fences.d.ts.map +1 -0
  187. package/dist/utils/markdown-heading-id.d.ts +85 -0
  188. package/dist/utils/markdown-heading-id.d.ts.map +1 -0
  189. package/dist/utils/markdown-section-extractor.d.ts.map +1 -1
  190. package/package.json +7 -1
  191. package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +307 -0
  192. package/src/chat-protocol/__tests__/chat-protocol.test.ts +516 -0
  193. package/src/chat-protocol/__tests__/env-flag.test.ts +52 -0
  194. package/src/chat-protocol/__tests__/ip-normalize.test.ts +136 -0
  195. package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +228 -0
  196. package/src/chat-protocol/decode.ts +278 -0
  197. package/src/chat-protocol/encode.ts +71 -0
  198. package/src/chat-protocol/env-flag.ts +39 -0
  199. package/src/chat-protocol/events.ts +232 -0
  200. package/src/chat-protocol/frames.ts +245 -0
  201. package/src/chat-protocol/index.ts +21 -0
  202. package/src/chat-protocol/ip-normalize.ts +146 -0
  203. package/src/chat-protocol/nats-decoder.ts +252 -0
  204. package/src/components/chat/__tests__/chat-message-list.test.tsx +293 -5
  205. package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +207 -0
  206. package/src/components/chat/__tests__/chat-pending-turn.test.tsx +209 -0
  207. package/src/components/chat/chat-message-enhanced.tsx +111 -17
  208. package/src/components/chat/chat-message-list.tsx +358 -109
  209. package/src/components/chat/embeddable-chat.tsx +6 -0
  210. package/src/components/chat/hooks/.index.md +30 -33
  211. package/src/components/chat/hooks/.use-nats-chat-adapter.md +36 -55
  212. package/src/components/chat/hooks/__tests__/__snapshots__/conversation-id-persistence-golden.test.ts.snap +37 -0
  213. package/src/components/chat/hooks/__tests__/__snapshots__/sse-stream-golden.test.ts.snap +0 -0
  214. package/src/components/chat/hooks/__tests__/conversation-id-persistence-golden.test.ts +438 -0
  215. package/src/components/chat/hooks/__tests__/sse-stream-golden.test.ts +318 -0
  216. package/src/components/chat/hooks/index.ts +0 -1
  217. package/src/components/chat/hooks/use-chat-history-hydration.ts +142 -0
  218. package/src/components/chat/hooks/use-chat.ts +20 -0
  219. package/src/components/chat/hooks/use-nats-chat-adapter.ts +243 -823
  220. package/src/components/chat/hooks/use-sse-chat-adapter.ts +417 -774
  221. package/src/components/chat/index.ts +5 -0
  222. package/src/components/chat/stream/__tests__/__snapshots__/chat-stream-reducer-golden.test.ts.snap +940 -0
  223. package/src/components/chat/stream/__tests__/chat-dialog-store.test.ts +756 -0
  224. package/src/components/chat/stream/__tests__/chat-stream-reducer-golden.test.ts +460 -0
  225. package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +894 -0
  226. package/src/components/chat/stream/__tests__/delta-batcher.test.ts +255 -0
  227. package/src/components/chat/stream/__tests__/use-chat-stream-reducer.test.ts +98 -0
  228. package/src/components/chat/stream/chat-dialog-store.ts +536 -0
  229. package/src/components/chat/stream/chat-stream-reducer.ts +1796 -0
  230. package/src/components/chat/stream/delta-batcher.ts +159 -0
  231. package/src/components/chat/stream/index.ts +55 -0
  232. package/src/components/chat/stream/message-mutations.ts +338 -0
  233. package/src/components/chat/stream/use-chat-stream-reducer.ts +126 -0
  234. package/src/components/chat/thinking-display.tsx +1 -1
  235. package/src/components/chat/types/.api.types.md +39 -49
  236. package/src/components/chat/types/.processing.types.md +29 -58
  237. package/src/components/chat/types/api.types.ts +6 -73
  238. package/src/components/chat/types/processing.types.ts +11 -52
  239. package/src/components/chat/types/unified-chat-state.types.ts +6 -0
  240. package/src/components/chat/utils/.index.md +31 -40
  241. package/src/components/chat/utils/__tests__/__snapshots__/process-historical-messages-golden.test.ts.snap +420 -0
  242. package/src/components/chat/utils/__tests__/__snapshots__/segment-accumulator-golden.test.ts.snap +605 -0
  243. package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +317 -0
  244. package/src/components/chat/utils/__tests__/segment-accumulator-golden.test.ts +270 -0
  245. package/src/components/chat/utils/chat-conversation-storage.ts +83 -0
  246. package/src/components/chat/utils/extract-incomplete-message-state.ts +63 -5
  247. package/src/components/chat/utils/index.ts +5 -8
  248. package/src/components/chat/utils/process-historical-messages.ts +352 -384
  249. package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +22 -4
  250. package/src/components/shared/legal-document/legal-document-page.tsx +1 -1
  251. package/src/components/shared/product-release/release-detail-page.tsx +19 -4
  252. package/src/components/ui/__tests__/__snapshots__/markdown-parity.test.tsx.snap +2420 -0
  253. package/src/components/ui/__tests__/markdown-parity.test.tsx +2528 -0
  254. package/src/components/ui/index.ts +1 -2
  255. package/src/components/ui/markdown/__tests__/mermaid-security.test.ts +115 -0
  256. package/src/components/ui/markdown/__tests__/mermaid-stale-render.test.tsx +95 -0
  257. package/src/components/ui/markdown/__tests__/sanitize-invariant.test.ts +309 -0
  258. package/src/components/ui/markdown/__tests__/streaming.test.tsx +388 -0
  259. package/src/components/ui/markdown/base-components.tsx +360 -0
  260. package/src/components/ui/markdown/engine.tsx +315 -0
  261. package/src/components/ui/markdown/heading-ids.ts +239 -0
  262. package/src/components/ui/markdown/index.ts +49 -0
  263. package/src/components/ui/markdown/mermaid-diagram.tsx +291 -0
  264. package/src/components/ui/markdown/rich/embed-overrides.tsx +199 -0
  265. package/src/components/ui/markdown/rich/rich-markdown-renderer.tsx +184 -0
  266. package/src/components/ui/markdown/rich/shortcodes.ts +170 -0
  267. package/src/components/ui/markdown/sanitize.ts +3499 -0
  268. package/src/components/ui/markdown/simple-markdown-renderer.tsx +28 -0
  269. package/src/components/ui/markdown/streaming.ts +362 -0
  270. package/src/components/ui/markdown/text-size.ts +106 -0
  271. package/src/components/ui/release-changelog-section.tsx +1 -1
  272. package/src/components/ui/ticket-info-section.tsx +1 -1
  273. package/src/contexts/chat-runtime-context.tsx +8 -0
  274. package/src/utils/index.ts +1 -0
  275. package/src/utils/markdown-fences.ts +127 -0
  276. package/src/utils/markdown-heading-id.ts +348 -0
  277. package/src/utils/markdown-section-extractor.ts +41 -56
  278. package/dist/chunk-2B6773QM.cjs.map +0 -1
  279. package/dist/chunk-5RXKOGH6.cjs.map +0 -1
  280. package/dist/chunk-77DFPD35.js.map +0 -1
  281. package/dist/chunk-AHDJMEEN.js.map +0 -1
  282. package/dist/chunk-FZOBQG6M.cjs.map +0 -1
  283. package/dist/chunk-H7JX2BG7.cjs.map +0 -1
  284. package/dist/chunk-KFQXO7QB.js.map +0 -1
  285. package/dist/chunk-S52LJNMU.js.map +0 -1
  286. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts +0 -6
  287. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +0 -1
  288. package/dist/components/chat/utils/chunk-parser.d.ts +0 -25
  289. package/dist/components/chat/utils/chunk-parser.d.ts.map +0 -1
  290. package/dist/components/ui/rich-markdown-renderer.d.ts +0 -34
  291. package/dist/components/ui/rich-markdown-renderer.d.ts.map +0 -1
  292. package/dist/components/ui/simple-markdown-renderer.d.ts +0 -73
  293. package/dist/components/ui/simple-markdown-renderer.d.ts.map +0 -1
  294. package/src/components/chat/hooks/.use-realtime-chunk-processor.md +0 -77
  295. package/src/components/chat/hooks/use-realtime-chunk-processor.ts +0 -479
  296. package/src/components/chat/utils/.chunk-parser.md +0 -62
  297. package/src/components/chat/utils/chunk-parser.ts +0 -256
  298. package/src/components/ui/.simple-markdown-renderer.md +0 -52
  299. package/src/components/ui/rich-markdown-renderer.tsx +0 -1223
  300. package/src/components/ui/simple-markdown-renderer.tsx +0 -964
  301. /package/dist/{chunk-AJNDDXKC.js.map → chunk-4NWARV5I.js.map} +0 -0
  302. /package/dist/{chunk-HQYJINFV.js.map → chunk-CORIBKHG.js.map} +0 -0
  303. /package/dist/{chunk-PNXNWGBY.js.map → chunk-GTWO5NAY.js.map} +0 -0
  304. /package/dist/{chunk-SCCTJ2UI.js.map → chunk-HSACATCD.js.map} +0 -0
  305. /package/dist/{chunk-KLYHZRAD.js.map → chunk-PB3BTZDU.js.map} +0 -0
  306. /package/dist/{chunk-YJSKQ2ZV.js.map → chunk-RQLBHKXN.js.map} +0 -0
  307. /package/dist/{chunk-CWQACI4Z.js.map → chunk-RW2LPL72.js.map} +0 -0
  308. /package/dist/{chunk-3BFX6HJM.js.map → chunk-UUYRJPGJ.js.map} +0 -0
  309. /package/dist/{chunk-F6BYUO5U.js.map → chunk-VHG6AX5V.js.map} +0 -0
  310. /package/dist/{chunk-PXF5J24T.js.map → chunk-XQ2B74SI.js.map} +0 -0
  311. /package/dist/{chunk-LLWBPPZ4.js.map → chunk-YZ2OIYQ3.js.map} +0 -0
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ /**
4
+ * SimpleMarkdownRenderer — the chat/doc composition over the unified
5
+ * `MarkdownEngine`. This IS the real component (not a compat alias): the
6
+ * old 966-line standalone implementation was deleted in the unification;
7
+ * its entire prop surface maps 1:1 onto the engine. Chat consumers keep
8
+ * layering `additionalRemarkPlugins` (card/mention links) and
9
+ * `componentOverrides` exactly as before.
10
+ */
11
+ import React from 'react';
12
+ import { MarkdownEngine, type MarkdownEngineProps } from './engine';
13
+ import type { ResolveLinkResult } from '../../../types/doc-source';
14
+
15
+ export type { ResolveLinkResult };
16
+
17
+ export interface SimpleMarkdownRendererProps
18
+ extends Omit<MarkdownEngineProps, 'extraAllowedHtmlTags'> {}
19
+
20
+ /**
21
+ * A type-narrowed ALIAS of the engine, not a wrapper: the composition adds
22
+ * no behavior (it only hides `extraAllowedHtmlTags` from the prop surface),
23
+ * and `MarkdownEngine` is already `memo`'d. Wrapping it in a second `memo`
24
+ * bought nothing and cost an extra shallow-compare pass plus a fiber per
25
+ * chat segment.
26
+ */
27
+ export const SimpleMarkdownRenderer =
28
+ MarkdownEngine as React.NamedExoticComponent<SimpleMarkdownRendererProps>;
@@ -0,0 +1,362 @@
1
+ /**
2
+ * Streaming helpers for the unified markdown engine (pure, no React).
3
+ *
4
+ * Design contract (unification plan §D1 "Streaming rendering performance"):
5
+ * - Markdown is NOT context-free across blocks. The splitter only cuts at
6
+ * boundaries it can PROVE are atomic; when atomicity is unprovable the
7
+ * content is left in one unit (unmemoized) — correctness beats cache hits.
8
+ * - Blocks containing `card://` / `mention://` markers are never memoized:
9
+ * their text is identical before/after the entity ref resolves, so a
10
+ * text-keyed cache would serve the stale unresolved card mid-stream.
11
+ * - On stream completion the engine discards all of this and does ONE
12
+ * authoritative whole-document parse — streaming can never permanently
13
+ * diverge (late reference defs, footnotes, list renumbering self-heal).
14
+ * - Tail completion auto-closes ONLY unterminated fenced code blocks
15
+ * (fence-count based — unambiguous). Inline emphasis/links are NOT
16
+ * auto-closed: a stray `*` in "2 * 3" or a non-link `[` would be
17
+ * mis-wrapped, which is worse than the brief flicker it prevents.
18
+ */
19
+
20
+ import { createFenceTracker, isBlankLine, type FenceTracker } from '../../../utils/markdown-fences'
21
+
22
+ export interface StreamingBlock {
23
+ /** Raw markdown source of this unit (including trailing blank lines). */
24
+ text: string
25
+ /** Position index — cache key component so identical blocks never alias. */
26
+ index: number
27
+ /**
28
+ * 1-based line of this unit's first line WITHIN the whole document.
29
+ * Each unit is parsed by its own `ReactMarkdown`, so hast positions are
30
+ * unit-relative; the engine adds `startLine - 1` back to look ids up in
31
+ * the document-wide heading-id map (see ./heading-ids.ts).
32
+ */
33
+ startLine: number
34
+ /**
35
+ * True when this unit may be render-cached: it is complete (not the
36
+ * trailing unit), atomic, and free of cross-block / late-resolving
37
+ * constructs (card/mention markers, reference definitions/uses,
38
+ * footnotes).
39
+ */
40
+ memoizable: boolean
41
+ }
42
+
43
+ /** Constructs that resolve across blocks or after the text settles. */
44
+ const NON_MEMOIZABLE_RE = /card:\/\/|mention:\/\/|^\s*\[[^\]]*\]:\s|\]\[|\[\^/m
45
+
46
+ /**
47
+ * Raw HTML block-level containers. A blank line inside an OPEN one of these
48
+ * is not a block boundary: cutting there hands react-markdown an unbalanced
49
+ * fragment, so the container's body renders outside the container and the
50
+ * stray close tag is dropped (`<details>` disclosure bodies escaping the
51
+ * widget mid-stream — and the wrong render getting cached).
52
+ */
53
+ const RAW_BLOCK_TAGS = new Set([
54
+ 'details', 'div', 'table', 'section', 'article', 'aside', 'blockquote',
55
+ 'figure', 'form', 'fieldset', 'header', 'footer', 'main', 'nav', 'dl',
56
+ 'ol', 'ul', 'pre', 'video', 'audio', 'picture', 'iframe', 'template',
57
+ 'svg',
58
+ ])
59
+
60
+ /** `<tag …>` / `</tag>` occurrences, used for raw-HTML depth tracking. */
61
+ const HTML_TAG_RE = /<(\/?)([a-zA-Z][a-zA-Z0-9-]*)([^>]*)>/g
62
+
63
+ /**
64
+ * A start tag whose `>` has NOT arrived yet on this line
65
+ * (`<div\n class="x">`). Only the trailing occurrence matters, and only
66
+ * for RAW_BLOCK_TAGS — restricting it there keeps prose like `a <b` from
67
+ * latching the scanner into "inside a tag" forever.
68
+ */
69
+ const DANGLING_OPEN_TAG_RE = /<([a-zA-Z][a-zA-Z0-9-]*)(?:\s[^>]*)?$/
70
+
71
+ /**
72
+ * A line that may START a raw HTML block. CommonMark puts the `<` at column
73
+ * 0..3, but a raw HTML block nested in a list item is indented to the item's
74
+ * CONTENT column (`1. step` → 4, and up to 7 for a wide ordered marker).
75
+ * The old column-0..3 gate missed exactly those, so an unbalanced raw-HTML
76
+ * container nested in a list item never shifted the scanner's depth and its
77
+ * unit was marked memoizable — contradicting the "unbalanced raw-HTML units
78
+ * are never cached" policy this module documents.
79
+ */
80
+ const HTML_BLOCK_START_RE = /^\s{0,7}<\/?[a-zA-Z]/
81
+
82
+ /**
83
+ * Cap on how long the raw-HTML latch may suppress cutting. An unclosed
84
+ * `<div>` (or a start tag whose `>` never arrives) would otherwise pin
85
+ * `htmlDepth > 0` for the ENTIRE remaining message, collapsing it into one
86
+ * ever-growing non-memoizable unit and disabling the atomic-block
87
+ * optimization outright.
88
+ *
89
+ * THE TRADEOFF, STATED HONESTLY: past the cap the splitter resumes cutting
90
+ * even though a container is still open, so a genuinely >200-line authored
91
+ * `<div>` / `<table>` wrapper IS cut mid-container and visibly breaks apart
92
+ * while streaming (its body renders outside the wrapper, the stray close tag
93
+ * is dropped). Those units are still marked non-memoizable, so nothing WRONG
94
+ * is ever cached and the stream-completion whole-document reparse restores
95
+ * the correct structure — but during the stream the render is wrong, not
96
+ * merely uncached. (An earlier comment here claimed "never a wrong render";
97
+ * that was only true below the cap.)
98
+ *
99
+ * The cap is kept because the alternative — never cutting while latched —
100
+ * makes ONE unbounded unit out of every message containing an unclosed tag,
101
+ * which is the common case (an LLM mid-emission always has one) and costs a
102
+ * full reparse of the entire message on every token.
103
+ */
104
+ const HTML_LATCH_LINE_LIMIT = 200
105
+
106
+ /**
107
+ * Two properties of the fence tracker, stated so they stop being accidents.
108
+ * A reviewer confirmed both hold today, but neither was written down, so a
109
+ * future change could quietly break them.
110
+ *
111
+ * 1. EOF ON THE OPENER LINE EMITS AN EMPTY PAIR. When the stream stops exactly
112
+ * on a fence opener, `completeStreamingTail` closes it at EOF and the render
113
+ * contains a bare ```/``` — a momentarily empty `<pre>`. It is cosmetic and
114
+ * self-heals on the next delta. Do NOT "fix" it by suppressing the close:
115
+ * that leaks an unterminated fence into the parse instead, which is worse.
116
+ *
117
+ * 2. THIS SCANNER AND THE SANITIZER'S MASK CAN DISAGREE ABOUT WHICH FENCE IS
118
+ * OPEN, AND BOTH MUST FAIL CLOSED. `scanLine`'s `pendingOpenTag` early
119
+ * return skips `fences.push`, so a fence opened on a line consumed by an
120
+ * unclosed HTML tag is never recorded here — while `sanitize.ts`'s
121
+ * `blankFencedRegions` runs its own tracker over the same text and may see
122
+ * it. The divergence is safe ONLY because each consumer degrades toward
123
+ * doing less: `completeStreamingTail` closes nothing it did not see open,
124
+ * and `blankFencedRegions` blanks nothing it cannot locate, so the output
125
+ * degrades to "render as-is" rather than to a corrupted document. Any
126
+ * change to the early return, to `fences` bookkeeping, or to either
127
+ * consumer must preserve fail-closed behaviour in BOTH directions
128
+ * (scanner-ahead and scanner-behind).
129
+ */
130
+
131
+ /**
132
+ * Line-scanner state shared by `splitStreamingBlocks` and
133
+ * `completeStreamingTail` — ONE implementation of "where are we in the
134
+ * document", so the splitter and the tail-completer can never disagree
135
+ * about which fence is open.
136
+ */
137
+ interface ScanState {
138
+ /**
139
+ * Fenced-code state machine. NOT a local copy: `createFenceTracker` lives
140
+ * in the server-safe `utils/markdown-fences` and is the SAME instance
141
+ * shape the heading scanner uses, so "is this line inside a fence" has
142
+ * exactly one definition. The previous duplicate here (a verbatim
143
+ * `FENCE_RE` plus its own open/close state machine, cross-referenced by a
144
+ * comment) is precisely the drift that produced the `~~~` bug.
145
+ */
146
+ fences: FenceTracker
147
+ /** Depth of currently open raw HTML block containers. */
148
+ htmlDepth: number
149
+ /** Raw-block tag name of a start tag still waiting for its `>`, or null. */
150
+ pendingOpenTag: string | null
151
+ /** Consecutive lines spent with the raw-HTML latch engaged. */
152
+ htmlLatchLines: number
153
+ }
154
+
155
+ function createScanState(): ScanState {
156
+ return {
157
+ fences: createFenceTracker(),
158
+ htmlDepth: 0,
159
+ pendingOpenTag: null,
160
+ htmlLatchLines: 0,
161
+ }
162
+ }
163
+
164
+ /** Raw-HTML state is unbalanced (a container or a start tag is still open). */
165
+ function htmlLatched(state: ScanState): boolean {
166
+ return state.htmlDepth > 0 || state.pendingOpenTag !== null
167
+ }
168
+
169
+ /** True while raw-HTML state forbids cutting (latch cap not yet reached). */
170
+ function htmlBlocksCut(state: ScanState): boolean {
171
+ return htmlLatched(state) && state.htmlLatchLines <= HTML_LATCH_LINE_LIMIT
172
+ }
173
+
174
+ /** Scan `line` from `from` for complete tags, then record any dangling opener. */
175
+ function scanTags(state: ScanState, line: string, from: number): void {
176
+ HTML_TAG_RE.lastIndex = from
177
+ let m: RegExpExecArray | null
178
+ let end = from
179
+ while ((m = HTML_TAG_RE.exec(line)) !== null) {
180
+ end = HTML_TAG_RE.lastIndex
181
+ const tag = m[2].toLowerCase()
182
+ if (!RAW_BLOCK_TAGS.has(tag)) continue
183
+ if (m[1] === '/') {
184
+ if (state.htmlDepth > 0) state.htmlDepth--
185
+ } else if (!m[3].trimEnd().endsWith('/')) {
186
+ state.htmlDepth++
187
+ }
188
+ }
189
+ const dangling = DANGLING_OPEN_TAG_RE.exec(line.slice(end))
190
+ if (dangling && RAW_BLOCK_TAGS.has(dangling[1].toLowerCase())) {
191
+ state.pendingOpenTag = dangling[1].toLowerCase()
192
+ }
193
+ }
194
+
195
+ /** Advance `state` across one raw source line (mutates). */
196
+ function scanLine(state: ScanState, line: string): void {
197
+ // A start tag spanning lines wins over everything: its continuation lines
198
+ // are attribute soup, not markdown.
199
+ if (state.pendingOpenTag !== null) {
200
+ const gt = line.indexOf('>')
201
+ if (gt === -1) {
202
+ state.htmlLatchLines++
203
+ return
204
+ }
205
+ const selfClosing = line.slice(0, gt).trimEnd().endsWith('/')
206
+ const tag = state.pendingOpenTag
207
+ state.pendingOpenTag = null
208
+ if (!selfClosing && RAW_BLOCK_TAGS.has(tag)) state.htmlDepth++
209
+ scanTags(state, line, gt + 1)
210
+ state.htmlLatchLines = htmlLatched(state) ? state.htmlLatchLines + 1 : 0
211
+ return
212
+ }
213
+
214
+ // Fence delimiters and fence content are both opaque to the HTML scanner —
215
+ // only `text` lines carry markup that can shift raw-HTML depth.
216
+ if (state.fences.push(line) !== 'text') return
217
+
218
+ // Depth > 0 means we are INSIDE a container, where the closing tag may sit
219
+ // anywhere on a line (`some text </details>`). Scanning only column-0..3
220
+ // lines there meant such a close never decremented, so the latch stuck.
221
+ // At depth 0 only a line that can BEGIN a raw HTML block is considered, so
222
+ // `a < b` prose can't shift depth.
223
+ if (state.htmlDepth === 0 && !HTML_BLOCK_START_RE.test(line)) return
224
+ scanTags(state, line, 0)
225
+ state.htmlLatchLines = htmlLatched(state) ? state.htmlLatchLines + 1 : 0
226
+ }
227
+
228
+ /**
229
+ * Split streaming markdown into atomic units at blank lines that are
230
+ * provably block boundaries. A blank line does NOT split when:
231
+ * - inside a fenced code block,
232
+ * - the next non-blank line is indented (loose-list / blockquote
233
+ * continuation cannot be ruled out),
234
+ * - the next non-blank line starts a list item or blockquote AND the
235
+ * previous unit ended in a list item or blockquote (loose list /
236
+ * multi-paragraph quote continuation).
237
+ * The FINAL unit is always the live tail (never memoizable).
238
+ *
239
+ * BLANK LINES COME FROM `isBlankLine` (utils/markdown-fences), never from
240
+ * `String.prototype.trim()`. This file is the THIRD consumer of that module
241
+ * (it already instantiates `createFenceTracker`) and makes exactly the
242
+ * CommonMark blank-line decision the helper was minted for, yet it kept four
243
+ * hand-rolled `trim() === ''` tests — so the module's "every blank-line test"
244
+ * SSOT claim was false here. `trim()` strips the full Unicode White_Space set
245
+ * (U+00A0 NBSP, U+000B VT, U+000C FF, U+FEFF BOM), none of which CommonMark
246
+ * treats as blank: `splitStreamingBlocks('foo\n<NBSP>\nbar\n\ntail')` returned
247
+ * THREE units where the non-blank filler control (`foo\nx\nbar\n\ntail`)
248
+ * returns TWO — one NBSP line cut a paragraph into two `ReactMarkdown` units on
249
+ * the STREAMING path, rendering two `<p>` where the non-streaming and SEO twins
250
+ * render one. Not a security hole (the fence tracker and `htmlBlocksCut` still
251
+ * gate the cut), but a live divergence. It survived because the swallow sweep
252
+ * drove only `SimpleMarkdownRenderer`; the sweep now drives this splitter too,
253
+ * in both the exotic-blank and CRLF spellings.
254
+ */
255
+ export function splitStreamingBlocks(content: string): StreamingBlock[] {
256
+ const lines = content.split('\n')
257
+ const units: string[][] = []
258
+ const unitTouchedHtml: boolean[] = []
259
+ let current: string[] = []
260
+ let currentTouchedHtml = false
261
+ const state = createScanState()
262
+
263
+ const isListOrQuote = (line: string) =>
264
+ /^\s{0,3}(?:[-+*]\s|\d{1,9}[.)]\s|>)/.test(line)
265
+
266
+ const lastNonBlank = (arr: string[]): string | undefined => {
267
+ for (let i = arr.length - 1; i >= 0; i--) {
268
+ if (!isBlankLine(arr[i])) return arr[i]
269
+ }
270
+ return undefined
271
+ }
272
+
273
+ for (let i = 0; i < lines.length; i++) {
274
+ const line = lines[i]
275
+ const depthBefore = state.htmlDepth
276
+ const pendingBefore = state.pendingOpenTag
277
+ scanLine(state, line)
278
+ if (
279
+ state.htmlDepth !== depthBefore ||
280
+ state.pendingOpenTag !== pendingBefore ||
281
+ htmlLatched(state)
282
+ )
283
+ currentTouchedHtml = true
284
+ current.push(line)
285
+
286
+ const isBlank = isBlankLine(line)
287
+ // Never cut inside an open fence, nor inside an unbalanced raw HTML
288
+ // block container / a start tag still waiting for its `>`.
289
+ if (!isBlank || state.fences.openChar() !== null || htmlBlocksCut(state)) continue
290
+
291
+ // Candidate boundary at a blank line outside fences. Look ahead to the
292
+ // next non-blank line to decide whether cutting here is provably safe.
293
+ let j = i + 1
294
+ while (j < lines.length && isBlankLine(lines[j])) j++
295
+ if (j >= lines.length) continue // trailing blanks stay with the tail
296
+
297
+ const next = lines[j]
298
+ if (/^\s/.test(next)) continue // indented continuation — unprovable
299
+ const prev = lastNonBlank(current)
300
+ if (isListOrQuote(next) && prev !== undefined && isListOrQuote(prev)) {
301
+ // Loose list / multi-paragraph blockquote continuation — do not cut.
302
+ continue
303
+ }
304
+
305
+ // A unit whose entire content is whitespace is not a block — with 2+
306
+ // blank lines between blocks the loop reaches a cut point with nothing
307
+ // but blanks accumulated, and emitting it costs a `ReactMarkdown`
308
+ // instance (and a React key) that renders nothing. Leave `current`
309
+ // accumulating so the blanks fold into the NEXT unit; line numbering
310
+ // (and therefore `startLine`) is unaffected either way.
311
+ if (current.every(isBlankLine)) continue
312
+
313
+ units.push(current)
314
+ unitTouchedHtml.push(currentTouchedHtml)
315
+ current = []
316
+ currentTouchedHtml = false
317
+ }
318
+ if (current.length) {
319
+ units.push(current)
320
+ unitTouchedHtml.push(currentTouchedHtml)
321
+ }
322
+
323
+ let cursorLine = 1
324
+ return units.map((unitLines, idx) => {
325
+ const text = unitLines.join('\n')
326
+ const isTail = idx === units.length - 1
327
+ const startLine = cursorLine
328
+ cursorLine += unitLines.length
329
+ return {
330
+ text,
331
+ index: idx,
332
+ startLine,
333
+ // Units carrying UNBALANCED raw HTML are never cached: an open
334
+ // container (or a start tag still awaiting its `>`) is exactly the
335
+ // case where rehype-raw's reparse makes a stale cached render
336
+ // structurally wrong rather than merely late. A raw container that
337
+ // opens AND closes within the unit (`<div>x</div>`) leaves the
338
+ // scanner's depth unchanged, so it stays memoizable — correctly: its
339
+ // parse cannot be invalidated by later tokens.
340
+ memoizable: !isTail && !unitTouchedHtml[idx] && !NON_MEMOIZABLE_RE.test(text),
341
+ }
342
+ })
343
+ }
344
+
345
+ /**
346
+ * Auto-close an unterminated fenced code block at the streaming tail so the
347
+ * half-open fence doesn't flip the rest of the message into code while
348
+ * tokens arrive.
349
+ *
350
+ * Uses the SAME scanner as `splitStreamingBlocks` (never a parallel
351
+ * fence-count heuristic — a naive count treats ``` and ~~~ as
352
+ * interchangeable and appends a spurious closer to `` ```\n~~~\n``` ``),
353
+ * and appends the RECORDED opener so a `~~~` block is closed with `~~~`.
354
+ */
355
+ export function completeStreamingTail(content: string): string {
356
+ const state = createScanState()
357
+ for (const line of content.split('\n')) scanLine(state, line)
358
+ const openChar = state.fences.openChar()
359
+ if (openChar === null) return content
360
+ const closer = openChar.repeat(state.fences.openLength())
361
+ return content.endsWith('\n') ? `${content}${closer}` : `${content}\n${closer}`
362
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Text-size presets for the unified markdown engine.
3
+ *
4
+ * `default` / `compact` / `large` are carried over verbatim from the old
5
+ * `SimpleMarkdownRenderer`. `article` is NEW — it reproduces the exact
6
+ * typography the old `RichMarkdownRenderer` hardcoded per element, so
7
+ * content surfaces (blog, case studies, releases, legal, docs) keep
8
+ * byte-identical classes through the unification.
9
+ *
10
+ * ODS-TOKENS FLAG (ODS_TOKEN_RULES §Typography / §General): the `article`
11
+ * preset carries raw px classes (`text-[32px] md:text-[40px] …`) because
12
+ * ODS currently has NO composite tokens for long-form prose — `text-h1`/
13
+ * `text-h2` are Azeret Mono UI-label scale and `text-h5` is uppercase,
14
+ * so mapping prose headings onto them would visibly regress articles.
15
+ * The `code` entry of EVERY preset (`text-[13px]` / `text-[14px]` /
16
+ * `text-[16px]`), not just `article`'s, is raw px for the same reason: ODS
17
+ * has no code/mono type scale. `base-components.tsx` carries the matching
18
+ * flag for inline code's `text-[0.9em]` (the mono FAMILY is no longer an
19
+ * exemption — both code surfaces now use `font-mono`).
20
+ * Per the unification plan, the missing prose- and code-typography tokens
21
+ * are FLAGGED here for addition to ODS; the raw-px carry-over is the
22
+ * documented, review-gated exemption recorded in the unification PR.
23
+ * Do NOT copy these px classes anywhere else.
24
+ */
25
+
26
+ export type TextSizeElement =
27
+ | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
28
+ | 'p' | 'li' | 'blockquote' | 'code' | 'th' | 'td';
29
+
30
+ export type TextSizeClassMap = Partial<Record<TextSizeElement, string>>;
31
+ export type TextSizePreset = 'default' | 'compact' | 'large' | 'article';
32
+ export type TextSizeConfig =
33
+ | TextSizePreset
34
+ | TextSizeClassMap
35
+ | { preset: TextSizePreset; overrides: TextSizeClassMap };
36
+
37
+ export const TEXT_SIZE_PRESETS: Record<TextSizePreset, Record<TextSizeElement, string>> = {
38
+ default: {
39
+ h1: 'text-heading-1',
40
+ h2: 'text-heading-2',
41
+ h3: 'text-2xl md:text-3xl',
42
+ h4: 'text-xl',
43
+ h5: 'text-lg md:text-xl',
44
+ h6: 'text-base md:text-lg',
45
+ p: 'md:text-h4 lg:text-h4',
46
+ li: 'text-base md:text-lg',
47
+ blockquote: 'text-lg',
48
+ code: 'text-[14px]',
49
+ th: 'text-xs md:text-sm',
50
+ td: 'text-xs md:text-sm',
51
+ },
52
+ compact: {
53
+ h1: 'text-heading-2',
54
+ h2: 'text-heading-3',
55
+ h3: 'text-xl md:text-2xl',
56
+ h4: 'text-lg md:text-xl',
57
+ h5: 'text-base md:text-lg',
58
+ h6: 'text-sm md:text-base',
59
+ p: 'text-base md:text-lg',
60
+ li: 'text-base md:text-lg',
61
+ blockquote: 'text-base md:text-lg',
62
+ code: 'text-[13px]',
63
+ th: 'text-xs md:text-sm',
64
+ td: 'text-xs md:text-sm',
65
+ },
66
+ large: {
67
+ h1: 'text-heading-1',
68
+ h2: 'text-heading-1',
69
+ h3: 'text-heading-2',
70
+ h4: 'text-2xl md:text-3xl',
71
+ h5: 'text-xl md:text-2xl',
72
+ h6: 'text-lg md:text-xl',
73
+ p: 'text-h3',
74
+ li: 'text-lg md:text-xl',
75
+ blockquote: 'text-xl md:text-2xl',
76
+ code: 'text-[16px]',
77
+ th: 'text-sm md:text-base',
78
+ td: 'text-sm md:text-base',
79
+ },
80
+ // Old RichMarkdownRenderer typography, byte-for-byte (see header note).
81
+ article: {
82
+ h1: 'text-[32px] md:text-[40px] lg:text-[48px] leading-[1.25]',
83
+ h2: 'text-[28px] md:text-[32px]',
84
+ h3: 'text-[24px] md:text-[28px]',
85
+ h4: 'text-[20px] md:text-[22px]',
86
+ h5: 'text-[18px] md:text-[20px]',
87
+ h6: 'text-[16px] md:text-[18px]',
88
+ // Full class string incl. spacing/leading — `cn` (tailwind-merge)
89
+ // resolves these OVER the base map's `leading-relaxed mb-4`, keeping
90
+ // Rich's article rhythm (`my-4`, `leading-[1.6]`, `font-sans`).
91
+ p: 'font-sans text-[16px] md:text-[18px] lg:text-[20px] leading-[1.6] my-4',
92
+ li: 'text-[16px] md:text-[18px]',
93
+ blockquote: 'text-[1.125em]',
94
+ code: 'text-[14px]',
95
+ th: 'text-xs md:text-sm',
96
+ td: 'text-xs md:text-sm',
97
+ },
98
+ };
99
+
100
+ export function resolveTextSizeConfig(config?: TextSizeConfig): Record<TextSizeElement, string> {
101
+ const defaultSizes = TEXT_SIZE_PRESETS.default;
102
+ if (!config) return defaultSizes;
103
+ if (typeof config === 'string') return TEXT_SIZE_PRESETS[config];
104
+ if ('preset' in config) return { ...TEXT_SIZE_PRESETS[config.preset], ...config.overrides };
105
+ return { ...defaultSizes, ...config };
106
+ }
@@ -4,7 +4,7 @@ import React, { useState } from 'react';
4
4
  import { Badge } from './badge';
5
5
  import { ChevronDown } from 'lucide-react';
6
6
  import { FadePreview } from './fade-preview';
7
- import { RichMarkdownRenderer } from './rich-markdown-renderer';
7
+ import { RichMarkdownRenderer } from './markdown';
8
8
  import type { ChangelogEntry } from '../../types/product-release';
9
9
 
10
10
  interface ReleaseChangelogSectionProps {
@@ -12,7 +12,7 @@ import { TicketDetailSection } from "./ticket-detail-section"
12
12
  import { type TicketAttachment, TicketAttachmentsList } from "./ticket-attachments-list"
13
13
  import type { TicketNote } from "./ticket-note-card"
14
14
  import { TicketNotesSection } from "./ticket-notes-section"
15
- import { SimpleMarkdownRenderer } from "./simple-markdown-renderer"
15
+ import { SimpleMarkdownRenderer } from "./markdown/simple-markdown-renderer"
16
16
 
17
17
  export type { TicketAssigneeOption }
18
18
 
@@ -57,6 +57,14 @@ export interface ChatRuntime {
57
57
  listEngagementsUrl?: string
58
58
  /** GET slash-command catalog. Hub: '/api/docs/commands'. */
59
59
  commandsUrl: string
60
+ /** GET server-side conversation history (`?conversationId=<id>`) — the
61
+ * chat panel's mount-time hydration read against the server transcript
62
+ * store (localStorage keeps only the server-issued conversation id).
63
+ * OPTIONAL — defaults to `<chatStreamUrl>/history`, which is correct for
64
+ * same-origin hosts AND for reverse-proxy embedders (the proxied chat
65
+ * prefix covers it). Set explicitly only when the history route lives
66
+ * elsewhere. */
67
+ chatHistoryUrl?: string
60
68
  /** GET RAG-search endpoint behind `<DocSearchBar>` (the in-source search
61
69
  * bar mounted by `<DocViewer>` / `<DocsHubPage>` when `showAIChat` is on).
62
70
  * Hub: '/api/docs/search'. OPTIONAL — falls back to the hub path so
@@ -334,6 +334,7 @@ export * from './humanity-signals'
334
334
  export * from './doc-path-utils'
335
335
  export * from './doc-tree-nav'
336
336
  export * from './tree-builder'
337
+ export * from './markdown-heading-id'
337
338
  export * from './markdown-section-extractor'
338
339
  export * from './markdown-to-plain'
339
340
  export * from './embed-url-converters'
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Fenced-code tracker — THE definition of "am I inside a code fence".
3
+ * Server-safe: no React, no DOM.
4
+ *
5
+ * Lives in its own module because it is a GENERAL CommonMark fence machine
6
+ * with two unrelated consumers: the heading scanner
7
+ * (`utils/markdown-heading-id`) and the streaming block splitter
8
+ * (`components/ui/markdown/streaming`). It used to live inside
9
+ * `markdown-heading-id`, which meant a streaming renderer imported its fence
10
+ * state machine from a module named after heading slugs. `markdown-heading-id`
11
+ * re-exports these names so the public `utils` surface is unchanged.
12
+ */
13
+
14
+ /**
15
+ * CommonMark fenced-code opener/closer. Fence indent is capped at 3 spaces
16
+ * (4+ is an indented code block, whose backticks are literal content), and the
17
+ * run length is captured so a closer can be required to be at least as long as
18
+ * its opener. Matched on the RAW line — `trimStart()` would let a fence inside
19
+ * 4-space-indented code toggle the state.
20
+ *
21
+ * `\r`-TOLERANT BY CONSTRUCTION. The previous spelling ended `(.*)$`, and `.`
22
+ * excludes `\r` while `$` (no `m` flag) anchors at end of input — so on a CRLF
23
+ * document, split on `\n`, NO line ever matched and the tracker was completely
24
+ * fence-blind: `['```js\r','const a = 1\r','```\r','after\r']` classified as
25
+ * four `text` lines where the LF spelling gives `open/inside/close/text`. The
26
+ * sanitizer's closer haystack then never blanked the fenced region, so a
27
+ * `</textarea>` written as a CRLF code sample satisfied `hasLaterCloser` and a
28
+ * prose `<textarea>` above it stayed LIVE (reproduced in the DOM; the LF twin
29
+ * of the same fixture escapes correctly). The `\r` is absorbed by `\r?$` rather
30
+ * than normalized out of the text, because the sanitizer's mask is
31
+ * LENGTH-PRESERVING and every offset in it is an index into the original
32
+ * string — stripping `\r` would shift them all.
33
+ *
34
+ * THE ONLY COPY. This constant plus its open/close state machine used to be
35
+ * duplicated verbatim in `components/ui/markdown/streaming.ts`, kept in sync
36
+ * by a code comment — which is exactly how the `~~~`-blind extractor drifted
37
+ * in the first place. Both consumers now instantiate `createFenceTracker()`.
38
+ */
39
+ const FENCE_RE = /^ {0,3}(`{3,}|~{3,})([^\n]*?)\r?$/
40
+
41
+ /**
42
+ * CommonMark's BLANK LINE — spaces and tabs only. The single definition, shared
43
+ * by every consumer of this module (the sanitizer's mask passes, the heading
44
+ * scanner, the HTML-block range walk).
45
+ *
46
+ * NOT `String.prototype.trim()`. JS `trim()` strips the full Unicode
47
+ * White_Space set — U+00A0 NBSP, U+000B VT, U+000C FF, U+2028/9, U+FEFF BOM —
48
+ * none of which CommonMark (or remark) treats as blank. Every place that meant
49
+ * "is this a CommonMark blank line?" and asked `trim() === ''` therefore
50
+ * TERMINATED a construct one line early while remark kept it open. Verified
51
+ * end-to-end: `<div>\n \n\`<textarea>\`\n\nrest\n` left a LIVE `<textarea>`
52
+ * in the DOM (`escapeUnknownHtmlTags` returned the input byte-identical),
53
+ * because the tracked HTML-block range ended at the NBSP filler line and the
54
+ * inline-code shelter below it stopped being inside a tracked block. Same for
55
+ * VT / FF / BOM, for `<pre>` / `<details>` wrappers, and for a live third-party
56
+ * `<iframe>`.
57
+ *
58
+ * `\r` IS accepted: a CRLF document split on `\n` spells its blank lines
59
+ * `"\r"`, and the mask is length-preserving so stripping `\r` from the source
60
+ * is not an option (see `FENCE_RE`'s `\r?$`).
61
+ */
62
+ const BLANK_LINE_RE = /^[ \t\r]*$/
63
+
64
+ /** True when `line` is a CommonMark blank line (spaces/tabs only, `\r`-tolerant). */
65
+ export function isBlankLine(line: string): boolean {
66
+ return BLANK_LINE_RE.test(line)
67
+ }
68
+
69
+ /**
70
+ * What one source line is, relative to fenced code:
71
+ * - `open` — this line opened a fence
72
+ * - `close` — this line closed the open fence
73
+ * - `inside`— this line is fence content (including a would-be delimiter that
74
+ * does NOT close the open fence: wrong marker char, too short, or
75
+ * carrying an info string)
76
+ * - `text` — this line is ordinary markdown outside any fence
77
+ *
78
+ * NOTE: no consumer discriminates the three non-`text` roles today — both the
79
+ * heading scanner and the streaming splitter only test `!== 'text'`, and the
80
+ * closer-haystack mask below only tests `=== 'open'` / `=== 'close'`. The
81
+ * four-value union is kept because the state machine already computes the
82
+ * distinction for free and a future consumer (e.g. fence-aware syntax
83
+ * post-processing) would otherwise have to re-derive it.
84
+ */
85
+ export type FenceLineRole = 'open' | 'close' | 'inside' | 'text'
86
+
87
+ export interface FenceTracker {
88
+ /** Advance across one raw source line (mutates) and classify it. */
89
+ push(line: string): FenceLineRole
90
+ /** Marker CHARACTER of the currently open fence (`` ` `` / `~`), or null. */
91
+ openChar(): string | null
92
+ /** Run length of the currently open fence (a closer must be ≥ this). */
93
+ openLength(): number
94
+ }
95
+
96
+ /** A fresh fenced-code state machine. Server-safe: no React, no DOM. */
97
+ export function createFenceTracker(): FenceTracker {
98
+ let fenceChar: string | null = null
99
+ let fenceLength = 0
100
+ return {
101
+ push(line: string): FenceLineRole {
102
+ const fence = FENCE_RE.exec(line)
103
+ if (fence) {
104
+ const run = fence[1]
105
+ if (fenceChar === null) {
106
+ fenceChar = run[0]
107
+ fenceLength = run.length
108
+ return 'open'
109
+ }
110
+ // A closer must use the SAME marker char, be at least as long as the
111
+ // opener, and (per CommonMark) carry no info string. "No info string"
112
+ // means spaces/tabs only — `trim()` would accept an NBSP tail and close
113
+ // a fence CommonMark keeps open, i.e. blank LESS of the haystack
114
+ // (fail-OPEN). Hence `isBlankLine`, not `trim()`.
115
+ if (run[0] === fenceChar && run.length >= fenceLength && isBlankLine(fence[2])) {
116
+ fenceChar = null
117
+ fenceLength = 0
118
+ return 'close'
119
+ }
120
+ return 'inside'
121
+ }
122
+ return fenceChar === null ? 'text' : 'inside'
123
+ },
124
+ openChar: () => fenceChar,
125
+ openLength: () => fenceLength,
126
+ }
127
+ }