@flamingo-stack/openframe-frontend-core 0.0.510 → 0.0.511-snapshot.20260804140004

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/dist/chat-protocol/decode.d.ts.map +1 -1
  2. package/dist/chat-protocol/frames.d.ts +26 -1
  3. package/dist/chat-protocol/frames.d.ts.map +1 -1
  4. package/dist/chat-protocol/index.cjs +24 -0
  5. package/dist/chat-protocol/index.cjs.map +1 -1
  6. package/dist/chat-protocol/index.js +24 -0
  7. package/dist/chat-protocol/index.js.map +1 -1
  8. package/dist/{chunk-GE3JUTW2.js → chunk-27OKD7ZV.js} +44 -14
  9. package/dist/chunk-27OKD7ZV.js.map +1 -0
  10. package/dist/{chunk-RHQ7BJKK.cjs → chunk-2WWDZR4D.cjs} +73 -73
  11. package/dist/{chunk-RHQ7BJKK.cjs.map → chunk-2WWDZR4D.cjs.map} +1 -1
  12. package/dist/{chunk-J2JUAS4C.cjs → chunk-2XL7UK5Y.cjs} +11 -11
  13. package/dist/{chunk-J2JUAS4C.cjs.map → chunk-2XL7UK5Y.cjs.map} +1 -1
  14. package/dist/{chunk-Q4K3TGNP.cjs → chunk-3NECEXDF.cjs} +74 -44
  15. package/dist/chunk-3NECEXDF.cjs.map +1 -0
  16. package/dist/{chunk-JOCFPAS6.js → chunk-4NFDSMB5.js} +6 -6
  17. package/dist/{chunk-SYZFFRVD.cjs → chunk-5BAGE7AI.cjs} +13 -13
  18. package/dist/{chunk-SYZFFRVD.cjs.map → chunk-5BAGE7AI.cjs.map} +1 -1
  19. package/dist/{chunk-53JKCFFE.cjs → chunk-5JBDT2MQ.cjs} +12 -12
  20. package/dist/{chunk-53JKCFFE.cjs.map → chunk-5JBDT2MQ.cjs.map} +1 -1
  21. package/dist/{chunk-XJVRAKEX.cjs → chunk-67YDSTH7.cjs} +56 -33
  22. package/dist/chunk-67YDSTH7.cjs.map +1 -0
  23. package/dist/{chunk-WNOGR5AA.js → chunk-6LMTBNLN.js} +3 -3
  24. package/dist/{chunk-6FIDTXYC.js → chunk-7AQSXQEL.js} +3 -3
  25. package/dist/{chunk-JMBIGH4H.cjs → chunk-7VBEOGCR.cjs} +83 -83
  26. package/dist/{chunk-JMBIGH4H.cjs.map → chunk-7VBEOGCR.cjs.map} +1 -1
  27. package/dist/{chunk-5AZZQQYS.cjs → chunk-AWLFJMOJ.cjs} +40 -40
  28. package/dist/{chunk-5AZZQQYS.cjs.map → chunk-AWLFJMOJ.cjs.map} +1 -1
  29. package/dist/{chunk-RJ3D3PE2.cjs → chunk-DQDPDUI3.cjs} +9 -9
  30. package/dist/{chunk-RJ3D3PE2.cjs.map → chunk-DQDPDUI3.cjs.map} +1 -1
  31. package/dist/{chunk-2RLI5XVI.cjs → chunk-F2YI5N7E.cjs} +16 -16
  32. package/dist/{chunk-2RLI5XVI.cjs.map → chunk-F2YI5N7E.cjs.map} +1 -1
  33. package/dist/{chunk-LBOWSKGM.js → chunk-FI35LJ3L.js} +5 -5
  34. package/dist/{chunk-BTUHMCYW.js → chunk-FS3YSNSU.js} +193 -6
  35. package/dist/chunk-FS3YSNSU.js.map +1 -0
  36. package/dist/{chunk-RB2EW6IZ.cjs → chunk-GOFTC2HV.cjs} +3690 -3472
  37. package/dist/chunk-GOFTC2HV.cjs.map +1 -0
  38. package/dist/{chunk-CTMLY2GN.js → chunk-HGZPH7S5.js} +5 -5
  39. package/dist/{chunk-7VUKIWF6.cjs → chunk-HIV2GIFP.cjs} +3 -3
  40. package/dist/{chunk-7VUKIWF6.cjs.map → chunk-HIV2GIFP.cjs.map} +1 -1
  41. package/dist/{chunk-DBR5JLX3.js → chunk-IHNUMRMU.js} +46 -23
  42. package/dist/chunk-IHNUMRMU.js.map +1 -0
  43. package/dist/{chunk-R4HKB3YD.cjs → chunk-IIPBTWQR.cjs} +22 -22
  44. package/dist/{chunk-R4HKB3YD.cjs.map → chunk-IIPBTWQR.cjs.map} +1 -1
  45. package/dist/{chunk-TBGSQTJK.js → chunk-IK2WRYVA.js} +6 -6
  46. package/dist/{chunk-WSAPKKDU.cjs → chunk-JIW5JI3E.cjs} +9 -9
  47. package/dist/{chunk-WSAPKKDU.cjs.map → chunk-JIW5JI3E.cjs.map} +1 -1
  48. package/dist/{chunk-MLGGF24T.js → chunk-JKAX5K7C.js} +8 -8
  49. package/dist/{chunk-UWPT23SA.js → chunk-JO3NPEK6.js} +3 -3
  50. package/dist/{chunk-HCZJZMU7.js → chunk-JOFGJYIT.js} +3 -3
  51. package/dist/{chunk-A62C4C4V.cjs → chunk-KI4EGKNV.cjs} +3 -2
  52. package/dist/chunk-KI4EGKNV.cjs.map +1 -0
  53. package/dist/{chunk-OB7YDOTK.cjs → chunk-NJADHL2G.cjs} +8 -8
  54. package/dist/{chunk-OB7YDOTK.cjs.map → chunk-NJADHL2G.cjs.map} +1 -1
  55. package/dist/{chunk-EYTXD62B.cjs → chunk-OGVIGAMZ.cjs} +232 -45
  56. package/dist/chunk-OGVIGAMZ.cjs.map +1 -0
  57. package/dist/{chunk-HX4CJYXL.js → chunk-OT4RV6WY.js} +10 -10
  58. package/dist/{chunk-Z35QWWJH.cjs → chunk-RJXI25TJ.cjs} +34 -34
  59. package/dist/{chunk-Z35QWWJH.cjs.map → chunk-RJXI25TJ.cjs.map} +1 -1
  60. package/dist/{chunk-6TL5XIAW.cjs → chunk-SG25J6LV.cjs} +119 -119
  61. package/dist/{chunk-6TL5XIAW.cjs.map → chunk-SG25J6LV.cjs.map} +1 -1
  62. package/dist/{chunk-M4MKQKFF.js → chunk-UBTOOE55.js} +2 -2
  63. package/dist/{chunk-FEGEC6FY.js → chunk-V5WLIFKN.js} +5660 -5442
  64. package/dist/chunk-V5WLIFKN.js.map +1 -0
  65. package/dist/{chunk-EJ3EBYNV.cjs → chunk-VVXUUBML.cjs} +32 -32
  66. package/dist/{chunk-EJ3EBYNV.cjs.map → chunk-VVXUUBML.cjs.map} +1 -1
  67. package/dist/{chunk-SRYV4H3M.js → chunk-VZX3SSFL.js} +3 -3
  68. package/dist/{chunk-5CDPRCPS.cjs → chunk-WVZHCDB5.cjs} +23 -23
  69. package/dist/{chunk-5CDPRCPS.cjs.map → chunk-WVZHCDB5.cjs.map} +1 -1
  70. package/dist/{chunk-VDJF4ZAU.js → chunk-XG7FLAKQ.js} +3 -3
  71. package/dist/{chunk-SZMA65OW.js → chunk-XURRAFIB.js} +2 -2
  72. package/dist/{chunk-RG2WRBPT.js → chunk-Z3ADCYC5.js} +2 -2
  73. package/dist/{chunk-2AASLY6D.js → chunk-ZIQMVAGA.js} +3 -3
  74. package/dist/{chunk-4GPWLGKT.js → chunk-ZRILYK5U.js} +2 -1
  75. package/dist/components/calendar.cjs +4 -4
  76. package/dist/components/calendar.js +3 -3
  77. package/dist/components/case-studies/index.cjs +17 -17
  78. package/dist/components/case-studies/index.js +7 -7
  79. package/dist/components/chat/approval-batch-message.d.ts +2 -2
  80. package/dist/components/chat/approval-batch-message.d.ts.map +1 -1
  81. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  82. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  83. package/dist/components/chat/entity-cards/deleted-data-card.d.ts +27 -0
  84. package/dist/components/chat/entity-cards/deleted-data-card.d.ts.map +1 -0
  85. package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
  86. package/dist/components/chat/hooks/use-chat-card-item.d.ts +3 -0
  87. package/dist/components/chat/hooks/use-chat-card-item.d.ts.map +1 -1
  88. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  89. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  90. package/dist/components/chat/index.cjs +13 -7
  91. package/dist/components/chat/index.cjs.map +1 -1
  92. package/dist/components/chat/index.js +12 -6
  93. package/dist/components/chat/remark-mention-chips.d.ts +5 -2
  94. package/dist/components/chat/remark-mention-chips.d.ts.map +1 -1
  95. package/dist/components/chat/remark-strip-citations.d.ts +22 -0
  96. package/dist/components/chat/remark-strip-citations.d.ts.map +1 -0
  97. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -1
  98. package/dist/components/chat/stream/message-mutations.d.ts +49 -5
  99. package/dist/components/chat/stream/message-mutations.d.ts.map +1 -1
  100. package/dist/components/chat/types/component.types.d.ts +7 -7
  101. package/dist/components/chat/types/component.types.d.ts.map +1 -1
  102. package/dist/components/chat/types/message.types.d.ts +11 -4
  103. package/dist/components/chat/types/message.types.d.ts.map +1 -1
  104. package/dist/components/chat/types/unified-chat-state.types.d.ts +4 -1
  105. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  106. package/dist/components/chat/utils/discuss-ref-prompt.d.ts +48 -0
  107. package/dist/components/chat/utils/discuss-ref-prompt.d.ts.map +1 -0
  108. package/dist/components/chat/utils/index.d.ts +2 -0
  109. package/dist/components/chat/utils/index.d.ts.map +1 -1
  110. package/dist/components/chat/utils/message-segment-accumulator.d.ts +5 -3
  111. package/dist/components/chat/utils/message-segment-accumulator.d.ts.map +1 -1
  112. package/dist/components/chat/utils/resolve-fetched-card-href.d.ts +63 -0
  113. package/dist/components/chat/utils/resolve-fetched-card-href.d.ts.map +1 -0
  114. package/dist/components/chat/utils/source-row-cta.d.ts.map +1 -1
  115. package/dist/components/contact/index.cjs +8 -8
  116. package/dist/components/contact/index.js +7 -7
  117. package/dist/components/docs/index.cjs +10 -10
  118. package/dist/components/docs/index.js +9 -9
  119. package/dist/components/embeds/index.cjs +8 -8
  120. package/dist/components/embeds/index.js +7 -7
  121. package/dist/components/faq/index.cjs +9 -9
  122. package/dist/components/faq/index.js +8 -8
  123. package/dist/components/features/index.cjs +7 -7
  124. package/dist/components/features/index.js +6 -6
  125. package/dist/components/help-center-pages/index.cjs +28 -28
  126. package/dist/components/help-center-pages/index.js +19 -19
  127. package/dist/components/icons/index.cjs +3 -3
  128. package/dist/components/icons/index.js +2 -2
  129. package/dist/components/index.cjs +253 -247
  130. package/dist/components/index.cjs.map +1 -1
  131. package/dist/components/index.js +23 -17
  132. package/dist/components/index.js.map +1 -1
  133. package/dist/components/meeting-scheduler/index.cjs +92 -92
  134. package/dist/components/meeting-scheduler/index.js +9 -9
  135. package/dist/components/navigation/index.cjs +7 -7
  136. package/dist/components/navigation/index.js +6 -6
  137. package/dist/components/onboarding-guides/index.cjs +11 -11
  138. package/dist/components/onboarding-guides/index.js +10 -10
  139. package/dist/components/related-content/index.cjs +9 -9
  140. package/dist/components/related-content/index.js +8 -8
  141. package/dist/components/shared/doc-search/resolve-search-result-action.d.ts +11 -3
  142. package/dist/components/shared/doc-search/resolve-search-result-action.d.ts.map +1 -1
  143. package/dist/components/shared/doc-search/use-doc-search.d.ts +7 -0
  144. package/dist/components/shared/doc-search/use-doc-search.d.ts.map +1 -1
  145. package/dist/components/tickets/index.cjs +11 -11
  146. package/dist/components/tickets/index.js +10 -10
  147. package/dist/components/ui/actions-menu.d.ts.map +1 -1
  148. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  149. package/dist/components/ui/file-manager/index.cjs +70 -70
  150. package/dist/components/ui/file-manager/index.js +3 -3
  151. package/dist/components/ui/index.cjs +13 -7
  152. package/dist/components/ui/index.cjs.map +1 -1
  153. package/dist/components/ui/index.js +12 -6
  154. package/dist/components/ui/markdown/base-components.d.ts.map +1 -1
  155. package/dist/components/ui/overlay-open-registry.d.ts +50 -0
  156. package/dist/components/ui/overlay-open-registry.d.ts.map +1 -0
  157. package/dist/components/ui/portal-container.d.ts +24 -0
  158. package/dist/components/ui/portal-container.d.ts.map +1 -1
  159. package/dist/hooks/index.cjs +4 -4
  160. package/dist/hooks/index.js +3 -3
  161. package/dist/index.cjs +20 -10
  162. package/dist/index.cjs.map +1 -1
  163. package/dist/index.js +24 -14
  164. package/dist/utils/anchor-id.d.ts +32 -0
  165. package/dist/utils/anchor-id.d.ts.map +1 -0
  166. package/dist/utils/content-href.d.ts +27 -6
  167. package/dist/utils/content-href.d.ts.map +1 -1
  168. package/dist/utils/index.cjs +213 -152
  169. package/dist/utils/index.cjs.map +1 -1
  170. package/dist/utils/index.d.ts +1 -0
  171. package/dist/utils/index.d.ts.map +1 -1
  172. package/dist/utils/index.js +212 -153
  173. package/dist/utils/index.js.map +1 -1
  174. package/dist/utils/list-url.d.ts.map +1 -1
  175. package/dist/utils/same-page-hash-nav.d.ts +17 -6
  176. package/dist/utils/same-page-hash-nav.d.ts.map +1 -1
  177. package/package.json +1 -1
  178. package/src/chat-protocol/decode.ts +43 -0
  179. package/src/chat-protocol/frames.ts +27 -0
  180. package/src/components/chat/__tests__/remark-mention-chips.test.ts +67 -0
  181. package/src/components/chat/approval-batch-message.tsx +2 -2
  182. package/src/components/chat/chat-message-enhanced.tsx +5 -3
  183. package/src/components/chat/chat-message-list.tsx +49 -5
  184. package/src/components/chat/embeddable-chat.tsx +19 -2
  185. package/src/components/chat/entity-cards/deleted-data-card.tsx +61 -0
  186. package/src/components/chat/entity-cards/dispatch.tsx +205 -102
  187. package/src/components/chat/hooks/__tests__/sse-stream-golden.test.ts +1 -1
  188. package/src/components/chat/hooks/use-chat-card-item.ts +15 -1
  189. package/src/components/chat/hooks/use-nats-chat-adapter.ts +28 -4
  190. package/src/components/chat/hooks/use-sse-chat-adapter.ts +57 -32
  191. package/src/components/chat/remark-mention-chips.ts +6 -3
  192. package/src/components/chat/remark-strip-citations.ts +39 -0
  193. package/src/components/chat/stream/__tests__/chat-stream-reducer-approval-batch.test.ts +196 -0
  194. package/src/components/chat/stream/chat-stream-reducer.ts +67 -2
  195. package/src/components/chat/stream/message-mutations.ts +170 -40
  196. package/src/components/chat/types/component.types.ts +9 -7
  197. package/src/components/chat/types/message.types.ts +14 -4
  198. package/src/components/chat/types/unified-chat-state.types.ts +4 -1
  199. package/src/components/chat/utils/__tests__/discuss-ref-prompt.test.ts +67 -0
  200. package/src/components/chat/utils/__tests__/resolve-fetched-card-href.test.ts +156 -0
  201. package/src/components/chat/utils/discuss-ref-prompt.ts +63 -0
  202. package/src/components/chat/utils/index.ts +16 -0
  203. package/src/components/chat/utils/message-segment-accumulator.ts +24 -29
  204. package/src/components/chat/utils/resolve-fetched-card-href.ts +104 -0
  205. package/src/components/chat/utils/source-row-cta.ts +5 -1
  206. package/src/components/shared/doc-search/__tests__/resolve-search-result-action.test.ts +145 -0
  207. package/src/components/shared/doc-search/resolve-search-result-action.ts +46 -10
  208. package/src/components/shared/doc-search/use-doc-search.ts +50 -14
  209. package/src/components/ui/__tests__/overlay-open-registry.test.tsx +87 -0
  210. package/src/components/ui/actions-menu.tsx +65 -2
  211. package/src/components/ui/dropdown-menu.tsx +62 -12
  212. package/src/components/ui/markdown/base-components.tsx +26 -0
  213. package/src/components/ui/overlay-open-registry.tsx +99 -0
  214. package/src/components/ui/portal-container.tsx +31 -0
  215. package/src/utils/__tests__/anchor-id.test.ts +139 -0
  216. package/src/utils/__tests__/content-href.test.ts +74 -0
  217. package/src/utils/__tests__/list-url.test.ts +11 -1
  218. package/src/utils/anchor-id.ts +106 -0
  219. package/src/utils/content-href.ts +113 -13
  220. package/src/utils/index.ts +8 -0
  221. package/src/utils/list-url.ts +6 -1
  222. package/src/utils/same-page-hash-nav.ts +28 -9
  223. package/dist/chunk-A62C4C4V.cjs.map +0 -1
  224. package/dist/chunk-BTUHMCYW.js.map +0 -1
  225. package/dist/chunk-DBR5JLX3.js.map +0 -1
  226. package/dist/chunk-EYTXD62B.cjs.map +0 -1
  227. package/dist/chunk-FEGEC6FY.js.map +0 -1
  228. package/dist/chunk-GE3JUTW2.js.map +0 -1
  229. package/dist/chunk-Q4K3TGNP.cjs.map +0 -1
  230. package/dist/chunk-RB2EW6IZ.cjs.map +0 -1
  231. package/dist/chunk-XJVRAKEX.cjs.map +0 -1
  232. /package/dist/{chunk-JOCFPAS6.js.map → chunk-4NFDSMB5.js.map} +0 -0
  233. /package/dist/{chunk-WNOGR5AA.js.map → chunk-6LMTBNLN.js.map} +0 -0
  234. /package/dist/{chunk-6FIDTXYC.js.map → chunk-7AQSXQEL.js.map} +0 -0
  235. /package/dist/{chunk-LBOWSKGM.js.map → chunk-FI35LJ3L.js.map} +0 -0
  236. /package/dist/{chunk-CTMLY2GN.js.map → chunk-HGZPH7S5.js.map} +0 -0
  237. /package/dist/{chunk-TBGSQTJK.js.map → chunk-IK2WRYVA.js.map} +0 -0
  238. /package/dist/{chunk-MLGGF24T.js.map → chunk-JKAX5K7C.js.map} +0 -0
  239. /package/dist/{chunk-UWPT23SA.js.map → chunk-JO3NPEK6.js.map} +0 -0
  240. /package/dist/{chunk-HCZJZMU7.js.map → chunk-JOFGJYIT.js.map} +0 -0
  241. /package/dist/{chunk-HX4CJYXL.js.map → chunk-OT4RV6WY.js.map} +0 -0
  242. /package/dist/{chunk-M4MKQKFF.js.map → chunk-UBTOOE55.js.map} +0 -0
  243. /package/dist/{chunk-SRYV4H3M.js.map → chunk-VZX3SSFL.js.map} +0 -0
  244. /package/dist/{chunk-VDJF4ZAU.js.map → chunk-XG7FLAKQ.js.map} +0 -0
  245. /package/dist/{chunk-SZMA65OW.js.map → chunk-XURRAFIB.js.map} +0 -0
  246. /package/dist/{chunk-RG2WRBPT.js.map → chunk-Z3ADCYC5.js.map} +0 -0
  247. /package/dist/{chunk-2AASLY6D.js.map → chunk-ZIQMVAGA.js.map} +0 -0
  248. /package/dist/{chunk-4GPWLGKT.js.map → chunk-ZRILYK5U.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"list-url.d.ts","sourceRoot":"","sources":["../../src/utils/list-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAWH;;;;iEAIiE;AACjE,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAEtE;AA6BD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,SAAK,GAAG,MAAM,GAAG,IAAI,CAa5F"}
1
+ {"version":3,"file":"list-url.d.ts","sourceRoot":"","sources":["../../src/utils/list-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAWH;;;;iEAIiE;AACjE,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAOtE;AA6BD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,SAAK,GAAG,MAAM,GAAG,IAAI,CAa5F"}
@@ -15,12 +15,23 @@ export declare const HUB_HEADER_OFFSET_PX = 80;
15
15
  */
16
16
  export declare function normalizeHashFragment(hash: string): string;
17
17
  /**
18
- * Resolve the DOM element a hash fragment id points at. Tries the raw
19
- * id first (ordinary anchors), then the percent-decoded form — href
20
- * composers like `buildTicketOpenHref` encode the id into the fragment
21
- * while rows render the raw id, so `#ticket-a%2Fb` must still find
22
- * `id="ticket-a/b"`. A malformed escape sequence falls back to the
23
- * raw lookup result.
18
+ * Resolve the DOM element a hash fragment id points at — THE hash resolver;
19
+ * `navigateSamePageHash`, `useScrollToHash` and the markdown link renderer
20
+ * all go through this one, so a fragment that resolves for a deep link
21
+ * resolves for an in-body click too.
22
+ *
23
+ * Three passes, cheapest first:
24
+ * 1. the raw id (ordinary anchors — the overwhelming common case);
25
+ * 2. the percent-decoded form — href composers like `buildTicketOpenHref`
26
+ * encode the id into the fragment while rows render the raw id, so
27
+ * `#ticket-a%2Fb` must still find `id="ticket-a/b"`. A malformed escape
28
+ * sequence just falls through;
29
+ * 3. a NORMALIZED match against the document's headings — doc bodies are
30
+ * authored on GitHub, whose slugger keeps the hyphen left behind by a
31
+ * stripped emoji (`## 📚 Table of Contents` → `#-table-of-contents`)
32
+ * while our ids trim it. Without this every in-body TOC link in every
33
+ * GitHub-authored doc resolves to nothing and clicking one does nothing.
34
+ * See `utils/anchor-id`.
24
35
  */
25
36
  export declare function getHashTargetElement(id: string): HTMLElement | null;
26
37
  export interface NavigateSamePageHashOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"same-page-hash-nav.d.ts","sourceRoot":"","sources":["../../src/utils/same-page-hash-nav.ts"],"names":[],"mappings":"AAEA;;sCAEsC;AACtC,eAAO,MAAM,uBAAuB,KAAK,CAAA;AAEzC;yCACyC;AACzC,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAWnE;AAED,MAAM,WAAW,2BAA2B;IAC1C,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;gEAC4D;IAC5D,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAkET"}
1
+ {"version":3,"file":"same-page-hash-nav.d.ts","sourceRoot":"","sources":["../../src/utils/same-page-hash-nav.ts"],"names":[],"mappings":"AAGA;;sCAEsC;AACtC,eAAO,MAAM,uBAAuB,KAAK,CAAA;AAEzC;yCACyC;AACzC,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAkBnE;AAED,MAAM,WAAW,2BAA2B;IAC1C,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;gEAC4D;IAC5D,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAkET"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flamingo-stack/openframe-frontend-core",
3
- "version": "0.0.510",
3
+ "version": "0.0.511-snapshot.20260804140004",
4
4
  "description": "Shared design system and components for all Flamingo platforms",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -100,6 +100,49 @@ function mapLeadingFrame(meta: any, out: ChatStreamEvent[]): void {
100
100
  ...(typeof meta.receiptText === 'string' ? { receiptText: meta.receiptText } : {}),
101
101
  requestId: typeof meta.proposalId === 'string' ? meta.proposalId : undefined,
102
102
  })
103
+ } else if (meta.kind === 'approval_batch' && meta.batchId && Array.isArray(meta.proposals)) {
104
+ // Server-grouped multi-proposal turn → ONE batch event carrying a
105
+ // tool-call row per proposal. `toolExecutionRequestId` is the row's
106
+ // PROPOSAL id (each row resolves through its own per-proposal
107
+ // confirm); the batch's `requestId` is the stable anchor the shell
108
+ // uses for status flips. Field rows ride as the row's expandable
109
+ // args so per-proposal detail stays reachable inside the batch.
110
+ const toolCalls = (meta.proposals as Array<Record<string, any>>)
111
+ .filter((p) => p && typeof p.proposalId === 'string')
112
+ .map((p) => {
113
+ const rawFields = Array.isArray(p.fields)
114
+ ? (p.fields as Array<{ label?: string; value?: string }>).filter(
115
+ (f) => f && f.label && f.value,
116
+ )
117
+ : []
118
+ // Prefer human-readable identity labels for the row's
119
+ // disambiguator; opaque-id labels ("Task", "Ticket") only as
120
+ // the last-resort first field.
121
+ const detail =
122
+ rawFields.find((f) => /^(title|subject|name)$/i.test(String(f.label))) ??
123
+ rawFields[0]
124
+ const base =
125
+ typeof p.title === 'string' && p.title.length > 0 ? p.title : String(p.toolName ?? 'Tool call')
126
+ return {
127
+ toolExecutionRequestId: String(p.proposalId),
128
+ toolName: String(p.toolName ?? 'tool'),
129
+ toolTitle: detail ? `${base} — ${detail.value}` : base,
130
+ requiresApproval: true,
131
+ toolCallArguments:
132
+ rawFields.length > 0
133
+ ? Object.fromEntries(rawFields.map((f) => [String(f.label), String(f.value)]))
134
+ : null,
135
+ }
136
+ })
137
+ if (toolCalls.length > 0) {
138
+ out.push({
139
+ type: 'approval-request',
140
+ requestId: String(meta.batchId),
141
+ approvalType: 'chat',
142
+ toolCalls,
143
+ status: 'pending',
144
+ })
145
+ }
103
146
  } else if (meta.kind === 'approval_request' && meta.proposalId) {
104
147
  const proposalId = String(meta.proposalId)
105
148
  const toolName = String(meta.toolName ?? 'tool')
@@ -165,6 +165,32 @@ export interface ApprovalRequestFrame {
165
165
  ttlSeconds?: number
166
166
  }
167
167
 
168
+ /** A single write-tool turn produced MULTIPLE proposals (batch
169
+ * operations — "delete both", "close all three"). The SERVER is the
170
+ * authority on the grouping: it collects the turn's proposals and
171
+ * ships ONE batch frame instead of N `approval_request` frames, so
172
+ * clients render the bulk-approval component without any adjacency
173
+ * heuristics. Single-proposal turns keep the classic
174
+ * `approval_request` frame. */
175
+ export interface ApprovalBatchFrame {
176
+ kind: 'approval_batch'
177
+ /** Stable batch anchor — client-side only (locates the batch segment
178
+ * for status flips). MUST NOT equal any row's `proposalId`: the
179
+ * click-time optimistic status flip keys on this id, and an anchor
180
+ * that doubles as a row id would falsely tick that row's execution
181
+ * as succeeded before its confirm ran (masking a failed write). The
182
+ * server emits `batch:<first proposalId>`. */
183
+ batchId: string
184
+ proposals: Array<{
185
+ proposalId: string
186
+ toolName: string
187
+ title?: string
188
+ fields?: ApprovalRequestField[]
189
+ expiresAt?: string
190
+ ttlSeconds?: number
191
+ }>
192
+ }
193
+
168
194
  /** Server-driven post-approve / post-reject frame (confirm-tool route).
169
195
  * Mirror of the hub's `DecisionResolvedFrame` in
170
196
  * `lib/data/chat-agent-utils.ts` (tool_name widened to string — the
@@ -205,6 +231,7 @@ export type SseLeadingFrame =
205
231
  | TextLeadingFrame
206
232
  | ToolErrorFrame
207
233
  | ApprovalRequestFrame
234
+ | ApprovalBatchFrame
208
235
  | DecisionResolvedFrame
209
236
 
210
237
  // =============================================================================
@@ -0,0 +1,67 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import type { Root, Text, Link, Paragraph } from 'mdast'
3
+ import { remarkMentionChips } from '../remark-mention-chips'
4
+
5
+ /** One-paragraph tree holding a single text leaf — what the plugin walks. */
6
+ function treeOf(text: string): Root {
7
+ return { type: 'root', children: [{ type: 'paragraph', children: [{ type: 'text', value: text }] }] }
8
+ }
9
+
10
+ /**
11
+ * unified's `Plugin` type declares a `this: Processor` parameter, so calling the
12
+ * attacher bare (outside a pipeline) is a TS2684 — cast the attacher itself, not
13
+ * just its return value, to drop that `this`.
14
+ */
15
+ const attach = remarkMentionChips as unknown as () => (t: Root) => void
16
+
17
+ function run(text: string): Array<Text | Link> {
18
+ const tree = treeOf(text)
19
+ attach()(tree)
20
+ return (tree.children[0] as Paragraph).children as Array<Text | Link>
21
+ }
22
+
23
+ /** `mention://marker:id` urls the plugin emitted, in order. */
24
+ function mentions(text: string): string[] {
25
+ return run(text).flatMap((n) => (n.type === 'link' ? [n.url] : []))
26
+ }
27
+
28
+ describe('remarkMentionChips markers', () => {
29
+ it('parses the lowercase markers', () => {
30
+ expect(mentions('see @device:64f0a1 and @kb:5')).toEqual([
31
+ 'mention://device:64f0a1',
32
+ 'mention://kb:5',
33
+ ])
34
+ })
35
+
36
+ it('parses a camelCase marker', () => {
37
+ // Regression: `ContextItemType.SCHEDULED_SCRIPT` ships as `scheduledScript`,
38
+ // and a lowercase-only grammar matched NOTHING in the token — the mention
39
+ // silently stayed raw text in the bubble instead of becoming a chip.
40
+ expect(mentions('run @scheduledScript:6a6c9342f8e10b2477900c08 now')).toEqual([
41
+ 'mention://scheduledScript:6a6c9342f8e10b2477900c08',
42
+ ])
43
+ })
44
+
45
+ it('keeps the surrounding text intact around a camelCase mention', () => {
46
+ const parts = run('run @scheduledScript:6a6c9342f8e10b2477900c08 now')
47
+ expect(parts.map((n) => (n.type === 'text' ? n.value : `[${(n as Link).url}]`))).toEqual([
48
+ 'run ',
49
+ '[mention://scheduledScript:6a6c9342f8e10b2477900c08]',
50
+ ' now',
51
+ ])
52
+ })
53
+
54
+ it('fires inside punctuation but never mid-word', () => {
55
+ expect(mentions('(@scheduledScript:6a6c9342f8e10b2477900c08)')).toEqual([
56
+ 'mention://scheduledScript:6a6c9342f8e10b2477900c08',
57
+ ])
58
+ // An email-shaped host:port is not a mention — the `@` is preceded by a word char.
59
+ expect(mentions('mail user@Host:1234 stays text')).toEqual([])
60
+ })
61
+
62
+ it('leaves a trailing sentence period out of the id', () => {
63
+ expect(mentions('ran @scheduledScript:6a6c9342f8e10b2477900c08.')).toEqual([
64
+ 'mention://scheduledScript:6a6c9342f8e10b2477900c08',
65
+ ])
66
+ })
67
+ })
@@ -25,8 +25,8 @@ import {
25
25
  export interface ApprovalBatchMessageProps extends React.HTMLAttributes<HTMLDivElement> {
26
26
  data: ApprovalBatchSegment["data"]
27
27
  status?: ApprovalBatchSegment["status"]
28
- onApprove?: (requestId?: string) => void | Promise<void>
29
- onReject?: (requestId?: string) => void | Promise<void>
28
+ onApprove?: (requestId?: string) => void | boolean | Promise<void | boolean>
29
+ onReject?: (requestId?: string) => void | boolean | Promise<void | boolean>
30
30
  /**
31
31
  * Cap the tool-call list height so it scrolls internally while the footer
32
32
  * (explanations + Approve/Reject) stays pinned below. Omit for the default
@@ -15,6 +15,7 @@ import { GuideDisplay } from "./guide-display"
15
15
  import { SimpleMarkdownRenderer } from "../ui/markdown/simple-markdown-renderer"
16
16
  import type { ChatRef } from "./chat-ref.types"
17
17
  import { remarkCardLinks } from "./remark-card-links"
18
+ import { remarkStripCitations } from "./remark-strip-citations"
18
19
  import { remarkMentionChips } from "./remark-mention-chips"
19
20
  import { BlockCard, type BlockCardProps } from "./entity-cards/block-card"
20
21
  import { ChatContextChipStrip } from "./chat-context-picker"
@@ -27,8 +28,9 @@ import type { MessageSegment, MessageContent, ChatMessageEnhancedProps } from ".
27
28
  * (e.g. it matches `x@device:1` mid-word, which the plugin skips), so a context
28
29
  * item would be stripped from the chip strip yet never rendered inline — lost
29
30
  * from display entirely. The id is capture group 2 (group 1 is the boundary).
30
- * Marker lowercase; id is the mention-token charset. */
31
- const MENTION_MARKER_REGEX = /(^|[^\w@])@[a-z]+:([A-Za-z0-9_.+/=-]*[A-Za-z0-9_+/=])/g
31
+ * Marker `[a-zA-Z]+` (markers are mostly lowercase but not all — e.g.
32
+ * `scheduledScript`); id is the mention-token charset. */
33
+ const MENTION_MARKER_REGEX = /(^|[^\w@])@[a-zA-Z]+:([A-Za-z0-9_.+/=-]*[A-Za-z0-9_+/=])/g
32
34
 
33
35
  /**
34
36
  * Same regex shape as `remarkCardLinks` — kept in lockstep so the
@@ -109,7 +111,7 @@ const ChatMessageEnhanced = forwardRef<HTMLDivElement, ChatMessageEnhancedProps>
109
111
  // (user). Each is gated independently so neither fires without its data.
110
112
  const cardRemarkPlugins = useMemo(
111
113
  () => [
112
- ...(hasMarkerSupport ? [remarkCardLinks] : []),
114
+ ...(hasMarkerSupport ? [remarkCardLinks, remarkStripCitations] : []),
113
115
  ...(hasMentionSupport ? [remarkMentionChips] : []),
114
116
  ],
115
117
  [hasMarkerSupport, hasMentionSupport],
@@ -6,6 +6,7 @@ import { cn } from "../../utils/cn"
6
6
  import { ChatMessageEnhanced } from "./chat-message-enhanced"
7
7
  import { ChatMessageListSkeleton } from "./chat-message-skeleton"
8
8
  import { DotsLoaderIcon, Arrow02DownIcon } from "../icons-v2-generated"
9
+ import { OverlayOpenRegistryProvider } from "../ui/overlay-open-registry"
9
10
  import { CyclingPhrase } from "./cycling-phrase"
10
11
  import type { ChatMessageListProps } from "./types"
11
12
  import { SCROLL_ANCHOR } from "./types/message.types"
@@ -141,10 +142,44 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
141
142
  // effect below owns first-paint positioning so re-opening a chat
142
143
  // with prior history lands at the bottom without a smooth-scroll
143
144
  // animation playing over the cold-paint.
144
- const { scrollRef, contentRef, scrollToBottom, stopScroll } = useStickToBottom({
145
- resize: 'smooth',
146
- initial: false,
147
- })
145
+ const { scrollRef, contentRef, scrollToBottom, stopScroll, isAtBottom } =
146
+ useStickToBottom({
147
+ resize: 'smooth',
148
+ initial: false,
149
+ })
150
+
151
+ // ---- Overlay guard: don't move the ground under an open menu -------
152
+ // A ⋯ menu inside a bubble stays anchored to its trigger. While a reply
153
+ // streams in, the follow-the-bottom scroll carries that trigger upward and
154
+ // drags the menu with it, until the menu sits at the panel edge detached
155
+ // from its card. So: while any overlay in this thread is open, stop
156
+ // chasing the bottom (the same thing ChatGPT/Claude do once you interact
157
+ // with the transcript), then catch up on close if we were at the bottom
158
+ // when it opened. New content still renders — we simply stop scrolling to
159
+ // it, and the existing "scroll to bottom" affordance stays available.
160
+ const overlayOpenRef = useRef(false)
161
+ const wasAtBottomOnOpenRef = useRef(false)
162
+ const isAtBottomRef = useRef(isAtBottom)
163
+ isAtBottomRef.current = isAtBottom
164
+ const handleOverlayOpenChange = useCallback(
165
+ (hasOpenOverlay: boolean) => {
166
+ overlayOpenRef.current = hasOpenOverlay
167
+ if (hasOpenOverlay) {
168
+ wasAtBottomOnOpenRef.current = isAtBottomRef.current
169
+ // Cancel any in-flight spring so the thread settles immediately
170
+ // instead of drifting for another few hundred ms under the menu.
171
+ stopScroll()
172
+ return
173
+ }
174
+ // Closed again: resume only if the reader was pinned to the bottom
175
+ // before opening it. Someone who had scrolled up to read history keeps
176
+ // their position.
177
+ if (wasAtBottomOnOpenRef.current) {
178
+ void scrollToBottom({ animation: 'smooth' })
179
+ }
180
+ },
181
+ [scrollToBottom, stopScroll],
182
+ )
148
183
 
149
184
  // ---- Prepend / load-more state (NOT owned by the library) --------
150
185
  const sentinelRef = useRef<HTMLDivElement>(null)
@@ -263,7 +298,12 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
263
298
  const prevLastIdx = prevLastId ? messages.findIndex((m) => m.id === prevLastId) : -1
264
299
  const newSlice = prevLastIdx >= 0 ? messages.slice(prevLastIdx + 1) : messages.slice(prevCount)
265
300
  const hasNewUser = newSlice.some((m) => m.role === 'user')
266
- if (hasNewUser) {
301
+ // An open overlay suppresses even this one: `ignoreEscapes` would yank
302
+ // the thread out from under it — and with it the follow intent, which
303
+ // would re-assert the jump on every later resize. A user who just sent
304
+ // a message with a menu open is rare; a menu jumping away mid-click is
305
+ // not worth it.
306
+ if (hasNewUser && !overlayOpenRef.current) {
267
307
  // Arms the follow intent for the WHOLE turn: the answer's
268
308
  // async growth (attachment images, streamed tokens, entity
269
309
  // cards resolving out of their skeletons long after the
@@ -657,6 +697,8 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
657
697
 
658
698
  useLayoutEffect(() => {
659
699
  if (!autoScroll) return
700
+ // Top-anchored display turns also move the ground — same guard.
701
+ if (overlayOpenRef.current) return
660
702
  const last = messages[messages.length - 1]
661
703
  if (!last || last.role !== 'assistant' || last.scrollAnchor !== SCROLL_ANCHOR.TOP) return
662
704
  // Metadata frame arrives BEFORE body. Wait for content so we don't
@@ -847,6 +889,7 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
847
889
  !(pendingApprovals && pendingApprovals.length > 0)
848
890
 
849
891
  return (
892
+ <OverlayOpenRegistryProvider onOpenChange={handleOverlayOpenChange}>
850
893
  <div className="flex-1 min-h-0 flex flex-col">
851
894
  {/* Positioning box for the jump-to-bottom button: it wraps ONLY the
852
895
  scroller, so `absolute bottom-…` really is the scroller's lower
@@ -1029,6 +1072,7 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
1029
1072
  </div>
1030
1073
  )}
1031
1074
  </div>
1075
+ </OverlayOpenRegistryProvider>
1032
1076
  )
1033
1077
  },
1034
1078
  )
@@ -52,7 +52,7 @@ import { MingoHistoryRail } from './mingo-history-rail'
52
52
  import { GuideWelcome, type GuideWelcomeProps } from './guide-welcome'
53
53
  import { accentFromIdentityIcon, getAgentAccent } from './quick-action-chip'
54
54
  import { GuideModeBanner } from './guide-mode-banner'
55
- import { PortalContainerContext } from '../ui/portal-container'
55
+ import { CollisionBoundaryContext, PortalContainerContext } from '../ui/portal-container'
56
56
  import { ChatPanelHeader, type ChatPanelHeaderProps } from './chat-panel-header'
57
57
  import { SquareAvatar } from '../ui/square-avatar'
58
58
  import { ChatHeaderIconButton } from './chat-header-icon-button'
@@ -1795,6 +1795,13 @@ function EmbeddableChatInner({
1795
1795
 
1796
1796
  // Host node for in-panel Radix portals (see the body wrapper below).
1797
1797
  const [portalHost, setPortalHost] = useState<HTMLDivElement | null>(null)
1798
+ // The panel element itself, published as the COLLISION boundary for overlays
1799
+ // opened inside it. Radix otherwise collides against the viewport, so a ⋯ menu
1800
+ // near the top of the thread flips upward past the panel and renders over the
1801
+ // app header (reported: an entity card's menu landing in the top-right corner,
1802
+ // detached from its card). `portalHost` can't serve here — it is
1803
+ // `display: contents` and has no box to measure.
1804
+ const [panelBoundary, setPanelBoundary] = useState<HTMLDivElement | null>(null)
1798
1805
 
1799
1806
  // ── Split (wide) Mingo layout ─────────────────────────────────────────────
1800
1807
  // The panel measures its own width and, in Mingo mode, promotes the inline
@@ -1817,6 +1824,14 @@ function EmbeddableChatInner({
1817
1824
  return () => ro.disconnect()
1818
1825
  }, [])
1819
1826
 
1827
+ // One ref for two consumers: the width measurement above (a plain ref, read
1828
+ // once on mount) and the collision-boundary context (state, so provider
1829
+ // consumers re-render once the node exists).
1830
+ const setPanelNode = useCallback((node: HTMLDivElement | null) => {
1831
+ panelMeasureRef.current = node
1832
+ setPanelBoundary(node)
1833
+ }, [])
1834
+
1820
1835
  // Rail collapse toggle (Figma ⟶| control), persisted so it survives the
1821
1836
  // drawer remounting on close.
1822
1837
  const [railCollapsed, setRailCollapsed] = useState(false)
@@ -2003,6 +2018,7 @@ function EmbeddableChatInner({
2003
2018
  // document root. See `PortalContainerContext`.
2004
2019
  const body = (
2005
2020
  <PortalContainerContext.Provider value={portalHost}>
2021
+ <CollisionBoundaryContext.Provider value={panelBoundary}>
2006
2022
  {/* Panel surface depends on state (Figma):
2007
2023
  • Narrow "Current Chats" LIST + FULL-PANEL archive page → grey
2008
2024
  `ods-card` (#212121) — matching the grey rail those lists live
@@ -2017,7 +2033,7 @@ function EmbeddableChatInner({
2017
2033
  grey), so the chat block on the right must keep the conversation's
2018
2034
  dark surface instead of following the archive. */}
2019
2035
  <div
2020
- ref={panelMeasureRef}
2036
+ ref={setPanelNode}
2021
2037
  className={`flex h-full flex-col overflow-hidden transition-colors duration-200 ${
2022
2038
  (archiveOpen && !splitActive) || stackedListView
2023
2039
  ? 'bg-ods-card'
@@ -2587,6 +2603,7 @@ function EmbeddableChatInner({
2587
2603
  {/* Portal target for in-panel Radix overlays — `display: contents`
2588
2604
  so it adds no box; content is positioned `fixed` by Radix. */}
2589
2605
  <div ref={setPortalHost} style={{ display: 'contents' }} />
2606
+ </CollisionBoundaryContext.Provider>
2590
2607
  </PortalContainerContext.Provider>
2591
2608
  )
2592
2609
 
@@ -0,0 +1,61 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Deleted Data Card — generic TOMBSTONE placeholder for an entity a chat
5
+ * action removed (ClickUp task today; any deletable entity tomorrow).
6
+ *
7
+ * Follows the established tombstone pattern (Teams / Slack "This message
8
+ * was deleted"): keep a visible, muted placeholder so the conversation
9
+ * thread's integrity holds — the reader sees WHAT was removed and that
10
+ * it may be recoverable, instead of a dead link or a silent gap.
11
+ *
12
+ * PURE PRESENTATION from the ChatRef — no fetch, no navigation (the
13
+ * underlying entity no longer resolves anywhere). Ref contract:
14
+ * - `title` — the deleted entity's display title
15
+ * - `metadata.entity_label` — what kind of thing it was ("ClickUp task")
16
+ * - `metadata.recovery_note` — optional recoverability copy
17
+ * ("Recoverable from the ClickUp Trash for 30 days")
18
+ */
19
+
20
+ import React from 'react'
21
+ import { Trash2 } from 'lucide-react'
22
+ import {
23
+ COMPACT_CARD_ICON_SLOT,
24
+ COMPACT_CARD_META_ROW,
25
+ COMPACT_CARD_OUTER_STATIC,
26
+ COMPACT_CARD_TEXT_COL,
27
+ COMPACT_CARD_TITLE,
28
+ COMPACT_CARD_TITLE_ROW,
29
+ } from '../utils/compact-card-classes'
30
+
31
+ export interface DeletedDataCardProps {
32
+ title?: string | null
33
+ /** What kind of entity was deleted ("ClickUp task", "HubSpot ticket"). */
34
+ entityLabel?: string | null
35
+ /** Recoverability copy ("Recoverable from the ClickUp Trash for 30 days"). */
36
+ recoveryNote?: string | null
37
+ className?: string
38
+ }
39
+
40
+ export function DeletedDataCard({ title, entityLabel, recoveryNote, className }: DeletedDataCardProps) {
41
+ const label = entityLabel || 'Item'
42
+ return (
43
+ <div className={`${COMPACT_CARD_OUTER_STATIC} opacity-70 ${className ?? ''}`}>
44
+ <div className={COMPACT_CARD_ICON_SLOT}>
45
+ <Trash2 className="h-4 w-4 text-ods-text-secondary" />
46
+ </div>
47
+ <div className={COMPACT_CARD_TEXT_COL}>
48
+ <div className={COMPACT_CARD_TITLE_ROW}>
49
+ <span className={`${COMPACT_CARD_TITLE} line-through text-ods-text-secondary`}>
50
+ {title || `Deleted ${label.toLowerCase()}`}
51
+ </span>
52
+ </div>
53
+ <div className={COMPACT_CARD_META_ROW}>
54
+ <span className="text-ods-text-secondary">
55
+ {label} deleted{recoveryNote ? ` · ${recoveryNote}` : ''}
56
+ </span>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ )
61
+ }