@adminide-stack/yantra-mobile 12.0.51 → 12.0.52-alpha.1

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 (41) hide show
  1. package/lib/components/KeyboardComposerDock.js +4 -3
  2. package/lib/components/KeyboardComposerDock.js.map +1 -1
  3. package/lib/features/attachments/buildGatewayMedia.js +22 -15
  4. package/lib/features/attachments/buildGatewayMedia.js.map +1 -1
  5. package/lib/features/attachments/uploadChatAttachments.js +6 -4
  6. package/lib/features/attachments/uploadChatAttachments.js.map +1 -1
  7. package/lib/features/attachments/useImageAttachments.js +63 -17
  8. package/lib/features/attachments/useImageAttachments.js.map +1 -1
  9. package/lib/features/chat/AskUserCard.js +367 -0
  10. package/lib/features/chat/AskUserCard.js.map +1 -0
  11. package/lib/features/chat/ChatTranscript.js +203 -98
  12. package/lib/features/chat/ChatTranscript.js.map +1 -1
  13. package/lib/features/chat/ToolTimeline.js +118 -0
  14. package/lib/features/chat/ToolTimeline.js.map +1 -0
  15. package/lib/features/chat/askUser.js +136 -0
  16. package/lib/features/chat/askUser.js.map +1 -0
  17. package/lib/features/chat/streamingMarkdown.js +7 -0
  18. package/lib/features/chat/streamingMarkdown.js.map +1 -0
  19. package/lib/features/chat/toolActivity.js +77 -0
  20. package/lib/features/chat/toolActivity.js.map +1 -0
  21. package/lib/features/chat/transcriptGroups.js +29 -0
  22. package/lib/features/chat/transcriptGroups.js.map +1 -0
  23. package/lib/hooks/mintSurfaceSystemToken.js +28 -14
  24. package/lib/hooks/mintSurfaceSystemToken.js.map +1 -1
  25. package/lib/hooks/systemTokenJwt.js +75 -0
  26. package/lib/hooks/systemTokenJwt.js.map +1 -0
  27. package/lib/hooks/useCdecliAutoConnect.js +116 -138
  28. package/lib/hooks/useCdecliAutoConnect.js.map +1 -1
  29. package/lib/hooks/useChatApi.js +43 -17
  30. package/lib/hooks/useChatApi.js.map +1 -1
  31. package/lib/hooks/useChatStream.js +61 -13
  32. package/lib/hooks/useChatStream.js.map +1 -1
  33. package/lib/hooks/useObtainSystemToken.js +121 -36
  34. package/lib/hooks/useObtainSystemToken.js.map +1 -1
  35. package/lib/screens/Chat/index.js +29 -5
  36. package/lib/screens/Chat/index.js.map +1 -1
  37. package/lib/screens/Home/HomeScreen.js +21 -6
  38. package/lib/screens/Home/HomeScreen.js.map +1 -1
  39. package/lib/screens/Home/components/ChatHistoryLanding.js +46 -16
  40. package/lib/screens/Home/components/ChatHistoryLanding.js.map +1 -1
  41. package/package.json +4 -4
@@ -1,4 +1,4 @@
1
- import {jsx,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useCallback,useEffect,memo}from'react';import {View,StyleSheet,FlatList,Platform,Pressable,Text}from'react-native';import {dismissKeyboard}from'../../utils/keyboardController.js';import {useKeyboardLiftHeight}from'../../components/KeyboardComposerDock.js';import Markdown from'react-native-markdown-display';import {shouldRenderUserTranscriptTurn,displayUserMessageText}from'../attachments/displayUserMessageText.js';import {MessageAttachmentPreviews}from'../attachments/MessageAttachmentPreviews.js';var __defProp = Object.defineProperty;
1
+ import {jsx,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useCallback,useEffect,memo}from'react';import {View,StyleSheet,FlatList,Platform,Pressable}from'react-native';import {dismissKeyboard}from'../../utils/keyboardController.js';import {useKeyboardLiftHeight}from'../../components/KeyboardComposerDock.js';import Markdown from'react-native-markdown-display';import {shouldRenderUserTranscriptTurn,displayUserMessageText}from'../attachments/displayUserMessageText.js';import {MessageAttachmentPreviews}from'../attachments/MessageAttachmentPreviews.js';import {ToolTimeline}from'./ToolTimeline.js';import {parseToolActivity}from'./toolActivity.js';import {parseAskUser,displayAskUserAssistantText,stripAskUser}from'./askUser.js';import {collapseDuplicateUserRows,groupConsecutiveRoleRows}from'./transcriptGroups.js';import {closeOpenMarkdownFences}from'./streamingMarkdown.js';var __defProp = Object.defineProperty;
2
2
  var __defProps = Object.defineProperties;
3
3
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
@@ -30,21 +30,61 @@ function ChatTranscript({
30
30
  const isProgrammaticScrollRef = useRef(false);
31
31
  const ignoreScrollUntilRef = useRef(0);
32
32
  const keyboardHeight = useKeyboardLiftHeight();
33
- const historyRows = useMemo(() => messages.filter((msg) => msg.role !== "user" || shouldRenderUserTranscriptTurn(msg.content, msg.attachments)).map((msg) => __spreadProps(__spreadValues({}, msg), {
34
- streaming: false
35
- })), [messages]);
33
+ const historyRows = useMemo(() => {
34
+ const rows2 = [];
35
+ messages.forEach((msg, index) => {
36
+ var _a, _b, _c, _d;
37
+ if (msg.role === "user") {
38
+ if (shouldRenderUserTranscriptTurn(msg.content, msg.attachments)) {
39
+ rows2.push(__spreadProps(__spreadValues({}, msg), {
40
+ streaming: false
41
+ }));
42
+ }
43
+ return;
44
+ }
45
+ if (msg.role !== "assistant") {
46
+ rows2.push(__spreadProps(__spreadValues({}, msg), {
47
+ streaming: false
48
+ }));
49
+ return;
50
+ }
51
+ const askAnswered = Boolean(parseAskUser(msg.content)) && messages.slice(index + 1).some((m) => m.role === "user");
52
+ const visible = displayAskUserAssistantText(msg.content, askAnswered).trim();
53
+ const tools = parseToolActivity(stripAskUser(msg.content), false);
54
+ const hasAsk = Boolean(parseAskUser(msg.content));
55
+ if (hasAsk && !visible && tools.steps.length === 0 && !((_b = (_a = msg.attachments) == null ? void 0 : _a.length) != null ? _b : 0)) {
56
+ return;
57
+ }
58
+ if (!hasAsk) {
59
+ const leftover = stripAskUser(msg.content).trim();
60
+ if (!leftover && !((_d = (_c = msg.attachments) == null ? void 0 : _c.length) != null ? _d : 0)) return;
61
+ }
62
+ rows2.push(__spreadProps(__spreadValues({}, msg), {
63
+ streaming: false,
64
+ askAnswered
65
+ }));
66
+ });
67
+ return rows2;
68
+ }, [messages]);
36
69
  const rows = useMemo(() => {
37
- const stream = streamingContent == null ? void 0 : streamingContent.trim();
38
- if (!stream) return historyRows;
39
- return [...historyRows, {
70
+ const history = collapseDuplicateUserRows(historyRows);
71
+ const rawStream = streamingContent != null ? streamingContent : "";
72
+ const last = history[history.length - 1];
73
+ const streamAskAnswered = (last == null ? void 0 : last.role) === "user" && Boolean(parseAskUser(rawStream));
74
+ const streamProse = displayAskUserAssistantText(rawStream, streamAskAnswered).trim();
75
+ const streamTools = parseToolActivity(stripAskUser(rawStream), true);
76
+ if (!streamProse && streamTools.steps.length === 0) return history;
77
+ return [...history, {
40
78
  id: "__streaming__",
41
79
  role: "assistant",
42
- content: stream,
43
- streaming: true
80
+ content: rawStream,
81
+ streaming: true,
82
+ askAnswered: streamAskAnswered
44
83
  }];
45
84
  }, [historyRows, streamingContent]);
85
+ const groups = useMemo(() => groupConsecutiveRoleRows(rows), [rows]);
46
86
  const scrollToLatest = useCallback((animated) => {
47
- if (rows.length === 0) return;
87
+ if (groups.length === 0) return;
48
88
  isProgrammaticScrollRef.current = true;
49
89
  stickToBottomRef.current = true;
50
90
  const list = listRef.current;
@@ -56,12 +96,12 @@ function ChatTranscript({
56
96
  offset: Number.MAX_SAFE_INTEGER,
57
97
  animated
58
98
  });
59
- }, [rows.length]);
99
+ }, [groups.length]);
60
100
  useEffect(() => {
61
- if (rows.length === 0 || !stickToBottomRef.current) return void 0;
101
+ if (groups.length === 0 || !stickToBottomRef.current) return void 0;
62
102
  const t = setTimeout(() => scrollToLatest(false), 0);
63
103
  return () => clearTimeout(t);
64
- }, [rows.length, streamingContent == null ? void 0 : streamingContent.length, scrollToLatest]);
104
+ }, [groups.length, streamingContent == null ? void 0 : streamingContent.length, scrollToLatest]);
65
105
  useEffect(() => {
66
106
  if (keyboardHeight <= 0) return void 0;
67
107
  stickToBottomRef.current = true;
@@ -98,91 +138,50 @@ function ChatTranscript({
98
138
  }, [scrollToLatest]);
99
139
  const renderItem = useCallback(({
100
140
  item
101
- }) => /* @__PURE__ */ jsx(TranscriptRowView, { item, isDark, renderMessageActions: item.streaming ? void 0 : renderMessageActions }), [isDark, renderMessageActions]);
102
- return /* @__PURE__ */ jsx(View, { style: styles.list, children: /* @__PURE__ */ jsx(FlatList, { ref: listRef, data: rows, keyExtractor: (item) => item.id, renderItem, extraData: streamingContent, initialNumToRender: 12, maxToRenderPerBatch: 8, updateCellsBatchingPeriod: 50, windowSize: 7, onScroll: handleScroll, onContentSizeChange: handleContentSizeChange, onLayout: () => {
141
+ }) => /* @__PURE__ */ jsx(TranscriptGroupView, { group: item, isDark, renderMessageActions }), [isDark, renderMessageActions]);
142
+ return /* @__PURE__ */ jsx(View, { style: styles.list, children: /* @__PURE__ */ jsx(FlatList, { ref: listRef, data: groups, keyExtractor: (item) => item.id, renderItem, extraData: streamingContent, initialNumToRender: 12, maxToRenderPerBatch: 8, updateCellsBatchingPeriod: 50, windowSize: 7, onScroll: handleScroll, onContentSizeChange: handleContentSizeChange, onLayout: () => {
103
143
  if (stickToBottomRef.current) scrollToLatest(false);
104
144
  }, scrollEventThrottle: 16, keyboardShouldPersistTaps: "handled", keyboardDismissMode: Platform.OS === "ios" ? "interactive" : "on-drag", automaticallyAdjustKeyboardInsets: false, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", contentContainerStyle: [styles.listContent, listContentStyle, styles.listContentFill], style: styles.list }) });
105
145
  }
106
- const TranscriptRowView = memo(function TranscriptRowView2({
107
- item,
146
+ const TranscriptGroupView = memo(function TranscriptGroupView2({
147
+ group,
108
148
  isDark,
109
149
  renderMessageActions
110
150
  }) {
111
- var _a;
112
- const isUser = item.role === "user";
113
- const attachments = (_a = item.attachments) != null ? _a : [];
114
- const text = isUser ? displayUserMessageText(item.content) : item.content;
115
- if (isUser) {
116
- return /* @__PURE__ */ jsxs(Pressable, { style: styles.userCol, onPress: dismissKeyboard, accessible: false, children: [
117
- attachments.length > 0 ? /* @__PURE__ */ jsx(MessageAttachmentPreviews, { attachments, align: "end", isDark }) : null,
118
- text ? /* @__PURE__ */ jsx(View, { style: styles.userBubble, children: /* @__PURE__ */ jsx(Text, { style: styles.userText, children: text }) }) : null
119
- ] });
151
+ if (group.role === "user") {
152
+ return /* @__PURE__ */ jsx(Pressable, { style: styles.userCol, onPress: dismissKeyboard, accessible: false, children: group.items.map((item, index) => {
153
+ var _a;
154
+ const attachments = (_a = item.attachments) != null ? _a : [];
155
+ const text = displayUserMessageText(item.content);
156
+ if (!text && attachments.length === 0) return null;
157
+ return /* @__PURE__ */ jsxs(View, { style: [styles.userTurn, index > 0 && styles.userFollow], children: [
158
+ attachments.length > 0 ? /* @__PURE__ */ jsx(MessageAttachmentPreviews, { attachments, align: "end", isDark }) : null,
159
+ text ? /* @__PURE__ */ jsx(View, { style: styles.userBubble, children: /* @__PURE__ */ jsx(Markdown, { style: USER_MARKDOWN, children: text }) }) : null
160
+ ] }, item.id);
161
+ }) });
120
162
  }
121
163
  const markdownColor = isDark ? "#e2e8f0" : "#111827";
122
164
  const codeBg = isDark ? "#1e293b" : "#f3f4f6";
123
- return /* @__PURE__ */ jsxs(Pressable, { style: styles.assistantCol, onPress: dismissKeyboard, accessible: false, children: [
165
+ const last = group.items[group.items.length - 1];
166
+ return /* @__PURE__ */ jsx(View, { style: styles.assistantGroup, children: group.items.map((item, index) => /* @__PURE__ */ jsx(AssistantTurnView, { item, isDark, markdownColor, codeBg, tightTop: index > 0, renderMessageActions: item.streaming || item !== last ? void 0 : renderMessageActions }, item.id)) });
167
+ });
168
+ const AssistantTurnView = memo(function AssistantTurnView2({
169
+ item,
170
+ isDark,
171
+ markdownColor,
172
+ codeBg,
173
+ tightTop,
174
+ renderMessageActions
175
+ }) {
176
+ var _a;
177
+ const attachments = (_a = item.attachments) != null ? _a : [];
178
+ const stripped = stripAskUser(item.content);
179
+ const toolActivity = parseToolActivity(stripped, !!item.streaming);
180
+ const text = parseToolActivity(displayAskUserAssistantText(item.content, !!item.askAnswered), !!item.streaming).text;
181
+ return /* @__PURE__ */ jsxs(Pressable, { style: [styles.assistantCol, tightTop && styles.assistantColTight], onPress: dismissKeyboard, accessible: false, children: [
124
182
  attachments.length > 0 ? /* @__PURE__ */ jsx(MessageAttachmentPreviews, { attachments, align: "start", isDark }) : null,
125
- text ? item.streaming ? /* @__PURE__ */ jsx(Text, { style: [styles.assistantStreamText, {
126
- color: markdownColor
127
- }], children: text }) : /* @__PURE__ */ jsx(Markdown, { style: {
128
- body: {
129
- color: markdownColor,
130
- fontSize: 15,
131
- lineHeight: 22
132
- },
133
- heading1: {
134
- fontSize: 18,
135
- fontWeight: "700",
136
- marginTop: 12,
137
- marginBottom: 4,
138
- color: markdownColor
139
- },
140
- heading2: {
141
- fontSize: 16,
142
- fontWeight: "700",
143
- marginTop: 10,
144
- marginBottom: 4,
145
- color: markdownColor
146
- },
147
- heading3: {
148
- fontSize: 15,
149
- fontWeight: "600",
150
- marginTop: 8,
151
- marginBottom: 4,
152
- color: markdownColor
153
- },
154
- strong: {
155
- fontWeight: "700"
156
- },
157
- paragraph: {
158
- marginTop: 4,
159
- marginBottom: 4
160
- },
161
- list_item: {
162
- marginVertical: 2
163
- },
164
- bullet_list: {
165
- marginVertical: 4
166
- },
167
- ordered_list: {
168
- marginVertical: 4
169
- },
170
- code_inline: {
171
- backgroundColor: codeBg,
172
- paddingHorizontal: 4,
173
- borderRadius: 4,
174
- fontSize: 14,
175
- color: markdownColor
176
- },
177
- code_block: {
178
- backgroundColor: codeBg,
179
- padding: 12,
180
- borderRadius: 8,
181
- marginVertical: 8,
182
- fontSize: 14,
183
- color: markdownColor
184
- }
185
- }, children: text }) : null,
183
+ toolActivity.steps.length > 0 ? /* @__PURE__ */ jsx(ToolTimeline, { steps: toolActivity.steps, active: !!item.streaming, isDark }) : null,
184
+ text ? /* @__PURE__ */ jsx(Markdown, { style: assistantMarkdownStyle(markdownColor, codeBg), children: item.streaming ? closeOpenMarkdownFences(text) : text }) : null,
186
185
  renderMessageActions ? renderMessageActions({
187
186
  id: item.id,
188
187
  role: "assistant",
@@ -190,6 +189,107 @@ const TranscriptRowView = memo(function TranscriptRowView2({
190
189
  }) : null
191
190
  ] });
192
191
  });
192
+ const USER_MARKDOWN = {
193
+ body: {
194
+ color: "#ffffff",
195
+ fontSize: 15,
196
+ lineHeight: 22
197
+ },
198
+ paragraph: {
199
+ marginTop: 4,
200
+ marginBottom: 4,
201
+ color: "#ffffff"
202
+ },
203
+ strong: {
204
+ fontWeight: "700",
205
+ color: "#ffffff"
206
+ },
207
+ em: {
208
+ color: "#ffffff"
209
+ },
210
+ text: {
211
+ color: "#ffffff"
212
+ },
213
+ bullet_list: {
214
+ marginVertical: 4
215
+ },
216
+ ordered_list: {
217
+ marginVertical: 4
218
+ },
219
+ list_item: {
220
+ marginVertical: 2,
221
+ color: "#ffffff"
222
+ }
223
+ };
224
+ function assistantMarkdownStyle(markdownColor, codeBg) {
225
+ return {
226
+ body: {
227
+ color: markdownColor,
228
+ fontSize: 15,
229
+ lineHeight: 22
230
+ },
231
+ heading1: {
232
+ fontSize: 18,
233
+ fontWeight: "700",
234
+ marginTop: 12,
235
+ marginBottom: 4,
236
+ color: markdownColor
237
+ },
238
+ heading2: {
239
+ fontSize: 16,
240
+ fontWeight: "700",
241
+ marginTop: 10,
242
+ marginBottom: 4,
243
+ color: markdownColor
244
+ },
245
+ heading3: {
246
+ fontSize: 15,
247
+ fontWeight: "600",
248
+ marginTop: 8,
249
+ marginBottom: 4,
250
+ color: markdownColor
251
+ },
252
+ strong: {
253
+ fontWeight: "700"
254
+ },
255
+ paragraph: {
256
+ marginTop: 4,
257
+ marginBottom: 4
258
+ },
259
+ list_item: {
260
+ marginVertical: 2
261
+ },
262
+ bullet_list: {
263
+ marginVertical: 4
264
+ },
265
+ ordered_list: {
266
+ marginVertical: 4
267
+ },
268
+ code_inline: {
269
+ backgroundColor: codeBg,
270
+ paddingHorizontal: 4,
271
+ borderRadius: 4,
272
+ fontSize: 14,
273
+ color: markdownColor
274
+ },
275
+ code_block: {
276
+ backgroundColor: codeBg,
277
+ padding: 12,
278
+ borderRadius: 8,
279
+ marginVertical: 8,
280
+ fontSize: 14,
281
+ color: markdownColor
282
+ },
283
+ fence: {
284
+ backgroundColor: codeBg,
285
+ padding: 12,
286
+ borderRadius: 8,
287
+ marginVertical: 8,
288
+ fontSize: 14,
289
+ color: markdownColor
290
+ }
291
+ };
292
+ }
193
293
  const styles = StyleSheet.create({
194
294
  list: {
195
295
  flex: 1
@@ -206,19 +306,25 @@ const styles = StyleSheet.create({
206
306
  userCol: {
207
307
  width: "100%",
208
308
  alignItems: "flex-end",
209
- marginBottom: 8,
309
+ marginBottom: 12,
210
310
  paddingHorizontal: 4
211
311
  },
312
+ userTurn: {
313
+ maxWidth: "92%",
314
+ alignItems: "flex-end"
315
+ },
316
+ userFollow: {
317
+ marginTop: 4
318
+ },
212
319
  userBubble: {
213
320
  backgroundColor: USER_BUBBLE_BG,
214
321
  borderRadius: 16,
215
- padding: 12,
216
- maxWidth: "92%"
322
+ paddingHorizontal: 12,
323
+ paddingVertical: 8
217
324
  },
218
- userText: {
219
- color: "#ffffff",
220
- fontSize: 15,
221
- lineHeight: 22
325
+ assistantGroup: {
326
+ width: "100%",
327
+ marginBottom: 4
222
328
  },
223
329
  assistantCol: {
224
330
  width: "100%",
@@ -226,8 +332,7 @@ const styles = StyleSheet.create({
226
332
  marginBottom: 8,
227
333
  paddingHorizontal: 4
228
334
  },
229
- assistantStreamText: {
230
- fontSize: 15,
231
- lineHeight: 22
335
+ assistantColTight: {
336
+ marginTop: -4
232
337
  }
233
338
  });export{ChatTranscript};//# sourceMappingURL=ChatTranscript.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChatTranscript.js","sources":["../../../src/features/chat/ChatTranscript.tsx"],"sourcesContent":["/**\n * Chat thread list that can show attachment previews.\n *\n * `@messenger-box/platform-mobile` PlanModeView maps each row to GiftedChat\n * `{ text }` only, so an image send becomes the gateway caption\n * \"Attached: IMG_0005.jpg\" with no thumbnail. This list keeps streaming\n * markdown for the assistant and draws Manus-style previews on the user side.\n *\n * Rows stay oldest → newest (same as web). A previous `inverted` FlatList\n * reversed that order on device. The composer dock already lifts with the\n * keyboard; this list just fills the leftover space and pins to the latest\n * turn.\n */\nimport React, { memo, useCallback, useEffect, useMemo, useRef } from 'react';\nimport { FlatList, Platform, Pressable, StyleSheet, Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { dismissKeyboard } from '../../utils/keyboardController';\nimport { useKeyboardLiftHeight } from '../../components/KeyboardComposerDock';\nimport Markdown from 'react-native-markdown-display';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\nimport { displayUserMessageText, shouldRenderUserTranscriptTurn } from '../attachments/displayUserMessageText';\nimport { MessageAttachmentPreviews } from '../attachments/MessageAttachmentPreviews';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n attachments?: MessageAttachment[];\n};\n\ntype TranscriptRow = TranscriptMessage & { streaming?: boolean };\n\nconst USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n renderMessageActions,\n listContentStyle,\n isDark = false,\n}: {\n messages: TranscriptMessage[];\n streamingContent?: string;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n listContentStyle?: StyleProp<ViewStyle>;\n isDark?: boolean;\n}) {\n const listRef = useRef<FlatList<TranscriptRow>>(null);\n const stickToBottomRef = useRef(true);\n const isProgrammaticScrollRef = useRef(false);\n const ignoreScrollUntilRef = useRef(0);\n const keyboardHeight = useKeyboardLiftHeight();\n\n const historyRows = useMemo<TranscriptRow[]>(\n () =>\n messages\n .filter((msg) => msg.role !== 'user' || shouldRenderUserTranscriptTurn(msg.content, msg.attachments))\n .map((msg) => ({ ...msg, streaming: false })),\n [messages],\n );\n\n const rows = useMemo<TranscriptRow[]>(() => {\n const stream = streamingContent?.trim();\n if (!stream) return historyRows;\n return [\n ...historyRows,\n {\n id: '__streaming__',\n role: 'assistant',\n content: stream,\n streaming: true,\n },\n ];\n }, [historyRows, streamingContent]);\n\n const scrollToLatest = useCallback(\n (animated: boolean) => {\n if (rows.length === 0) return;\n isProgrammaticScrollRef.current = true;\n stickToBottomRef.current = true;\n const list = listRef.current;\n if (!list) return;\n list.scrollToEnd({ animated });\n // Tall bubbles: scrollToEnd can stop at the start of the last cell.\n list.scrollToOffset({ offset: Number.MAX_SAFE_INTEGER, animated });\n },\n [rows.length],\n );\n\n useEffect(() => {\n if (rows.length === 0 || !stickToBottomRef.current) return undefined;\n const t = setTimeout(() => scrollToLatest(false), 0);\n return () => clearTimeout(t);\n }, [rows.length, streamingContent?.length, scrollToLatest]);\n\n // Spacer and list height update on the same keyboard frame. Scroll only\n // after that shrink, or we stay on the top of a long turn (image 1).\n useEffect(() => {\n if (keyboardHeight <= 0) return undefined;\n stickToBottomRef.current = true;\n ignoreScrollUntilRef.current = Date.now() + 400;\n const frame = requestAnimationFrame(() => scrollToLatest(false));\n const afterLayout = setTimeout(() => scrollToLatest(false), 64);\n const afterKeyboard = setTimeout(() => scrollToLatest(false), 280);\n return () => {\n cancelAnimationFrame(frame);\n clearTimeout(afterLayout);\n clearTimeout(afterKeyboard);\n };\n }, [keyboardHeight, scrollToLatest]);\n\n const handleScroll = useCallback(\n (e: {\n nativeEvent: {\n contentOffset: { y: number };\n contentSize: { height: number };\n layoutMeasurement: { height: number };\n };\n }) => {\n if (Date.now() < ignoreScrollUntilRef.current) {\n stickToBottomRef.current = true;\n return;\n }\n const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;\n const nearLatest = contentOffset.y + layoutMeasurement.height >= contentSize.height - 80;\n if (!isProgrammaticScrollRef.current) {\n stickToBottomRef.current = nearLatest;\n } else if (nearLatest) {\n isProgrammaticScrollRef.current = false;\n }\n },\n [],\n );\n\n const handleContentSizeChange = useCallback(() => {\n if (!stickToBottomRef.current) return;\n scrollToLatest(false);\n }, [scrollToLatest]);\n\n const renderItem = useCallback(\n ({ item }: { item: TranscriptRow }) => (\n <TranscriptRowView\n item={item}\n isDark={isDark}\n renderMessageActions={item.streaming ? undefined : renderMessageActions}\n />\n ),\n [isDark, renderMessageActions],\n );\n\n return (\n <View style={styles.list}>\n <FlatList\n ref={listRef}\n data={rows}\n keyExtractor={(item) => item.id}\n renderItem={renderItem}\n extraData={streamingContent}\n initialNumToRender={12}\n maxToRenderPerBatch={8}\n updateCellsBatchingPeriod={50}\n windowSize={7}\n onScroll={handleScroll}\n onContentSizeChange={handleContentSizeChange}\n onLayout={() => {\n if (stickToBottomRef.current) scrollToLatest(false);\n }}\n scrollEventThrottle={16}\n keyboardShouldPersistTaps=\"handled\"\n keyboardDismissMode={Platform.OS === 'ios' ? 'interactive' : 'on-drag'}\n automaticallyAdjustKeyboardInsets={false}\n automaticallyAdjustContentInsets={false}\n contentInsetAdjustmentBehavior=\"never\"\n contentContainerStyle={[styles.listContent, listContentStyle, styles.listContentFill]}\n style={styles.list}\n />\n </View>\n );\n}\n\nconst TranscriptRowView = memo(function TranscriptRowView({\n item,\n isDark,\n renderMessageActions,\n}: {\n item: TranscriptRow;\n isDark: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n const isUser = item.role === 'user';\n const attachments = item.attachments ?? [];\n const text = isUser ? displayUserMessageText(item.content, attachments) : item.content;\n\n if (isUser) {\n return (\n <Pressable style={styles.userCol} onPress={dismissKeyboard} accessible={false}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"end\" isDark={isDark} />\n ) : null}\n {text ? (\n <View style={styles.userBubble}>\n <Text style={styles.userText}>{text}</Text>\n </View>\n ) : null}\n </Pressable>\n );\n }\n\n const markdownColor = isDark ? '#e2e8f0' : '#111827';\n const codeBg = isDark ? '#1e293b' : '#f3f4f6';\n\n return (\n <Pressable style={styles.assistantCol} onPress={dismissKeyboard} accessible={false}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"start\" isDark={isDark} />\n ) : null}\n {text ? (\n item.streaming ? (\n <Text style={[styles.assistantStreamText, { color: markdownColor }]}>{text}</Text>\n ) : (\n <Markdown\n style={{\n body: { color: markdownColor, fontSize: 15, lineHeight: 22 },\n heading1: {\n fontSize: 18,\n fontWeight: '700',\n marginTop: 12,\n marginBottom: 4,\n color: markdownColor,\n },\n heading2: {\n fontSize: 16,\n fontWeight: '700',\n marginTop: 10,\n marginBottom: 4,\n color: markdownColor,\n },\n heading3: {\n fontSize: 15,\n fontWeight: '600',\n marginTop: 8,\n marginBottom: 4,\n color: markdownColor,\n },\n strong: { fontWeight: '700' },\n paragraph: { marginTop: 4, marginBottom: 4 },\n list_item: { marginVertical: 2 },\n bullet_list: { marginVertical: 4 },\n ordered_list: { marginVertical: 4 },\n code_inline: {\n backgroundColor: codeBg,\n paddingHorizontal: 4,\n borderRadius: 4,\n fontSize: 14,\n color: markdownColor,\n },\n code_block: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n }}\n >\n {text}\n </Markdown>\n )\n ) : null}\n {renderMessageActions\n ? renderMessageActions({ id: item.id, role: 'assistant', content: item.content })\n : null}\n </Pressable>\n );\n});\n\nconst styles = StyleSheet.create({\n list: {\n flex: 1,\n },\n listContent: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingHorizontal: 8,\n },\n listContentFill: {\n flexGrow: 1,\n justifyContent: 'flex-end',\n },\n userCol: {\n width: '100%',\n alignItems: 'flex-end',\n marginBottom: 8,\n paddingHorizontal: 4,\n },\n userBubble: {\n backgroundColor: USER_BUBBLE_BG,\n borderRadius: 16,\n padding: 12,\n maxWidth: '92%',\n },\n userText: {\n color: '#ffffff',\n fontSize: 15,\n lineHeight: 22,\n },\n assistantCol: {\n width: '100%',\n alignItems: 'flex-start',\n marginBottom: 8,\n paddingHorizontal: 4,\n },\n assistantStreamText: {\n fontSize: 15,\n lineHeight: 22,\n },\n});\n"],"names":["TranscriptRowView"],"mappings":"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"}
1
+ {"version":3,"file":"ChatTranscript.js","sources":["../../../src/features/chat/ChatTranscript.tsx"],"sourcesContent":["/**\n * Chat thread list that can show attachment previews.\n *\n * `@messenger-box/platform-mobile` PlanModeView maps each row to GiftedChat\n * `{ text }` only, so an image send becomes the gateway caption\n * \"Attached: IMG_0005.jpg\" with no thumbnail. This list keeps streaming\n * markdown for the assistant and draws Manus-style previews on the user side.\n *\n * Rows stay oldest → newest (same as web). A previous `inverted` FlatList\n * reversed that order on device. The composer dock already lifts with the\n * keyboard; this list just fills the leftover space and pins to the latest\n * turn.\n */\nimport React, { memo, useCallback, useEffect, useMemo, useRef } from 'react';\nimport { FlatList, Platform, Pressable, StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { dismissKeyboard } from '../../utils/keyboardController';\nimport { useKeyboardLiftHeight } from '../../components/KeyboardComposerDock';\nimport Markdown from 'react-native-markdown-display';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\nimport { displayUserMessageText, shouldRenderUserTranscriptTurn } from '../attachments/displayUserMessageText';\nimport { MessageAttachmentPreviews } from '../attachments/MessageAttachmentPreviews';\nimport { ToolTimeline } from './ToolTimeline';\nimport { parseToolActivity } from './toolActivity';\nimport { parseAskUser, stripAskUser, displayAskUserAssistantText } from './askUser';\nimport { collapseDuplicateUserRows, groupConsecutiveRoleRows, type RoleGroup } from './transcriptGroups';\nimport { closeOpenMarkdownFences } from './streamingMarkdown';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n attachments?: MessageAttachment[];\n};\n\ntype TranscriptRow = TranscriptMessage & { streaming?: boolean; askAnswered?: boolean };\ntype TranscriptGroup = RoleGroup<TranscriptRow>;\n\nconst USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n renderMessageActions,\n listContentStyle,\n isDark = false,\n}: {\n messages: TranscriptMessage[];\n streamingContent?: string;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n listContentStyle?: StyleProp<ViewStyle>;\n isDark?: boolean;\n}) {\n const listRef = useRef<FlatList<TranscriptGroup>>(null);\n const stickToBottomRef = useRef(true);\n const isProgrammaticScrollRef = useRef(false);\n const ignoreScrollUntilRef = useRef(0);\n const keyboardHeight = useKeyboardLiftHeight();\n\n const historyRows = useMemo<TranscriptRow[]>(() => {\n const rows: TranscriptRow[] = [];\n messages.forEach((msg, index) => {\n if (msg.role === 'user') {\n if (shouldRenderUserTranscriptTurn(msg.content, msg.attachments)) {\n rows.push({ ...msg, streaming: false });\n }\n return;\n }\n if (msg.role !== 'assistant') {\n rows.push({ ...msg, streaming: false });\n return;\n }\n const askAnswered =\n Boolean(parseAskUser(msg.content)) && messages.slice(index + 1).some((m) => m.role === 'user');\n const visible = displayAskUserAssistantText(msg.content, askAnswered).trim();\n const tools = parseToolActivity(stripAskUser(msg.content), false);\n const hasAsk = Boolean(parseAskUser(msg.content));\n if (hasAsk && !visible && tools.steps.length === 0 && !(msg.attachments?.length ?? 0)) {\n return;\n }\n if (!hasAsk) {\n const leftover = stripAskUser(msg.content).trim();\n if (!leftover && !(msg.attachments?.length ?? 0)) return;\n }\n rows.push({ ...msg, streaming: false, askAnswered });\n });\n return rows;\n }, [messages]);\n\n const rows = useMemo<TranscriptRow[]>(() => {\n const history = collapseDuplicateUserRows(historyRows);\n const rawStream = streamingContent ?? '';\n const last = history[history.length - 1];\n const streamAskAnswered = last?.role === 'user' && Boolean(parseAskUser(rawStream));\n const streamProse = displayAskUserAssistantText(rawStream, streamAskAnswered).trim();\n const streamTools = parseToolActivity(stripAskUser(rawStream), true);\n if (!streamProse && streamTools.steps.length === 0) return history;\n return [\n ...history,\n {\n id: '__streaming__',\n role: 'assistant',\n content: rawStream,\n streaming: true,\n askAnswered: streamAskAnswered,\n },\n ];\n }, [historyRows, streamingContent]);\n\n const groups = useMemo(() => groupConsecutiveRoleRows(rows), [rows]);\n\n const scrollToLatest = useCallback(\n (animated: boolean) => {\n if (groups.length === 0) return;\n isProgrammaticScrollRef.current = true;\n stickToBottomRef.current = true;\n const list = listRef.current;\n if (!list) return;\n list.scrollToEnd({ animated });\n // Tall bubbles: scrollToEnd can stop at the start of the last cell.\n list.scrollToOffset({ offset: Number.MAX_SAFE_INTEGER, animated });\n },\n [groups.length],\n );\n\n useEffect(() => {\n if (groups.length === 0 || !stickToBottomRef.current) return undefined;\n const t = setTimeout(() => scrollToLatest(false), 0);\n return () => clearTimeout(t);\n }, [groups.length, streamingContent?.length, scrollToLatest]);\n\n // Spacer and list height update on the same keyboard frame. Scroll only\n // after that shrink, or we stay on the top of a long turn (image 1).\n useEffect(() => {\n if (keyboardHeight <= 0) return undefined;\n stickToBottomRef.current = true;\n ignoreScrollUntilRef.current = Date.now() + 400;\n const frame = requestAnimationFrame(() => scrollToLatest(false));\n const afterLayout = setTimeout(() => scrollToLatest(false), 64);\n const afterKeyboard = setTimeout(() => scrollToLatest(false), 280);\n return () => {\n cancelAnimationFrame(frame);\n clearTimeout(afterLayout);\n clearTimeout(afterKeyboard);\n };\n }, [keyboardHeight, scrollToLatest]);\n\n const handleScroll = useCallback(\n (e: {\n nativeEvent: {\n contentOffset: { y: number };\n contentSize: { height: number };\n layoutMeasurement: { height: number };\n };\n }) => {\n if (Date.now() < ignoreScrollUntilRef.current) {\n stickToBottomRef.current = true;\n return;\n }\n const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;\n const nearLatest = contentOffset.y + layoutMeasurement.height >= contentSize.height - 80;\n if (!isProgrammaticScrollRef.current) {\n stickToBottomRef.current = nearLatest;\n } else if (nearLatest) {\n isProgrammaticScrollRef.current = false;\n }\n },\n [],\n );\n\n const handleContentSizeChange = useCallback(() => {\n if (!stickToBottomRef.current) return;\n scrollToLatest(false);\n }, [scrollToLatest]);\n\n const renderItem = useCallback(\n ({ item }: { item: TranscriptGroup }) => (\n <TranscriptGroupView group={item} isDark={isDark} renderMessageActions={renderMessageActions} />\n ),\n [isDark, renderMessageActions],\n );\n\n return (\n <View style={styles.list}>\n <FlatList\n ref={listRef}\n data={groups}\n keyExtractor={(item) => item.id}\n renderItem={renderItem}\n extraData={streamingContent}\n initialNumToRender={12}\n maxToRenderPerBatch={8}\n updateCellsBatchingPeriod={50}\n windowSize={7}\n onScroll={handleScroll}\n onContentSizeChange={handleContentSizeChange}\n onLayout={() => {\n if (stickToBottomRef.current) scrollToLatest(false);\n }}\n scrollEventThrottle={16}\n keyboardShouldPersistTaps=\"handled\"\n keyboardDismissMode={Platform.OS === 'ios' ? 'interactive' : 'on-drag'}\n automaticallyAdjustKeyboardInsets={false}\n automaticallyAdjustContentInsets={false}\n contentInsetAdjustmentBehavior=\"never\"\n contentContainerStyle={[styles.listContent, listContentStyle, styles.listContentFill]}\n style={styles.list}\n />\n </View>\n );\n}\n\nconst TranscriptGroupView = memo(function TranscriptGroupView({\n group,\n isDark,\n renderMessageActions,\n}: {\n group: TranscriptGroup;\n isDark: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n if (group.role === 'user') {\n return (\n <Pressable style={styles.userCol} onPress={dismissKeyboard} accessible={false}>\n {group.items.map((item, index) => {\n const attachments = item.attachments ?? [];\n const text = displayUserMessageText(item.content, attachments);\n if (!text && attachments.length === 0) return null;\n return (\n <View key={item.id} style={[styles.userTurn, index > 0 && styles.userFollow]}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"end\" isDark={isDark} />\n ) : null}\n {text ? (\n <View style={styles.userBubble}>\n <Markdown style={USER_MARKDOWN}>{text}</Markdown>\n </View>\n ) : null}\n </View>\n );\n })}\n </Pressable>\n );\n }\n\n const markdownColor = isDark ? '#e2e8f0' : '#111827';\n const codeBg = isDark ? '#1e293b' : '#f3f4f6';\n const last = group.items[group.items.length - 1];\n\n return (\n <View style={styles.assistantGroup}>\n {group.items.map((item, index) => (\n <AssistantTurnView\n key={item.id}\n item={item}\n isDark={isDark}\n markdownColor={markdownColor}\n codeBg={codeBg}\n tightTop={index > 0}\n renderMessageActions={item.streaming || item !== last ? undefined : renderMessageActions}\n />\n ))}\n </View>\n );\n});\n\nconst AssistantTurnView = memo(function AssistantTurnView({\n item,\n isDark,\n markdownColor,\n codeBg,\n tightTop,\n renderMessageActions,\n}: {\n item: TranscriptRow;\n isDark: boolean;\n markdownColor: string;\n codeBg: string;\n tightTop: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n const attachments = item.attachments ?? [];\n const stripped = stripAskUser(item.content);\n const toolActivity = parseToolActivity(stripped, !!item.streaming);\n const text = parseToolActivity(\n displayAskUserAssistantText(item.content, !!item.askAnswered),\n !!item.streaming,\n ).text;\n\n return (\n <Pressable\n style={[styles.assistantCol, tightTop && styles.assistantColTight]}\n onPress={dismissKeyboard}\n accessible={false}\n >\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"start\" isDark={isDark} />\n ) : null}\n {toolActivity.steps.length > 0 ? (\n <ToolTimeline steps={toolActivity.steps} active={!!item.streaming} isDark={isDark} />\n ) : null}\n {text ? (\n <Markdown style={assistantMarkdownStyle(markdownColor, codeBg)}>\n {item.streaming ? closeOpenMarkdownFences(text) : text}\n </Markdown>\n ) : null}\n {renderMessageActions\n ? renderMessageActions({ id: item.id, role: 'assistant', content: item.content })\n : null}\n </Pressable>\n );\n});\n\nconst USER_MARKDOWN = {\n body: { color: '#ffffff', fontSize: 15, lineHeight: 22 },\n paragraph: { marginTop: 4, marginBottom: 4, color: '#ffffff' },\n strong: { fontWeight: '700' as const, color: '#ffffff' },\n em: { color: '#ffffff' },\n text: { color: '#ffffff' },\n bullet_list: { marginVertical: 4 },\n ordered_list: { marginVertical: 4 },\n list_item: { marginVertical: 2, color: '#ffffff' },\n};\n\nfunction assistantMarkdownStyle(markdownColor: string, codeBg: string) {\n return {\n body: { color: markdownColor, fontSize: 15, lineHeight: 22 },\n heading1: {\n fontSize: 18,\n fontWeight: '700' as const,\n marginTop: 12,\n marginBottom: 4,\n color: markdownColor,\n },\n heading2: {\n fontSize: 16,\n fontWeight: '700' as const,\n marginTop: 10,\n marginBottom: 4,\n color: markdownColor,\n },\n heading3: {\n fontSize: 15,\n fontWeight: '600' as const,\n marginTop: 8,\n marginBottom: 4,\n color: markdownColor,\n },\n strong: { fontWeight: '700' as const },\n paragraph: { marginTop: 4, marginBottom: 4 },\n list_item: { marginVertical: 2 },\n bullet_list: { marginVertical: 4 },\n ordered_list: { marginVertical: 4 },\n code_inline: {\n backgroundColor: codeBg,\n paddingHorizontal: 4,\n borderRadius: 4,\n fontSize: 14,\n color: markdownColor,\n },\n code_block: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n fence: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n };\n}\n\nconst styles = StyleSheet.create({\n list: {\n flex: 1,\n },\n listContent: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingHorizontal: 8,\n },\n listContentFill: {\n flexGrow: 1,\n justifyContent: 'flex-end',\n },\n userCol: {\n width: '100%',\n alignItems: 'flex-end',\n marginBottom: 12,\n paddingHorizontal: 4,\n },\n userTurn: {\n maxWidth: '92%',\n alignItems: 'flex-end',\n },\n userFollow: {\n marginTop: 4,\n },\n userBubble: {\n backgroundColor: USER_BUBBLE_BG,\n borderRadius: 16,\n paddingHorizontal: 12,\n paddingVertical: 8,\n },\n assistantGroup: {\n width: '100%',\n marginBottom: 4,\n },\n assistantCol: {\n width: '100%',\n alignItems: 'flex-start',\n marginBottom: 8,\n paddingHorizontal: 4,\n },\n assistantColTight: {\n marginTop: -4,\n },\n});\n"],"names":["rows","TranscriptGroupView","AssistantTurnView"],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ import {jsxs,jsx}from'react/jsx-runtime';import {MaterialCommunityIcons}from'@expo/vector-icons';import {memo,useState,useRef,useEffect}from'react';import {View,StyleSheet,Pressable,ActivityIndicator,Text}from'react-native';const ACCENT = "#2563eb";
2
+ const DONE = "#10b981";
3
+ function formatElapsed(ms) {
4
+ const s = Math.round(ms / 1e3);
5
+ if (s < 60) return `${s}s`;
6
+ const m = Math.floor(s / 60);
7
+ return `${m}m ${s % 60}s`;
8
+ }
9
+ const ToolTimeline = memo(function ToolTimeline2({
10
+ steps,
11
+ active,
12
+ isDark = false
13
+ }) {
14
+ const [open, setOpen] = useState(active);
15
+ const startedAtRef = useRef(null);
16
+ const [elapsed, setElapsed] = useState(0);
17
+ useEffect(() => {
18
+ if (active) {
19
+ if (startedAtRef.current === null) startedAtRef.current = Date.now();
20
+ const tick = () => {
21
+ var _a;
22
+ return setElapsed(Date.now() - ((_a = startedAtRef.current) != null ? _a : Date.now()));
23
+ };
24
+ tick();
25
+ const timer = setInterval(tick, 1e3);
26
+ return () => clearInterval(timer);
27
+ }
28
+ setOpen(false);
29
+ return void 0;
30
+ }, [active]);
31
+ if (steps.length === 0) return null;
32
+ const border = isDark ? "#1f2937" : "#e5e7eb";
33
+ const bg = isDark ? "#0b1220" : "#f9fafb";
34
+ const muted = isDark ? "#94a3b8" : "#6b7280";
35
+ const strong = isDark ? "#e2e8f0" : "#111827";
36
+ const count = steps.length;
37
+ const summary = active ? `Working \xB7 ${formatElapsed(elapsed)}` : `Used ${count} tool${count === 1 ? "" : "s"}${elapsed ? ` \xB7 ${formatElapsed(elapsed)}` : ""}`;
38
+ return /* @__PURE__ */ jsxs(View, { style: [styles.wrap, {
39
+ borderColor: border,
40
+ backgroundColor: bg
41
+ }], children: [
42
+ /* @__PURE__ */ jsxs(Pressable, { accessibilityRole: "button", accessibilityState: {
43
+ expanded: open
44
+ }, accessibilityLabel: summary, onPress: () => setOpen((v) => !v), style: ({
45
+ pressed
46
+ }) => [styles.header, {
47
+ opacity: pressed ? 0.7 : 1
48
+ }], children: [
49
+ active ? /* @__PURE__ */ jsx(ActivityIndicator, { size: "small", color: ACCENT, style: styles.headerIcon }) : /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "wrench-outline", size: 14, color: muted, style: styles.headerIcon }),
50
+ /* @__PURE__ */ jsx(Text, { style: [styles.summary, {
51
+ color: muted
52
+ }], numberOfLines: 1, children: summary }),
53
+ /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: open ? "chevron-down" : "chevron-right", size: 18, color: muted })
54
+ ] }),
55
+ open ? /* @__PURE__ */ jsx(View, { style: [styles.list, {
56
+ borderTopColor: border
57
+ }], children: steps.map((step) => /* @__PURE__ */ jsxs(View, { style: styles.step, children: [
58
+ step.status === "running" ? /* @__PURE__ */ jsx(ActivityIndicator, { size: "small", color: ACCENT, style: styles.stepIcon }) : /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "check", size: 13, color: DONE, style: styles.stepIcon }),
59
+ /* @__PURE__ */ jsx(Text, { style: [styles.stepLabel, {
60
+ color: step.status === "running" ? strong : muted
61
+ }], numberOfLines: 1, children: step.label }),
62
+ step.detail ? /* @__PURE__ */ jsxs(Text, { style: [styles.stepDetail, {
63
+ color: muted
64
+ }], numberOfLines: 1, children: [
65
+ "\xB7 ",
66
+ step.detail
67
+ ] }) : null
68
+ ] }, step.id)) }) : null
69
+ ] });
70
+ });
71
+ const styles = StyleSheet.create({
72
+ wrap: {
73
+ width: "100%",
74
+ borderWidth: 1,
75
+ borderRadius: 10,
76
+ marginBottom: 8,
77
+ overflow: "hidden"
78
+ },
79
+ header: {
80
+ flexDirection: "row",
81
+ alignItems: "center",
82
+ paddingHorizontal: 10,
83
+ minHeight: 44
84
+ // touch target
85
+ },
86
+ headerIcon: {
87
+ width: 18,
88
+ marginRight: 6
89
+ },
90
+ summary: {
91
+ flex: 1,
92
+ fontSize: 12,
93
+ fontWeight: "600"
94
+ },
95
+ list: {
96
+ borderTopWidth: 1,
97
+ paddingHorizontal: 10,
98
+ paddingVertical: 6
99
+ },
100
+ step: {
101
+ flexDirection: "row",
102
+ alignItems: "center",
103
+ paddingVertical: 3
104
+ },
105
+ stepIcon: {
106
+ width: 16,
107
+ marginRight: 6
108
+ },
109
+ stepLabel: {
110
+ fontSize: 12,
111
+ flexShrink: 1
112
+ },
113
+ stepDetail: {
114
+ fontSize: 12,
115
+ marginLeft: 4,
116
+ flexShrink: 1
117
+ }
118
+ });export{ToolTimeline,ToolTimeline as default};//# sourceMappingURL=ToolTimeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolTimeline.js","sources":["../../../src/features/chat/ToolTimeline.tsx"],"sourcesContent":["/**\n * <ToolTimeline> — the agent's tool activity, as a collapsible strip.\n *\n * The cdecli agent narrates tool use into the streamed assistant text as bare\n * `⚙ ToolSearch` rows. Rendered raw they read as debug noise — which is exactly\n * what mobile was showing. {@link ./toolActivity} pulls them out of the prose;\n * this draws them.\n *\n * Same behaviour as the web timeline (auto-expand while working, last step\n * spins, collapse to \"Used N tools\" when done), rebuilt for touch: a full-width\n * 44pt header row instead of a hover-styled button, ActivityIndicator instead of\n * a CSS spin, and no title tooltips (there is nowhere to hover) so details\n * truncate in place.\n */\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport React, { memo, useEffect, useRef, useState } from 'react';\nimport { ActivityIndicator, Pressable, StyleSheet, Text, View } from 'react-native';\nimport type { ToolStep } from './toolActivity';\n\nconst ACCENT = '#2563eb';\nconst DONE = '#10b981';\n\nfunction formatElapsed(ms: number): string {\n const s = Math.round(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n return `${m}m ${s % 60}s`;\n}\n\nexport const ToolTimeline = memo(function ToolTimeline({\n steps,\n active,\n isDark = false,\n}: {\n steps: ToolStep[];\n /** True while the turn is still streaming: drives the spinner and the timer. */\n active: boolean;\n isDark?: boolean;\n}) {\n // Expanded while working so live progress is visible; collapses once done.\n const [open, setOpen] = useState(active);\n // Captured on first activity, frozen at completion. Ref-held so re-renders\n // do not restart it.\n const startedAtRef = useRef<number | null>(null);\n const [elapsed, setElapsed] = useState(0);\n\n useEffect(() => {\n if (active) {\n if (startedAtRef.current === null) startedAtRef.current = Date.now();\n const tick = () => setElapsed(Date.now() - (startedAtRef.current ?? Date.now()));\n tick();\n const timer = setInterval(tick, 1000);\n return () => clearInterval(timer);\n }\n setOpen(false);\n return undefined;\n }, [active]);\n\n if (steps.length === 0) return null;\n\n const border = isDark ? '#1f2937' : '#e5e7eb';\n const bg = isDark ? '#0b1220' : '#f9fafb';\n const muted = isDark ? '#94a3b8' : '#6b7280';\n const strong = isDark ? '#e2e8f0' : '#111827';\n\n const count = steps.length;\n const summary = active\n ? `Working · ${formatElapsed(elapsed)}`\n : `Used ${count} tool${count === 1 ? '' : 's'}${elapsed ? ` · ${formatElapsed(elapsed)}` : ''}`;\n\n return (\n <View style={[styles.wrap, { borderColor: border, backgroundColor: bg }]}>\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ expanded: open }}\n accessibilityLabel={summary}\n onPress={() => setOpen((v) => !v)}\n style={({ pressed }) => [styles.header, { opacity: pressed ? 0.7 : 1 }]}\n >\n {active ? (\n <ActivityIndicator size=\"small\" color={ACCENT} style={styles.headerIcon} />\n ) : (\n <MaterialCommunityIcons name=\"wrench-outline\" size={14} color={muted} style={styles.headerIcon} />\n )}\n <Text style={[styles.summary, { color: muted }]} numberOfLines={1}>\n {summary}\n </Text>\n <MaterialCommunityIcons name={open ? 'chevron-down' : 'chevron-right'} size={18} color={muted} />\n </Pressable>\n\n {open ? (\n <View style={[styles.list, { borderTopColor: border }]}>\n {steps.map((step) => (\n <View key={step.id} style={styles.step}>\n {step.status === 'running' ? (\n <ActivityIndicator size=\"small\" color={ACCENT} style={styles.stepIcon} />\n ) : (\n <MaterialCommunityIcons name=\"check\" size={13} color={DONE} style={styles.stepIcon} />\n )}\n <Text\n style={[styles.stepLabel, { color: step.status === 'running' ? strong : muted }]}\n numberOfLines={1}\n >\n {step.label}\n </Text>\n {step.detail ? (\n <Text style={[styles.stepDetail, { color: muted }]} numberOfLines={1}>\n · {step.detail}\n </Text>\n ) : null}\n </View>\n ))}\n </View>\n ) : null}\n </View>\n );\n});\n\nconst styles = StyleSheet.create({\n wrap: {\n width: '100%',\n borderWidth: 1,\n borderRadius: 10,\n marginBottom: 8,\n overflow: 'hidden',\n },\n header: {\n flexDirection: 'row',\n alignItems: 'center',\n paddingHorizontal: 10,\n minHeight: 44, // touch target\n },\n headerIcon: { width: 18, marginRight: 6 },\n summary: { flex: 1, fontSize: 12, fontWeight: '600' },\n list: { borderTopWidth: 1, paddingHorizontal: 10, paddingVertical: 6 },\n step: { flexDirection: 'row', alignItems: 'center', paddingVertical: 3 },\n stepIcon: { width: 16, marginRight: 6 },\n stepLabel: { fontSize: 12, flexShrink: 1 },\n stepDetail: { fontSize: 12, marginLeft: 4, flexShrink: 1 },\n});\n\nexport default ToolTimeline;\n"],"names":["ToolTimeline"],"mappings":"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"}