@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.
- package/README.md +1 -1
- package/lib/commonjs/blocks/AskBlocks.js +1 -338
- package/lib/commonjs/blocks/BlockRenderer.js +1 -250
- package/lib/commonjs/blocks/ChromePrototypes.js +1 -514
- package/lib/commonjs/blocks/DataBlocks.js +1 -545
- package/lib/commonjs/blocks/ExtraBlocks.js +1 -1821
- package/lib/commonjs/blocks/MediaBlocks.js +1 -140
- package/lib/commonjs/blocks/ReceiptBlocks.js +1 -596
- package/lib/commonjs/blocks/context.js +1 -42
- package/lib/commonjs/blocks/icons.js +1 -96
- package/lib/commonjs/blocks/index.js +1 -95
- package/lib/commonjs/blocks/primitives.js +1 -312
- package/lib/commonjs/blocks/primitivesIndex.js +1 -56
- package/lib/commonjs/components/ApprovalCard.js +1 -140
- package/lib/commonjs/components/AskBuoyHeader.js +1 -127
- package/lib/commonjs/components/AskBuoyIcon.js +1 -19
- package/lib/commonjs/components/AskBuoyModal.js +1 -322
- package/lib/commonjs/components/ChangesBar.js +1 -211
- package/lib/commonjs/components/Composer.js +1 -175
- package/lib/commonjs/components/EmptyState.js +1 -154
- package/lib/commonjs/components/Markdown.js +1 -252
- package/lib/commonjs/components/MessageList.js +1 -648
- package/lib/commonjs/components/TypingDots.js +1 -63
- package/lib/commonjs/index.js +1 -205
- package/lib/commonjs/preset.js +1 -90
- package/lib/commonjs/store/askBuoyStore.js +1 -279
- package/lib/commonjs/store/useAskBuoy.js +1 -299
- package/lib/commonjs/sync/askBuoySyncAdapter.js +1 -119
- package/lib/commonjs/sync/askBuoySyncAdapter.test.js +1 -136
- package/lib/commonjs/sync/wire.js +1 -58
- package/lib/commonjs/types.js +1 -6
- package/lib/module/blocks/AskBlocks.js +1 -331
- package/lib/module/blocks/BlockRenderer.js +1 -245
- package/lib/module/blocks/ChromePrototypes.js +1 -503
- package/lib/module/blocks/DataBlocks.js +1 -532
- package/lib/module/blocks/ExtraBlocks.js +1 -1794
- package/lib/module/blocks/MediaBlocks.js +1 -135
- package/lib/module/blocks/ReceiptBlocks.js +1 -584
- package/lib/module/blocks/context.js +1 -35
- package/lib/module/blocks/icons.js +1 -91
- package/lib/module/blocks/index.js +1 -8
- package/lib/module/blocks/primitives.js +1 -301
- package/lib/module/blocks/primitivesIndex.js +1 -5
- package/lib/module/components/ApprovalCard.js +1 -135
- package/lib/module/components/AskBuoyHeader.js +1 -124
- package/lib/module/components/AskBuoyIcon.js +1 -8
- package/lib/module/components/AskBuoyModal.js +1 -317
- package/lib/module/components/ChangesBar.js +1 -206
- package/lib/module/components/Composer.js +1 -170
- package/lib/module/components/EmptyState.js +1 -149
- package/lib/module/components/Markdown.js +1 -246
- package/lib/module/components/MessageList.js +1 -642
- package/lib/module/components/TypingDots.js +1 -58
- package/lib/module/index.js +1 -29
- package/lib/module/preset.js +1 -73
- package/lib/module/store/askBuoyStore.js +1 -275
- package/lib/module/store/useAskBuoy.js +1 -293
- package/lib/module/sync/askBuoySyncAdapter.js +1 -114
- package/lib/module/sync/askBuoySyncAdapter.test.js +1 -135
- package/lib/module/sync/wire.js +1 -47
- package/lib/module/types.js +1 -4
- package/lib/typescript/preset.d.ts +1 -1
- package/package.json +8 -8
- package/lib/commonjs/blocks/AskBlocks.js.map +0 -1
- package/lib/commonjs/blocks/BlockRenderer.js.map +0 -1
- package/lib/commonjs/blocks/ChromePrototypes.js.map +0 -1
- package/lib/commonjs/blocks/DataBlocks.js.map +0 -1
- package/lib/commonjs/blocks/ExtraBlocks.js.map +0 -1
- package/lib/commonjs/blocks/MediaBlocks.js.map +0 -1
- package/lib/commonjs/blocks/ReceiptBlocks.js.map +0 -1
- package/lib/commonjs/blocks/context.js.map +0 -1
- package/lib/commonjs/blocks/icons.js.map +0 -1
- package/lib/commonjs/blocks/index.js.map +0 -1
- package/lib/commonjs/blocks/primitives.js.map +0 -1
- package/lib/commonjs/blocks/primitivesIndex.js.map +0 -1
- package/lib/commonjs/components/ApprovalCard.js.map +0 -1
- package/lib/commonjs/components/AskBuoyHeader.js.map +0 -1
- package/lib/commonjs/components/AskBuoyIcon.js.map +0 -1
- package/lib/commonjs/components/AskBuoyModal.js.map +0 -1
- package/lib/commonjs/components/ChangesBar.js.map +0 -1
- package/lib/commonjs/components/Composer.js.map +0 -1
- package/lib/commonjs/components/EmptyState.js.map +0 -1
- package/lib/commonjs/components/Markdown.js.map +0 -1
- package/lib/commonjs/components/MessageList.js.map +0 -1
- package/lib/commonjs/components/TypingDots.js.map +0 -1
- package/lib/commonjs/index.js.map +0 -1
- package/lib/commonjs/preset.js.map +0 -1
- package/lib/commonjs/store/askBuoyStore.js.map +0 -1
- package/lib/commonjs/store/useAskBuoy.js.map +0 -1
- package/lib/commonjs/sync/askBuoySyncAdapter.js.map +0 -1
- package/lib/commonjs/sync/askBuoySyncAdapter.test.js.map +0 -1
- package/lib/commonjs/sync/wire.js.map +0 -1
- package/lib/commonjs/types.js.map +0 -1
- package/lib/module/blocks/AskBlocks.js.map +0 -1
- package/lib/module/blocks/BlockRenderer.js.map +0 -1
- package/lib/module/blocks/ChromePrototypes.js.map +0 -1
- package/lib/module/blocks/DataBlocks.js.map +0 -1
- package/lib/module/blocks/ExtraBlocks.js.map +0 -1
- package/lib/module/blocks/MediaBlocks.js.map +0 -1
- package/lib/module/blocks/ReceiptBlocks.js.map +0 -1
- package/lib/module/blocks/context.js.map +0 -1
- package/lib/module/blocks/icons.js.map +0 -1
- package/lib/module/blocks/index.js.map +0 -1
- package/lib/module/blocks/primitives.js.map +0 -1
- package/lib/module/blocks/primitivesIndex.js.map +0 -1
- package/lib/module/components/ApprovalCard.js.map +0 -1
- package/lib/module/components/AskBuoyHeader.js.map +0 -1
- package/lib/module/components/AskBuoyIcon.js.map +0 -1
- package/lib/module/components/AskBuoyModal.js.map +0 -1
- package/lib/module/components/ChangesBar.js.map +0 -1
- package/lib/module/components/Composer.js.map +0 -1
- package/lib/module/components/EmptyState.js.map +0 -1
- package/lib/module/components/Markdown.js.map +0 -1
- package/lib/module/components/MessageList.js.map +0 -1
- package/lib/module/components/TypingDots.js.map +0 -1
- package/lib/module/index.js.map +0 -1
- package/lib/module/preset.js.map +0 -1
- package/lib/module/store/askBuoyStore.js.map +0 -1
- package/lib/module/store/useAskBuoy.js.map +0 -1
- package/lib/module/sync/askBuoySyncAdapter.js.map +0 -1
- package/lib/module/sync/askBuoySyncAdapter.test.js.map +0 -1
- package/lib/module/sync/wire.js.map +0 -1
- package/lib/module/types.js.map +0 -1
- package/lib/typescript/blocks/AskBlocks.d.ts.map +0 -1
- package/lib/typescript/blocks/BlockRenderer.d.ts.map +0 -1
- package/lib/typescript/blocks/ChromePrototypes.d.ts.map +0 -1
- package/lib/typescript/blocks/DataBlocks.d.ts.map +0 -1
- package/lib/typescript/blocks/ExtraBlocks.d.ts.map +0 -1
- package/lib/typescript/blocks/MediaBlocks.d.ts.map +0 -1
- package/lib/typescript/blocks/ReceiptBlocks.d.ts.map +0 -1
- package/lib/typescript/blocks/context.d.ts.map +0 -1
- package/lib/typescript/blocks/icons.d.ts.map +0 -1
- package/lib/typescript/blocks/index.d.ts.map +0 -1
- package/lib/typescript/blocks/primitives.d.ts.map +0 -1
- package/lib/typescript/blocks/primitivesIndex.d.ts.map +0 -1
- package/lib/typescript/components/ApprovalCard.d.ts.map +0 -1
- package/lib/typescript/components/AskBuoyHeader.d.ts.map +0 -1
- package/lib/typescript/components/AskBuoyIcon.d.ts.map +0 -1
- package/lib/typescript/components/AskBuoyModal.d.ts.map +0 -1
- package/lib/typescript/components/ChangesBar.d.ts.map +0 -1
- package/lib/typescript/components/Composer.d.ts.map +0 -1
- package/lib/typescript/components/EmptyState.d.ts.map +0 -1
- package/lib/typescript/components/Markdown.d.ts.map +0 -1
- package/lib/typescript/components/MessageList.d.ts.map +0 -1
- package/lib/typescript/components/TypingDots.d.ts.map +0 -1
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
- package/lib/typescript/store/askBuoyStore.d.ts.map +0 -1
- package/lib/typescript/store/useAskBuoy.d.ts.map +0 -1
- package/lib/typescript/sync/askBuoySyncAdapter.d.ts.map +0 -1
- package/lib/typescript/sync/askBuoySyncAdapter.test.d.ts.map +0 -1
- package/lib/typescript/sync/wire.d.ts.map +0 -1
- 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"}});
|