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