@buoy-gg/ask-buoy 7.0.34 → 7.0.35

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 (239) hide show
  1. package/README.md +1 -1
  2. package/lib/commonjs/blocks/AskBlocks.js +338 -1
  3. package/lib/commonjs/blocks/AskBlocks.js.map +1 -0
  4. package/lib/commonjs/blocks/BlockRenderer.js +250 -1
  5. package/lib/commonjs/blocks/BlockRenderer.js.map +1 -0
  6. package/lib/commonjs/blocks/ChromePrototypes.js +514 -1
  7. package/lib/commonjs/blocks/ChromePrototypes.js.map +1 -0
  8. package/lib/commonjs/blocks/DataBlocks.js +688 -1
  9. package/lib/commonjs/blocks/DataBlocks.js.map +1 -0
  10. package/lib/commonjs/blocks/ExtraBlocks.js +1854 -1
  11. package/lib/commonjs/blocks/ExtraBlocks.js.map +1 -0
  12. package/lib/commonjs/blocks/MediaBlocks.js +140 -1
  13. package/lib/commonjs/blocks/MediaBlocks.js.map +1 -0
  14. package/lib/commonjs/blocks/ReceiptBlocks.js +586 -1
  15. package/lib/commonjs/blocks/ReceiptBlocks.js.map +1 -0
  16. package/lib/commonjs/blocks/context.js +42 -1
  17. package/lib/commonjs/blocks/context.js.map +1 -0
  18. package/lib/commonjs/blocks/icons.js +96 -1
  19. package/lib/commonjs/blocks/icons.js.map +1 -0
  20. package/lib/commonjs/blocks/index.js +95 -1
  21. package/lib/commonjs/blocks/index.js.map +1 -0
  22. package/lib/commonjs/blocks/listRow.test.js +55 -0
  23. package/lib/commonjs/blocks/listRow.test.js.map +1 -0
  24. package/lib/commonjs/blocks/pageState.js +34 -0
  25. package/lib/commonjs/blocks/pageState.js.map +1 -0
  26. package/lib/commonjs/blocks/pageState.test.js +57 -0
  27. package/lib/commonjs/blocks/pageState.test.js.map +1 -0
  28. package/lib/commonjs/blocks/primitives.js +401 -1
  29. package/lib/commonjs/blocks/primitives.js.map +1 -0
  30. package/lib/commonjs/blocks/primitivesIndex.js +62 -1
  31. package/lib/commonjs/blocks/primitivesIndex.js.map +1 -0
  32. package/lib/commonjs/components/ApprovalCard.js +140 -1
  33. package/lib/commonjs/components/ApprovalCard.js.map +1 -0
  34. package/lib/commonjs/components/AskBuoyHeader.js +164 -1
  35. package/lib/commonjs/components/AskBuoyHeader.js.map +1 -0
  36. package/lib/commonjs/components/AskBuoyIcon.js +19 -1
  37. package/lib/commonjs/components/AskBuoyIcon.js.map +1 -0
  38. package/lib/commonjs/components/AskBuoyModal.js +353 -1
  39. package/lib/commonjs/components/AskBuoyModal.js.map +1 -0
  40. package/lib/commonjs/components/AskBuoySettings.js +69 -0
  41. package/lib/commonjs/components/AskBuoySettings.js.map +1 -0
  42. package/lib/commonjs/components/ChangesBar.js +211 -1
  43. package/lib/commonjs/components/ChangesBar.js.map +1 -0
  44. package/lib/commonjs/components/Composer.js +175 -1
  45. package/lib/commonjs/components/Composer.js.map +1 -0
  46. package/lib/commonjs/components/EmptyState.js +154 -1
  47. package/lib/commonjs/components/EmptyState.js.map +1 -0
  48. package/lib/commonjs/components/Markdown.js +252 -1
  49. package/lib/commonjs/components/Markdown.js.map +1 -0
  50. package/lib/commonjs/components/MessageList.js +683 -1
  51. package/lib/commonjs/components/MessageList.js.map +1 -0
  52. package/lib/commonjs/components/ThinkingStrip.js +223 -0
  53. package/lib/commonjs/components/ThinkingStrip.js.map +1 -0
  54. package/lib/commonjs/components/TypingDots.js +63 -1
  55. package/lib/commonjs/components/TypingDots.js.map +1 -0
  56. package/lib/commonjs/components/useCopyAction.js +50 -0
  57. package/lib/commonjs/components/useCopyAction.js.map +1 -0
  58. package/lib/commonjs/index.js +252 -1
  59. package/lib/commonjs/index.js.map +1 -0
  60. package/lib/commonjs/preset.js +90 -1
  61. package/lib/commonjs/preset.js.map +1 -0
  62. package/lib/commonjs/store/askBuoySettings.js +67 -0
  63. package/lib/commonjs/store/askBuoySettings.js.map +1 -0
  64. package/lib/commonjs/store/askBuoyStore.js +365 -1
  65. package/lib/commonjs/store/askBuoyStore.js.map +1 -0
  66. package/lib/commonjs/store/restoredIds.test.js +47 -0
  67. package/lib/commonjs/store/restoredIds.test.js.map +1 -0
  68. package/lib/commonjs/store/traceText.js +81 -0
  69. package/lib/commonjs/store/traceText.js.map +1 -0
  70. package/lib/commonjs/store/traceText.test.js +112 -0
  71. package/lib/commonjs/store/traceText.test.js.map +1 -0
  72. package/lib/commonjs/store/transcriptPersistence.js +208 -0
  73. package/lib/commonjs/store/transcriptPersistence.js.map +1 -0
  74. package/lib/commonjs/store/transcriptPersistence.test.js +185 -0
  75. package/lib/commonjs/store/transcriptPersistence.test.js.map +1 -0
  76. package/lib/commonjs/store/useAskBuoy.js +400 -1
  77. package/lib/commonjs/store/useAskBuoy.js.map +1 -0
  78. package/lib/commonjs/store/useAskBuoySettings.js +14 -0
  79. package/lib/commonjs/store/useAskBuoySettings.js.map +1 -0
  80. package/lib/commonjs/sync/askBuoySyncAdapter.js +119 -1
  81. package/lib/commonjs/sync/askBuoySyncAdapter.js.map +1 -0
  82. package/lib/commonjs/sync/askBuoySyncAdapter.test.js +141 -1
  83. package/lib/commonjs/sync/askBuoySyncAdapter.test.js.map +1 -0
  84. package/lib/commonjs/sync/wire.js +58 -1
  85. package/lib/commonjs/sync/wire.js.map +1 -0
  86. package/lib/commonjs/types.js +6 -1
  87. package/lib/commonjs/types.js.map +1 -0
  88. package/lib/module/blocks/AskBlocks.js +331 -1
  89. package/lib/module/blocks/AskBlocks.js.map +1 -0
  90. package/lib/module/blocks/BlockRenderer.js +245 -1
  91. package/lib/module/blocks/BlockRenderer.js.map +1 -0
  92. package/lib/module/blocks/ChromePrototypes.js +503 -1
  93. package/lib/module/blocks/ChromePrototypes.js.map +1 -0
  94. package/lib/module/blocks/DataBlocks.js +674 -1
  95. package/lib/module/blocks/DataBlocks.js.map +1 -0
  96. package/lib/module/blocks/ExtraBlocks.js +1827 -1
  97. package/lib/module/blocks/ExtraBlocks.js.map +1 -0
  98. package/lib/module/blocks/MediaBlocks.js +135 -1
  99. package/lib/module/blocks/MediaBlocks.js.map +1 -0
  100. package/lib/module/blocks/ReceiptBlocks.js +574 -1
  101. package/lib/module/blocks/ReceiptBlocks.js.map +1 -0
  102. package/lib/module/blocks/context.js +35 -1
  103. package/lib/module/blocks/context.js.map +1 -0
  104. package/lib/module/blocks/icons.js +91 -1
  105. package/lib/module/blocks/icons.js.map +1 -0
  106. package/lib/module/blocks/index.js +8 -1
  107. package/lib/module/blocks/index.js.map +1 -0
  108. package/lib/module/blocks/listRow.test.js +56 -0
  109. package/lib/module/blocks/listRow.test.js.map +1 -0
  110. package/lib/module/blocks/pageState.js +30 -0
  111. package/lib/module/blocks/pageState.js.map +1 -0
  112. package/lib/module/blocks/pageState.test.js +56 -0
  113. package/lib/module/blocks/pageState.test.js.map +1 -0
  114. package/lib/module/blocks/primitives.js +389 -1
  115. package/lib/module/blocks/primitives.js.map +1 -0
  116. package/lib/module/blocks/primitivesIndex.js +5 -1
  117. package/lib/module/blocks/primitivesIndex.js.map +1 -0
  118. package/lib/module/components/ApprovalCard.js +135 -1
  119. package/lib/module/components/ApprovalCard.js.map +1 -0
  120. package/lib/module/components/AskBuoyHeader.js +161 -1
  121. package/lib/module/components/AskBuoyHeader.js.map +1 -0
  122. package/lib/module/components/AskBuoyIcon.js +8 -1
  123. package/lib/module/components/AskBuoyIcon.js.map +1 -0
  124. package/lib/module/components/AskBuoyModal.js +348 -1
  125. package/lib/module/components/AskBuoyModal.js.map +1 -0
  126. package/lib/module/components/AskBuoySettings.js +64 -0
  127. package/lib/module/components/AskBuoySettings.js.map +1 -0
  128. package/lib/module/components/ChangesBar.js +206 -1
  129. package/lib/module/components/ChangesBar.js.map +1 -0
  130. package/lib/module/components/Composer.js +170 -1
  131. package/lib/module/components/Composer.js.map +1 -0
  132. package/lib/module/components/EmptyState.js +149 -1
  133. package/lib/module/components/EmptyState.js.map +1 -0
  134. package/lib/module/components/Markdown.js +246 -1
  135. package/lib/module/components/Markdown.js.map +1 -0
  136. package/lib/module/components/MessageList.js +677 -1
  137. package/lib/module/components/MessageList.js.map +1 -0
  138. package/lib/module/components/ThinkingStrip.js +218 -0
  139. package/lib/module/components/ThinkingStrip.js.map +1 -0
  140. package/lib/module/components/TypingDots.js +58 -1
  141. package/lib/module/components/TypingDots.js.map +1 -0
  142. package/lib/module/components/useCopyAction.js +46 -0
  143. package/lib/module/components/useCopyAction.js.map +1 -0
  144. package/lib/module/index.js +34 -1
  145. package/lib/module/index.js.map +1 -0
  146. package/lib/module/preset.js +73 -1
  147. package/lib/module/preset.js.map +1 -0
  148. package/lib/module/store/askBuoySettings.js +62 -0
  149. package/lib/module/store/askBuoySettings.js.map +1 -0
  150. package/lib/module/store/askBuoyStore.js +361 -1
  151. package/lib/module/store/askBuoyStore.js.map +1 -0
  152. package/lib/module/store/restoredIds.test.js +46 -0
  153. package/lib/module/store/restoredIds.test.js.map +1 -0
  154. package/lib/module/store/traceText.js +75 -0
  155. package/lib/module/store/traceText.js.map +1 -0
  156. package/lib/module/store/traceText.test.js +111 -0
  157. package/lib/module/store/traceText.test.js.map +1 -0
  158. package/lib/module/store/transcriptPersistence.js +198 -0
  159. package/lib/module/store/transcriptPersistence.js.map +1 -0
  160. package/lib/module/store/transcriptPersistence.test.js +184 -0
  161. package/lib/module/store/transcriptPersistence.test.js.map +1 -0
  162. package/lib/module/store/useAskBuoy.js +394 -1
  163. package/lib/module/store/useAskBuoy.js.map +1 -0
  164. package/lib/module/store/useAskBuoySettings.js +9 -0
  165. package/lib/module/store/useAskBuoySettings.js.map +1 -0
  166. package/lib/module/sync/askBuoySyncAdapter.js +114 -1
  167. package/lib/module/sync/askBuoySyncAdapter.js.map +1 -0
  168. package/lib/module/sync/askBuoySyncAdapter.test.js +140 -1
  169. package/lib/module/sync/askBuoySyncAdapter.test.js.map +1 -0
  170. package/lib/module/sync/wire.js +47 -1
  171. package/lib/module/sync/wire.js.map +1 -0
  172. package/lib/module/types.js +4 -1
  173. package/lib/module/types.js.map +1 -0
  174. package/lib/typescript/blocks/AskBlocks.d.ts.map +1 -0
  175. package/lib/typescript/blocks/BlockRenderer.d.ts.map +1 -0
  176. package/lib/typescript/blocks/ChromePrototypes.d.ts.map +1 -0
  177. package/lib/typescript/blocks/DataBlocks.d.ts +31 -0
  178. package/lib/typescript/blocks/DataBlocks.d.ts.map +1 -0
  179. package/lib/typescript/blocks/ExtraBlocks.d.ts +11 -0
  180. package/lib/typescript/blocks/ExtraBlocks.d.ts.map +1 -0
  181. package/lib/typescript/blocks/MediaBlocks.d.ts.map +1 -0
  182. package/lib/typescript/blocks/ReceiptBlocks.d.ts.map +1 -0
  183. package/lib/typescript/blocks/context.d.ts.map +1 -0
  184. package/lib/typescript/blocks/icons.d.ts.map +1 -0
  185. package/lib/typescript/blocks/index.d.ts.map +1 -0
  186. package/lib/typescript/blocks/listRow.test.d.ts +2 -0
  187. package/lib/typescript/blocks/listRow.test.d.ts.map +1 -0
  188. package/lib/typescript/blocks/pageState.d.ts +23 -0
  189. package/lib/typescript/blocks/pageState.d.ts.map +1 -0
  190. package/lib/typescript/blocks/pageState.test.d.ts +2 -0
  191. package/lib/typescript/blocks/pageState.test.d.ts.map +1 -0
  192. package/lib/typescript/blocks/primitives.d.ts +39 -9
  193. package/lib/typescript/blocks/primitives.d.ts.map +1 -0
  194. package/lib/typescript/blocks/primitivesIndex.d.ts +2 -1
  195. package/lib/typescript/blocks/primitivesIndex.d.ts.map +1 -0
  196. package/lib/typescript/components/ApprovalCard.d.ts.map +1 -0
  197. package/lib/typescript/components/AskBuoyHeader.d.ts +11 -1
  198. package/lib/typescript/components/AskBuoyHeader.d.ts.map +1 -0
  199. package/lib/typescript/components/AskBuoyIcon.d.ts.map +1 -0
  200. package/lib/typescript/components/AskBuoyModal.d.ts.map +1 -0
  201. package/lib/typescript/components/AskBuoySettings.d.ts +2 -0
  202. package/lib/typescript/components/AskBuoySettings.d.ts.map +1 -0
  203. package/lib/typescript/components/ChangesBar.d.ts.map +1 -0
  204. package/lib/typescript/components/Composer.d.ts.map +1 -0
  205. package/lib/typescript/components/EmptyState.d.ts.map +1 -0
  206. package/lib/typescript/components/Markdown.d.ts.map +1 -0
  207. package/lib/typescript/components/MessageList.d.ts +12 -5
  208. package/lib/typescript/components/MessageList.d.ts.map +1 -0
  209. package/lib/typescript/components/ThinkingStrip.d.ts +5 -0
  210. package/lib/typescript/components/ThinkingStrip.d.ts.map +1 -0
  211. package/lib/typescript/components/TypingDots.d.ts.map +1 -0
  212. package/lib/typescript/components/useCopyAction.d.ts +5 -0
  213. package/lib/typescript/components/useCopyAction.d.ts.map +1 -0
  214. package/lib/typescript/index.d.ts +5 -0
  215. package/lib/typescript/index.d.ts.map +1 -0
  216. package/lib/typescript/preset.d.ts.map +1 -0
  217. package/lib/typescript/store/askBuoySettings.d.ts +22 -0
  218. package/lib/typescript/store/askBuoySettings.d.ts.map +1 -0
  219. package/lib/typescript/store/askBuoyStore.d.ts +74 -13
  220. package/lib/typescript/store/askBuoyStore.d.ts.map +1 -0
  221. package/lib/typescript/store/restoredIds.test.d.ts +2 -0
  222. package/lib/typescript/store/restoredIds.test.d.ts.map +1 -0
  223. package/lib/typescript/store/traceText.d.ts +33 -0
  224. package/lib/typescript/store/traceText.d.ts.map +1 -0
  225. package/lib/typescript/store/traceText.test.d.ts +2 -0
  226. package/lib/typescript/store/traceText.test.d.ts.map +1 -0
  227. package/lib/typescript/store/transcriptPersistence.d.ts +40 -0
  228. package/lib/typescript/store/transcriptPersistence.d.ts.map +1 -0
  229. package/lib/typescript/store/transcriptPersistence.test.d.ts +2 -0
  230. package/lib/typescript/store/transcriptPersistence.test.d.ts.map +1 -0
  231. package/lib/typescript/store/useAskBuoy.d.ts.map +1 -0
  232. package/lib/typescript/store/useAskBuoySettings.d.ts +3 -0
  233. package/lib/typescript/store/useAskBuoySettings.d.ts.map +1 -0
  234. package/lib/typescript/sync/askBuoySyncAdapter.d.ts.map +1 -0
  235. package/lib/typescript/sync/askBuoySyncAdapter.test.d.ts.map +1 -0
  236. package/lib/typescript/sync/wire.d.ts.map +1 -0
  237. package/lib/typescript/types.d.ts +24 -1
  238. package/lib/typescript/types.d.ts.map +1 -0
  239. package/package.json +8 -8
@@ -1 +1,683 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.MessageList=MessageList,exports.transcriptToText=transcriptToText;var _react=require("react"),_reactNative=require("react-native"),_sharedUi=require("@buoy-gg/shared-ui"),_agentCore=require("@buoy-gg/agent-core"),_BlockRenderer=require("../blocks/BlockRenderer"),_Markdown=require("./Markdown"),_TypingDots=require("./TypingDots"),_jsxRuntime=require("react/jsx-runtime");function StatusMark({status:e}){switch(e){case"running":return(0,_jsxRuntime.jsx)(_reactNative.ActivityIndicator,{size:"small",color:_sharedUi.night.color.textSecondary});case"ok":return(0,_jsxRuntime.jsx)(_reactNative.View,{style:[styles.mark,styles.markOk],children:(0,_jsxRuntime.jsx)(_sharedUi.Check,{size:10,color:_sharedUi.night.color.onAccent})});case"failed":return(0,_jsxRuntime.jsx)(_reactNative.View,{style:[styles.mark,styles.markFailed],children:(0,_jsxRuntime.jsx)(_sharedUi.X,{size:10,color:_sharedUi.night.color.text})});case"refused":return(0,_jsxRuntime.jsx)(_reactNative.View,{style:[styles.mark,styles.markRefused],children:(0,_jsxRuntime.jsx)(_sharedUi.AlertTriangle,{size:10,color:_sharedUi.night.color.onAccent})})}}function statusText(e){return"running"===e.status?{text:"",tone:"muted"}:"refused"===e.status?{text:"Refused",tone:"warning"}:"failed"===e.status?{text:"Failed",tone:"danger"}:{text:e.summary||"Done",tone:"muted"}}function ActivityRow({call:e,last:t}){const[r,s]=(0,_react.useState)(!1),i=statusText(e),a=r?_sharedUi.ChevronDown:_sharedUi.ChevronRight,o="failed"===e.status&&e.summary&&"Failed"!==e.summary?e.summary:null;return(0,_jsxRuntime.jsxs)(_reactNative.Pressable,{testID:`buoy-ask-buoy-tool-${e.toolId}`,accessibilityRole:"button",accessibilityLabel:`${e.label}, ${i.text||"running"}`,accessibilityState:{expanded:r},onPress:()=>s(e=>!e),style:[styles.row,!t&&styles.rowDivider],children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.rowLine,children:[(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.markSlot,children:(0,_jsxRuntime.jsx)(StatusMark,{status:e.status})}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.rowTitle,children:e.label}),i.text?(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.rowStatus,"danger"===i.tone&&styles.rowStatusDanger,"warning"===i.tone&&styles.rowStatusWarning],numberOfLines:1,children:i.text}):null,(0,_jsxRuntime.jsx)(a,{size:12,color:_sharedUi.night.color.textTertiary})]}),r&&(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.rowDetail,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.rowTool,children:(0,_agentCore.toolTitle)(e.toolId).toUpperCase()}),e.description?(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.rowDescription,children:e.description}):null,o?(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.rowFailure,children:o}):null]})]})}const COLLAPSE_AFTER=4;function ActivityCard({calls:e}){const[t,r]=(0,_react.useState)(!1),s=e.some(e=>"running"===e.status),i=e.length>4&&!t&&!s,a=e.filter(e=>"failed"===e.status).length,o=e.filter(e=>"refused"===e.status).length,n=i?e.filter(e=>"failed"===e.status||"refused"===e.status):e;return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.card,testID:"buoy-ask-buoy-activity",children:[i&&(0,_jsxRuntime.jsx)(_reactNative.Pressable,{onPress:()=>r(!0),accessibilityRole:"button",style:[styles.row,n.length>0&&styles.rowDivider],testID:"buoy-ask-buoy-activity-expand",children:(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.rowLine,children:[(0,_jsxRuntime.jsxs)(_reactNative.Text,{style:styles.rowTitle,children:["Did ",e.length," things",a?` · ${a} failed`:"",o?` · ${o} refused`:""]}),(0,_jsxRuntime.jsx)(_sharedUi.ChevronRight,{size:12,color:_sharedUi.night.color.textTertiary})]})}),n.map((e,t)=>(0,_jsxRuntime.jsx)(ActivityRow,{call:e,last:t===n.length-1},e.id))]})}function Blocks({blocks:e}){return 0===e.length?null:(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.blocks,children:e.map(({block:e})=>(0,_jsxRuntime.jsx)(_BlockRenderer.BlockRenderer,{block:e},e.id))})}function UserMessage({message:e}){return(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.userWrap,children:(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.userBubble,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.userText,selectable:!0,children:e.text})})})}function AssistantMessage({message:e,onRetry:t}){const r=e.toolCalls.some(e=>"running"===e.status),s=!!e.streaming&&0===e.text.length&&!r,i=e.blocks.filter(e=>(0,_agentCore.isAskBlock)(e.block)),a=e.blocks.filter(e=>!(0,_agentCore.isAskBlock)(e.block));return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.assistantWrap,children:[e.toolCalls.length>0&&(0,_jsxRuntime.jsx)(ActivityCard,{calls:e.toolCalls}),(0,_jsxRuntime.jsx)(Blocks,{blocks:a}),e.text.length>0&&(0,_jsxRuntime.jsx)(_Markdown.Markdown,{text:e.text,style:styles.assistantText}),(0,_jsxRuntime.jsx)(Blocks,{blocks:i}),s&&(0,_jsxRuntime.jsx)(_TypingDots.TypingDots,{}),e.error&&(0,_jsxRuntime.jsx)(ErrorNote,{error:e.error,onRetry:t})]})}function ErrorNote({error:e,onRetry:t}){const r=/fetch failed|network|ECONN|timed? ?out|socket|abort/i.test(e)?"Couldn't reach your AI endpoint. Check the connection and try again.":"Something went wrong with that turn.";return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.errorBox,testID:"buoy-ask-buoy-error",children:[(0,_jsxRuntime.jsx)(_sharedUi.AlertTriangle,{size:14,color:_sharedUi.night.color.danger}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.errorBody,children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.errorText,children:r}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.errorDetail,selectable:!0,children:e}),t&&(0,_jsxRuntime.jsx)(_reactNative.Pressable,{testID:"buoy-ask-buoy-retry",accessibilityRole:"button",onPress:t,style:({pressed:e})=>[styles.retry,e&&styles.retryPressed],children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.retryText,children:"Retry"})})]})]})}function transcriptToText(e){const t=[];for(const r of e)if("user"===r.role)t.push(`You: ${r.text}`);else{for(const e of r.toolCalls){const r="failed"===e.status||"refused"===e.status?` (${e.status})`:"";t.push(` · ${e.label}${e.summary?` — ${e.summary}`:""}${r}`)}r.text&&t.push(`Ask Buoy: ${r.text}`),r.error&&t.push(` ! ${r.error}`)}return t.join("\n")}function TranscriptActions({message:e,messages:t}){const r=(0,_react.useCallback)(()=>{_reactNative.Share.share({message:e.text}).catch(()=>{})},[e.text]),s=(0,_react.useCallback)(()=>{_reactNative.Share.share({message:transcriptToText(t)}).catch(()=>{})},[t]);return(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.transcriptActions,children:[e.text.length>0&&(0,_jsxRuntime.jsx)(_reactNative.Pressable,{testID:"buoy-ask-buoy-share-reply",accessibilityRole:"button",onPress:r,style:({pressed:e})=>[styles.shareBtn,e&&styles.sharePressed],children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.shareText,children:"Share reply"})}),(0,_jsxRuntime.jsx)(_reactNative.Pressable,{testID:"buoy-ask-buoy-share-session",accessibilityRole:"button",onPress:s,style:({pressed:e})=>[styles.shareBtn,e&&styles.sharePressed],children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.shareText,children:"Share session"})})]})}function Row({message:e,messages:t,onRetry:r,isLast:s}){if("user"===e.role)return(0,_jsxRuntime.jsx)(UserMessage,{message:e});const i=!e.streaming&&!e.error;return(0,_jsxRuntime.jsxs)(_reactNative.View,{children:[(0,_jsxRuntime.jsx)(AssistantMessage,{message:e,onRetry:r}),s&&i&&(0,_jsxRuntime.jsx)(TranscriptActions,{message:e,messages:t})]})}function MessageList({messages:e,ListHeaderComponent:t,ListEmptyComponent:r,ListFooterComponent:s,onRetry:i}){const a=(0,_react.useRef)(null),o=(0,_react.useRef)(!0),n=(0,_react.useRef)(!1),l=(0,_react.useCallback)(e=>{const{contentOffset:t,contentSize:r,layoutMeasurement:s}=e.nativeEvent,i=r.height-s.height-t.y;o.current=i<80},[]),c=(0,_react.useCallback)(()=>{n.current=!0},[]),d=(0,_react.useCallback)(e=>{n.current&&l(e)},[l]),u=(0,_react.useCallback)(e=>{n.current=!1,l(e)},[l]),h=(0,_react.useCallback)(()=>{o.current&&(a.current?.scrollToEnd({animated:!1}),requestAnimationFrame(()=>a.current?.scrollToEnd({animated:!1})))},[]),g=e[e.length-1],x=g?`${g.id}:${g.text.length}:${g.toolCalls.length}:${g.error??""}`:"";return(0,_react.useEffect)(()=>{h()},[x,h]),(0,_jsxRuntime.jsx)(_reactNative.FlatList,{ref:a,testID:"buoy-ask-buoy-messages-scroll",style:styles.list,contentContainerStyle:styles.listContent,data:e,keyExtractor:e=>e.id,renderItem:({item:t})=>(0,_jsxRuntime.jsx)(Row,{message:t,messages:e,isLast:t.id===g?.id,onRetry:t.id===g?.id?i:void 0}),onScroll:d,onScrollBeginDrag:c,onScrollEndDrag:u,onMomentumScrollEnd:l,scrollEventThrottle:32,onContentSizeChange:h,keyboardShouldPersistTaps:"handled",keyboardDismissMode:"interactive",ListHeaderComponent:t,ListEmptyComponent:r,ListFooterComponent:s})}const styles=_reactNative.StyleSheet.create({list:{flex:1,backgroundColor:"transparent"},listContent:{paddingHorizontal:_sharedUi.night.gutter,paddingTop:12,paddingBottom:16,gap:16,flexGrow:1},userWrap:{alignItems:"flex-end"},userBubble:{maxWidth:"84%",paddingHorizontal:14,paddingVertical:9,borderRadius:18,borderBottomRightRadius:6,backgroundColor:_sharedUi.night.color.accentFill,borderWidth:_reactNative.StyleSheet.hairlineWidth,borderColor:_sharedUi.night.color.accentBorder},userText:{color:_sharedUi.night.color.text,fontSize:_sharedUi.night.font.row,lineHeight:21},assistantWrap:{alignItems:"stretch",gap:10},blocks:{gap:10},assistantText:{color:_sharedUi.night.color.text,fontSize:_sharedUi.night.font.row,lineHeight:22,paddingHorizontal:2},card:{borderRadius:_sharedUi.night.radius.card,borderWidth:_reactNative.StyleSheet.hairlineWidth,borderColor:_sharedUi.night.color.border,backgroundColor:_sharedUi.night.color.surface,overflow:"hidden"},row:{paddingHorizontal:12,paddingVertical:10},rowDivider:{borderBottomWidth:_reactNative.StyleSheet.hairlineWidth,borderBottomColor:_sharedUi.night.color.separator},rowLine:{flexDirection:"row",alignItems:"center",gap:10},markSlot:{width:18,height:18,alignItems:"center",justifyContent:"center"},mark:{width:16,height:16,borderRadius:8,alignItems:"center",justifyContent:"center"},markOk:{backgroundColor:_sharedUi.night.color.accent},markFailed:{backgroundColor:_sharedUi.night.color.danger},markRefused:{backgroundColor:_sharedUi.night.color.warning},rowTitle:{flex:1,color:_sharedUi.night.color.text,fontSize:_sharedUi.night.font.body,lineHeight:19},rowStatus:{color:_sharedUi.night.color.textTertiary,fontSize:_sharedUi.night.font.caption,maxWidth:120},rowStatusDanger:{color:_sharedUi.night.color.danger},rowStatusWarning:{color:_sharedUi.night.color.warning},rowDetail:{paddingLeft:28,paddingTop:8,gap:4},rowTool:{color:_sharedUi.night.color.textTertiary,fontSize:_sharedUi.night.font.micro,fontWeight:"600",letterSpacing:.8},rowDescription:{color:_sharedUi.night.color.textSecondary,fontSize:_sharedUi.night.font.caption,lineHeight:18},rowFailure:{color:_sharedUi.night.color.danger,fontSize:_sharedUi.night.font.caption,lineHeight:18},errorBox:{flexDirection:"row",alignItems:"flex-start",gap:8,paddingHorizontal:12,paddingVertical:10,borderRadius:_sharedUi.night.radius.row,backgroundColor:_sharedUi.night.color.dangerSoft},errorBody:{flex:1,gap:4},errorText:{color:_sharedUi.night.color.danger,fontSize:_sharedUi.night.font.caption,fontWeight:"600",lineHeight:18},errorDetail:{color:_sharedUi.night.color.textSecondary,fontSize:_sharedUi.night.font.micro,lineHeight:15},retry:{alignSelf:"flex-start",marginTop:6,paddingHorizontal:14,paddingVertical:7,borderRadius:_sharedUi.night.radius.row,backgroundColor:_sharedUi.night.color.button},retryPressed:{opacity:.7},retryText:{color:_sharedUi.night.color.accent,fontSize:_sharedUi.night.font.caption,fontWeight:"600"},transcriptActions:{flexDirection:"row",gap:8,marginTop:8},shareBtn:{paddingHorizontal:10,paddingVertical:5,borderRadius:_sharedUi.night.radius.badge,backgroundColor:_sharedUi.night.color.fill},sharePressed:{opacity:.7},shareText:{color:_sharedUi.night.color.textSecondary,fontSize:_sharedUi.night.font.micro,fontWeight:"600"}});
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.MessageList = MessageList;
7
+ exports.transcriptToText = transcriptToText;
8
+ var _react = require("react");
9
+ var _reactNative = require("react-native");
10
+ var _sharedUi = require("@buoy-gg/shared-ui");
11
+ var _agentCore = require("@buoy-gg/agent-core");
12
+ var _BlockRenderer = require("../blocks/BlockRenderer");
13
+ var _Markdown = require("./Markdown");
14
+ var _ThinkingStrip = require("./ThinkingStrip");
15
+ var _TypingDots = require("./TypingDots");
16
+ var _useCopyAction = require("./useCopyAction");
17
+ var _traceText = require("../store/traceText");
18
+ var _useAskBuoySettings = require("../store/useAskBuoySettings");
19
+ var _jsxRuntime = require("react/jsx-runtime");
20
+ /**
21
+ * The transcript.
22
+ *
23
+ * Owns the ONLY scroller in the sheet. JsModal's default wrapper uses
24
+ * `contentContainerStyle: { flexGrow: 1 }`, so a `flex: 1` scroller nested
25
+ * inside resolves against the CONTENT rather than the viewport: it grows to
26
+ * full content height, never scrolls, and the outer wrapper scrolls instead.
27
+ * That is invisible to a human and fatal to Detox ("View is not scrollable at
28
+ * the given start point"). The sheet passes `disableScrollWrapper` and this
29
+ * list is the one scroller.
30
+ *
31
+ * Layout, and why:
32
+ * - The user's messages are bubbles on the right; the assistant's text is
33
+ * plain, full-width prose on the left. A grey bubble around every answer
34
+ * cost 28pt of width per line and made the two sides look like the same
35
+ * speaker.
36
+ * - Tool calls render as an ACTIVITY CARD above the answer they produced —
37
+ * one row per call, a status mark, a short human title ("Open /bag") and a
38
+ * result word ("Done", "12 items", "Not found"). Nothing in a row is
39
+ * truncated to the point of being unreadable: the title wraps, and tapping
40
+ * the row expands it to the catalog's full sentence plus the tool it ran on.
41
+ * The previous version showed the model-facing sentence cut off at two
42
+ * lines with the result's JSON key names beside it, which was unreadable
43
+ * AND uninformative.
44
+ */
45
+
46
+ function StatusMark({
47
+ status
48
+ }) {
49
+ switch (status) {
50
+ case "running":
51
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
52
+ size: "small",
53
+ color: _sharedUi.night.color.textSecondary
54
+ });
55
+ case "ok":
56
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
57
+ style: [styles.mark, styles.markOk],
58
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Check, {
59
+ size: 10,
60
+ color: _sharedUi.night.color.onAccent
61
+ })
62
+ });
63
+ case "failed":
64
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
65
+ style: [styles.mark, styles.markFailed],
66
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.X, {
67
+ size: 10,
68
+ color: _sharedUi.night.color.text
69
+ })
70
+ });
71
+ case "refused":
72
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
73
+ style: [styles.mark, styles.markRefused],
74
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AlertTriangle, {
75
+ size: 10,
76
+ color: _sharedUi.night.color.onAccent
77
+ })
78
+ });
79
+ }
80
+ }
81
+
82
+ /** The trailing word: what happened, in one glance. */
83
+ function statusText(call) {
84
+ if (call.status === "running") return {
85
+ text: "",
86
+ tone: "muted"
87
+ };
88
+ if (call.status === "refused") return {
89
+ text: "Refused",
90
+ tone: "warning"
91
+ };
92
+ if (call.status === "failed") return {
93
+ text: "Failed",
94
+ tone: "danger"
95
+ };
96
+ return {
97
+ text: call.summary || "Done",
98
+ tone: "muted"
99
+ };
100
+ }
101
+ function ActivityRow({
102
+ call,
103
+ last
104
+ }) {
105
+ const [open, setOpen] = (0, _react.useState)(false);
106
+ const status = statusText(call);
107
+ const Chevron = open ? _sharedUi.ChevronDown : _sharedUi.ChevronRight;
108
+ const failureDetail = call.status === "failed" && call.summary && call.summary !== "Failed" ? call.summary : null;
109
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
110
+ testID: `buoy-ask-buoy-tool-${call.toolId}`,
111
+ accessibilityRole: "button",
112
+ accessibilityLabel: `${call.label}, ${status.text || "running"}`,
113
+ accessibilityState: {
114
+ expanded: open
115
+ },
116
+ onPress: () => setOpen(v => !v),
117
+ style: [styles.row, !last && styles.rowDivider],
118
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
119
+ style: styles.rowLine,
120
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
121
+ style: styles.markSlot,
122
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(StatusMark, {
123
+ status: call.status
124
+ })
125
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
126
+ style: styles.rowTitle,
127
+ children: call.label
128
+ }), status.text ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
129
+ style: [styles.rowStatus, status.tone === "danger" && styles.rowStatusDanger, status.tone === "warning" && styles.rowStatusWarning],
130
+ numberOfLines: 1,
131
+ children: status.text
132
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(Chevron, {
133
+ size: 12,
134
+ color: _sharedUi.night.color.textTertiary
135
+ })]
136
+ }), open && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
137
+ style: styles.rowDetail,
138
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
139
+ style: styles.rowTool,
140
+ children: (0, _agentCore.toolTitle)(call.toolId).toUpperCase()
141
+ }), call.description ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
142
+ style: styles.rowDescription,
143
+ children: call.description
144
+ }) : null, failureDetail ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
145
+ style: styles.rowFailure,
146
+ children: failureDetail
147
+ }) : null]
148
+ })]
149
+ });
150
+ }
151
+ const COLLAPSE_AFTER = 4;
152
+
153
+ /**
154
+ * More than four steps collapse to one line — "Did 15 things · 5 failed" —
155
+ * with the failed and refused rows pinned so the bad news is never hidden.
156
+ */
157
+ function ActivityCard({
158
+ calls
159
+ }) {
160
+ const [expanded, setExpanded] = (0, _react.useState)(false);
161
+ const running = calls.some(c => c.status === "running");
162
+ const collapse = calls.length > COLLAPSE_AFTER && !expanded && !running;
163
+ const failed = calls.filter(c => c.status === "failed").length;
164
+ const refused = calls.filter(c => c.status === "refused").length;
165
+ const shown = collapse ? calls.filter(c => c.status === "failed" || c.status === "refused") : calls;
166
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
167
+ style: styles.card,
168
+ testID: "buoy-ask-buoy-activity",
169
+ children: [collapse && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
170
+ onPress: () => setExpanded(true),
171
+ accessibilityRole: "button",
172
+ style: [styles.row, shown.length > 0 && styles.rowDivider],
173
+ testID: "buoy-ask-buoy-activity-expand",
174
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
175
+ style: styles.rowLine,
176
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
177
+ style: styles.rowTitle,
178
+ children: ["Did ", calls.length, " things", failed ? ` · ${failed} failed` : "", refused ? ` · ${refused} refused` : ""]
179
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronRight, {
180
+ size: 12,
181
+ color: _sharedUi.night.color.textTertiary
182
+ })]
183
+ })
184
+ }), shown.map((c, i) => /*#__PURE__*/(0, _jsxRuntime.jsx)(ActivityRow, {
185
+ call: c,
186
+ last: i === shown.length - 1
187
+ }, c.id))]
188
+ });
189
+ }
190
+
191
+ /** Receipts and model blocks render between the activity card and the prose; Ask blocks last. */
192
+ function Blocks({
193
+ blocks
194
+ }) {
195
+ if (blocks.length === 0) return null;
196
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
197
+ style: styles.blocks,
198
+ children: blocks.map(({
199
+ block
200
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_BlockRenderer.BlockRenderer, {
201
+ block: block
202
+ }, block.id))
203
+ });
204
+ }
205
+ function UserMessage({
206
+ message
207
+ }) {
208
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
209
+ style: styles.userWrap,
210
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
211
+ style: styles.userBubble,
212
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
213
+ style: styles.userText,
214
+ selectable: true,
215
+ children: message.text
216
+ })
217
+ })
218
+ });
219
+ }
220
+ function AssistantMessage({
221
+ message,
222
+ onRetry
223
+ }) {
224
+ const {
225
+ showThinking
226
+ } = (0, _useAskBuoySettings.useAskBuoySettings)();
227
+ const running = message.toolCalls.some(c => c.status === "running");
228
+ const waiting = !!message.streaming && message.text.length === 0 && !running;
229
+ const askBlocks = message.blocks.filter(b => (0, _agentCore.isAskBlock)(b.block));
230
+ const showBlocks = message.blocks.filter(b => !(0, _agentCore.isAskBlock)(b.block));
231
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
232
+ style: styles.assistantWrap,
233
+ children: [message.toolCalls.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(ActivityCard, {
234
+ calls: message.toolCalls
235
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(Blocks, {
236
+ blocks: showBlocks
237
+ }), message.text.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Markdown.Markdown, {
238
+ text: message.text,
239
+ style: styles.assistantText
240
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(Blocks, {
241
+ blocks: askBlocks
242
+ }), showThinking && !message.streaming && /*#__PURE__*/(0, _jsxRuntime.jsx)(_ThinkingStrip.ThinkingStrip, {
243
+ message: message
244
+ }), waiting && /*#__PURE__*/(0, _jsxRuntime.jsx)(_TypingDots.TypingDots, {}), message.error && /*#__PURE__*/(0, _jsxRuntime.jsx)(ErrorNote, {
245
+ error: message.error,
246
+ onRetry: onRetry
247
+ })]
248
+ });
249
+ }
250
+
251
+ /**
252
+ * A failed turn. The headline is for the tester ("couldn't reach the
253
+ * endpoint"); the raw message stays underneath for whoever gets the bug
254
+ * report, because "fetch failed: UnexpectedException: The network connection
255
+ * was lost" is what a developer needs and nothing a tester can act on.
256
+ */
257
+ function ErrorNote({
258
+ error,
259
+ onRetry
260
+ }) {
261
+ const network = /fetch failed|network|ECONN|timed? ?out|socket|abort/i.test(error);
262
+ const headline = network ? "Couldn't reach your AI endpoint. Check the connection and try again." : "Something went wrong with that turn.";
263
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
264
+ style: styles.errorBox,
265
+ testID: "buoy-ask-buoy-error",
266
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AlertTriangle, {
267
+ size: 14,
268
+ color: _sharedUi.night.color.danger
269
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
270
+ style: styles.errorBody,
271
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
272
+ style: styles.errorText,
273
+ children: headline
274
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
275
+ style: styles.errorDetail,
276
+ selectable: true,
277
+ children: error
278
+ }), onRetry && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
279
+ testID: "buoy-ask-buoy-retry",
280
+ accessibilityRole: "button",
281
+ onPress: onRetry,
282
+ style: ({
283
+ pressed
284
+ }) => [styles.retry, pressed && styles.retryPressed],
285
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
286
+ style: styles.retryText,
287
+ children: "Retry"
288
+ })
289
+ })]
290
+ })]
291
+ });
292
+ }
293
+
294
+ /**
295
+ * The whole conversation as plain text — the only thing Copy ever produces,
296
+ * and the only way a session leaves the DEVICE (what it saves to disk for a
297
+ * reload goes nowhere — see `transcriptPersistence`).
298
+ *
299
+ * Plain text on purpose. This lands in a Slack message, a ticket comment or a
300
+ * notes file, all of which are read by a person; a JSON document is a worse
301
+ * version of that everywhere except a parser, and nothing here parses it.
302
+ *
303
+ * With `Show agent thinking` on it carries the working too — the reasoning and
304
+ * each step with its parameters — because reading the run somewhere that is
305
+ * not a phone is the entire point of that setting.
306
+ */
307
+ function transcriptToText(messages, withTrace = false) {
308
+ const lines = [];
309
+ for (const m of messages) {
310
+ if (m.role === "user") {
311
+ lines.push(`You: ${m.text}`);
312
+ continue;
313
+ }
314
+ if (withTrace) {
315
+ lines.push(...(0, _traceText.traceToText)(m));
316
+ } else {
317
+ for (const c of m.toolCalls) {
318
+ const status = c.status === "failed" || c.status === "refused" ? ` (${c.status})` : "";
319
+ lines.push(` · ${c.label}${c.summary ? ` — ${c.summary}` : ""}${status}`);
320
+ }
321
+ }
322
+ if (m.text) lines.push(`Ask Buoy: ${m.text}`);
323
+ if (m.error) lines.push(` ! ${m.error}`);
324
+ }
325
+ return lines.join("\n");
326
+ }
327
+
328
+ /** Copy, with the result visible for a moment. Shares the header's behaviour. */
329
+ function CopyPill({
330
+ testID,
331
+ label,
332
+ getText
333
+ }) {
334
+ const {
335
+ copied,
336
+ copy
337
+ } = (0, _useCopyAction.useCopyAction)(getText);
338
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
339
+ testID: testID,
340
+ accessibilityRole: "button",
341
+ accessibilityLabel: label,
342
+ onPress: copy,
343
+ style: ({
344
+ pressed
345
+ }) => [styles.copyBtn, styles.iconBtn, pressed && styles.copyPressed],
346
+ children: [copied ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Check, {
347
+ size: 11,
348
+ color: _sharedUi.night.color.accent
349
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Copy, {
350
+ size: 11,
351
+ color: _sharedUi.night.color.textSecondary
352
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
353
+ style: [styles.copyText, copied && styles.copiedText],
354
+ children: copied ? "Copied" : label
355
+ })]
356
+ });
357
+ }
358
+
359
+ /**
360
+ * Under a finished answer: one Copy, and nothing else.
361
+ *
362
+ * It is on EVERY answer rather than only the newest, because the moment you
363
+ * decide to keep a conversation is usually a few replies after the one that
364
+ * went wrong, and scrolling to the bottom to find the button is friction at
365
+ * exactly the wrong time. Every one of them copies the WHOLE chat — one button
366
+ * with one meaning beats four pills the reader has to choose between.
367
+ */
368
+ function TranscriptActions({
369
+ messages
370
+ }) {
371
+ const {
372
+ showThinking
373
+ } = (0, _useAskBuoySettings.useAskBuoySettings)();
374
+ const getText = (0, _react.useCallback)(() => transcriptToText(messages, showThinking), [messages, showThinking]);
375
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
376
+ style: styles.transcriptActions,
377
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(CopyPill, {
378
+ testID: "buoy-ask-buoy-copy",
379
+ label: "Copy",
380
+ getText: getText
381
+ })
382
+ });
383
+ }
384
+ function Row({
385
+ message,
386
+ messages,
387
+ onRetry
388
+ }) {
389
+ if (message.role === "user") return /*#__PURE__*/(0, _jsxRuntime.jsx)(UserMessage, {
390
+ message: message
391
+ });
392
+ const finished = !message.streaming && !message.error;
393
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
394
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(AssistantMessage, {
395
+ message: message,
396
+ onRetry: onRetry
397
+ }), finished && /*#__PURE__*/(0, _jsxRuntime.jsx)(TranscriptActions, {
398
+ messages: messages
399
+ })]
400
+ });
401
+ }
402
+ function MessageList({
403
+ messages,
404
+ ListHeaderComponent,
405
+ ListEmptyComponent,
406
+ ListFooterComponent,
407
+ onRetry
408
+ }) {
409
+ const ref = (0, _react.useRef)(null);
410
+ // Only auto-scroll when the user is already at the bottom. Yanking them back
411
+ // down while they are reading an earlier answer is the classic chat-UI sin.
412
+ //
413
+ // "At the bottom" is decided from the USER's gestures, not from every scroll
414
+ // event. An animated scrollToEnd fires intermediate events while the content
415
+ // is still growing under it, and reading those as "the user scrolled up"
416
+ // silently unpinned the list mid-answer — the reply then streamed in below
417
+ // the fold and the last thing on screen was the question.
418
+ const pinned = (0, _react.useRef)(true);
419
+ const dragging = (0, _react.useRef)(false);
420
+ const measure = (0, _react.useCallback)(e => {
421
+ const {
422
+ contentOffset,
423
+ contentSize,
424
+ layoutMeasurement
425
+ } = e.nativeEvent;
426
+ const distance = contentSize.height - layoutMeasurement.height - contentOffset.y;
427
+ pinned.current = distance < 80;
428
+ }, []);
429
+ const onScrollBeginDrag = (0, _react.useCallback)(() => {
430
+ dragging.current = true;
431
+ }, []);
432
+ const onScroll = (0, _react.useCallback)(e => {
433
+ if (dragging.current) measure(e);
434
+ }, [measure]);
435
+ const onScrollEndDrag = (0, _react.useCallback)(e => {
436
+ dragging.current = false;
437
+ measure(e);
438
+ }, [measure]);
439
+ const scrollToEnd = (0, _react.useCallback)(() => {
440
+ if (!pinned.current) return;
441
+ // The size change fires before the new row has finished laying out its
442
+ // wrapped text; one frame later the end really is the end.
443
+ ref.current?.scrollToEnd({
444
+ animated: false
445
+ });
446
+ requestAnimationFrame(() => ref.current?.scrollToEnd({
447
+ animated: false
448
+ }));
449
+ }, []);
450
+
451
+ // A row can grow without the list reporting a size change in the same tick
452
+ // (an error box appended to the last message). Follow the transcript too.
453
+ const last = messages[messages.length - 1];
454
+ const tail = last ? `${last.id}:${last.text.length}:${last.toolCalls.length}:${last.error ?? ""}` : "";
455
+ (0, _react.useEffect)(() => {
456
+ scrollToEnd();
457
+ }, [tail, scrollToEnd]);
458
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
459
+ ref: ref,
460
+ testID: "buoy-ask-buoy-messages-scroll",
461
+ style: styles.list,
462
+ contentContainerStyle: styles.listContent,
463
+ data: messages,
464
+ keyExtractor: m => m.id,
465
+ renderItem: ({
466
+ item
467
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(Row, {
468
+ message: item,
469
+ messages: messages,
470
+ onRetry: item.id === last?.id ? onRetry : undefined
471
+ }),
472
+ onScroll: onScroll,
473
+ onScrollBeginDrag: onScrollBeginDrag,
474
+ onScrollEndDrag: onScrollEndDrag,
475
+ onMomentumScrollEnd: measure,
476
+ scrollEventThrottle: 32,
477
+ onContentSizeChange: scrollToEnd
478
+ // Mandatory with a focused input below: without it the first tap after
479
+ // typing only dismisses the keyboard and the button never fires.
480
+ ,
481
+ keyboardShouldPersistTaps: "handled",
482
+ keyboardDismissMode: "interactive",
483
+ ListHeaderComponent: ListHeaderComponent,
484
+ ListEmptyComponent: ListEmptyComponent,
485
+ ListFooterComponent: ListFooterComponent
486
+ });
487
+ }
488
+ const styles = _reactNative.StyleSheet.create({
489
+ list: {
490
+ flex: 1,
491
+ backgroundColor: "transparent"
492
+ },
493
+ listContent: {
494
+ paddingHorizontal: _sharedUi.night.gutter,
495
+ paddingTop: 12,
496
+ paddingBottom: 16,
497
+ gap: 16,
498
+ flexGrow: 1
499
+ },
500
+ // User
501
+ userWrap: {
502
+ alignItems: "flex-end"
503
+ },
504
+ userBubble: {
505
+ maxWidth: "84%",
506
+ paddingHorizontal: 14,
507
+ paddingVertical: 9,
508
+ borderRadius: 18,
509
+ borderBottomRightRadius: 6,
510
+ backgroundColor: _sharedUi.night.color.accentFill,
511
+ borderWidth: _reactNative.StyleSheet.hairlineWidth,
512
+ borderColor: _sharedUi.night.color.accentBorder
513
+ },
514
+ userText: {
515
+ color: _sharedUi.night.color.text,
516
+ fontSize: _sharedUi.night.font.row,
517
+ lineHeight: 21
518
+ },
519
+ // Assistant
520
+ assistantWrap: {
521
+ alignItems: "stretch",
522
+ gap: 10
523
+ },
524
+ blocks: {
525
+ gap: 10
526
+ },
527
+ assistantText: {
528
+ color: _sharedUi.night.color.text,
529
+ fontSize: _sharedUi.night.font.row,
530
+ lineHeight: 22,
531
+ paddingHorizontal: 2
532
+ },
533
+ // Activity card
534
+ card: {
535
+ borderRadius: _sharedUi.night.radius.card,
536
+ borderWidth: _reactNative.StyleSheet.hairlineWidth,
537
+ borderColor: _sharedUi.night.color.border,
538
+ backgroundColor: _sharedUi.night.color.surface,
539
+ overflow: "hidden"
540
+ },
541
+ row: {
542
+ paddingHorizontal: 12,
543
+ paddingVertical: 10
544
+ },
545
+ rowDivider: {
546
+ borderBottomWidth: _reactNative.StyleSheet.hairlineWidth,
547
+ borderBottomColor: _sharedUi.night.color.separator
548
+ },
549
+ rowLine: {
550
+ flexDirection: "row",
551
+ alignItems: "center",
552
+ gap: 10
553
+ },
554
+ markSlot: {
555
+ width: 18,
556
+ height: 18,
557
+ alignItems: "center",
558
+ justifyContent: "center"
559
+ },
560
+ mark: {
561
+ width: 16,
562
+ height: 16,
563
+ borderRadius: 8,
564
+ alignItems: "center",
565
+ justifyContent: "center"
566
+ },
567
+ markOk: {
568
+ backgroundColor: _sharedUi.night.color.accent
569
+ },
570
+ markFailed: {
571
+ backgroundColor: _sharedUi.night.color.danger
572
+ },
573
+ markRefused: {
574
+ backgroundColor: _sharedUi.night.color.warning
575
+ },
576
+ rowTitle: {
577
+ flex: 1,
578
+ color: _sharedUi.night.color.text,
579
+ fontSize: _sharedUi.night.font.body,
580
+ lineHeight: 19
581
+ },
582
+ rowStatus: {
583
+ color: _sharedUi.night.color.textTertiary,
584
+ fontSize: _sharedUi.night.font.caption,
585
+ maxWidth: 120
586
+ },
587
+ rowStatusDanger: {
588
+ color: _sharedUi.night.color.danger
589
+ },
590
+ rowStatusWarning: {
591
+ color: _sharedUi.night.color.warning
592
+ },
593
+ rowDetail: {
594
+ paddingLeft: 28,
595
+ paddingTop: 8,
596
+ gap: 4
597
+ },
598
+ rowTool: {
599
+ color: _sharedUi.night.color.textTertiary,
600
+ fontSize: _sharedUi.night.font.micro,
601
+ fontWeight: "600",
602
+ letterSpacing: 0.8
603
+ },
604
+ rowDescription: {
605
+ color: _sharedUi.night.color.textSecondary,
606
+ fontSize: _sharedUi.night.font.caption,
607
+ lineHeight: 18
608
+ },
609
+ rowFailure: {
610
+ color: _sharedUi.night.color.danger,
611
+ fontSize: _sharedUi.night.font.caption,
612
+ lineHeight: 18
613
+ },
614
+ // Error
615
+ errorBox: {
616
+ flexDirection: "row",
617
+ alignItems: "flex-start",
618
+ gap: 8,
619
+ paddingHorizontal: 12,
620
+ paddingVertical: 10,
621
+ borderRadius: _sharedUi.night.radius.row,
622
+ backgroundColor: _sharedUi.night.color.dangerSoft
623
+ },
624
+ errorBody: {
625
+ flex: 1,
626
+ gap: 4
627
+ },
628
+ errorText: {
629
+ color: _sharedUi.night.color.danger,
630
+ fontSize: _sharedUi.night.font.caption,
631
+ fontWeight: "600",
632
+ lineHeight: 18
633
+ },
634
+ errorDetail: {
635
+ color: _sharedUi.night.color.textSecondary,
636
+ fontSize: _sharedUi.night.font.micro,
637
+ lineHeight: 15
638
+ },
639
+ retry: {
640
+ alignSelf: "flex-start",
641
+ marginTop: 6,
642
+ paddingHorizontal: 14,
643
+ paddingVertical: 7,
644
+ borderRadius: _sharedUi.night.radius.row,
645
+ backgroundColor: _sharedUi.night.color.button
646
+ },
647
+ retryPressed: {
648
+ opacity: 0.7
649
+ },
650
+ retryText: {
651
+ color: _sharedUi.night.color.accent,
652
+ fontSize: _sharedUi.night.font.caption,
653
+ fontWeight: "600"
654
+ },
655
+ transcriptActions: {
656
+ flexDirection: "row",
657
+ marginTop: 8
658
+ },
659
+ copyBtn: {
660
+ paddingHorizontal: 10,
661
+ paddingVertical: 5,
662
+ borderRadius: _sharedUi.night.radius.badge,
663
+ backgroundColor: _sharedUi.night.color.fill
664
+ },
665
+ iconBtn: {
666
+ flexDirection: "row",
667
+ alignItems: "center",
668
+ gap: 4,
669
+ paddingHorizontal: 8
670
+ },
671
+ copyPressed: {
672
+ opacity: 0.7
673
+ },
674
+ copyText: {
675
+ color: _sharedUi.night.color.textSecondary,
676
+ fontSize: _sharedUi.night.font.micro,
677
+ fontWeight: "600"
678
+ },
679
+ copiedText: {
680
+ color: _sharedUi.night.color.accent
681
+ }
682
+ });
683
+ //# sourceMappingURL=MessageList.js.map