@buoy-gg/ask-buoy 7.0.35 → 7.0.38

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 (286) hide show
  1. package/README.md +73 -15
  2. package/lib/commonjs/blocks/AskBlocks.js +1 -338
  3. package/lib/commonjs/blocks/BlockRenderer.js +1 -250
  4. package/lib/commonjs/blocks/ChromePrototypes.js +1 -514
  5. package/lib/commonjs/blocks/DataBlocks.js +1 -688
  6. package/lib/commonjs/blocks/ExtraBlocks.js +1 -1854
  7. package/lib/commonjs/blocks/MediaBlocks.js +1 -140
  8. package/lib/commonjs/blocks/ReceiptBlocks.js +1 -586
  9. package/lib/commonjs/blocks/blockText.js +1 -0
  10. package/lib/commonjs/blocks/blockText.test.js +1 -0
  11. package/lib/commonjs/blocks/context.js +1 -42
  12. package/lib/commonjs/blocks/icons.js +1 -96
  13. package/lib/commonjs/blocks/index.js +1 -95
  14. package/lib/commonjs/blocks/listRow.test.js +1 -55
  15. package/lib/commonjs/blocks/pageState.js +1 -34
  16. package/lib/commonjs/blocks/pageState.test.js +1 -57
  17. package/lib/commonjs/blocks/primitives.js +1 -401
  18. package/lib/commonjs/blocks/primitivesIndex.js +1 -62
  19. package/lib/commonjs/components/ApprovalCard.js +1 -140
  20. package/lib/commonjs/components/AskBuoyHeader.js +1 -164
  21. package/lib/commonjs/components/AskBuoyIcon.js +1 -19
  22. package/lib/commonjs/components/AskBuoyModal.js +1 -353
  23. package/lib/commonjs/components/AskBuoySettings.js +1 -69
  24. package/lib/commonjs/components/ChangesBar.js +1 -211
  25. package/lib/commonjs/components/Composer.js +1 -175
  26. package/lib/commonjs/components/EmptyState.js +1 -154
  27. package/lib/commonjs/components/KeyboardPad.js +1 -0
  28. package/lib/commonjs/components/Markdown.js +1 -252
  29. package/lib/commonjs/components/MessageList.js +1 -683
  30. package/lib/commonjs/components/QueuedMessages.js +1 -0
  31. package/lib/commonjs/components/ThinkingStrip.js +1 -223
  32. package/lib/commonjs/components/TypingDots.js +1 -63
  33. package/lib/commonjs/components/markdownParse.js +1 -0
  34. package/lib/commonjs/components/markdownParse.test.js +1 -0
  35. package/lib/commonjs/components/useCopyAction.js +1 -50
  36. package/lib/commonjs/index.js +1 -252
  37. package/lib/commonjs/preset.js +1 -90
  38. package/lib/commonjs/store/activityGroups.js +1 -0
  39. package/lib/commonjs/store/activityGroups.test.js +1 -0
  40. package/lib/commonjs/store/askBuoySettings.js +1 -67
  41. package/lib/commonjs/store/askBuoyStore.js +1 -365
  42. package/lib/commonjs/store/blockPlacement.js +1 -0
  43. package/lib/commonjs/store/blockPlacement.test.js +1 -0
  44. package/lib/commonjs/store/queue.test.js +1 -0
  45. package/lib/commonjs/store/resetConversation.js +1 -0
  46. package/lib/commonjs/store/restorePending.test.js +1 -0
  47. package/lib/commonjs/store/restoredIds.test.js +1 -47
  48. package/lib/commonjs/store/suggestions.js +1 -0
  49. package/lib/commonjs/store/suggestions.test.js +1 -0
  50. package/lib/commonjs/store/systemLine.test.js +1 -0
  51. package/lib/commonjs/store/ticketPrompt.js +1 -0
  52. package/lib/commonjs/store/traceText.js +1 -81
  53. package/lib/commonjs/store/traceText.test.js +1 -112
  54. package/lib/commonjs/store/transcriptPersistence.js +1 -208
  55. package/lib/commonjs/store/transcriptPersistence.test.js +1 -185
  56. package/lib/commonjs/store/turnRendering.test.js +1 -0
  57. package/lib/commonjs/store/useAskBuoy.js +1 -400
  58. package/lib/commonjs/store/useAskBuoySettings.js +1 -14
  59. package/lib/commonjs/sync/askBuoySyncAdapter.js +1 -119
  60. package/lib/commonjs/sync/askBuoySyncAdapter.test.js +1 -141
  61. package/lib/commonjs/sync/wire.js +1 -58
  62. package/lib/commonjs/types.js +1 -6
  63. package/lib/module/blocks/AskBlocks.js +1 -331
  64. package/lib/module/blocks/BlockRenderer.js +1 -245
  65. package/lib/module/blocks/ChromePrototypes.js +1 -503
  66. package/lib/module/blocks/DataBlocks.js +1 -674
  67. package/lib/module/blocks/ExtraBlocks.js +1 -1827
  68. package/lib/module/blocks/MediaBlocks.js +1 -135
  69. package/lib/module/blocks/ReceiptBlocks.js +1 -574
  70. package/lib/module/blocks/blockText.js +1 -0
  71. package/lib/module/blocks/blockText.test.js +1 -0
  72. package/lib/module/blocks/context.js +1 -35
  73. package/lib/module/blocks/icons.js +1 -91
  74. package/lib/module/blocks/index.js +1 -8
  75. package/lib/module/blocks/listRow.test.js +1 -56
  76. package/lib/module/blocks/pageState.js +1 -30
  77. package/lib/module/blocks/pageState.test.js +1 -56
  78. package/lib/module/blocks/primitives.js +1 -389
  79. package/lib/module/blocks/primitivesIndex.js +1 -5
  80. package/lib/module/components/ApprovalCard.js +1 -135
  81. package/lib/module/components/AskBuoyHeader.js +1 -161
  82. package/lib/module/components/AskBuoyIcon.js +1 -8
  83. package/lib/module/components/AskBuoyModal.js +1 -348
  84. package/lib/module/components/AskBuoySettings.js +1 -64
  85. package/lib/module/components/ChangesBar.js +1 -206
  86. package/lib/module/components/Composer.js +1 -170
  87. package/lib/module/components/EmptyState.js +1 -149
  88. package/lib/module/components/KeyboardPad.js +1 -0
  89. package/lib/module/components/Markdown.js +1 -246
  90. package/lib/module/components/MessageList.js +1 -677
  91. package/lib/module/components/QueuedMessages.js +1 -0
  92. package/lib/module/components/ThinkingStrip.js +1 -218
  93. package/lib/module/components/TypingDots.js +1 -58
  94. package/lib/module/components/markdownParse.js +1 -0
  95. package/lib/module/components/markdownParse.test.js +1 -0
  96. package/lib/module/components/useCopyAction.js +1 -46
  97. package/lib/module/index.js +1 -34
  98. package/lib/module/preset.js +1 -73
  99. package/lib/module/store/activityGroups.js +1 -0
  100. package/lib/module/store/activityGroups.test.js +1 -0
  101. package/lib/module/store/askBuoySettings.js +1 -62
  102. package/lib/module/store/askBuoyStore.js +1 -361
  103. package/lib/module/store/blockPlacement.js +1 -0
  104. package/lib/module/store/blockPlacement.test.js +1 -0
  105. package/lib/module/store/queue.test.js +1 -0
  106. package/lib/module/store/resetConversation.js +1 -0
  107. package/lib/module/store/restorePending.test.js +1 -0
  108. package/lib/module/store/restoredIds.test.js +1 -46
  109. package/lib/module/store/suggestions.js +1 -0
  110. package/lib/module/store/suggestions.test.js +1 -0
  111. package/lib/module/store/systemLine.test.js +1 -0
  112. package/lib/module/store/ticketPrompt.js +1 -0
  113. package/lib/module/store/traceText.js +1 -75
  114. package/lib/module/store/traceText.test.js +1 -111
  115. package/lib/module/store/transcriptPersistence.js +1 -198
  116. package/lib/module/store/transcriptPersistence.test.js +1 -184
  117. package/lib/module/store/turnRendering.test.js +1 -0
  118. package/lib/module/store/useAskBuoy.js +1 -394
  119. package/lib/module/store/useAskBuoySettings.js +1 -9
  120. package/lib/module/sync/askBuoySyncAdapter.js +1 -114
  121. package/lib/module/sync/askBuoySyncAdapter.test.js +1 -140
  122. package/lib/module/sync/wire.js +1 -47
  123. package/lib/module/types.js +1 -4
  124. package/lib/typescript/blocks/DataBlocks.d.ts +11 -0
  125. package/lib/typescript/blocks/ExtraBlocks.d.ts +18 -2
  126. package/lib/typescript/blocks/blockText.d.ts +14 -0
  127. package/lib/typescript/blocks/blockText.test.d.ts +2 -0
  128. package/lib/typescript/blocks/primitives.d.ts +16 -1
  129. package/lib/typescript/components/ApprovalCard.d.ts +9 -1
  130. package/lib/typescript/components/AskBuoyHeader.d.ts +31 -1
  131. package/lib/typescript/components/Composer.d.ts +16 -1
  132. package/lib/typescript/components/EmptyState.d.ts +5 -1
  133. package/lib/typescript/components/KeyboardPad.d.ts +33 -0
  134. package/lib/typescript/components/Markdown.d.ts +0 -10
  135. package/lib/typescript/components/MessageList.d.ts +7 -2
  136. package/lib/typescript/components/QueuedMessages.d.ts +10 -0
  137. package/lib/typescript/components/markdownParse.d.ts +49 -0
  138. package/lib/typescript/components/markdownParse.test.d.ts +2 -0
  139. package/lib/typescript/preset.d.ts +4 -2
  140. package/lib/typescript/store/activityGroups.d.ts +29 -0
  141. package/lib/typescript/store/activityGroups.test.d.ts +2 -0
  142. package/lib/typescript/store/askBuoySettings.d.ts +22 -0
  143. package/lib/typescript/store/askBuoyStore.d.ts +174 -9
  144. package/lib/typescript/store/blockPlacement.d.ts +30 -0
  145. package/lib/typescript/store/blockPlacement.test.d.ts +2 -0
  146. package/lib/typescript/store/queue.test.d.ts +2 -0
  147. package/lib/typescript/store/resetConversation.d.ts +38 -0
  148. package/lib/typescript/store/restorePending.test.d.ts +2 -0
  149. package/lib/typescript/store/suggestions.d.ts +32 -0
  150. package/lib/typescript/store/suggestions.test.d.ts +2 -0
  151. package/lib/typescript/store/systemLine.test.d.ts +2 -0
  152. package/lib/typescript/store/ticketPrompt.d.ts +11 -0
  153. package/lib/typescript/store/transcriptPersistence.d.ts +41 -2
  154. package/lib/typescript/store/turnRendering.test.d.ts +2 -0
  155. package/lib/typescript/store/useAskBuoy.d.ts +26 -3
  156. package/lib/typescript/sync/askBuoySyncAdapter.d.ts +29 -6
  157. package/package.json +10 -10
  158. package/lib/commonjs/blocks/AskBlocks.js.map +0 -1
  159. package/lib/commonjs/blocks/BlockRenderer.js.map +0 -1
  160. package/lib/commonjs/blocks/ChromePrototypes.js.map +0 -1
  161. package/lib/commonjs/blocks/DataBlocks.js.map +0 -1
  162. package/lib/commonjs/blocks/ExtraBlocks.js.map +0 -1
  163. package/lib/commonjs/blocks/MediaBlocks.js.map +0 -1
  164. package/lib/commonjs/blocks/ReceiptBlocks.js.map +0 -1
  165. package/lib/commonjs/blocks/context.js.map +0 -1
  166. package/lib/commonjs/blocks/icons.js.map +0 -1
  167. package/lib/commonjs/blocks/index.js.map +0 -1
  168. package/lib/commonjs/blocks/listRow.test.js.map +0 -1
  169. package/lib/commonjs/blocks/pageState.js.map +0 -1
  170. package/lib/commonjs/blocks/pageState.test.js.map +0 -1
  171. package/lib/commonjs/blocks/primitives.js.map +0 -1
  172. package/lib/commonjs/blocks/primitivesIndex.js.map +0 -1
  173. package/lib/commonjs/components/ApprovalCard.js.map +0 -1
  174. package/lib/commonjs/components/AskBuoyHeader.js.map +0 -1
  175. package/lib/commonjs/components/AskBuoyIcon.js.map +0 -1
  176. package/lib/commonjs/components/AskBuoyModal.js.map +0 -1
  177. package/lib/commonjs/components/AskBuoySettings.js.map +0 -1
  178. package/lib/commonjs/components/ChangesBar.js.map +0 -1
  179. package/lib/commonjs/components/Composer.js.map +0 -1
  180. package/lib/commonjs/components/EmptyState.js.map +0 -1
  181. package/lib/commonjs/components/Markdown.js.map +0 -1
  182. package/lib/commonjs/components/MessageList.js.map +0 -1
  183. package/lib/commonjs/components/ThinkingStrip.js.map +0 -1
  184. package/lib/commonjs/components/TypingDots.js.map +0 -1
  185. package/lib/commonjs/components/useCopyAction.js.map +0 -1
  186. package/lib/commonjs/index.js.map +0 -1
  187. package/lib/commonjs/preset.js.map +0 -1
  188. package/lib/commonjs/store/askBuoySettings.js.map +0 -1
  189. package/lib/commonjs/store/askBuoyStore.js.map +0 -1
  190. package/lib/commonjs/store/restoredIds.test.js.map +0 -1
  191. package/lib/commonjs/store/traceText.js.map +0 -1
  192. package/lib/commonjs/store/traceText.test.js.map +0 -1
  193. package/lib/commonjs/store/transcriptPersistence.js.map +0 -1
  194. package/lib/commonjs/store/transcriptPersistence.test.js.map +0 -1
  195. package/lib/commonjs/store/useAskBuoy.js.map +0 -1
  196. package/lib/commonjs/store/useAskBuoySettings.js.map +0 -1
  197. package/lib/commonjs/sync/askBuoySyncAdapter.js.map +0 -1
  198. package/lib/commonjs/sync/askBuoySyncAdapter.test.js.map +0 -1
  199. package/lib/commonjs/sync/wire.js.map +0 -1
  200. package/lib/commonjs/types.js.map +0 -1
  201. package/lib/module/blocks/AskBlocks.js.map +0 -1
  202. package/lib/module/blocks/BlockRenderer.js.map +0 -1
  203. package/lib/module/blocks/ChromePrototypes.js.map +0 -1
  204. package/lib/module/blocks/DataBlocks.js.map +0 -1
  205. package/lib/module/blocks/ExtraBlocks.js.map +0 -1
  206. package/lib/module/blocks/MediaBlocks.js.map +0 -1
  207. package/lib/module/blocks/ReceiptBlocks.js.map +0 -1
  208. package/lib/module/blocks/context.js.map +0 -1
  209. package/lib/module/blocks/icons.js.map +0 -1
  210. package/lib/module/blocks/index.js.map +0 -1
  211. package/lib/module/blocks/listRow.test.js.map +0 -1
  212. package/lib/module/blocks/pageState.js.map +0 -1
  213. package/lib/module/blocks/pageState.test.js.map +0 -1
  214. package/lib/module/blocks/primitives.js.map +0 -1
  215. package/lib/module/blocks/primitivesIndex.js.map +0 -1
  216. package/lib/module/components/ApprovalCard.js.map +0 -1
  217. package/lib/module/components/AskBuoyHeader.js.map +0 -1
  218. package/lib/module/components/AskBuoyIcon.js.map +0 -1
  219. package/lib/module/components/AskBuoyModal.js.map +0 -1
  220. package/lib/module/components/AskBuoySettings.js.map +0 -1
  221. package/lib/module/components/ChangesBar.js.map +0 -1
  222. package/lib/module/components/Composer.js.map +0 -1
  223. package/lib/module/components/EmptyState.js.map +0 -1
  224. package/lib/module/components/Markdown.js.map +0 -1
  225. package/lib/module/components/MessageList.js.map +0 -1
  226. package/lib/module/components/ThinkingStrip.js.map +0 -1
  227. package/lib/module/components/TypingDots.js.map +0 -1
  228. package/lib/module/components/useCopyAction.js.map +0 -1
  229. package/lib/module/index.js.map +0 -1
  230. package/lib/module/preset.js.map +0 -1
  231. package/lib/module/store/askBuoySettings.js.map +0 -1
  232. package/lib/module/store/askBuoyStore.js.map +0 -1
  233. package/lib/module/store/restoredIds.test.js.map +0 -1
  234. package/lib/module/store/traceText.js.map +0 -1
  235. package/lib/module/store/traceText.test.js.map +0 -1
  236. package/lib/module/store/transcriptPersistence.js.map +0 -1
  237. package/lib/module/store/transcriptPersistence.test.js.map +0 -1
  238. package/lib/module/store/useAskBuoy.js.map +0 -1
  239. package/lib/module/store/useAskBuoySettings.js.map +0 -1
  240. package/lib/module/sync/askBuoySyncAdapter.js.map +0 -1
  241. package/lib/module/sync/askBuoySyncAdapter.test.js.map +0 -1
  242. package/lib/module/sync/wire.js.map +0 -1
  243. package/lib/module/types.js.map +0 -1
  244. package/lib/typescript/blocks/AskBlocks.d.ts.map +0 -1
  245. package/lib/typescript/blocks/BlockRenderer.d.ts.map +0 -1
  246. package/lib/typescript/blocks/ChromePrototypes.d.ts.map +0 -1
  247. package/lib/typescript/blocks/DataBlocks.d.ts.map +0 -1
  248. package/lib/typescript/blocks/ExtraBlocks.d.ts.map +0 -1
  249. package/lib/typescript/blocks/MediaBlocks.d.ts.map +0 -1
  250. package/lib/typescript/blocks/ReceiptBlocks.d.ts.map +0 -1
  251. package/lib/typescript/blocks/context.d.ts.map +0 -1
  252. package/lib/typescript/blocks/icons.d.ts.map +0 -1
  253. package/lib/typescript/blocks/index.d.ts.map +0 -1
  254. package/lib/typescript/blocks/listRow.test.d.ts.map +0 -1
  255. package/lib/typescript/blocks/pageState.d.ts.map +0 -1
  256. package/lib/typescript/blocks/pageState.test.d.ts.map +0 -1
  257. package/lib/typescript/blocks/primitives.d.ts.map +0 -1
  258. package/lib/typescript/blocks/primitivesIndex.d.ts.map +0 -1
  259. package/lib/typescript/components/ApprovalCard.d.ts.map +0 -1
  260. package/lib/typescript/components/AskBuoyHeader.d.ts.map +0 -1
  261. package/lib/typescript/components/AskBuoyIcon.d.ts.map +0 -1
  262. package/lib/typescript/components/AskBuoyModal.d.ts.map +0 -1
  263. package/lib/typescript/components/AskBuoySettings.d.ts.map +0 -1
  264. package/lib/typescript/components/ChangesBar.d.ts.map +0 -1
  265. package/lib/typescript/components/Composer.d.ts.map +0 -1
  266. package/lib/typescript/components/EmptyState.d.ts.map +0 -1
  267. package/lib/typescript/components/Markdown.d.ts.map +0 -1
  268. package/lib/typescript/components/MessageList.d.ts.map +0 -1
  269. package/lib/typescript/components/ThinkingStrip.d.ts.map +0 -1
  270. package/lib/typescript/components/TypingDots.d.ts.map +0 -1
  271. package/lib/typescript/components/useCopyAction.d.ts.map +0 -1
  272. package/lib/typescript/index.d.ts.map +0 -1
  273. package/lib/typescript/preset.d.ts.map +0 -1
  274. package/lib/typescript/store/askBuoySettings.d.ts.map +0 -1
  275. package/lib/typescript/store/askBuoyStore.d.ts.map +0 -1
  276. package/lib/typescript/store/restoredIds.test.d.ts.map +0 -1
  277. package/lib/typescript/store/traceText.d.ts.map +0 -1
  278. package/lib/typescript/store/traceText.test.d.ts.map +0 -1
  279. package/lib/typescript/store/transcriptPersistence.d.ts.map +0 -1
  280. package/lib/typescript/store/transcriptPersistence.test.d.ts.map +0 -1
  281. package/lib/typescript/store/useAskBuoy.d.ts.map +0 -1
  282. package/lib/typescript/store/useAskBuoySettings.d.ts.map +0 -1
  283. package/lib/typescript/sync/askBuoySyncAdapter.d.ts.map +0 -1
  284. package/lib/typescript/sync/askBuoySyncAdapter.test.d.ts.map +0 -1
  285. package/lib/typescript/sync/wire.d.ts.map +0 -1
  286. package/lib/typescript/types.d.ts.map +0 -1
@@ -1,135 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * The approval prompt, shown only when the app opted into `requireApproval`.
5
- *
6
- * It renders the EFFECT, not the payload. That is the whole design constraint:
7
- * every other agent product assumes a developer is reading the proposed
8
- * command before approving it, and the people this tool is for cannot evaluate
9
- * `redux_dispatch({type:'SET_ENTITLEMENTS', payload:{…}})`. An approval prompt
10
- * nobody can read is not oversight — it is a rubber stamp that looks like
11
- * oversight, which is worse than having no prompt at all.
12
- */
13
- import { Pressable, StyleSheet, Text, View } from "react-native";
14
- import { AlertTriangle, night } from "@buoy-gg/shared-ui";
15
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
- export function ApprovalCard({
17
- pending,
18
- onAnswer
19
- }) {
20
- return /*#__PURE__*/_jsxs(View, {
21
- style: styles.card,
22
- testID: "buoy-ask-buoy-approval",
23
- children: [/*#__PURE__*/_jsxs(View, {
24
- style: styles.titleRow,
25
- children: [/*#__PURE__*/_jsx(AlertTriangle, {
26
- size: 14,
27
- color: night.color.warning
28
- }), /*#__PURE__*/_jsx(Text, {
29
- style: styles.title,
30
- children: "Allow this change?"
31
- })]
32
- }), /*#__PURE__*/_jsx(Text, {
33
- style: styles.label,
34
- children: pending.label
35
- }), pending.description ? /*#__PURE__*/_jsx(Text, {
36
- style: styles.description,
37
- children: pending.description
38
- }) : null, /*#__PURE__*/_jsx(Text, {
39
- style: styles.reason,
40
- children: pending.reason
41
- }), /*#__PURE__*/_jsxs(View, {
42
- style: styles.actions,
43
- children: [/*#__PURE__*/_jsx(Pressable, {
44
- testID: "buoy-ask-buoy-approve",
45
- accessibilityRole: "button",
46
- onPress: () => onAnswer(true),
47
- style: ({
48
- pressed
49
- }) => [styles.button, styles.approve, pressed && styles.pressed],
50
- children: /*#__PURE__*/_jsx(Text, {
51
- style: styles.approveText,
52
- children: "Allow"
53
- })
54
- }), /*#__PURE__*/_jsx(Pressable, {
55
- testID: "buoy-ask-buoy-reject",
56
- accessibilityRole: "button",
57
- onPress: () => onAnswer(false),
58
- style: ({
59
- pressed
60
- }) => [styles.button, styles.reject, pressed && styles.pressed],
61
- children: /*#__PURE__*/_jsx(Text, {
62
- style: styles.rejectText,
63
- children: "Not now"
64
- })
65
- })]
66
- })]
67
- });
68
- }
69
- const styles = StyleSheet.create({
70
- card: {
71
- marginHorizontal: night.gutter,
72
- marginBottom: 10,
73
- padding: night.cardPad,
74
- borderRadius: night.radius.card,
75
- borderWidth: StyleSheet.hairlineWidth,
76
- borderColor: night.color.accentBorderStrong,
77
- backgroundColor: night.color.surface,
78
- gap: 6
79
- },
80
- titleRow: {
81
- flexDirection: "row",
82
- alignItems: "center",
83
- gap: 6
84
- },
85
- title: {
86
- color: night.color.text,
87
- fontSize: night.font.caption,
88
- fontWeight: "600"
89
- },
90
- label: {
91
- color: night.color.text,
92
- fontSize: night.font.row,
93
- fontWeight: "600",
94
- lineHeight: 20
95
- },
96
- description: {
97
- color: night.color.textSecondary,
98
- fontSize: night.font.caption,
99
- lineHeight: 18
100
- },
101
- reason: {
102
- color: night.color.warning,
103
- fontSize: night.font.caption,
104
- lineHeight: 18
105
- },
106
- actions: {
107
- flexDirection: "row",
108
- gap: 8,
109
- marginTop: 6
110
- },
111
- button: {
112
- paddingHorizontal: 16,
113
- paddingVertical: 9,
114
- borderRadius: night.radius.row
115
- },
116
- approve: {
117
- backgroundColor: night.color.accent
118
- },
119
- approveText: {
120
- color: night.color.onAccent,
121
- fontWeight: "600",
122
- fontSize: night.font.caption
123
- },
124
- reject: {
125
- backgroundColor: night.color.button
126
- },
127
- rejectText: {
128
- color: night.color.text,
129
- fontSize: night.font.caption
130
- },
131
- pressed: {
132
- opacity: 0.7
133
- }
134
- });
135
- //# sourceMappingURL=ApprovalCard.js.map
1
+ "use strict";import{useState}from"react";import{Pressable,ScrollView,StyleSheet,Text,TextInput,View}from"react-native";import{AlertTriangle,ChevronDown,night}from"@buoy-gg/shared-ui";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export function ApprovalCard({pending:e,onAnswer:t}){const[o,r]=useState("");return _jsxs(View,{style:styles.card,testID:"buoy-ask-buoy-approval",children:[_jsxs(View,{style:styles.titleRow,children:[_jsx(AlertTriangle,{size:14,color:night.color.warning}),_jsx(Text,{style:styles.title,children:"Allow this change?"})]}),e.restored?_jsx(Text,{style:styles.restored,testID:"buoy-ask-buoy-approval-restored",children:"Asked before the app restarted — check it still applies."}):null,_jsx(Text,{style:styles.label,children:e.label}),_jsx(Text,{style:styles.reason,children:e.reason}),e.description?_jsx(ApprovalDetails,{text:e.description}):null,_jsx(TextInput,{testID:"buoy-ask-buoy-approval-note",value:o,onChangeText:r,placeholder:"Add a note for the agent (optional)",placeholderTextColor:night.color.textTertiary,style:styles.note,returnKeyType:"done",blurOnSubmit:!0}),_jsxs(View,{style:styles.actions,children:[_jsx(Pressable,{testID:"buoy-ask-buoy-approve",accessibilityRole:"button",onPress:()=>t({approved:!0,reason:o}),style:({pressed:e})=>[styles.button,styles.approve,e&&styles.pressed],children:_jsx(Text,{style:styles.approveText,children:"Allow"})}),e.restored?null:_jsx(Pressable,{testID:"buoy-ask-buoy-approve-trust",accessibilityRole:"button",onPress:()=>t({approved:!0,reason:o,trust:!0}),style:({pressed:e})=>[styles.button,styles.trust,e&&styles.pressed],children:_jsx(Text,{style:styles.trustText,children:"Allow for this chat"})}),_jsx(Pressable,{testID:"buoy-ask-buoy-reject",accessibilityRole:"button",onPress:()=>t({approved:!1,reason:o}),style:({pressed:e})=>[styles.button,styles.reject,e&&styles.pressed],children:_jsx(Text,{style:styles.rejectText,children:"Not now"})})]})]})}function ApprovalDetails({text:e}){const[t,o]=useState(!1);return _jsxs(View,{children:[_jsxs(Pressable,{testID:"buoy-ask-buoy-approval-details",accessibilityRole:"button",accessibilityState:{expanded:t},onPress:()=>o(e=>!e),style:({pressed:e})=>[styles.disclosure,e&&styles.pressed],hitSlop:6,children:[_jsx(View,{style:t?styles.chevronOpen:styles.chevronClosed,children:_jsx(ChevronDown,{size:12,color:night.color.textTertiary})}),_jsx(Text,{style:styles.disclosureText,children:t?"Hide details":"Details"})]}),t?_jsx(ScrollView,{style:styles.detail,contentContainerStyle:styles.detailContent,nestedScrollEnabled:!0,children:_jsx(Text,{style:styles.description,children:e})}):null]})}const styles=StyleSheet.create({card:{marginHorizontal:night.gutter,marginBottom:10,padding:night.cardPad,borderRadius:night.radius.card,borderWidth:StyleSheet.hairlineWidth,borderColor:night.color.accentBorderStrong,backgroundColor:night.color.surface,gap:6},titleRow:{flexDirection:"row",alignItems:"center",gap:6},title:{color:night.color.text,fontSize:night.font.caption,fontWeight:"600"},label:{color:night.color.text,fontSize:night.font.row,fontWeight:"600",lineHeight:20},description:{color:night.color.textSecondary,fontSize:night.font.caption,lineHeight:18},reason:{color:night.color.warning,fontSize:night.font.caption,lineHeight:18},restored:{color:night.color.info,backgroundColor:night.color.infoSoft,fontSize:night.font.micro,lineHeight:16,paddingHorizontal:8,paddingVertical:5,borderRadius:8},disclosure:{flexDirection:"row",alignItems:"center",gap:4,paddingVertical:4},disclosureText:{color:night.color.textTertiary,fontSize:night.font.micro,fontWeight:"600"},chevronOpen:{transform:[{rotate:"0deg"}]},chevronClosed:{transform:[{rotate:"-90deg"}]},detail:{maxHeight:150,marginTop:2},detailContent:{paddingBottom:2},note:{color:night.color.text,fontSize:night.font.caption,paddingHorizontal:10,paddingVertical:7,borderRadius:night.radius.row,borderWidth:StyleSheet.hairlineWidth,borderColor:night.color.border,backgroundColor:night.color.button,marginTop:2},actions:{flexDirection:"row",flexWrap:"wrap",gap:8,marginTop:6},button:{paddingHorizontal:16,paddingVertical:9,borderRadius:night.radius.row},approve:{backgroundColor:night.color.accent},approveText:{color:night.color.onAccent,fontWeight:"600",fontSize:night.font.caption},trust:{backgroundColor:night.color.button,borderWidth:StyleSheet.hairlineWidth,borderColor:night.color.accentBorderStrong},trustText:{color:night.color.accent,fontWeight:"600",fontSize:night.font.caption},reject:{backgroundColor:night.color.button},rejectText:{color:night.color.text,fontSize:night.font.caption},pressed:{opacity:.7}});
@@ -1,161 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * The sheet header.
5
- *
6
- * Built on the shared `ModalHeader` so it matches Console, Network and Events
7
- * 1:1 — the stock JsModal header was designed for a single centred title and
8
- * paints its "Double tap: Toggle • Triple tap: Close" hint through the middle
9
- * of it, which is how "Ask Buoy" ended up overprinted by grey italics.
10
- *
11
- * The subtitle is the one piece of live status the sheet has: what the agent
12
- * is doing right now, in words a tester can act on. "Thinking…" while a
13
- * response is streaming with no tool in flight, the current tool's title
14
- * while one runs, and the reachable tool count at rest so a "why can't it
15
- * see my network calls" question has an answer in the header.
16
- *
17
- * "New conversation" moved up here from a 11pt text link under the composer.
18
- * A reset that lives under the keyboard is a reset nobody finds. Copy sits
19
- * beside it for the same reason: the transcript actions under the newest
20
- * answer are only reachable once you have scrolled back down to it.
21
- */
22
- import { useEffect, useState } from "react";
23
- import { StyleSheet, TouchableOpacity, View } from "react-native";
24
- import { Check, Copy, ModalHeader, night, Otto, Plus, Settings } from "@buoy-gg/shared-ui";
25
- import { useCopyAction } from "./useCopyAction";
26
-
27
- /**
28
- * Slower than the sprite's default 140ms.
29
- *
30
- * This mascot is ambient status a user stares at for the length of a turn,
31
- * not a moment of delight they glance at once — at the stock tick it read as
32
- * fidgeting in the corner of the eye while you were trying to read the
33
- * answer next to it. Every other placement keeps the default.
34
- */
35
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
36
- const HEADER_TICK_MS = 200;
37
-
38
- /** No conversation to copy — the button is left out rather than left dead. */
39
- const NO_TRANSCRIPT = () => "";
40
- export function AskBuoyHeader({
41
- configured,
42
- busy,
43
- failed = false,
44
- activeLabel,
45
- toolCount,
46
- hasMessages,
47
- onBack,
48
- onReset,
49
- getTranscript,
50
- onOpenSettings
51
- }) {
52
- const {
53
- copied,
54
- copy
55
- } = useCopyAction(getTranscript ?? NO_TRANSCRIPT);
56
-
57
- /**
58
- * ONE mascot state per turn, held to the end of it.
59
- *
60
- * `activeLabel` drops to null in the gap between two tool calls, so deriving
61
- * the state from it directly flipped robot → thinking → robot on every
62
- * boundary. A turn that runs four tools swapped the sprite eight times in
63
- * about three seconds — a strobe that reads as a glitch, not as progress.
64
- * So the robot latches: once this turn has driven a tool it stays `robot`
65
- * until the turn ends, which is also the honest summary of that turn.
66
- *
67
- * The SUBTITLE still tracks each call, because updating a line of text is
68
- * informative where restarting an animation is just noise.
69
- */
70
- const [droveTool, setDroveTool] = useState(false);
71
- useEffect(() => {
72
- if (!busy) setDroveTool(false);else if (activeLabel) setDroveTool(true);
73
- }, [busy, activeLabel]);
74
-
75
- /**
76
- * Otto mirrors the subtitle, never contradicts it. This is the one place in
77
- * the product where a mascot earns continuous presence: the user is waiting
78
- * on a machine and the header is the only thing reporting on it.
79
- *
80
- * `robot` while a tool runs is literal — the agent IS driving the tools.
81
- */
82
- const otto = !configured ? "confused" : failed ? "error" : busy ? activeLabel || droveTool ? "robot" : "thinking" : "idle";
83
- const subtitle = !configured ? "Not set up in this app" : busy ? activeLabel ? `${activeLabel}…` : "Thinking…" : toolCount > 0 ? `Ready · ${toolCount} tool${toolCount === 1 ? "" : "s"} available` : "Ready";
84
- return /*#__PURE__*/_jsxs(ModalHeader, {
85
- children: [onBack ? /*#__PURE__*/_jsx(ModalHeader.Navigation, {
86
- onBack: onBack
87
- }) : null, /*#__PURE__*/_jsx(View, {
88
- style: styles.otto,
89
- testID: "buoy-ask-buoy-otto",
90
- children: /*#__PURE__*/_jsx(Otto, {
91
- state: otto,
92
- pixelSize: 2,
93
- tickMs: HEADER_TICK_MS,
94
- water: false
95
- })
96
- }), /*#__PURE__*/_jsx(ModalHeader.Content, {
97
- title: "Ask Buoy",
98
- subtitle: subtitle,
99
- testID: "buoy-ask-buoy-title"
100
- }), /*#__PURE__*/_jsxs(ModalHeader.Actions, {
101
- children: [getTranscript ? /*#__PURE__*/_jsx(TouchableOpacity, {
102
- testID: "buoy-ask-buoy-copy-chat",
103
- accessibilityRole: "button",
104
- accessibilityLabel: copied ? "Conversation copied" : "Copy conversation",
105
- disabled: !hasMessages,
106
- onPress: copy,
107
- style: [styles.actionBtn, !hasMessages && styles.actionBtnDisabled],
108
- children: copied ? /*#__PURE__*/_jsx(Check, {
109
- size: 14,
110
- color: night.color.accent
111
- }) : /*#__PURE__*/_jsx(Copy, {
112
- size: 14,
113
- color: hasMessages ? night.color.textSecondary : night.color.textTertiary
114
- })
115
- }) : null, onOpenSettings ? /*#__PURE__*/_jsx(TouchableOpacity, {
116
- testID: "buoy-ask-buoy-settings",
117
- accessibilityRole: "button",
118
- accessibilityLabel: "Settings",
119
- onPress: onOpenSettings,
120
- style: styles.actionBtn,
121
- children: /*#__PURE__*/_jsx(Settings, {
122
- size: 14,
123
- color: night.color.textSecondary
124
- })
125
- }) : null, /*#__PURE__*/_jsx(TouchableOpacity, {
126
- testID: "buoy-ask-buoy-reset",
127
- accessibilityRole: "button",
128
- accessibilityLabel: "New conversation",
129
- disabled: !hasMessages,
130
- onPress: onReset,
131
- style: [styles.actionBtn, !hasMessages && styles.actionBtnDisabled],
132
- children: /*#__PURE__*/_jsx(Plus, {
133
- size: 14,
134
- color: hasMessages ? night.color.textSecondary : night.color.textTertiary
135
- })
136
- })]
137
- })]
138
- });
139
- }
140
- const styles = StyleSheet.create({
141
- // 32pt of Otto (16 grid × 2pt) reading as the header's status glyph.
142
- otto: {
143
- marginRight: 8,
144
- justifyContent: "center"
145
- },
146
- // Same plate as the Console header's action buttons.
147
- actionBtn: {
148
- width: 32,
149
- height: 32,
150
- borderRadius: 8,
151
- backgroundColor: night.color.buttonSurface,
152
- borderWidth: 1,
153
- borderColor: night.color.border,
154
- alignItems: "center",
155
- justifyContent: "center"
156
- },
157
- actionBtnDisabled: {
158
- opacity: 0.55
159
- }
160
- });
161
- //# sourceMappingURL=AskBuoyHeader.js.map
1
+ "use strict";import{useEffect,useState}from"react";import{StyleSheet,TouchableOpacity,View}from"react-native";import{Check,Copy,FileText,ModalHeader,night,Otto,Plus,SettingsSolid}from"@buoy-gg/shared-ui";import{useCopyAction}from"./useCopyAction";import{formatElapsed}from"../store/activityGroups";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";const HEADER_TICK_MS=200,NO_TRANSCRIPT=()=>"";export function AskBuoyHeader({configured:t,busy:e,failed:o=!1,activeLabel:s,awaitingApproval:i=!1,startedAt:n=null,stopping:a=!1,readOnly:l=!1,toolCount:c,hasMessages:r,onBack:y,onReset:u,getTranscript:d,onOpenSettings:b,onTicket:h}){const{copied:p,copy:g}=useCopyAction(d??NO_TRANSCRIPT),[x,f]=useState(!1);useEffect(()=>{e?s&&f(!0):f(!1)},[e,s]);const S=e&&!i&&null!==n,[j,_]=useState(()=>Date.now());useEffect(()=>{if(!S)return;_(Date.now());const t=setInterval(()=>_(Date.now()),1e3);return()=>clearInterval(t)},[S]);const k=S&&null!==n?formatElapsed(j-n):null,m=t?o?"error":e?s||x?"robot":"thinking":"idle":"confused",C=a?"Stopping":s??"Thinking",T=t?i?"Waiting for you":e?k?`${C} · ${k}`:`${C}…`:l?"Read only"+(c>0?` · ${c} tool${1===c?"":"s"}`:""):c>0?`Ready · ${c} tool${1===c?"":"s"} available`:"Ready":"Not set up in this app";return _jsxs(ModalHeader,{children:[y?_jsx(ModalHeader.Navigation,{onBack:y}):null,_jsx(View,{style:styles.otto,testID:"buoy-ask-buoy-otto",children:_jsx(Otto,{state:m,pixelSize:2,tickMs:200,water:!1})}),_jsx(ModalHeader.Content,{title:"Ask Buoy",subtitle:T,testID:"buoy-ask-buoy-title"}),_jsxs(ModalHeader.Actions,{children:[h?_jsx(TouchableOpacity,{testID:"buoy-ask-buoy-ticket",accessibilityRole:"button",accessibilityLabel:"Summarize for a ticket",disabled:!r||e,onPress:h,style:[styles.actionBtn,(!r||e)&&styles.actionBtnDisabled],children:_jsx(FileText,{size:14,color:r&&!e?night.color.textSecondary:night.color.textTertiary})}):null,d?_jsx(TouchableOpacity,{testID:"buoy-ask-buoy-copy-chat",accessibilityRole:"button",accessibilityLabel:p?"Conversation copied":"Copy conversation",disabled:!r,onPress:g,style:[styles.actionBtn,!r&&styles.actionBtnDisabled],children:p?_jsx(Check,{size:14,color:night.color.accent}):_jsx(Copy,{size:14,color:r?night.color.textSecondary:night.color.textTertiary})}):null,b?_jsx(TouchableOpacity,{testID:"buoy-ask-buoy-settings",accessibilityRole:"button",accessibilityLabel:"Settings",onPress:b,style:styles.actionBtn,children:_jsx(SettingsSolid,{size:14,color:night.color.textSecondary})}):null,_jsx(TouchableOpacity,{testID:"buoy-ask-buoy-reset",accessibilityRole:"button",accessibilityLabel:"New conversation",disabled:!r,onPress:u,style:[styles.actionBtn,!r&&styles.actionBtnDisabled],children:_jsx(Plus,{size:14,color:r?night.color.textSecondary:night.color.textTertiary})})]})]})}const styles=StyleSheet.create({otto:{marginRight:8,justifyContent:"center"},actionBtn:{width:32,height:32,borderRadius:8,backgroundColor:night.color.buttonSurface,borderWidth:1,borderColor:night.color.border,alignItems:"center",justifyContent:"center"},actionBtnDisabled:{opacity:.55}});
@@ -1,8 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Re-exported from floating-tools-core so the tool package has one import
5
- * surface, matching how every other Buoy tool exposes its icon.
6
- */
7
- export { AskBuoyIcon, ASK_BUOY_ICON_COLOR } from "@buoy-gg/floating-tools-core";
8
- //# sourceMappingURL=AskBuoyIcon.js.map
1
+ "use strict";export{AskBuoyIcon,ASK_BUOY_ICON_COLOR}from"@buoy-gg/floating-tools-core";
@@ -1,348 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Ask Buoy — the sheet.
5
- *
6
- * Manual JsModal chrome rather than the declarative `InstalledApp.modal` path,
7
- * because `ToolModalShell` forwards neither `footer`/`footerHeight` nor
8
- * `initialHeight`, and a conversation in a 400pt sheet with no sticky composer
9
- * is not usable.
10
- *
11
- * The layout rules here are all learned ones, each noted at its line: one
12
- * scroller owned by the message list, no animated background competing with
13
- * streaming text, transparent containers so the sheet's own chrome shows, and
14
- * an iOS-only keyboard offset.
15
- *
16
- * Top to bottom: header (title, live status, new-conversation) · changes bar
17
- * (only once something has been changed) · transcript · approval card (only
18
- * with `requireApproval`) · composer. Nothing else. The old "New conversation"
19
- * text link under the composer is gone — it is the header's + button now.
20
- */
21
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
22
- import { Alert, Animated, KeyboardAvoidingView, Platform, StyleSheet, Text, View } from "react-native";
23
- import { isReversible } from "@buoy-gg/agent-core";
24
- import { dispatchToolAction, FeatureGate, Info, JsModal, ModalHeader, night, useSafeAreaInsets } from "@buoy-gg/shared-ui";
25
- import { BlockHandlersProvider } from "../blocks";
26
- import { ApprovalCard } from "./ApprovalCard";
27
- import { ChangesBar } from "./ChangesBar";
28
- import { Composer } from "./Composer";
29
- import { AskBuoyHeader } from "./AskBuoyHeader";
30
- import { AskBuoySettings } from "./AskBuoySettings";
31
- import { EmptyState } from "./EmptyState";
32
- import { MessageList, transcriptToText } from "./MessageList";
33
- import { askBuoyStore } from "../store/askBuoyStore";
34
- import { useAskBuoy } from "../store/useAskBuoy";
35
- import { useAskBuoySettings } from "../store/useAskBuoySettings";
36
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
37
- export function AskBuoyModal({
38
- visible,
39
- onClose,
40
- onBack,
41
- onMinimize,
42
- persistenceKey,
43
- config
44
- }) {
45
- const {
46
- state,
47
- send,
48
- stop,
49
- reset,
50
- approve,
51
- retry,
52
- answer,
53
- runAction,
54
- configured,
55
- toolCount
56
- } = useAskBuoy(config);
57
- const [changesOpen, setChangesOpen] = useState(false);
58
- // A hardcoded device map, not the native library — an unmapped phone
59
- // silently returns bottom: 0, which is how footers ended up sitting on the
60
- // home indicator.
61
- const insets = useSafeAreaInsets({
62
- minBottom: 16
63
- });
64
-
65
- /**
66
- * `dispatchToolAction` is imported STATICALLY at the top of this file, not
67
- * with `import()`. A dynamic import here resolves through Metro's
68
- * async-require path, which in a lazy dev bundle rejects with "Expected
69
- * HMRClient.setup() call at startup" — and because the promise was voided,
70
- * Undo did nothing at all while the bar went on claiming the changes were
71
- * undoable. shared-ui is already a static dependency of this component.
72
- *
73
- * A failed revert is SHOWN. `revertAll` reports what it could not put back,
74
- * and swallowing that is the same failure in a quieter form: the whole
75
- * reason mutations may auto-run is that undo is real.
76
- */
77
- const undoAll = useCallback(() => {
78
- const ledger = askBuoyStore.getLedger();
79
- if (!ledger) return;
80
- void (async () => {
81
- const outcome = await ledger.revertAll(dispatchToolAction);
82
- if (outcome.failed.length === 0) {
83
- askBuoyStore.setNotice(null);
84
- return;
85
- }
86
- const detail = outcome.failed.map(f => `${f.label} (${f.error})`).join("; ");
87
- askBuoyStore.setNotice(`Couldn't undo ${outcome.failed.length} change${outcome.failed.length === 1 ? "" : "s"} — ${detail}`);
88
- })();
89
- }, []);
90
- const toggleChanges = useCallback(() => setChangesOpen(v => !v), []);
91
-
92
- /**
93
- * PEEK. When the agent navigates or taps, the sheet drops to a 120pt strip
94
- * for ~2.5s so the user watches the app do it, then springs back to the
95
- * height it had (including a user's drag-resize — the value is read live,
96
- * never assumed). Rides JsModal's external `animatedHeight` seam, so the
97
- * modal's own drag-resize keeps working on the same value.
98
- */
99
- const sheetHeight = useRef(new Animated.Value(560)).current;
100
- const peeking = useRef(false);
101
- const runPeek = useCallback(() => {
102
- if (peeking.current) return;
103
- peeking.current = true;
104
- sheetHeight.stopAnimation(from => {
105
- const restoreTo = typeof from === "number" && from > 200 ? from : 560;
106
- Animated.timing(sheetHeight, {
107
- toValue: 120,
108
- duration: 220,
109
- useNativeDriver: false
110
- }).start(() => {
111
- setTimeout(() => {
112
- Animated.spring(sheetHeight, {
113
- toValue: restoreTo,
114
- friction: 9,
115
- useNativeDriver: false
116
- }).start(() => {
117
- peeking.current = false;
118
- });
119
- }, 2500);
120
- });
121
- });
122
- }, [sheetHeight]);
123
-
124
- // A new `peek` receipt (navigate, tap) triggers the shrink. Keyed by
125
- // message id + count so a fresh assistant message restarts the count.
126
- const peekKey = useMemo(() => {
127
- const last = state.messages[state.messages.length - 1];
128
- if (!last || last.role !== "assistant") return "";
129
- const peeks = last.blocks.filter(p => p.block.kind === "peek").length;
130
- return peeks > 0 ? `${last.id}:${peeks}` : "";
131
- }, [state.messages]);
132
- const prevPeekKey = useRef("");
133
- useEffect(() => {
134
- if (peekKey && peekKey !== prevPeekKey.current && config?.peek !== false) runPeek();
135
- prevPeekKey.current = peekKey;
136
- }, [peekKey, runPeek, config]);
137
-
138
- /**
139
- * New conversation, guarded. Clearing the session clears the ledger — the
140
- * ONLY undo surface — while the agent's changes stay live in the app, so
141
- * with reversible changes outstanding the user chooses their fate first.
142
- */
143
- const confirmReset = useCallback(() => {
144
- const ledger = askBuoyStore.getLedger();
145
- const reversible = ledger?.list().filter(e => !e.undoneAt && isReversible(e.effect)).length ?? 0;
146
- if (reversible === 0) {
147
- reset();
148
- return;
149
- }
150
- Alert.alert("Undo changes first?", `Ask Buoy made ${reversible} change${reversible === 1 ? "" : "s"} that can still be undone. Clearing the conversation loses the only automatic undo for ${reversible === 1 ? "it" : "them"}.`, [{
151
- text: "Undo, then clear",
152
- onPress: () => {
153
- void (async () => {
154
- const l = askBuoyStore.getLedger();
155
- if (l) await l.revertAll(dispatchToolAction);
156
- reset();
157
- })();
158
- }
159
- }, {
160
- text: "Clear anyway",
161
- style: "destructive",
162
- onPress: reset
163
- }, {
164
- text: "Cancel",
165
- style: "cancel"
166
- }]);
167
- }, [reset]);
168
-
169
- // The header's live status: the title of whichever tool call is in flight.
170
- const activeLabel = useMemo(() => {
171
- const last = state.messages[state.messages.length - 1];
172
- if (!last || last.role !== "assistant" || !last.streaming) return null;
173
- return last.toolCalls.find(c => c.status === "running")?.label ?? null;
174
- }, [state.messages]);
175
-
176
- /**
177
- * What a block can do in THIS host. `minimize` collapses the sheet to the
178
- * rail so the user can look at the app (the peek chip). `openTool` has no
179
- * seam yet, so those buttons don't render here rather than render dead.
180
- */
181
- const blockHandlers = useMemo(() => ({
182
- answer,
183
- runAction: (action, blockId) => {
184
- if (action.minimize) {
185
- onMinimize?.({
186
- mode: "bottomSheet"
187
- });
188
- return;
189
- }
190
- return runAction(action, blockId);
191
- },
192
- answered: state.answers,
193
- actionState: state.actionState,
194
- busy: state.busy,
195
- supports: {
196
- openTool: false,
197
- minimize: !!onMinimize,
198
- dispatch: true
199
- }
200
- }), [answer, runAction, onMinimize, state.answers, state.actionState, state.busy]);
201
-
202
- // The last assistant turn carrying an error is the only "it went wrong"
203
- // signal the header can read; it clears as soon as the next send starts.
204
- const lastMessage = state.messages[state.messages.length - 1];
205
- const failed = !state.busy && lastMessage?.role === "assistant" && !!lastMessage.error;
206
-
207
- // Built on press, not on render — a streaming answer would otherwise rebuild
208
- // the whole transcript on every frame for a button nobody tapped.
209
- const {
210
- showThinking
211
- } = useAskBuoySettings();
212
- const getTranscript = useCallback(() => transcriptToText(state.messages, showThinking), [state.messages, showThinking]);
213
-
214
- /**
215
- * Settings replaces the transcript in place rather than opening over it. The
216
- * sheet's own back arrow is the way out, and the conversation is untouched
217
- * underneath — a toggle you flip here should be visible the moment you go
218
- * back, not after dismissing something.
219
- */
220
- const [showSettings, setShowSettings] = useState(false);
221
- const closeSettings = useCallback(() => setShowSettings(false), []);
222
- const openSettings = useCallback(() => setShowSettings(true), []);
223
- const header = showSettings ? /*#__PURE__*/_jsxs(ModalHeader, {
224
- children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
225
- onBack: closeSettings
226
- }), /*#__PURE__*/_jsx(ModalHeader.Content, {
227
- title: "Settings",
228
- subtitle: "Ask Buoy",
229
- testID: "buoy-ask-buoy-settings-title"
230
- })]
231
- }) : /*#__PURE__*/_jsx(AskBuoyHeader, {
232
- configured: configured,
233
- busy: state.busy,
234
- failed: failed,
235
- activeLabel: activeLabel,
236
- toolCount: toolCount,
237
- hasMessages: state.messages.length > 0,
238
- onBack: onBack,
239
- onReset: confirmReset,
240
- getTranscript: getTranscript,
241
- onOpenSettings: openSettings
242
- });
243
- return /*#__PURE__*/_jsx(JsModal, {
244
- visible: visible,
245
- onClose: onClose
246
- // The message list is the only scroller. JsModal's default wrapper is
247
- // flexGrow:1, which makes a nested flex:1 scroller resolve against the
248
- // content and never scroll at all.
249
- ,
250
- disableScrollWrapper: true
251
- // ToolBackground is an animated star field. A chat re-renders on every
252
- // streamed token; two continuous animations competing is a dropped
253
- // frame budget for no benefit.
254
- ,
255
- background: false,
256
- initialHeight: 560,
257
- animatedHeight: sheetHeight,
258
- header: {
259
- customContent: header,
260
- showToggleButton: false
261
- },
262
- onBack: onBack,
263
- onMinimize: onMinimize,
264
- persistenceKey: persistenceKey ?? "buoy-ask-buoy",
265
- enablePersistence: true,
266
- children: /*#__PURE__*/_jsx(BlockHandlersProvider, {
267
- value: blockHandlers,
268
- children: /*#__PURE__*/_jsxs(View, {
269
- style: [styles.root, {
270
- paddingBottom: insets.bottom
271
- }],
272
- children: [/*#__PURE__*/_jsx(ChangesBar, {
273
- ledger: askBuoyStore.getLedger(),
274
- onUndoAll: undoAll,
275
- expanded: changesOpen,
276
- onToggle: toggleChanges
277
- }), state.notice && /*#__PURE__*/_jsxs(View, {
278
- style: styles.notice,
279
- testID: "buoy-ask-buoy-notice",
280
- children: [/*#__PURE__*/_jsx(Info, {
281
- size: 14,
282
- color: night.color.info
283
- }), /*#__PURE__*/_jsx(Text, {
284
- style: styles.noticeText,
285
- children: state.notice
286
- })]
287
- }), /*#__PURE__*/_jsx(KeyboardAvoidingView, {
288
- style: styles.flex
289
- // iOS only: Android's default softwareKeyboardLayoutMode is "resize",
290
- // so the window shrinks and a bottom-anchored view rides up already.
291
- // Padding on both would lift the composer twice.
292
- ,
293
- behavior: Platform.OS === "ios" ? "padding" : undefined,
294
- enabled: Platform.OS === "ios",
295
- keyboardVerticalOffset: insets.bottom,
296
- children: /*#__PURE__*/_jsx(FeatureGate, {
297
- featureName: "Ask Buoy",
298
- children: showSettings ? /*#__PURE__*/_jsx(AskBuoySettings, {}) : /*#__PURE__*/_jsxs(_Fragment, {
299
- children: [/*#__PURE__*/_jsx(MessageList, {
300
- messages: state.messages,
301
- onRetry: retry,
302
- ListEmptyComponent: /*#__PURE__*/_jsx(EmptyState, {
303
- configured: configured,
304
- onPick: send
305
- })
306
- }), state.pending && /*#__PURE__*/_jsx(ApprovalCard, {
307
- pending: state.pending,
308
- onAnswer: approve
309
- }), /*#__PURE__*/_jsx(Composer, {
310
- onSend: send,
311
- onStop: stop,
312
- busy: state.busy,
313
- disabled: !configured,
314
- placeholder: state.queued ? "Queued — sends when this turn finishes" : state.awaitingBlockId ? "Pick an option above, or type…" : undefined
315
- })]
316
- })
317
- })
318
- })]
319
- })
320
- })
321
- });
322
- }
323
- const styles = StyleSheet.create({
324
- // Transparent or it paints over the sheet's own chrome — the convention is
325
- // repeated verbatim across every Buoy tool body.
326
- root: {
327
- flex: 1,
328
- backgroundColor: "transparent"
329
- },
330
- flex: {
331
- flex: 1
332
- },
333
- notice: {
334
- flexDirection: "row",
335
- alignItems: "flex-start",
336
- gap: 8,
337
- paddingHorizontal: night.gutter,
338
- paddingVertical: 10,
339
- backgroundColor: night.color.infoSoft
340
- },
341
- noticeText: {
342
- flex: 1,
343
- color: night.color.text,
344
- fontSize: night.font.caption,
345
- lineHeight: 18
346
- }
347
- });
348
- //# sourceMappingURL=AskBuoyModal.js.map
1
+ "use strict";import{useCallback,useEffect,useMemo,useRef,useState}from"react";import{Alert,Animated,StyleSheet,Text,View}from"react-native";import{isReversible}from"@buoy-gg/agent-core";import{dispatchToolAction,FeatureGate,Info,JsModal,ModalHeader,night,setToolAttention,useSafeAreaInsets}from"@buoy-gg/shared-ui";import{BlockHandlersProvider}from"../blocks";import{ApprovalCard}from"./ApprovalCard";import{ChangesBar}from"./ChangesBar";import{Composer}from"./Composer";import{KeyboardPad}from"./KeyboardPad";import{AskBuoyHeader}from"./AskBuoyHeader";import{AskBuoySettings}from"./AskBuoySettings";import{EmptyState}from"./EmptyState";import{MessageList,transcriptToText}from"./MessageList";import{QueuedMessages}from"./QueuedMessages";import{askBuoyStore,queuePauseReason}from"../store/askBuoyStore";import{TICKET_PROMPT}from"../store/ticketPrompt";import{useAskBuoy}from"../store/useAskBuoy";import{useAskBuoySettings}from"../store/useAskBuoySettings";import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";export function AskBuoyModal({visible:e,onClose:t,onBack:s,onMinimize:o,persistenceKey:n,config:a}){const{state:r,send:i,stop:l,reset:u,undoThenReset:d,approve:c,retry:g,answer:m,runAction:p,editQueued:y,removeQueued:f,configured:b,toolCount:k,availableToolIds:h}=useAskBuoy(a),[A,x]=useState(!1),C=useCallback(()=>{l(),t()},[l,t]),S=useCallback(()=>askBuoyStore.clearDraft(),[]),[B,T]=useState(!1),v=useCallback(()=>T(!1),[]),j=useCallback(()=>T(!0),[]),_=!e,w=!!r.pending||!!r.awaitingBlockId,M=r.messages[r.messages.length-1],P=useRef(!1),R=useRef(r.busy);useEffect(()=>{_&&R.current&&!r.busy&&(P.current=!0),_||(P.current=!1),R.current=r.busy},[_,r.busy]),useEffect(()=>{_?w?setToolAttention("ask-buoy",{kind:"needsInput",label:r.pending?"Needs your approval":"Has a question for you"}):P.current&&"assistant"===M?.role&&M.error?setToolAttention("ask-buoy",{kind:"failed",label:"The last turn failed"}):P.current&&!r.busy?setToolAttention("ask-buoy",{kind:"done",label:"Finished"}):setToolAttention("ask-buoy",null):setToolAttention("ask-buoy",null)},[_,w,r.busy,r.pending,M]),useEffect(()=>()=>setToolAttention("ask-buoy",null),[]),useEffect(()=>{r.pending&&B&&T(!1)},[r.pending,B]);const $=useSafeAreaInsets({minBottom:16}),[I,H]=useState("bottomSheet"),E="floating"===I?0:$.bottom,K=useCallback(()=>{const e=askBuoyStore.getLedger();e&&(async()=>{const t=await e.revertAll(dispatchToolAction);if(0===t.failed.length)return void askBuoyStore.setNotice(null);const s=t.failed.map(e=>`${e.label} (${e.error})`).join("; ");askBuoyStore.setNotice(`Couldn't undo ${t.failed.length} change${1===t.failed.length?"":"s"} — ${s}`)})()},[]),L=useCallback(()=>x(e=>!e),[]),O=useRef(new Animated.Value(560)).current,z=useRef(!1),D=useCallback(()=>{z.current||(z.current=!0,O.stopAnimation(e=>{const t="number"==typeof e&&e>200?e:560;Animated.timing(O,{toValue:120,duration:220,useNativeDriver:!1}).start(()=>{setTimeout(()=>{Animated.spring(O,{toValue:t,friction:9,useNativeDriver:!1}).start(()=>{z.current=!1})},2500)})}))},[O]),F=useMemo(()=>{const e=r.messages[r.messages.length-1];if(!e||"assistant"!==e.role)return"";const t=e.blocks.filter(e=>"peek"===e.block.kind).length;return t>0?`${e.id}:${t}`:""},[r.messages]),N=useRef("");useEffect(()=>{F&&F!==N.current&&!1!==a?.peek&&D(),N.current=F},[F,D,a]);const V=useCallback(()=>{const e=askBuoyStore.getLedger(),t=e?.list().filter(e=>!e.undoneAt&&isReversible(e.effect)).length??0;0!==t?Alert.alert("Undo changes first?",`Ask Buoy made ${t} change${1===t?"":"s"} that can still be undone. Clearing the conversation loses the only automatic undo for ${1===t?"it":"them"}.`,[{text:"Undo, then clear",onPress:()=>{(async()=>{const e=await d();if(e.cleared)return;const t=e.reverted+e.failed.length,s=e.failed.map(e=>`${e.label} (${e.error})`).join("\n");Alert.alert(`Couldn't undo ${e.failed.length} of ${t} change${1===t?"":"s"}`,`${s}\n\nStill applied in the app. Clear the conversation anyway?`,[{text:"Keep conversation",style:"cancel"},{text:"Clear anyway",style:"destructive",onPress:u}])})()}},{text:"Clear anyway",style:"destructive",onPress:u},{text:"Cancel",style:"cancel"}]):u()},[u,d]),Q=useMemo(()=>{const e=r.messages[r.messages.length-1];return e&&"assistant"===e.role&&e.streaming?e.toolCalls.find(e=>"running"===e.status)?.label??null:null},[r.messages]),q=useMemo(()=>({answer:m,runAction:(e,t)=>{if(!e.minimize)return p(e,t);o?.({mode:"bottomSheet"})},answered:r.answers,actionState:r.actionState,busy:r.busy,supports:{openTool:!1,minimize:!!o,dispatch:!0}}),[m,p,o,r.answers,r.actionState,r.busy]),U=!r.busy&&"assistant"===M?.role&&!!M.error,G=r.busy||"assistant"!==M?.role||!M.stopped||M.error?null:M.id,J=queuePauseReason(r),{showThinking:W,readOnly:X}=useAskBuoySettings(),Y=useCallback(()=>transcriptToText(r.messages,W),[r.messages,W]),Z=useCallback(()=>i(TICKET_PROMPT),[i]),ee=B?_jsxs(ModalHeader,{children:[_jsx(ModalHeader.Navigation,{onBack:v}),_jsx(ModalHeader.Content,{title:"Settings",subtitle:"Ask Buoy",testID:"buoy-ask-buoy-settings-title"})]}):_jsx(AskBuoyHeader,{configured:b,busy:r.busy,failed:U,activeLabel:Q,awaitingApproval:!!r.pending,startedAt:"assistant"===M?.role&&M.streaming?M.startedAt??null:null,stopping:r.stopping,readOnly:X,toolCount:k,hasMessages:r.messages.length>0,onBack:s,onReset:V,getTranscript:Y,onOpenSettings:j,onTicket:Z});return _jsx(JsModal,{visible:e,onClose:C,disableScrollWrapper:!0,background:!1,initialHeight:560,animatedHeight:O,header:{customContent:ee,showToggleButton:!1},onBack:s,onMinimize:o,onModeChange:H,persistenceKey:n??"buoy-ask-buoy",enablePersistence:!0,children:_jsx(BlockHandlersProvider,{value:q,children:_jsxs(View,{style:[styles.root,{paddingBottom:E}],children:[_jsx(ChangesBar,{ledger:askBuoyStore.getLedger(),onUndoAll:K,expanded:A,onToggle:L}),r.notice&&_jsxs(View,{style:styles.notice,testID:"buoy-ask-buoy-notice",children:[_jsx(Info,{size:14,color:night.color.info}),_jsx(Text,{style:styles.noticeText,children:r.notice})]}),_jsx(KeyboardPad,{children:_jsx(FeatureGate,{featureName:"Ask Buoy",children:B?_jsx(AskBuoySettings,{}):_jsxs(_Fragment,{children:[_jsx(MessageList,{messages:r.messages,onRetry:g,waitingOnUser:!!r.pending||!!r.awaitingBlockId,ListEmptyComponent:_jsx(EmptyState,{configured:b,onPick:i,availableToolIds:h,readOnly:X||!!a?.policy?.readOnly})}),_jsx(QueuedMessages,{items:r.queued,paused:J,onEdit:y,onRemove:f}),r.pending&&_jsx(ApprovalCard,{pending:r.pending,onAnswer:c}),_jsx(Composer,{onSend:i,onStop:l,busy:r.busy,draft:r.draft,onDraftTaken:S,disabled:!b,focusKey:G,canFocus:e,placeholder:G?"Tell it what to do differently…":r.awaitingBlockId?"Pick an option above, or type…":void 0})]})})})]})})})}const styles=StyleSheet.create({root:{flex:1,backgroundColor:"transparent"},notice:{flexDirection:"row",alignItems:"flex-start",gap:8,paddingHorizontal:night.gutter,paddingVertical:10,backgroundColor:night.color.infoSoft},noticeText:{flex:1,color:night.color.text,fontSize:night.font.caption,lineHeight:18}});