@buoy-gg/ask-buoy 7.0.33 → 7.0.34

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