@flamingo-stack/openframe-frontend-core 0.0.498-1506.4000.1 → 0.0.498

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 (290) hide show
  1. package/dist/{chunk-ZHC2BCXK.js → chunk-2OPD6KRB.js} +4 -4
  2. package/dist/{chunk-5BQWVNRS.cjs → chunk-2R25CGSO.cjs} +62 -62
  3. package/dist/{chunk-5BQWVNRS.cjs.map → chunk-2R25CGSO.cjs.map} +1 -1
  4. package/dist/{chunk-242K7LZY.cjs → chunk-4EHQEMN4.cjs} +11 -11
  5. package/dist/{chunk-242K7LZY.cjs.map → chunk-4EHQEMN4.cjs.map} +1 -1
  6. package/dist/{chunk-4KKGHPNE.cjs → chunk-4NIBKEED.cjs} +9 -9
  7. package/dist/{chunk-4KKGHPNE.cjs.map → chunk-4NIBKEED.cjs.map} +1 -1
  8. package/dist/{chunk-5QWYIPMH.js → chunk-5ZL7PA63.js} +2 -2
  9. package/dist/{chunk-3VGRZ62V.js → chunk-6GAJETAP.js} +2 -2
  10. package/dist/{chunk-EO35UHFT.cjs → chunk-A3Z63ZL5.cjs} +60 -60
  11. package/dist/chunk-A3Z63ZL5.cjs.map +1 -0
  12. package/dist/{chunk-34C2JV2C.js → chunk-G6ARA2HD.js} +2 -2
  13. package/dist/{chunk-WNXIG6YY.js → chunk-K5HHLTSG.js} +2 -2
  14. package/dist/{chunk-42NYDEHH.js → chunk-KK4M4URN.js} +2 -2
  15. package/dist/{chunk-42SMPKGY.js → chunk-MF3VJKHT.js} +2 -2
  16. package/dist/{chunk-EORFD5WB.cjs → chunk-MSB3PEPP.cjs} +15 -15
  17. package/dist/{chunk-EORFD5WB.cjs.map → chunk-MSB3PEPP.cjs.map} +1 -1
  18. package/dist/{chunk-F7XPMG25.cjs → chunk-MVORKLWX.cjs} +27 -28
  19. package/dist/chunk-MVORKLWX.cjs.map +1 -0
  20. package/dist/{chunk-D3LUKR2Y.js → chunk-OYPUQJJ2.js} +10044 -12292
  21. package/dist/chunk-OYPUQJJ2.js.map +1 -0
  22. package/dist/{chunk-62TM2RVZ.cjs → chunk-T67MZAQJ.cjs} +26 -26
  23. package/dist/{chunk-62TM2RVZ.cjs.map → chunk-T67MZAQJ.cjs.map} +1 -1
  24. package/dist/{chunk-BUBQEZ5J.js → chunk-VLWVM6ZS.js} +2 -2
  25. package/dist/{chunk-632XJ2ZT.js → chunk-VOOEX4IZ.js} +5 -6
  26. package/dist/chunk-VOOEX4IZ.js.map +1 -0
  27. package/dist/{chunk-5ZY5JM4Z.js → chunk-W4XZ3FBZ.js} +10 -10
  28. package/dist/chunk-W4XZ3FBZ.js.map +1 -0
  29. package/dist/{chunk-2LYA3RBT.cjs → chunk-XI44QDCD.cjs} +37 -37
  30. package/dist/{chunk-2LYA3RBT.cjs.map → chunk-XI44QDCD.cjs.map} +1 -1
  31. package/dist/{chunk-WZCQW4MW.js → chunk-XSNKSMB3.js} +4 -4
  32. package/dist/{chunk-J3NHQ4JB.cjs → chunk-ZBTIN7RC.cjs} +31 -31
  33. package/dist/{chunk-J3NHQ4JB.cjs.map → chunk-ZBTIN7RC.cjs.map} +1 -1
  34. package/dist/{chunk-SS3D3DHL.cjs → chunk-ZEZTITQZ.cjs} +4886 -7134
  35. package/dist/chunk-ZEZTITQZ.cjs.map +1 -0
  36. package/dist/{chunk-44C23SSC.cjs → chunk-ZHNWWFIM.cjs} +5 -5
  37. package/dist/{chunk-44C23SSC.cjs.map → chunk-ZHNWWFIM.cjs.map} +1 -1
  38. package/dist/{chunk-H6GFQ4AO.cjs → chunk-ZTE32A7C.cjs} +7 -7
  39. package/dist/{chunk-H6GFQ4AO.cjs.map → chunk-ZTE32A7C.cjs.map} +1 -1
  40. package/dist/components/case-studies/index.cjs +8 -8
  41. package/dist/components/case-studies/index.js +2 -2
  42. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  43. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  44. package/dist/components/chat/hooks/index.d.ts +1 -0
  45. package/dist/components/chat/hooks/index.d.ts.map +1 -1
  46. package/dist/components/chat/hooks/use-chat.d.ts.map +1 -1
  47. package/dist/components/chat/hooks/use-chunk-catchup.d.ts.map +1 -1
  48. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +48 -15
  49. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  50. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts +6 -0
  51. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +1 -0
  52. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +57 -10
  53. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  54. package/dist/components/chat/index.cjs +2 -10
  55. package/dist/components/chat/index.cjs.map +1 -1
  56. package/dist/components/chat/index.d.ts +0 -1
  57. package/dist/components/chat/index.d.ts.map +1 -1
  58. package/dist/components/chat/index.js +13 -21
  59. package/dist/components/chat/types/api.types.d.ts +81 -1
  60. package/dist/components/chat/types/api.types.d.ts.map +1 -1
  61. package/dist/components/chat/types/processing.types.d.ts +93 -2
  62. package/dist/components/chat/types/processing.types.d.ts.map +1 -1
  63. package/dist/components/chat/utils/chunk-parser.d.ts +25 -0
  64. package/dist/components/chat/utils/chunk-parser.d.ts.map +1 -0
  65. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts +6 -34
  66. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
  67. package/dist/components/chat/utils/index.d.ts +3 -2
  68. package/dist/components/chat/utils/index.d.ts.map +1 -1
  69. package/dist/components/chat/utils/process-historical-messages.d.ts +11 -59
  70. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  71. package/dist/components/contact/index.cjs +3 -3
  72. package/dist/components/contact/index.js +2 -2
  73. package/dist/components/docs/index.cjs +5 -5
  74. package/dist/components/docs/index.js +4 -4
  75. package/dist/components/embeds/index.cjs +3 -3
  76. package/dist/components/embeds/index.js +2 -2
  77. package/dist/components/faq/index.cjs +3 -3
  78. package/dist/components/faq/index.js +2 -2
  79. package/dist/components/features/index.cjs +2 -2
  80. package/dist/components/features/index.js +1 -1
  81. package/dist/components/help-center-pages/index.cjs +20 -20
  82. package/dist/components/help-center-pages/index.js +11 -11
  83. package/dist/components/index.cjs +130 -172
  84. package/dist/components/index.cjs.map +1 -1
  85. package/dist/components/index.js +21 -63
  86. package/dist/components/index.js.map +1 -1
  87. package/dist/components/navigation/header.d.ts.map +1 -1
  88. package/dist/components/navigation/index.cjs +2 -2
  89. package/dist/components/navigation/index.js +1 -1
  90. package/dist/components/navigation/mingo-ai-button.d.ts.map +1 -1
  91. package/dist/components/onboarding-guides/index.cjs +5 -5
  92. package/dist/components/onboarding-guides/index.js +4 -4
  93. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
  94. package/dist/components/related-content/index.cjs +3 -3
  95. package/dist/components/related-content/index.js +2 -2
  96. package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
  97. package/dist/components/tickets/index.cjs +5 -5
  98. package/dist/components/tickets/index.js +4 -4
  99. package/dist/components/ui/index.cjs +2 -44
  100. package/dist/components/ui/index.cjs.map +1 -1
  101. package/dist/components/ui/index.d.ts +2 -1
  102. package/dist/components/ui/index.d.ts.map +1 -1
  103. package/dist/components/ui/index.js +13 -55
  104. package/dist/components/ui/rich-markdown-renderer.d.ts +34 -0
  105. package/dist/components/ui/rich-markdown-renderer.d.ts.map +1 -0
  106. package/dist/components/ui/simple-markdown-renderer.d.ts +73 -0
  107. package/dist/components/ui/simple-markdown-renderer.d.ts.map +1 -0
  108. package/dist/index.cjs +2 -60
  109. package/dist/index.cjs.map +1 -1
  110. package/dist/index.js +13 -71
  111. package/dist/utils/index.cjs +40 -169
  112. package/dist/utils/index.cjs.map +1 -1
  113. package/dist/utils/index.d.ts +0 -1
  114. package/dist/utils/index.d.ts.map +1 -1
  115. package/dist/utils/index.js +41 -162
  116. package/dist/utils/index.js.map +1 -1
  117. package/dist/utils/markdown-section-extractor.d.ts.map +1 -1
  118. package/package.json +1 -7
  119. package/src/components/chat/__tests__/chat-message-list.test.tsx +6 -381
  120. package/src/components/chat/chat-message-enhanced.tsx +20 -122
  121. package/src/components/chat/chat-message-list.tsx +132 -409
  122. package/src/components/chat/embeddable-chat.tsx +0 -6
  123. package/src/components/chat/hooks/.index.md +33 -30
  124. package/src/components/chat/hooks/.use-nats-chat-adapter.md +55 -36
  125. package/src/components/chat/hooks/.use-realtime-chunk-processor.md +77 -0
  126. package/src/components/chat/hooks/index.ts +1 -0
  127. package/src/components/chat/hooks/use-chat.ts +0 -8
  128. package/src/components/chat/hooks/use-chunk-catchup.ts +1 -26
  129. package/src/components/chat/hooks/use-nats-chat-adapter.ts +825 -254
  130. package/src/components/chat/hooks/use-realtime-chunk-processor.ts +489 -0
  131. package/src/components/chat/hooks/use-sse-chat-adapter.ts +684 -380
  132. package/src/components/chat/index.ts +0 -5
  133. package/src/components/chat/thinking-display.tsx +1 -1
  134. package/src/components/chat/types/.api.types.md +49 -39
  135. package/src/components/chat/types/.processing.types.md +58 -29
  136. package/src/components/chat/types/api.types.ts +73 -6
  137. package/src/components/chat/types/processing.types.ts +53 -11
  138. package/src/components/chat/utils/.chunk-parser.md +62 -0
  139. package/src/components/chat/utils/.index.md +40 -31
  140. package/src/components/chat/utils/__tests__/process-historical-messages-approvals.test.ts +0 -61
  141. package/src/components/chat/utils/chunk-parser.ts +262 -0
  142. package/src/components/chat/utils/extract-incomplete-message-state.ts +5 -63
  143. package/src/components/chat/utils/index.ts +8 -5
  144. package/src/components/chat/utils/process-historical-messages.ts +386 -358
  145. package/src/components/navigation/header.tsx +2 -1
  146. package/src/components/navigation/mingo-ai-button.tsx +4 -1
  147. package/src/components/navigation/mobile-nav-panel.tsx +5 -5
  148. package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +4 -22
  149. package/src/components/shared/legal-document/legal-document-page.tsx +1 -1
  150. package/src/components/shared/product-release/release-detail-page.tsx +4 -19
  151. package/src/components/ui/.simple-markdown-renderer.md +52 -0
  152. package/src/components/ui/index.ts +2 -1
  153. package/src/components/ui/release-changelog-section.tsx +1 -1
  154. package/src/components/ui/rich-markdown-renderer.tsx +1223 -0
  155. package/src/components/ui/simple-markdown-renderer.tsx +964 -0
  156. package/src/components/ui/ticket-info-section.tsx +1 -1
  157. package/src/utils/index.ts +0 -1
  158. package/src/utils/markdown-section-extractor.ts +56 -41
  159. package/dist/chat-protocol/decode.d.ts +0 -58
  160. package/dist/chat-protocol/decode.d.ts.map +0 -1
  161. package/dist/chat-protocol/encode.d.ts +0 -44
  162. package/dist/chat-protocol/encode.d.ts.map +0 -1
  163. package/dist/chat-protocol/env-flag.d.ts +0 -35
  164. package/dist/chat-protocol/env-flag.d.ts.map +0 -1
  165. package/dist/chat-protocol/events.d.ts +0 -207
  166. package/dist/chat-protocol/events.d.ts.map +0 -1
  167. package/dist/chat-protocol/frames.d.ts +0 -213
  168. package/dist/chat-protocol/frames.d.ts.map +0 -1
  169. package/dist/chat-protocol/index.cjs +0 -500
  170. package/dist/chat-protocol/index.cjs.map +0 -1
  171. package/dist/chat-protocol/index.d.ts +0 -18
  172. package/dist/chat-protocol/index.d.ts.map +0 -1
  173. package/dist/chat-protocol/index.js +0 -484
  174. package/dist/chat-protocol/index.js.map +0 -1
  175. package/dist/chat-protocol/ip-normalize.d.ts +0 -44
  176. package/dist/chat-protocol/ip-normalize.d.ts.map +0 -1
  177. package/dist/chat-protocol/nats-decoder.d.ts +0 -26
  178. package/dist/chat-protocol/nats-decoder.d.ts.map +0 -1
  179. package/dist/chunk-5ZY5JM4Z.js.map +0 -1
  180. package/dist/chunk-632XJ2ZT.js.map +0 -1
  181. package/dist/chunk-D3LUKR2Y.js.map +0 -1
  182. package/dist/chunk-EO35UHFT.cjs.map +0 -1
  183. package/dist/chunk-F7XPMG25.cjs.map +0 -1
  184. package/dist/chunk-SS3D3DHL.cjs.map +0 -1
  185. package/dist/components/chat/stream/chat-dialog-store.d.ts +0 -184
  186. package/dist/components/chat/stream/chat-dialog-store.d.ts.map +0 -1
  187. package/dist/components/chat/stream/chat-stream-reducer.d.ts +0 -367
  188. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +0 -1
  189. package/dist/components/chat/stream/delta-batcher.d.ts +0 -51
  190. package/dist/components/chat/stream/delta-batcher.d.ts.map +0 -1
  191. package/dist/components/chat/stream/index.d.ts +0 -17
  192. package/dist/components/chat/stream/index.d.ts.map +0 -1
  193. package/dist/components/chat/stream/message-mutations.d.ts +0 -77
  194. package/dist/components/chat/stream/message-mutations.d.ts.map +0 -1
  195. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts +0 -22
  196. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts.map +0 -1
  197. package/dist/components/ui/markdown/base-components.d.ts +0 -51
  198. package/dist/components/ui/markdown/base-components.d.ts.map +0 -1
  199. package/dist/components/ui/markdown/engine.d.ts +0 -99
  200. package/dist/components/ui/markdown/engine.d.ts.map +0 -1
  201. package/dist/components/ui/markdown/heading-ids.d.ts +0 -65
  202. package/dist/components/ui/markdown/heading-ids.d.ts.map +0 -1
  203. package/dist/components/ui/markdown/index.d.ts +0 -18
  204. package/dist/components/ui/markdown/index.d.ts.map +0 -1
  205. package/dist/components/ui/markdown/mermaid-diagram.d.ts +0 -79
  206. package/dist/components/ui/markdown/mermaid-diagram.d.ts.map +0 -1
  207. package/dist/components/ui/markdown/rich/embed-overrides.d.ts +0 -14
  208. package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +0 -1
  209. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts +0 -40
  210. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts.map +0 -1
  211. package/dist/components/ui/markdown/rich/shortcodes.d.ts +0 -8
  212. package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +0 -1
  213. package/dist/components/ui/markdown/sanitize.d.ts +0 -153
  214. package/dist/components/ui/markdown/sanitize.d.ts.map +0 -1
  215. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts +0 -23
  216. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts.map +0 -1
  217. package/dist/components/ui/markdown/streaming.d.ts +0 -78
  218. package/dist/components/ui/markdown/streaming.d.ts.map +0 -1
  219. package/dist/components/ui/markdown/text-size.d.ts +0 -34
  220. package/dist/components/ui/markdown/text-size.d.ts.map +0 -1
  221. package/dist/utils/markdown-fences.d.ts +0 -42
  222. package/dist/utils/markdown-fences.d.ts.map +0 -1
  223. package/dist/utils/markdown-heading-id.d.ts +0 -85
  224. package/dist/utils/markdown-heading-id.d.ts.map +0 -1
  225. package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +0 -317
  226. package/src/chat-protocol/__tests__/chat-protocol.test.ts +0 -516
  227. package/src/chat-protocol/__tests__/env-flag.test.ts +0 -52
  228. package/src/chat-protocol/__tests__/ip-normalize.test.ts +0 -136
  229. package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +0 -232
  230. package/src/chat-protocol/decode.ts +0 -278
  231. package/src/chat-protocol/encode.ts +0 -71
  232. package/src/chat-protocol/env-flag.ts +0 -39
  233. package/src/chat-protocol/events.ts +0 -245
  234. package/src/chat-protocol/frames.ts +0 -245
  235. package/src/chat-protocol/index.ts +0 -21
  236. package/src/chat-protocol/ip-normalize.ts +0 -146
  237. package/src/chat-protocol/nats-decoder.ts +0 -258
  238. package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +0 -207
  239. package/src/components/chat/__tests__/chat-pending-turn.test.tsx +0 -250
  240. package/src/components/chat/hooks/__tests__/__snapshots__/conversation-id-persistence-golden.test.ts.snap +0 -37
  241. package/src/components/chat/hooks/__tests__/__snapshots__/sse-stream-golden.test.ts.snap +0 -0
  242. package/src/components/chat/hooks/__tests__/chunk-catchup-dialog-staleness.test.ts +0 -92
  243. package/src/components/chat/hooks/__tests__/conversation-id-persistence-golden.test.ts +0 -438
  244. package/src/components/chat/hooks/__tests__/sse-stream-golden.test.ts +0 -318
  245. package/src/components/chat/stream/__tests__/__snapshots__/chat-stream-reducer-golden.test.ts.snap +0 -940
  246. package/src/components/chat/stream/__tests__/chat-dialog-store.test.ts +0 -806
  247. package/src/components/chat/stream/__tests__/chat-stream-reducer-golden.test.ts +0 -467
  248. package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +0 -1248
  249. package/src/components/chat/stream/__tests__/delta-batcher.test.ts +0 -255
  250. package/src/components/chat/stream/__tests__/use-chat-stream-reducer.test.ts +0 -105
  251. package/src/components/chat/stream/chat-dialog-store.ts +0 -536
  252. package/src/components/chat/stream/chat-stream-reducer.ts +0 -1876
  253. package/src/components/chat/stream/delta-batcher.ts +0 -168
  254. package/src/components/chat/stream/index.ts +0 -55
  255. package/src/components/chat/stream/message-mutations.ts +0 -340
  256. package/src/components/chat/stream/use-chat-stream-reducer.ts +0 -126
  257. package/src/components/chat/utils/__tests__/__snapshots__/process-historical-messages-golden.test.ts.snap +0 -420
  258. package/src/components/chat/utils/__tests__/__snapshots__/segment-accumulator-golden.test.ts.snap +0 -605
  259. package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +0 -394
  260. package/src/components/chat/utils/__tests__/segment-accumulator-golden.test.ts +0 -270
  261. package/src/components/ui/__tests__/__snapshots__/markdown-parity.test.tsx.snap +0 -2420
  262. package/src/components/ui/__tests__/markdown-parity.test.tsx +0 -2540
  263. package/src/components/ui/markdown/__tests__/mermaid-security.test.ts +0 -176
  264. package/src/components/ui/markdown/__tests__/mermaid-stale-render.test.tsx +0 -151
  265. package/src/components/ui/markdown/__tests__/sanitize-invariant.test.ts +0 -354
  266. package/src/components/ui/markdown/__tests__/sanitize-render.test.tsx +0 -118
  267. package/src/components/ui/markdown/__tests__/streaming.test.tsx +0 -436
  268. package/src/components/ui/markdown/base-components.tsx +0 -360
  269. package/src/components/ui/markdown/engine.tsx +0 -315
  270. package/src/components/ui/markdown/heading-ids.ts +0 -239
  271. package/src/components/ui/markdown/index.ts +0 -49
  272. package/src/components/ui/markdown/mermaid-diagram.tsx +0 -323
  273. package/src/components/ui/markdown/rich/embed-overrides.tsx +0 -199
  274. package/src/components/ui/markdown/rich/rich-markdown-renderer.tsx +0 -184
  275. package/src/components/ui/markdown/rich/shortcodes.ts +0 -170
  276. package/src/components/ui/markdown/sanitize.ts +0 -3584
  277. package/src/components/ui/markdown/simple-markdown-renderer.tsx +0 -28
  278. package/src/components/ui/markdown/streaming.ts +0 -390
  279. package/src/components/ui/markdown/text-size.ts +0 -106
  280. package/src/utils/markdown-fences.ts +0 -127
  281. package/src/utils/markdown-heading-id.ts +0 -348
  282. /package/dist/{chunk-ZHC2BCXK.js.map → chunk-2OPD6KRB.js.map} +0 -0
  283. /package/dist/{chunk-5QWYIPMH.js.map → chunk-5ZL7PA63.js.map} +0 -0
  284. /package/dist/{chunk-3VGRZ62V.js.map → chunk-6GAJETAP.js.map} +0 -0
  285. /package/dist/{chunk-34C2JV2C.js.map → chunk-G6ARA2HD.js.map} +0 -0
  286. /package/dist/{chunk-WNXIG6YY.js.map → chunk-K5HHLTSG.js.map} +0 -0
  287. /package/dist/{chunk-42NYDEHH.js.map → chunk-KK4M4URN.js.map} +0 -0
  288. /package/dist/{chunk-42SMPKGY.js.map → chunk-MF3VJKHT.js.map} +0 -0
  289. /package/dist/{chunk-BUBQEZ5J.js.map → chunk-VLWVM6ZS.js.map} +0 -0
  290. /package/dist/{chunk-WZCQW4MW.js.map → chunk-XSNKSMB3.js.map} +0 -0
@@ -129,8 +129,9 @@ export function Header({ config, platform }: HeaderProps) {
129
129
  leftIcon={item.icon}
130
130
  rightIcon={item.badge}
131
131
  onClick={() => {
132
+ // Single-open: opening a dropdown closes any other open one —
133
+ // only one nav dropdown may be expanded at a time.
132
134
  setOpenDropdowns(prev => ({
133
- ...prev,
134
135
  [item.id]: !prev[item.id],
135
136
  }));
136
137
  }}
@@ -51,7 +51,10 @@ export function MingoAiButton({ source, icon, label = 'Mingo AI', className, onC
51
51
  // Unified ODS top-navigation cell (Figma 2797-6808 desktop /
52
52
  // 2797-7275 mobile): full-height cell with a leading divider,
53
53
  // transparent at rest so it inherits the bar's background.
54
- 'group/mingo relative flex h-full shrink-0 items-center border-l border-ods-border bg-transparent px-[var(--spacing-system-m)] transition-colors duration-200 hover:bg-ods-bg-hover focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent',
54
+ // No hover background on the cell: the animated ring + shimmer are
55
+ // the ONLY hover treatment (a bg flip on top read as a double
56
+ // animation).
57
+ 'group/mingo relative flex h-full shrink-0 items-center border-l border-ods-border bg-transparent px-[var(--spacing-system-m)] focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent',
55
58
  className,
56
59
  )}
57
60
  >
@@ -103,12 +103,12 @@ export function MobileNavPanel({ isOpen, config }: MobileNavPanelProps) {
103
103
  className={cn(
104
104
  "fixed z-[9999] rounded-lg shadow-xl",
105
105
  config.className ? "" : "bg-ods-card border border-ods-border",
106
- // Responsive positioning and sizing — LEFT-anchored from md, under
107
- // the burger cell that sits at the left edge of the unified
108
- // top-navigation (it used to be right-anchored when the burger
109
- // lived on the right).
106
+ // Responsive positioning and sizing — LEFT-anchored from md, flush
107
+ // under the burger cell at the left edge of the unified
108
+ // top-navigation (a larger inset read as a stray gap between the
109
+ // burger and its panel).
110
110
  "right-2 left-2 md:right-auto",
111
- "md:left-6 md:w-[400px] md:max-w-[calc(100vw-3rem)]",
111
+ "md:left-2 md:w-[400px] md:max-w-[calc(100vw-1rem)]",
112
112
  // Small-viewport (svh) sizing anchored below the measured header —
113
113
  // 100vh overflowed under mobile browser chrome, leaving the bottom
114
114
  // entries and footer unreachable while the body was scroll-locked.
@@ -15,7 +15,7 @@
15
15
  * renderer with extra plugins.
16
16
  */
17
17
 
18
- import { Fragment, type ComponentType, type ReactNode } from 'react'
18
+ import { type ComponentType, type ReactNode } from 'react'
19
19
  import { PageLayout } from '../layout/page-layout'
20
20
 
21
21
  import { useRouter } from '../../embed-shims'
@@ -30,7 +30,7 @@ import { VideoBitesStrip } from '../features/video-bites-strip'
30
30
  import { toStripProfile } from '../features/video-bites-shared'
31
31
  import { useVideoWarmup } from '../features/use-video-warmup'
32
32
  import { getCaptionsUrl } from '../features/captions-url'
33
- import { RichMarkdownRenderer } from '../ui/markdown'
33
+ import { RichMarkdownRenderer } from '../ui/rich-markdown-renderer'
34
34
  import { EntityTagBadges } from '../features/entity-tag-badges'
35
35
  import { LoadError } from '../ui/error-state'
36
36
  import { ArticleAuthorByline } from '../shared/article-author-byline'
@@ -244,26 +244,8 @@ export function OnboardingGuideDetailView({
244
244
  </div>
245
245
  )}
246
246
 
247
- {/*
248
- * Host slot — cross-type related-content / FAQ rail.
249
- *
250
- * KEYED DELIBERATELY. This is one member of PageLayout's static children
251
- * array, and the only member whose element may be created in a SERVER
252
- * component and crossed over the RSC boundary. Static JSX children are
253
- * normally exempt from the key warning because `jsxs` runs
254
- * `validateChildKeys` and stamps `_store.validated = 1` on each element —
255
- * but a slot arriving over the wire is referenced as a lazy chunk wrapper,
256
- * so validation marks the WRAPPER and the fulfilled path never copies the
257
- * flag onto the revived element. It reaches the reconciler with
258
- * `validated: 0, key: null` and warns, blaming PageLayout (the owner of the
259
- * enclosing element) rather than anything actually at fault.
260
- *
261
- * A keyed Fragment emits no DOM, so rendered output is byte-identical.
262
- * Do NOT "fix" this in PageLayout via `React.Children.toArray` — that would
263
- * auto-key every consumer's children and change reconciliation identity for
264
- * all of them, and that file carries an explicit FROZEN contract.
265
- */}
266
- <Fragment key="related-content">{relatedContent}</Fragment>
247
+ {/* Host slot — cross-type related-content / FAQ rail. */}
248
+ {relatedContent}
267
249
  </PageLayout>
268
250
  )
269
251
  }
@@ -19,7 +19,7 @@
19
19
 
20
20
  import type { ComponentType } from 'react';
21
21
  import { PageShell, PageLayout } from '../../ui';
22
- import { RichMarkdownRenderer } from '../../ui/markdown';
22
+ import { RichMarkdownRenderer } from '../../ui/rich-markdown-renderer';
23
23
  import { useRouter } from '../../../embed-shims/next-navigation';
24
24
  import { useLegalDocs, type LegalDocument } from './use-legal-docs';
25
25
  import { formatLegalDate } from '../../../utils/format';
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { useState, useEffect, Fragment, ComponentType, type ReactNode } from 'react';
3
+ import { useState, useEffect, ComponentType, type ReactNode } from 'react';
4
4
  import Link from '../../../embed-shims/next-link';
5
5
  import { useRouter } from '../../../embed-shims/next-navigation';
6
6
  import { Card, CardContent } from '../../ui/card';
@@ -11,7 +11,7 @@ import { PageShell } from '../../layout/article-detail-layout';
11
11
  import { PageLayout } from '../../layout/page-layout';
12
12
  import { FadePreview } from '../../ui/fade-preview';
13
13
  import { ReleaseChangelogSection } from '../../ui/release-changelog-section';
14
- import { RichMarkdownRenderer } from '../../ui/markdown';
14
+ import { RichMarkdownRenderer } from '../../ui/rich-markdown-renderer';
15
15
  import { EntityTagBadges } from '../../features/entity-tag-badges';
16
16
  import { EntityMetadataAuthorCell } from '../../chat/entity-cards/entity-author-card';
17
17
  import type { EntityAuthor } from '../../../types/entity-author';
@@ -582,23 +582,8 @@ export function ReleaseDetailPage({
582
582
  )}
583
583
  </div>
584
584
 
585
- {/*
586
- * Host slot — end-of-article byline + related-content / FAQ rail.
587
- *
588
- * KEYED for the same reason as `onboarding-guide-detail-view.tsx`: this is a
589
- * member of PageLayout's static children array, and a slot element built in a
590
- * SERVER component crosses the RSC boundary as a lazy chunk wrapper, so
591
- * `validateChildKeys` marks the wrapper and the fulfilled path never copies
592
- * `_store.validated` onto the revived element — it reaches the reconciler
593
- * unvalidated and warns.
594
- *
595
- * Today the hub's only consumer of this view is a `'use client'` wrapper that
596
- * builds the slot locally, so no element actually crosses the boundary and
597
- * this is pre-emptive. It is kept because the trap is invisible from here: a
598
- * future server-built slot would warn with no clue pointing at this line.
599
- * A keyed Fragment emits no DOM, so output is byte-identical either way.
600
- */}
601
- <Fragment key="related-content">{relatedContent}</Fragment>
585
+ {/* Host slot — end-of-article byline + related-content / FAQ rail. */}
586
+ {relatedContent}
602
587
  </PageLayout>
603
588
  );
604
589
  }
@@ -0,0 +1,52 @@
1
+ <!-- source-hash: 9a47236bd9fd4f90eece958d3708b946 -->
2
+ A client-side React component that renders Markdown content with GFM support, syntax highlighting, raw HTML passthrough, and a layered XSS sanitization pipeline designed for safe rendering of LLM-generated output.
3
+
4
+ ## Key Components
5
+
6
+ ### Rehype Plugins
7
+
8
+ - **`rehypeStripUnsafe()`** — Custom HAST sanitizer that runs after `rehype-raw`. Strips dangerous elements (`<script>`, `<style>`, `<object>`, etc.), removes `on*` event handler attributes, and blocks `javascript:` / `data:` URI schemes on all URL-bearing attributes including multi-candidate `srcset`.
9
+
10
+ - **`srcsetHasUnsafeCandidate(srcset)`** — Helper that splits `srcset` attribute values by comma and validates each URL candidate individually to prevent injection via secondary candidates.
11
+
12
+ - **`escapeUnknownHtmlT...` (truncated)** — Pre-pass that escapes unrecognized HTML-like tags in raw markdown text before `rehype-raw` processes them, preventing React warnings/errors from LLM-emitted pseudo-XML tags like `<ticket>` or `<their>`.
13
+
14
+ ### URL Transform
15
+
16
+ - **`cardAwareUrlTransform(url, key)`** — Extends `react-markdown`'s default URL sanitizer to allow `card://` and `mention://` internal schemes on `href` attributes only (used by `remarkCardLinks` and `remarkMentionChips` plugins), while keeping all other dangerous schemes blocked.
17
+
18
+ ### Constants
19
+
20
+ - **`SAFE_HTML_TAGS`** — Allowlist of permitted HTML tag names forwarded to React. Explicitly excludes `<video>` (handled by a separate pipeline component).
21
+ - **`TAG_LIKE_REGEX`** — ReDoS-safe regex (CodeQL-hardened, bounded quantifiers) for matching HTML-like tags in markdown source.
22
+
23
+ ## Usage Example
24
+
25
+ ```typescript
26
+ import { SimpleMarkdownRenderer } from './simple-markdown-renderer';
27
+
28
+ // Basic rendering with safe LLM output
29
+ <SimpleMarkdownRenderer content="## Hello\n\nThis is **markdown** with `code`." />
30
+
31
+ // With authenticated image sources and link resolution
32
+ <SimpleMarkdownRenderer
33
+ content={llmMarkdownOutput}
34
+ resolveLink={(href) => ({ url: href, label: href })}
35
+ />
36
+ ```
37
+
38
+ ## Security Model
39
+
40
+ The sanitization runs in two layers:
41
+
42
+ ```mermaid
43
+ graph TD
44
+ A[Raw Markdown] --> B[escapeUnknownHtmlTags pre-pass]
45
+ B --> C[remark-gfm / remark-breaks]
46
+ C --> D[rehype-raw]
47
+ D --> E[rehypeStripUnsafe HAST sanitizer]
48
+ E --> F[rehype-highlight]
49
+ F --> G[Safe React Output]
50
+ ```
51
+
52
+ > **Source:** [`simple-markdown-renderer.tsx`](https://github.com/flamingo-stack/openframe-oss-lib/blob/main/simple-markdown-renderer.tsx)
@@ -176,6 +176,7 @@ export * from './ticket-attachments-list'
176
176
  export * from './ticket-note-card'
177
177
  export * from './ticket-notes-section'
178
178
  export * from './marquee-wall'
179
- export * from './markdown'
179
+ export * from './simple-markdown-renderer'
180
+ export { RichMarkdownRenderer, type RichMarkdownRendererProps } from './rich-markdown-renderer'
180
181
  export * from './filter-pill-row'
181
182
 
@@ -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 './markdown';
7
+ import { RichMarkdownRenderer } from './rich-markdown-renderer';
8
8
  import type { ChangelogEntry } from '../../types/product-release';
9
9
 
10
10
  interface ReleaseChangelogSectionProps {