@adminide-stack/yantra-mobile 12.0.80-alpha.0 → 12.0.80-alpha.2

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.
@@ -1,4 +1,4 @@
1
- import {jsx,Fragment,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';import {ToolTimeline}from'./ToolTimeline.js';import {ChatWidgetCard}from'./ChatWidgetCard.js';import {splitChatWidgets}from'./chatWidgets.js';import {parseToolActivity}from'./toolActivity.js';import {parseAskUser,displayAskUserAssistantText,stripAskUser}from'./askUser.js';import {collapseDuplicateUserRows,groupConsecutiveRoleRows}from'./transcriptGroups.js';import {AssistantMarkdown}from'./AssistantMarkdown.js';var __defProp = Object.defineProperty;
1
+ import {jsx,Fragment,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';import {ToolTimeline}from'./ToolTimeline.js';import {ChatWidgetCard}from'./ChatWidgetCard.js';import {splitChatWidgets}from'./chatWidgets.js';import {parseToolActivity}from'./toolActivity.js';import {parseAskUser,displayAskUserAssistantText,stripAskUser}from'./askUser.js';import {collapseDuplicateUserRows,groupConsecutiveRoleRows}from'./transcriptGroups.js';import {AssistantMarkdown}from'./AssistantMarkdown.js';import {PreResponseStrip}from'./PreResponseStrip.js';import {splitPreResponse}from'./preResponse.js';var __defProp = Object.defineProperty;
2
2
  var __defProps = Object.defineProperties;
3
3
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
@@ -21,6 +21,7 @@ const USER_BUBBLE_BG = "#2563eb";
21
21
  function ChatTranscript({
22
22
  messages,
23
23
  streamingContent,
24
+ quickReply,
24
25
  renderMessageActions,
25
26
  listContentStyle,
26
27
  listFooter,
@@ -34,7 +35,7 @@ function ChatTranscript({
34
35
  const historyRows = useMemo(() => {
35
36
  const rows2 = [];
36
37
  messages.forEach((msg, index) => {
37
- var _a, _b, _c, _d;
38
+ var _a, _b;
38
39
  if (msg.role === "user") {
39
40
  if (shouldRenderUserTranscriptTurn(msg.content, msg.attachments)) {
40
41
  rows2.push(__spreadProps(__spreadValues({}, msg), {
@@ -49,18 +50,25 @@ function ChatTranscript({
49
50
  }));
50
51
  return;
51
52
  }
52
- const askAnswered = Boolean(parseAskUser(msg.content)) && messages.slice(index + 1).some((m) => m.role === "user");
53
- const visible = displayAskUserAssistantText(msg.content, askAnswered).trim();
54
- const tools = parseToolActivity(stripAskUser(msg.content), false);
55
- const hasAsk = Boolean(parseAskUser(msg.content));
56
- if (hasAsk && !visible && tools.steps.length === 0 && !((_b = (_a = msg.attachments) == null ? void 0 : _a.length) != null ? _b : 0)) {
53
+ const {
54
+ preResponse,
55
+ text: content
56
+ } = splitPreResponse(msg.content);
57
+ const hasAttachments = ((_b = (_a = msg.attachments) == null ? void 0 : _a.length) != null ? _b : 0) > 0;
58
+ const askAnswered = Boolean(parseAskUser(content)) && messages.slice(index + 1).some((m) => m.role === "user");
59
+ const visible = displayAskUserAssistantText(content, askAnswered).trim();
60
+ const tools = parseToolActivity(stripAskUser(content), false);
61
+ const hasAsk = Boolean(parseAskUser(content));
62
+ if (hasAsk && !visible && tools.steps.length === 0 && !hasAttachments && !preResponse) {
57
63
  return;
58
64
  }
59
65
  if (!hasAsk) {
60
- const leftover = stripAskUser(msg.content).trim();
61
- if (!leftover && !((_d = (_c = msg.attachments) == null ? void 0 : _c.length) != null ? _d : 0)) return;
66
+ const leftover = stripAskUser(content).trim();
67
+ if (!leftover && !hasAttachments && !preResponse) return;
62
68
  }
63
69
  rows2.push(__spreadProps(__spreadValues({}, msg), {
70
+ content,
71
+ preResponse,
64
72
  streaming: false,
65
73
  askAnswered
66
74
  }));
@@ -69,13 +77,17 @@ function ChatTranscript({
69
77
  }, [messages]);
70
78
  const rows = useMemo(() => {
71
79
  const history = collapseDuplicateUserRows(historyRows);
72
- const rawStream = streamingContent != null ? streamingContent : "";
80
+ const {
81
+ preResponse: streamPre,
82
+ text: rawStream
83
+ } = splitPreResponse(streamingContent != null ? streamingContent : "");
84
+ const livePre = (quickReply == null ? void 0 : quickReply.trim()) || streamPre;
73
85
  const last = history[history.length - 1];
74
86
  const streamAskAnswered = (last == null ? void 0 : last.role) === "user" && Boolean(parseAskUser(rawStream));
75
87
  const streamProse = displayAskUserAssistantText(rawStream, streamAskAnswered).trim();
76
88
  const streamTools = parseToolActivity(stripAskUser(rawStream), true);
77
- if (!streamProse && streamTools.steps.length === 0) return history;
78
- if ((last == null ? void 0 : last.role) === "assistant") {
89
+ if (!streamProse && streamTools.steps.length === 0 && !livePre) return history;
90
+ if ((last == null ? void 0 : last.role) === "assistant" && (!livePre || last.preResponse === livePre)) {
79
91
  const lastText = last.content.trim();
80
92
  const streamText = rawStream.trim();
81
93
  if (lastText === streamText || lastText.startsWith(streamText) || streamText.startsWith(lastText)) {
@@ -86,10 +98,11 @@ function ChatTranscript({
86
98
  id: "__streaming__",
87
99
  role: "assistant",
88
100
  content: rawStream,
101
+ preResponse: livePre,
89
102
  streaming: true,
90
103
  askAnswered: streamAskAnswered
91
104
  }];
92
- }, [historyRows, streamingContent]);
105
+ }, [historyRows, streamingContent, quickReply]);
93
106
  const groups = useMemo(() => groupConsecutiveRoleRows(rows), [rows]);
94
107
  const scrollToLatest = useCallback((animated) => {
95
108
  if (groups.length === 0) return;
@@ -105,7 +118,7 @@ function ChatTranscript({
105
118
  if (groups.length === 0 || !stickToBottomRef.current) return void 0;
106
119
  const t = setTimeout(() => scrollToLatest(false), 0);
107
120
  return () => clearTimeout(t);
108
- }, [groups.length, streamingContent == null ? void 0 : streamingContent.length, scrollToLatest]);
121
+ }, [groups.length, streamingContent == null ? void 0 : streamingContent.length, quickReply == null ? void 0 : quickReply.length, scrollToLatest]);
109
122
  useEffect(() => {
110
123
  if (keyboardHeight <= 0) return void 0;
111
124
  stickToBottomRef.current = true;
@@ -143,7 +156,7 @@ function ChatTranscript({
143
156
  const renderItem = useCallback(({
144
157
  item
145
158
  }) => /* @__PURE__ */ jsx(TranscriptGroupView, { group: item, isDark, renderMessageActions }), [isDark, renderMessageActions]);
146
- return /* @__PURE__ */ jsx(View, { style: styles.list, children: /* @__PURE__ */ jsx(FlatList, { ref: listRef, data: groups, keyExtractor: (item) => item.id, renderItem, extraData: `${streamingContent != null ? streamingContent : ""}:${Boolean(listFooter)}`, ListFooterComponent: listFooter ? /* @__PURE__ */ jsx(Fragment, { children: listFooter }) : null, initialNumToRender: 12, maxToRenderPerBatch: 8, updateCellsBatchingPeriod: 50, windowSize: 21, scrollEnabled: true, nestedScrollEnabled: false, directionalLockEnabled: true, onScroll: handleScroll, onContentSizeChange: handleContentSizeChange, onLayout: () => {
159
+ return /* @__PURE__ */ jsx(View, { style: styles.list, children: /* @__PURE__ */ jsx(FlatList, { ref: listRef, data: groups, keyExtractor: (item) => item.id, renderItem, extraData: `${streamingContent != null ? streamingContent : ""}:${quickReply != null ? quickReply : ""}:${Boolean(listFooter)}`, ListFooterComponent: listFooter ? /* @__PURE__ */ jsx(Fragment, { children: listFooter }) : null, initialNumToRender: 12, maxToRenderPerBatch: 8, updateCellsBatchingPeriod: 50, windowSize: 21, scrollEnabled: true, nestedScrollEnabled: false, directionalLockEnabled: true, onScroll: handleScroll, onContentSizeChange: handleContentSizeChange, onLayout: () => {
147
160
  if (stickToBottomRef.current) scrollToLatest(false);
148
161
  }, scrollEventThrottle: 16, keyboardShouldPersistTaps: "handled", keyboardDismissMode: Platform.OS === "ios" ? "interactive" : "on-drag", automaticallyAdjustKeyboardInsets: false, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", contentContainerStyle: [styles.listContent, listContentStyle, !listFooter && styles.listContentFill], style: styles.list }) });
149
162
  }
@@ -184,15 +197,18 @@ const AssistantTurnView = memo(function AssistantTurnView2({
184
197
  text,
185
198
  widgets
186
199
  } = splitChatWidgets(parseToolActivity(displayAskUserAssistantText(item.content, !!item.askAnswered), !!item.streaming).text, !!item.streaming);
200
+ const preResponseOpen = item.streaming ? !text.trim() : !text.trim() && toolActivity.steps.length === 0 && widgets.length === 0 && attachments.length === 0;
187
201
  return /* @__PURE__ */ jsxs(View, { style: [styles.assistantCol, tightTop && styles.assistantColTight], children: [
188
202
  attachments.length > 0 ? /* @__PURE__ */ jsx(MessageAttachmentPreviews, { attachments, align: "start", isDark }) : null,
203
+ item.preResponse ? /* @__PURE__ */ jsx(PreResponseStrip, { text: item.preResponse, expanded: preResponseOpen, live: !!item.streaming, isDark }) : null,
189
204
  toolActivity.steps.length > 0 ? /* @__PURE__ */ jsx(ToolTimeline, { steps: toolActivity.steps, active: !!item.streaming, isDark }) : null,
190
205
  text ? /* @__PURE__ */ jsx(AssistantMarkdown, { text, streaming: !!item.streaming, isDark }) : null,
191
206
  widgets.map((widget, i) => /* @__PURE__ */ jsx(ChatWidgetCard, { widget, isDark }, `${widget.component}-${i}`)),
192
207
  renderMessageActions ? renderMessageActions({
193
208
  id: item.id,
194
209
  role: "assistant",
195
- content: item.content
210
+ // Copy and share take what the user reads, not the wire markup.
211
+ content: text.trim() || item.preResponse || item.content
196
212
  }) : null
197
213
  ] });
198
214
  });
@@ -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, type ReactNode } 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';\nimport { ToolTimeline } from './ToolTimeline';\nimport { ChatWidgetCard } from './ChatWidgetCard';\nimport { splitChatWidgets } from './chatWidgets';\nimport { parseToolActivity } from './toolActivity';\nimport { parseAskUser, stripAskUser, displayAskUserAssistantText } from './askUser';\nimport { collapseDuplicateUserRows, groupConsecutiveRoleRows, type RoleGroup } from './transcriptGroups';\nimport { AssistantMarkdown } from './AssistantMarkdown';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n displayContent?: string;\n attachments?: MessageAttachment[];\n /** Typed while the gateway was down: shown in place, waiting on the outbox. */\n queued?: boolean;\n};\n\ntype TranscriptRow = TranscriptMessage & { streaming?: boolean; askAnswered?: boolean };\ntype TranscriptGroup = RoleGroup<TranscriptRow>;\n\nexport const USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n renderMessageActions,\n listContentStyle,\n listFooter,\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 listFooter?: ReactNode;\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 if (last?.role === 'assistant') {\n const lastText = last.content.trim();\n const streamText = rawStream.trim();\n if (lastText === streamText || lastText.startsWith(streamText) || streamText.startsWith(lastText)) {\n return history;\n }\n }\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 },\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 ?? ''}:${Boolean(listFooter)}`}\n ListFooterComponent={listFooter ? <>{listFooter}</> : null}\n initialNumToRender={12}\n maxToRenderPerBatch={8}\n updateCellsBatchingPeriod={50}\n windowSize={21}\n scrollEnabled\n nestedScrollEnabled={false}\n directionalLockEnabled\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, !listFooter && 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.displayContent ?? 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, item.queued && styles.userBubbleQueued]}>\n <Markdown style={USER_MARKDOWN}>{text}</Markdown>\n </View>\n ) : null}\n {item.queued ? (\n <Text style={[styles.queuedCaption, { color: isDark ? '#94a3b8' : '#64748b' }]}>\n Queued · sends when the gateway connects\n </Text>\n ) : null}\n </View>\n );\n })}\n </Pressable>\n );\n }\n\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 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 tightTop,\n renderMessageActions,\n}: {\n item: TranscriptRow;\n isDark: boolean;\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 // Widget envelopes become native cards below the text, never raw JSON.\n const { text, widgets } = splitChatWidgets(\n parseToolActivity(displayAskUserAssistantText(item.content, !!item.askAnswered), !!item.streaming).text,\n !!item.streaming,\n );\n\n return (\n <View style={[styles.assistantCol, tightTop && styles.assistantColTight]}>\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 ? <AssistantMarkdown text={text} streaming={!!item.streaming} isDark={isDark} /> : null}\n {widgets.map((widget, i) => (\n <ChatWidgetCard key={`${widget.component}-${i}`} widget={widget} isDark={isDark} />\n ))}\n {renderMessageActions\n ? renderMessageActions({ id: item.id, role: 'assistant', content: item.content })\n : null}\n </View>\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\nconst styles = StyleSheet.create({\n list: {\n flex: 1,\n minHeight: 0,\n },\n listContent: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingHorizontal: 8,\n },\n listContentFill: {\n flexGrow: 1,\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 // Same bubble, held back: a queued turn reads as pending, not as delivered.\n userBubbleQueued: {\n opacity: 0.55,\n },\n queuedCaption: {\n fontSize: 11,\n fontWeight: '500',\n marginTop: 4,\n marginRight: 2,\n },\n assistantGroup: {\n width: '100%',\n marginBottom: 4,\n },\n assistantCol: {\n width: '100%',\n flexGrow: 0,\n alignItems: 'stretch',\n marginBottom: 8,\n paddingHorizontal: 4,\n },\n assistantColTight: {\n marginTop: -4,\n },\n});\n"],"names":["rows","TranscriptGroupView","AssistantTurnView"],"mappings":";;;;;;;;;;;;;;;;;;;AA0CO,MAAM,cAAiB,GAAA;AACvB,SAAS,cAAe,CAAA;AAAA,EAC7B,QAAA;AAAA,EACA,gBAAA;AAAA,EACA,oBAAA;AAAA,EACA,gBAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAS,GAAA;AACX,CAWG,EAAA;AACD,EAAM,MAAA,OAAA,GAAU,OAAkC,IAAI,CAAA;AACtD,EAAM,MAAA,gBAAA,GAAmB,OAAO,IAAI,CAAA;AACpC,EAAM,MAAA,uBAAA,GAA0B,OAAO,KAAK,CAAA;AAC5C,EAAM,MAAA,oBAAA,GAAuB,OAAO,CAAC,CAAA;AACrC,EAAA,MAAM,iBAAiB,qBAAsB,EAAA;AAC7C,EAAM,MAAA,WAAA,GAAc,QAAyB,MAAM;AACjD,IAAA,MAAMA,QAAwB,EAAC;AAC/B,IAAS,QAAA,CAAA,OAAA,CAAQ,CAAC,GAAA,EAAK,KAAU,KAAA;AArErC,MAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAsEM,MAAI,IAAA,GAAA,CAAI,SAAS,MAAQ,EAAA;AACvB,QAAA,IAAI,8BAA+B,CAAA,GAAA,CAAI,OAAS,EAAA,GAAA,CAAI,WAAW,CAAG,EAAA;AAChE,UAAAA,KAAAA,CAAK,IAAK,CAAA,aAAA,CAAA,cAAA,CAAA,EAAA,EACL,GADK,CAAA,EAAA;AAAA,YAER,SAAW,EAAA;AAAA,WACZ,CAAA,CAAA;AAAA;AAEH,QAAA;AAAA;AAEF,MAAI,IAAA,GAAA,CAAI,SAAS,WAAa,EAAA;AAC5B,QAAAA,KAAAA,CAAK,IAAK,CAAA,aAAA,CAAA,cAAA,CAAA,EAAA,EACL,GADK,CAAA,EAAA;AAAA,UAER,SAAW,EAAA;AAAA,SACZ,CAAA,CAAA;AACD,QAAA;AAAA;AAEF,MAAA,MAAM,cAAc,OAAQ,CAAA,YAAA,CAAa,GAAI,CAAA,OAAO,CAAC,CAAK,IAAA,QAAA,CAAS,KAAM,CAAA,KAAA,GAAQ,CAAC,CAAE,CAAA,IAAA,CAAK,CAAK,CAAA,KAAA,CAAA,CAAE,SAAS,MAAM,CAAA;AAC/G,MAAA,MAAM,UAAU,2BAA4B,CAAA,GAAA,CAAI,OAAS,EAAA,WAAW,EAAE,IAAK,EAAA;AAC3E,MAAA,MAAM,QAAQ,iBAAkB,CAAA,YAAA,CAAa,GAAI,CAAA,OAAO,GAAG,KAAK,CAAA;AAChE,MAAA,MAAM,MAAS,GAAA,OAAA,CAAQ,YAAa,CAAA,GAAA,CAAI,OAAO,CAAC,CAAA;AAChD,MAAA,IAAI,MAAU,IAAA,CAAC,OAAW,IAAA,KAAA,CAAM,KAAM,CAAA,MAAA,KAAW,CAAK,IAAA,EAAA,CAAE,EAAI,GAAA,CAAA,EAAA,GAAA,GAAA,CAAA,WAAA,KAAJ,IAAiB,GAAA,MAAA,GAAA,EAAA,CAAA,MAAA,KAAjB,YAA2B,CAAI,CAAA,EAAA;AACrF,QAAA;AAAA;AAEF,MAAA,IAAI,CAAC,MAAQ,EAAA;AACX,QAAA,MAAM,QAAW,GAAA,YAAA,CAAa,GAAI,CAAA,OAAO,EAAE,IAAK,EAAA;AAChD,QAAI,IAAA,CAAC,YAAY,EAAE,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,GAAA,CAAI,gBAAJ,IAAiB,GAAA,MAAA,GAAA,EAAA,CAAA,MAAA,KAAjB,YAA2B,CAAI,CAAA,EAAA;AAAA;AAEpD,MAAAA,KAAAA,CAAK,IAAK,CAAA,aAAA,CAAA,cAAA,CAAA,EAAA,EACL,GADK,CAAA,EAAA;AAAA,QAER,SAAW,EAAA,KAAA;AAAA,QACX;AAAA,OACD,CAAA,CAAA;AAAA,KACF,CAAA;AACD,IAAOA,OAAAA,KAAAA;AAAA,GACT,EAAG,CAAC,QAAQ,CAAC,CAAA;AACb,EAAM,MAAA,IAAA,GAAO,QAAyB,MAAM;AAC1C,IAAM,MAAA,OAAA,GAAU,0BAA0B,WAAW,CAAA;AACrD,IAAA,MAAM,YAAY,gBAAoB,IAAA,IAAA,GAAA,gBAAA,GAAA,EAAA;AACtC,IAAA,MAAM,IAAO,GAAA,OAAA,CAAQ,OAAQ,CAAA,MAAA,GAAS,CAAC,CAAA;AACvC,IAAA,MAAM,qBAAoB,IAAM,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAA,IAAA,MAAS,UAAU,OAAQ,CAAA,YAAA,CAAa,SAAS,CAAC,CAAA;AAClF,IAAA,MAAM,WAAc,GAAA,2BAAA,CAA4B,SAAW,EAAA,iBAAiB,EAAE,IAAK,EAAA;AACnF,IAAA,MAAM,WAAc,GAAA,iBAAA,CAAkB,YAAa,CAAA,SAAS,GAAG,IAAI,CAAA;AACnE,IAAA,IAAI,CAAC,WAAe,IAAA,WAAA,CAAY,KAAM,CAAA,MAAA,KAAW,GAAU,OAAA,OAAA;AAC3D,IAAI,IAAA,CAAA,IAAA,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAM,UAAS,WAAa,EAAA;AAC9B,MAAM,MAAA,QAAA,GAAW,IAAK,CAAA,OAAA,CAAQ,IAAK,EAAA;AACnC,MAAM,MAAA,UAAA,GAAa,UAAU,IAAK,EAAA;AAClC,MAAI,IAAA,QAAA,KAAa,cAAc,QAAS,CAAA,UAAA,CAAW,UAAU,CAAK,IAAA,UAAA,CAAW,UAAW,CAAA,QAAQ,CAAG,EAAA;AACjG,QAAO,OAAA,OAAA;AAAA;AACT;AAEF,IAAO,OAAA,CAAC,GAAG,OAAS,EAAA;AAAA,MAClB,EAAI,EAAA,eAAA;AAAA,MACJ,IAAM,EAAA,WAAA;AAAA,MACN,OAAS,EAAA,SAAA;AAAA,MACT,SAAW,EAAA,IAAA;AAAA,MACX,WAAa,EAAA;AAAA,KACd,CAAA;AAAA,GACA,EAAA,CAAC,WAAa,EAAA,gBAAgB,CAAC,CAAA;AAClC,EAAM,MAAA,MAAA,GAAS,QAAQ,MAAM,wBAAA,CAAyB,IAAI,CAAG,EAAA,CAAC,IAAI,CAAC,CAAA;AACnE,EAAM,MAAA,cAAA,GAAiB,WAAY,CAAA,CAAC,QAAsB,KAAA;AACxD,IAAI,IAAA,MAAA,CAAO,WAAW,CAAG,EAAA;AACzB,IAAA,uBAAA,CAAwB,OAAU,GAAA,IAAA;AAClC,IAAA,gBAAA,CAAiB,OAAU,GAAA,IAAA;AAC3B,IAAA,MAAM,OAAO,OAAQ,CAAA,OAAA;AACrB,IAAA,IAAI,CAAC,IAAM,EAAA;AACX,IAAA,IAAA,CAAK,WAAY,CAAA;AAAA,MACf;AAAA,KACD,CAAA;AAAA,GACA,EAAA,CAAC,MAAO,CAAA,MAAM,CAAC,CAAA;AAClB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,OAAO,MAAW,KAAA,CAAA,IAAK,CAAC,gBAAA,CAAiB,SAAgB,OAAA,MAAA;AAC7D,IAAA,MAAM,IAAI,UAAW,CAAA,MAAM,cAAe,CAAA,KAAK,GAAG,CAAC,CAAA;AACnD,IAAO,OAAA,MAAM,aAAa,CAAC,CAAA;AAAA,KAC1B,CAAC,MAAA,CAAO,QAAQ,gBAAkB,IAAA,IAAA,GAAA,MAAA,GAAA,gBAAA,CAAA,MAAA,EAAQ,cAAc,CAAC,CAAA;AAI5D,EAAA,SAAA,CAAU,MAAM;AACd,IAAI,IAAA,cAAA,IAAkB,GAAU,OAAA,MAAA;AAChC,IAAA,gBAAA,CAAiB,OAAU,GAAA,IAAA;AAC3B,IAAqB,oBAAA,CAAA,OAAA,GAAU,IAAK,CAAA,GAAA,EAAQ,GAAA,GAAA;AAC5C,IAAA,MAAM,KAAQ,GAAA,qBAAA,CAAsB,MAAM,cAAA,CAAe,KAAK,CAAC,CAAA;AAC/D,IAAA,MAAM,cAAc,UAAW,CAAA,MAAM,cAAe,CAAA,KAAK,GAAG,EAAE,CAAA;AAC9D,IAAA,MAAM,gBAAgB,UAAW,CAAA,MAAM,cAAe,CAAA,KAAK,GAAG,GAAG,CAAA;AACjE,IAAA,OAAO,MAAM;AACX,MAAA,oBAAA,CAAqB,KAAK,CAAA;AAC1B,MAAA,YAAA,CAAa,WAAW,CAAA;AACxB,MAAA,YAAA,CAAa,aAAa,CAAA;AAAA,KAC5B;AAAA,GACC,EAAA,CAAC,cAAgB,EAAA,cAAc,CAAC,CAAA;AACnC,EAAM,MAAA,YAAA,GAAe,WAAY,CAAA,CAAC,CAY5B,KAAA;AACJ,IAAA,IAAI,IAAK,CAAA,GAAA,EAAQ,GAAA,oBAAA,CAAqB,OAAS,EAAA;AAC7C,MAAA,gBAAA,CAAiB,OAAU,GAAA,IAAA;AAC3B,MAAA;AAAA;AAEF,IAAM,MAAA;AAAA,MACJ,aAAA;AAAA,MACA,WAAA;AAAA,MACA;AAAA,QACE,CAAE,CAAA,WAAA;AACN,IAAA,MAAM,aAAa,aAAc,CAAA,CAAA,GAAI,iBAAkB,CAAA,MAAA,IAAU,YAAY,MAAS,GAAA,EAAA;AACtF,IAAI,IAAA,CAAC,wBAAwB,OAAS,EAAA;AACpC,MAAA,gBAAA,CAAiB,OAAU,GAAA,UAAA;AAAA,eAClB,UAAY,EAAA;AACrB,MAAA,uBAAA,CAAwB,OAAU,GAAA,KAAA;AAAA;AACpC,GACF,EAAG,EAAE,CAAA;AACL,EAAM,MAAA,uBAAA,GAA0B,YAAY,MAAM;AAChD,IAAI,IAAA,CAAC,iBAAiB,OAAS,EAAA;AAC/B,IAAA,cAAA,CAAe,KAAK,CAAA;AAAA,GACtB,EAAG,CAAC,cAAc,CAAC,CAAA;AACnB,EAAM,MAAA,UAAA,GAAa,YAAY,CAAC;AAAA,IAC9B;AAAA,GACF,qBAEO,GAAA,CAAA,mBAAA,EAAA,EAAoB,KAAO,EAAA,IAAA,EAAM,MAAgB,EAAA,oBAAA,EAA4C,CAAI,EAAA,CAAC,MAAQ,EAAA,oBAAoB,CAAC,CAAA;AACtI,EAAO,uBAAA,GAAA,CAAC,QAAK,KAAO,EAAA,MAAA,CAAO,MACjB,QAAC,kBAAA,GAAA,CAAA,QAAA,EAAA,EAAS,GAAK,EAAA,OAAA,EAAS,IAAM,EAAA,MAAA,EAAQ,cAAc,CAAQ,IAAA,KAAA,IAAA,CAAK,EAAI,EAAA,UAAA,EAAwB,SAAW,EAAA,CAAA,EAAG,8CAAoB,EAAE,CAAA,CAAA,EAAI,OAAQ,CAAA,UAAU,CAAC,CAAA,CAAA,EAAI,qBAAqB,UAAa,mBAAA,GAAA,CAAA,QAAA,EAAA,EAAG,sBAAW,CAAM,GAAA,IAAA,EAAM,oBAAoB,EAAI,EAAA,mBAAA,EAAqB,CAAG,EAAA,yBAAA,EAA2B,EAAI,EAAA,UAAA,EAAY,IAAI,aAAa,EAAA,IAAA,EAAC,mBAAqB,EAAA,KAAA,EAAO,sBAAsB,EAAA,IAAA,EAAC,UAAU,YAAc,EAAA,mBAAA,EAAqB,uBAAyB,EAAA,QAAA,EAAU,MAAM;AACpd,IAAI,IAAA,gBAAA,CAAiB,OAAS,EAAA,cAAA,CAAe,KAAK,CAAA;AAAA,GACjD,EAAA,mBAAA,EAAqB,EAAI,EAAA,yBAAA,EAA0B,SAAU,EAAA,mBAAA,EAAqB,QAAS,CAAA,EAAA,KAAO,KAAQ,GAAA,aAAA,GAAgB,SAAW,EAAA,iCAAA,EAAmC,OAAO,gCAAkC,EAAA,KAAA,EAAO,8BAA+B,EAAA,OAAA,EAAQ,qBAAuB,EAAA,CAAC,MAAO,CAAA,WAAA,EAAa,gBAAkB,EAAA,CAAC,UAAc,IAAA,MAAA,CAAO,eAAe,CAAA,EAAG,KAAO,EAAA,MAAA,CAAO,MAAM,CACxX,EAAA,CAAA;AACR;AACA,MAAM,mBAAA,GAAsB,IAAK,CAAA,SAASC,oBAAoB,CAAA;AAAA,EAC5D,KAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAQG,EAAA;AACD,EAAI,IAAA,KAAA,CAAM,SAAS,MAAQ,EAAA;AACzB,IAAA,uBAAQ,GAAA,CAAA,SAAA,EAAA,EAAU,KAAO,EAAA,MAAA,CAAO,SAAS,OAAS,EAAA,eAAA,EAAiB,UAAY,EAAA,KAAA,EAClE,QAAM,EAAA,KAAA,CAAA,KAAA,CAAM,GAAI,CAAA,CAAC,MAAM,KAAU,KAAA;AA3NlD,MAAA,IAAA,EAAA,EAAA,EAAA;AA4NQ,MAAA,MAAM,WAAc,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,WAAL,KAAA,IAAA,GAAA,EAAA,GAAoB,EAAC;AACzC,MAAA,MAAM,OAAO,sBAAuB,CAAA,CAAA,EAAA,GAAA,IAAA,CAAK,mBAAL,IAAuB,GAAA,EAAA,GAAA,IAAA,CAAK,OAAoB,CAAA;AACpF,MAAA,IAAI,CAAC,IAAA,IAAQ,WAAY,CAAA,MAAA,KAAW,GAAU,OAAA,IAAA;AAC9C,MAAO,uBAAA,IAAA,CAAC,IAAmB,EAAA,EAAA,KAAA,EAAO,CAAC,MAAA,CAAO,UAAU,KAAQ,GAAA,CAAA,IAAK,MAAO,CAAA,UAAU,CAC7D,EAAA,QAAA,EAAA;AAAA,QAAY,WAAA,CAAA,MAAA,GAAS,oBAAK,GAAA,CAAA,yBAAA,EAAA,EAA0B,aAA0B,KAAM,EAAA,KAAA,EAAM,QAAgB,CAAK,GAAA,IAAA;AAAA,QAC/G,uBAAQ,GAAA,CAAA,IAAA,EAAA,EAAK,OAAO,CAAC,MAAA,CAAO,YAAY,IAAK,CAAA,MAAA,IAAU,MAAO,CAAA,gBAAgB,GACvE,QAAC,kBAAA,GAAA,CAAA,QAAA,EAAA,EAAS,OAAO,aAAgB,EAAA,QAAA,EAAA,IAAA,EAAK,GAC1C,CAAU,GAAA,IAAA;AAAA,QACb,KAAK,MAAS,mBAAA,GAAA,CAAC,QAAK,KAAO,EAAA,CAAC,OAAO,aAAe,EAAA;AAAA,UACnE,KAAA,EAAO,SAAS,SAAY,GAAA;AAAA,SAC7B,CAAG,EAAA,QAAA,EAAA,6CAAA,EAEkB,CAAU,GAAA;AAAA,OAAA,EAAA,EAThB,KAAK,EAUP,CAAA;AAAA,KACjB,CACK,EAAA,CAAA;AAAA;AAEV,EAAA,MAAM,OAAO,KAAM,CAAA,KAAA,CAAM,KAAM,CAAA,KAAA,CAAM,SAAS,CAAC,CAAA;AAC/C,EAAA,uBAAQ,GAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,MAAA,CAAO,cAChB,EAAA,QAAA,EAAA,KAAA,CAAM,KAAM,CAAA,GAAA,CAAI,CAAC,IAAA,EAAM,KAAU,qBAAA,GAAA,CAAC,iBAAgC,EAAA,EAAA,IAAA,EAAY,MAAgB,EAAA,QAAA,EAAU,KAAQ,GAAA,CAAA,EAAG,oBAAsB,EAAA,IAAA,CAAK,SAAa,IAAA,IAAA,KAAS,IAAO,GAAA,MAAA,GAAY,oBAA9H,EAAA,EAAA,IAAA,CAAK,EAA+I,CAAE,CACpN,EAAA,CAAA;AACR,CAAC,CAAA;AACD,MAAM,iBAAA,GAAoB,IAAK,CAAA,SAASC,kBAAkB,CAAA;AAAA,EACxD,IAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CASG,EAAA;AAhQH,EAAA,IAAA,EAAA;AAiQE,EAAA,MAAM,WAAc,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,WAAL,KAAA,IAAA,GAAA,EAAA,GAAoB,EAAC;AACzC,EAAM,MAAA,QAAA,GAAW,YAAa,CAAA,IAAA,CAAK,OAAO,CAAA;AAC1C,EAAA,MAAM,eAAe,iBAAkB,CAAA,QAAA,EAAU,CAAC,CAAC,KAAK,SAAS,CAAA;AAEjE,EAAM,MAAA;AAAA,IACJ,IAAA;AAAA,IACA;AAAA,GACF,GAAI,iBAAiB,iBAAkB,CAAA,2BAAA,CAA4B,KAAK,OAAS,EAAA,CAAC,CAAC,IAAK,CAAA,WAAW,GAAG,CAAC,CAAC,KAAK,SAAS,CAAA,CAAE,MAAM,CAAC,CAAC,KAAK,SAAS,CAAA;AAC9I,EAAO,uBAAA,IAAA,CAAC,QAAK,KAAO,EAAA,CAAC,OAAO,YAAc,EAAA,QAAA,IAAY,MAAO,CAAA,iBAAiB,CACnE,EAAA,QAAA,EAAA;AAAA,IAAY,WAAA,CAAA,MAAA,GAAS,oBAAK,GAAA,CAAA,yBAAA,EAAA,EAA0B,aAA0B,KAAM,EAAA,OAAA,EAAQ,QAAgB,CAAK,GAAA,IAAA;AAAA,IACjH,YAAa,CAAA,KAAA,CAAM,MAAS,GAAA,CAAA,uBAAK,YAAa,EAAA,EAAA,KAAA,EAAO,YAAa,CAAA,KAAA,EAAO,QAAQ,CAAC,CAAC,IAAK,CAAA,SAAA,EAAW,QAAgB,CAAK,GAAA,IAAA;AAAA,IACxH,IAAA,mBAAQ,GAAA,CAAA,iBAAA,EAAA,EAAkB,IAAY,EAAA,SAAA,EAAW,CAAC,CAAC,IAAA,CAAK,SAAW,EAAA,MAAA,EAAgB,CAAK,GAAA,IAAA;AAAA,IACxF,OAAQ,CAAA,GAAA,CAAI,CAAC,MAAA,EAAQ,sBAAO,GAAA,CAAA,cAAA,EAAA,EAAgD,MAAgB,EAAA,MAAA,EAAA,EAA5C,GAAG,MAAO,CAAA,SAAS,CAAI,CAAA,EAAA,CAAC,EAAoC,CAAE,CAAA;AAAA,IAC9G,uBAAuB,oBAAqB,CAAA;AAAA,MACnD,IAAI,IAAK,CAAA,EAAA;AAAA,MACT,IAAM,EAAA,WAAA;AAAA,MACN,SAAS,IAAK,CAAA;AAAA,KACf,CAAI,GAAA;AAAA,GACD,EAAA,CAAA;AACR,CAAC,CAAA;AACD,MAAM,aAAgB,GAAA;AAAA,EACpB,IAAM,EAAA;AAAA,IACJ,KAAO,EAAA,SAAA;AAAA,IACP,QAAU,EAAA,EAAA;AAAA,IACV,UAAY,EAAA;AAAA,GACd;AAAA,EACA,SAAW,EAAA;AAAA,IACT,SAAW,EAAA,CAAA;AAAA,IACX,YAAc,EAAA,CAAA;AAAA,IACd,KAAO,EAAA;AAAA,GACT;AAAA,EACA,MAAQ,EAAA;AAAA,IACN,UAAY,EAAA,KAAA;AAAA,IACZ,KAAO,EAAA;AAAA,GACT;AAAA,EACA,EAAI,EAAA;AAAA,IACF,KAAO,EAAA;AAAA,GACT;AAAA,EACA,IAAM,EAAA;AAAA,IACJ,KAAO,EAAA;AAAA,GACT;AAAA,EACA,WAAa,EAAA;AAAA,IACX,cAAgB,EAAA;AAAA,GAClB;AAAA,EACA,YAAc,EAAA;AAAA,IACZ,cAAgB,EAAA;AAAA,GAClB;AAAA,EACA,SAAW,EAAA;AAAA,IACT,cAAgB,EAAA,CAAA;AAAA,IAChB,KAAO,EAAA;AAAA;AAEX,CAAA;AACA,MAAM,MAAA,GAAS,WAAW,MAAO,CAAA;AAAA,EAC/B,IAAM,EAAA;AAAA,IACJ,IAAM,EAAA,CAAA;AAAA,IACN,SAAW,EAAA;AAAA,GACb;AAAA,EACA,WAAa,EAAA;AAAA,IACX,UAAY,EAAA,CAAA;AAAA,IACZ,aAAe,EAAA,CAAA;AAAA,IACf,iBAAmB,EAAA;AAAA,GACrB;AAAA,EACA,eAAiB,EAAA;AAAA,IACf,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,OAAS,EAAA;AAAA,IACP,KAAO,EAAA,MAAA;AAAA,IACP,UAAY,EAAA,UAAA;AAAA,IACZ,YAAc,EAAA,EAAA;AAAA,IACd,iBAAmB,EAAA;AAAA,GACrB;AAAA,EACA,QAAU,EAAA;AAAA,IACR,QAAU,EAAA,KAAA;AAAA,IACV,UAAY,EAAA;AAAA,GACd;AAAA,EACA,UAAY,EAAA;AAAA,IACV,SAAW,EAAA;AAAA,GACb;AAAA,EACA,UAAY,EAAA;AAAA,IACV,eAAiB,EAAA,cAAA;AAAA,IACjB,YAAc,EAAA,EAAA;AAAA,IACd,iBAAmB,EAAA,EAAA;AAAA,IACnB,eAAiB,EAAA;AAAA,GACnB;AAAA;AAAA,EAEA,gBAAkB,EAAA;AAAA,IAChB,OAAS,EAAA;AAAA,GACX;AAAA,EACA,aAAe,EAAA;AAAA,IACb,QAAU,EAAA,EAAA;AAAA,IACV,UAAY,EAAA,KAAA;AAAA,IACZ,SAAW,EAAA,CAAA;AAAA,IACX,WAAa,EAAA;AAAA,GACf;AAAA,EACA,cAAgB,EAAA;AAAA,IACd,KAAO,EAAA,MAAA;AAAA,IACP,YAAc,EAAA;AAAA,GAChB;AAAA,EACA,YAAc,EAAA;AAAA,IACZ,KAAO,EAAA,MAAA;AAAA,IACP,QAAU,EAAA,CAAA;AAAA,IACV,UAAY,EAAA,SAAA;AAAA,IACZ,YAAc,EAAA,CAAA;AAAA,IACd,iBAAmB,EAAA;AAAA,GACrB;AAAA,EACA,iBAAmB,EAAA;AAAA,IACjB,SAAW,EAAA;AAAA;AAEf,CAAC,CAAA"}
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, type ReactNode } 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';\nimport { ToolTimeline } from './ToolTimeline';\nimport { ChatWidgetCard } from './ChatWidgetCard';\nimport { splitChatWidgets } from './chatWidgets';\nimport { parseToolActivity } from './toolActivity';\nimport { parseAskUser, stripAskUser, displayAskUserAssistantText } from './askUser';\nimport { collapseDuplicateUserRows, groupConsecutiveRoleRows, type RoleGroup } from './transcriptGroups';\nimport { AssistantMarkdown } from './AssistantMarkdown';\nimport { PreResponseStrip } from './PreResponseStrip';\nimport { splitPreResponse } from './preResponse';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n displayContent?: string;\n attachments?: MessageAttachment[];\n /** Typed while the gateway was down: shown in place, waiting on the outbox. */\n queued?: boolean;\n};\n\ntype TranscriptRow = TranscriptMessage & {\n streaming?: boolean;\n askAnswered?: boolean;\n /** The fast lane's quick answer for this turn, drawn as a strip - never as text. */\n preResponse?: string | null;\n};\ntype TranscriptGroup = RoleGroup<TranscriptRow>;\n\nexport const USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n quickReply,\n renderMessageActions,\n listContentStyle,\n listFooter,\n isDark = false,\n}: {\n messages: TranscriptMessage[];\n streamingContent?: string;\n /** The running turn's quick answer (fast lane or brain), already split off the stream. */\n quickReply?: string;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n listContentStyle?: StyleProp<ViewStyle>;\n listFooter?: ReactNode;\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 // A persisted reply carries its quick answer prepended in the gateway's\n // markers; the row keeps the two apart from here on.\n const { preResponse, text: content } = splitPreResponse(msg.content);\n const hasAttachments = (msg.attachments?.length ?? 0) > 0;\n const askAnswered =\n Boolean(parseAskUser(content)) && messages.slice(index + 1).some((m) => m.role === 'user');\n const visible = displayAskUserAssistantText(content, askAnswered).trim();\n const tools = parseToolActivity(stripAskUser(content), false);\n const hasAsk = Boolean(parseAskUser(content));\n if (hasAsk && !visible && tools.steps.length === 0 && !hasAttachments && !preResponse) {\n return;\n }\n if (!hasAsk) {\n const leftover = stripAskUser(content).trim();\n if (!leftover && !hasAttachments && !preResponse) return;\n }\n rows.push({ ...msg, content, preResponse, streaming: false, askAnswered });\n });\n return rows;\n }, [messages]);\n\n const rows = useMemo<TranscriptRow[]>(() => {\n const history = collapseDuplicateUserRows(historyRows);\n // The chat hook hands the quick answer over split off the stream; a raw\n // stream (another host) may still carry the markers, open mid-stream.\n const { preResponse: streamPre, text: rawStream } = splitPreResponse(streamingContent ?? '');\n const livePre = quickReply?.trim() || streamPre;\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 && !livePre) return history;\n if (last?.role === 'assistant' && (!livePre || last.preResponse === livePre)) {\n const lastText = last.content.trim();\n const streamText = rawStream.trim();\n if (lastText === streamText || lastText.startsWith(streamText) || streamText.startsWith(lastText)) {\n return history;\n }\n }\n return [\n ...history,\n {\n id: '__streaming__',\n role: 'assistant',\n content: rawStream,\n preResponse: livePre,\n streaming: true,\n askAnswered: streamAskAnswered,\n },\n ];\n }, [historyRows, streamingContent, quickReply]);\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 },\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, quickReply?.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 ?? ''}:${quickReply ?? ''}:${Boolean(listFooter)}`}\n ListFooterComponent={listFooter ? <>{listFooter}</> : null}\n initialNumToRender={12}\n maxToRenderPerBatch={8}\n updateCellsBatchingPeriod={50}\n windowSize={21}\n scrollEnabled\n nestedScrollEnabled={false}\n directionalLockEnabled\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, !listFooter && 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.displayContent ?? 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, item.queued && styles.userBubbleQueued]}>\n <Markdown style={USER_MARKDOWN}>{text}</Markdown>\n </View>\n ) : null}\n {item.queued ? (\n <Text style={[styles.queuedCaption, { color: isDark ? '#94a3b8' : '#64748b' }]}>\n Queued · sends when the gateway connects\n </Text>\n ) : null}\n </View>\n );\n })}\n </Pressable>\n );\n }\n\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 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 tightTop,\n renderMessageActions,\n}: {\n item: TranscriptRow;\n isDark: boolean;\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 // Widget envelopes become native cards below the text, never raw JSON.\n const { text, widgets } = splitChatWidgets(\n parseToolActivity(displayAskUserAssistantText(item.content, !!item.askAnswered), !!item.streaming).text,\n !!item.streaming,\n );\n\n // The quick answer stays open while it is all there is to read: until the\n // agent's own words start on a live turn, or for good on a reply that never\n // got anything past it. Tool steps alone do not fold it - they are not an answer.\n const preResponseOpen = item.streaming\n ? !text.trim()\n : !text.trim() && toolActivity.steps.length === 0 && widgets.length === 0 && attachments.length === 0;\n\n return (\n <View style={[styles.assistantCol, tightTop && styles.assistantColTight]}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"start\" isDark={isDark} />\n ) : null}\n {item.preResponse ? (\n <PreResponseStrip\n text={item.preResponse}\n expanded={preResponseOpen}\n live={!!item.streaming}\n isDark={isDark}\n />\n ) : null}\n {toolActivity.steps.length > 0 ? (\n <ToolTimeline steps={toolActivity.steps} active={!!item.streaming} isDark={isDark} />\n ) : null}\n {text ? <AssistantMarkdown text={text} streaming={!!item.streaming} isDark={isDark} /> : null}\n {widgets.map((widget, i) => (\n <ChatWidgetCard key={`${widget.component}-${i}`} widget={widget} isDark={isDark} />\n ))}\n {renderMessageActions\n ? renderMessageActions({\n id: item.id,\n role: 'assistant',\n // Copy and share take what the user reads, not the wire markup.\n content: text.trim() || item.preResponse || item.content,\n })\n : null}\n </View>\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\nconst styles = StyleSheet.create({\n list: {\n flex: 1,\n minHeight: 0,\n },\n listContent: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingHorizontal: 8,\n },\n listContentFill: {\n flexGrow: 1,\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 // Same bubble, held back: a queued turn reads as pending, not as delivered.\n userBubbleQueued: {\n opacity: 0.55,\n },\n queuedCaption: {\n fontSize: 11,\n fontWeight: '500',\n marginTop: 4,\n marginRight: 2,\n },\n assistantGroup: {\n width: '100%',\n marginBottom: 4,\n },\n assistantCol: {\n width: '100%',\n flexGrow: 0,\n alignItems: 'stretch',\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,87 @@
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,Text}from'react-native';import {plainPreResponse}from'./preResponse.js';const ACCENT = "#2563eb";
2
+ const PreResponseStrip = memo(function PreResponseStrip2({
3
+ text,
4
+ expanded,
5
+ live = false,
6
+ isDark = false
7
+ }) {
8
+ const [open, setOpen] = useState(expanded);
9
+ const touchedRef = useRef(false);
10
+ useEffect(() => {
11
+ if (!touchedRef.current) setOpen(expanded);
12
+ }, [expanded]);
13
+ const body = plainPreResponse(text);
14
+ if (!body) return null;
15
+ const border = isDark ? "#1f2937" : "#e5e7eb";
16
+ const bg = isDark ? "#0b1220" : "#f9fafb";
17
+ const muted = isDark ? "#94a3b8" : "#6b7280";
18
+ const ink = isDark ? "#cbd5e1" : "#374151";
19
+ const preview = body.replace(/\s+/g, " ");
20
+ return /* @__PURE__ */ jsxs(View, { style: [styles.wrap, {
21
+ borderColor: border,
22
+ backgroundColor: bg
23
+ }], children: [
24
+ /* @__PURE__ */ jsxs(Pressable, { accessibilityRole: "button", accessibilityState: {
25
+ expanded: open
26
+ }, accessibilityLabel: "Quick answer", accessibilityHint: open ? "Hides the quick answer" : "Shows the quick answer", onPress: () => {
27
+ touchedRef.current = true;
28
+ setOpen((v) => !v);
29
+ }, style: ({
30
+ pressed
31
+ }) => [styles.header, {
32
+ opacity: pressed ? 0.7 : 1
33
+ }], children: [
34
+ /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "lightning-bolt", size: 14, color: live ? ACCENT : muted, style: styles.headerIcon }),
35
+ /* @__PURE__ */ jsx(Text, { style: [styles.label, {
36
+ color: muted
37
+ }], children: "Quick answer" }),
38
+ /* @__PURE__ */ jsx(Text, { style: [styles.preview, {
39
+ color: muted
40
+ }], numberOfLines: 1, children: open ? "" : ` \xB7 ${preview}` }),
41
+ /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: open ? "chevron-down" : "chevron-right", size: 18, color: muted })
42
+ ] }),
43
+ open ? /* @__PURE__ */ jsx(View, { style: [styles.body, {
44
+ borderTopColor: border
45
+ }], children: /* @__PURE__ */ jsx(Text, { style: [styles.bodyText, {
46
+ color: ink
47
+ }], selectable: true, children: body }) }) : null
48
+ ] });
49
+ });
50
+ const styles = StyleSheet.create({
51
+ wrap: {
52
+ width: "100%",
53
+ borderWidth: 1,
54
+ borderRadius: 10,
55
+ marginBottom: 8,
56
+ overflow: "hidden"
57
+ },
58
+ header: {
59
+ flexDirection: "row",
60
+ alignItems: "center",
61
+ paddingHorizontal: 10,
62
+ minHeight: 44
63
+ // touch target
64
+ },
65
+ headerIcon: {
66
+ width: 18,
67
+ marginRight: 6
68
+ },
69
+ label: {
70
+ fontSize: 12,
71
+ fontWeight: "600"
72
+ },
73
+ preview: {
74
+ flex: 1,
75
+ fontSize: 12,
76
+ marginRight: 4
77
+ },
78
+ body: {
79
+ borderTopWidth: 1,
80
+ paddingHorizontal: 10,
81
+ paddingVertical: 10
82
+ },
83
+ bodyText: {
84
+ fontSize: 14,
85
+ lineHeight: 20
86
+ }
87
+ });export{PreResponseStrip,PreResponseStrip as default};//# sourceMappingURL=PreResponseStrip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PreResponseStrip.js","sources":["../../../src/features/chat/PreResponseStrip.tsx"],"sourcesContent":["/**\n * <PreResponseStrip> — the fast lane's \"Quick answer\", as a collapsible strip.\n *\n * With `CDECLI_FAST_LANE=pre` (or the brain assist) a toolless model answers in\n * about a second while the real agent turn runs behind it. That answer is why\n * the turn felt fast, so it stays with the turn - but it must not compete with\n * the agent's reply, so once the agent starts talking it folds down to one\n * quiet line the user can re-open. Rendered raw it was the\n * `[PRE_RESPONSE]…[/PRE_RESPONSE]` markup in the bubble.\n *\n * Mobile counterpart of the browser `PreResponseStrip`, drawn like\n * {@link ./ToolTimeline} (same border, fill, 44pt header and chevron) so a turn\n * that shows both reads as one stack of secondary detail above the answer.\n */\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport React, { memo, useEffect, useRef, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { plainPreResponse } from './preResponse';\n\nconst ACCENT = '#2563eb';\n\nexport const PreResponseStrip = memo(function PreResponseStrip({\n text,\n expanded,\n live = false,\n isDark = false,\n}: {\n /** The fast answer. Nothing renders when empty. */\n text: string;\n /**\n * Open while the quick answer is all there is to read; folds once the\n * agent's own reply arrives.\n */\n expanded: boolean;\n /** True while this turn is still running: the bolt takes the accent color. */\n live?: boolean;\n isDark?: boolean;\n}) {\n const [open, setOpen] = useState(expanded);\n // Once the user opens or closes it themselves, stop steering it - an\n // auto-collapse that fights a deliberate tap reads as a bug.\n const touchedRef = useRef(false);\n\n useEffect(() => {\n if (!touchedRef.current) setOpen(expanded);\n }, [expanded]);\n\n const body = plainPreResponse(text);\n if (!body) return null;\n\n const border = isDark ? '#1f2937' : '#e5e7eb';\n const bg = isDark ? '#0b1220' : '#f9fafb';\n const muted = isDark ? '#94a3b8' : '#6b7280';\n const ink = isDark ? '#cbd5e1' : '#374151';\n const preview = body.replace(/\\s+/g, ' ');\n\n return (\n <View style={[styles.wrap, { borderColor: border, backgroundColor: bg }]}>\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ expanded: open }}\n accessibilityLabel=\"Quick answer\"\n accessibilityHint={open ? 'Hides the quick answer' : 'Shows the quick answer'}\n onPress={() => {\n touchedRef.current = true;\n setOpen((v) => !v);\n }}\n style={({ pressed }) => [styles.header, { opacity: pressed ? 0.7 : 1 }]}\n >\n <MaterialCommunityIcons\n name=\"lightning-bolt\"\n size={14}\n color={live ? ACCENT : muted}\n style={styles.headerIcon}\n />\n <Text style={[styles.label, { color: muted }]}>Quick answer</Text>\n <Text style={[styles.preview, { color: muted }]} numberOfLines={1}>\n {open ? '' : ` · ${preview}`}\n </Text>\n <MaterialCommunityIcons name={open ? 'chevron-down' : 'chevron-right'} size={18} color={muted} />\n </Pressable>\n\n {open ? (\n <View style={[styles.body, { borderTopColor: border }]}>\n <Text style={[styles.bodyText, { color: ink }]} selectable>\n {body}\n </Text>\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 label: { fontSize: 12, fontWeight: '600' },\n preview: { flex: 1, fontSize: 12, marginRight: 4 },\n body: { borderTopWidth: 1, paddingHorizontal: 10, paddingVertical: 10 },\n bodyText: { fontSize: 14, lineHeight: 20 },\n});\n\nexport default PreResponseStrip;\n"],"names":["PreResponseStrip"],"mappings":"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"}
@@ -17,7 +17,15 @@ function splitPreResponse(raw) {
17
17
  text: rest.trimStart()
18
18
  };
19
19
  }
20
+ function joinPreResponse(preResponse, text) {
21
+ const pre = (preResponse != null ? preResponse : "").trim();
22
+ if (!pre || text.includes(OPEN)) return text;
23
+ return `${OPEN}${pre}${CLOSE}${text}`;
24
+ }
25
+ function plainPreResponse(text) {
26
+ return text.replace(/^#{1,6}\s+/gm, "").replace(/(\*\*|__)(.+?)\1/g, "$2").replace(/`([^`\n]+)`/g, "$1").trim();
27
+ }
20
28
  function isFastLaneRunId(runId) {
21
29
  if (!runId) return false;
22
30
  return /^(fast-|pre-)/i.test(runId);
23
- }export{isFastLaneRunId,splitPreResponse};//# sourceMappingURL=preResponse.js.map
31
+ }export{isFastLaneRunId,joinPreResponse,plainPreResponse,splitPreResponse};//# sourceMappingURL=preResponse.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"preResponse.js","sources":["../../../src/features/chat/preResponse.ts"],"sourcesContent":["/**\n * Pre-response parsing for the fast-lane \"Quick answer\" strip.\n * Same contract as the browser `features/chat/preResponse.ts`.\n */\nexport interface ParsedPreResponse {\n preResponse: string | null;\n text: string;\n}\n\nconst OPEN = '[PRE_RESPONSE]';\nconst CLOSE = '[/PRE_RESPONSE]';\n\nexport function splitPreResponse(raw: string | null | undefined): ParsedPreResponse {\n const input = raw ?? '';\n const start = input.indexOf(OPEN);\n if (start === -1) return { preResponse: null, text: input };\n\n const bodyStart = start + OPEN.length;\n const end = input.indexOf(CLOSE, bodyStart);\n const body = end === -1 ? input.slice(bodyStart) : input.slice(bodyStart, end);\n const rest = end === -1 ? input.slice(0, start) : input.slice(0, start) + input.slice(end + CLOSE.length);\n const preResponse = body.trim();\n return { preResponse: preResponse || null, text: rest.trimStart() };\n}\n\nexport function isFastLaneRunId(runId: string | null | undefined): boolean {\n if (!runId) return false;\n return /^(fast-|pre-)/i.test(runId);\n}\n"],"names":[],"mappings":"AAQA,MAAM,IAAO,GAAA,gBAAA;AACb,MAAM,KAAQ,GAAA,iBAAA;AACP,SAAS,iBAAiB,GAAmD,EAAA;AAClF,EAAA,MAAM,QAAQ,GAAO,IAAA,IAAA,GAAA,GAAA,GAAA,EAAA;AACrB,EAAM,MAAA,KAAA,GAAQ,KAAM,CAAA,OAAA,CAAQ,IAAI,CAAA;AAChC,EAAI,IAAA,KAAA,KAAU,IAAW,OAAA;AAAA,IACvB,WAAa,EAAA,IAAA;AAAA,IACb,IAAM,EAAA;AAAA,GACR;AACA,EAAM,MAAA,SAAA,GAAY,QAAQ,IAAK,CAAA,MAAA;AAC/B,EAAA,MAAM,GAAM,GAAA,KAAA,CAAM,OAAQ,CAAA,KAAA,EAAO,SAAS,CAAA;AAC1C,EAAM,MAAA,IAAA,GAAO,GAAQ,KAAA,EAAA,GAAK,KAAM,CAAA,KAAA,CAAM,SAAS,CAAI,GAAA,KAAA,CAAM,KAAM,CAAA,SAAA,EAAW,GAAG,CAAA;AAC7E,EAAA,MAAM,OAAO,GAAQ,KAAA,EAAA,GAAK,KAAM,CAAA,KAAA,CAAM,GAAG,KAAK,CAAA,GAAI,KAAM,CAAA,KAAA,CAAM,GAAG,KAAK,CAAA,GAAI,MAAM,KAAM,CAAA,GAAA,GAAM,MAAM,MAAM,CAAA;AACxG,EAAM,MAAA,WAAA,GAAc,KAAK,IAAK,EAAA;AAC9B,EAAO,OAAA;AAAA,IACL,aAAa,WAAe,IAAA,IAAA;AAAA,IAC5B,IAAA,EAAM,KAAK,SAAU;AAAA,GACvB;AACF;AACO,SAAS,gBAAgB,KAA2C,EAAA;AACzE,EAAI,IAAA,CAAC,OAAc,OAAA,KAAA;AACnB,EAAO,OAAA,gBAAA,CAAiB,KAAK,KAAK,CAAA;AACpC"}
1
+ {"version":3,"file":"preResponse.js","sources":["../../../src/features/chat/preResponse.ts"],"sourcesContent":["/**\n * Pre-response parsing for the fast-lane \"Quick answer\" strip.\n * Same contract as the browser `features/chat/preResponse.ts`.\n *\n * The gateway frames the fast answer as `[PRE_RESPONSE]…[/PRE_RESPONSE]`: while\n * it streams the block is still open, and the agent's persisted reply carries\n * the finished block prepended. The transcript draws it as a\n * {@link ./PreResponseStrip}, never as text.\n */\nexport interface ParsedPreResponse {\n preResponse: string | null;\n text: string;\n}\n\nconst OPEN = '[PRE_RESPONSE]';\nconst CLOSE = '[/PRE_RESPONSE]';\n\nexport function splitPreResponse(raw: string | null | undefined): ParsedPreResponse {\n const input = raw ?? '';\n const start = input.indexOf(OPEN);\n if (start === -1) return { preResponse: null, text: input };\n\n const bodyStart = start + OPEN.length;\n const end = input.indexOf(CLOSE, bodyStart);\n const body = end === -1 ? input.slice(bodyStart) : input.slice(bodyStart, end);\n const rest = end === -1 ? input.slice(0, start) : input.slice(0, start) + input.slice(end + CLOSE.length);\n const preResponse = body.trim();\n return { preResponse: preResponse || null, text: rest.trimStart() };\n}\n\n/**\n * Put a fast answer back in front of the agent's reply, in the shape the\n * gateway persists (`wrapPreResponse(pre) + finalText`). A reply completed live\n * then reads exactly like the same reply reloaded from history, so the strip\n * does not drop out at the end of the turn and come back on the next refetch.\n */\nexport function joinPreResponse(preResponse: string | null | undefined, text: string): string {\n const pre = (preResponse ?? '').trim();\n if (!pre || text.includes(OPEN)) return text;\n return `${OPEN}${pre}${CLOSE}${text}`;\n}\n\n/** The quick answer without markdown emphasis or heading marks - the strip draws plain text. */\nexport function plainPreResponse(text: string): string {\n return text\n .replace(/^#{1,6}\\s+/gm, '')\n .replace(/(\\*\\*|__)(.+?)\\1/g, '$2')\n .replace(/`([^`\\n]+)`/g, '$1')\n .trim();\n}\n\nexport function isFastLaneRunId(runId: string | null | undefined): boolean {\n if (!runId) return false;\n return /^(fast-|pre-)/i.test(runId);\n}\n"],"names":[],"mappings":"AAaA,MAAM,IAAO,GAAA,gBAAA;AACb,MAAM,KAAQ,GAAA,iBAAA;AACP,SAAS,iBAAiB,GAAmD,EAAA;AAClF,EAAA,MAAM,QAAQ,GAAO,IAAA,IAAA,GAAA,GAAA,GAAA,EAAA;AACrB,EAAM,MAAA,KAAA,GAAQ,KAAM,CAAA,OAAA,CAAQ,IAAI,CAAA;AAChC,EAAI,IAAA,KAAA,KAAU,IAAW,OAAA;AAAA,IACvB,WAAa,EAAA,IAAA;AAAA,IACb,IAAM,EAAA;AAAA,GACR;AACA,EAAM,MAAA,SAAA,GAAY,QAAQ,IAAK,CAAA,MAAA;AAC/B,EAAA,MAAM,GAAM,GAAA,KAAA,CAAM,OAAQ,CAAA,KAAA,EAAO,SAAS,CAAA;AAC1C,EAAM,MAAA,IAAA,GAAO,GAAQ,KAAA,EAAA,GAAK,KAAM,CAAA,KAAA,CAAM,SAAS,CAAI,GAAA,KAAA,CAAM,KAAM,CAAA,SAAA,EAAW,GAAG,CAAA;AAC7E,EAAA,MAAM,OAAO,GAAQ,KAAA,EAAA,GAAK,KAAM,CAAA,KAAA,CAAM,GAAG,KAAK,CAAA,GAAI,KAAM,CAAA,KAAA,CAAM,GAAG,KAAK,CAAA,GAAI,MAAM,KAAM,CAAA,GAAA,GAAM,MAAM,MAAM,CAAA;AACxG,EAAM,MAAA,WAAA,GAAc,KAAK,IAAK,EAAA;AAC9B,EAAO,OAAA;AAAA,IACL,aAAa,WAAe,IAAA,IAAA;AAAA,IAC5B,IAAA,EAAM,KAAK,SAAU;AAAA,GACvB;AACF;AAQgB,SAAA,eAAA,CAAgB,aAAwC,IAAsB,EAAA;AAC5F,EAAM,MAAA,GAAA,GAAA,CAAO,WAAe,IAAA,IAAA,GAAA,WAAA,GAAA,EAAA,EAAI,IAAK,EAAA;AACrC,EAAA,IAAI,CAAC,GAAO,IAAA,IAAA,CAAK,QAAS,CAAA,IAAI,GAAU,OAAA,IAAA;AACxC,EAAA,OAAO,GAAG,IAAI,CAAA,EAAG,GAAG,CAAG,EAAA,KAAK,GAAG,IAAI,CAAA,CAAA;AACrC;AAGO,SAAS,iBAAiB,IAAsB,EAAA;AACrD,EAAA,OAAO,IAAK,CAAA,OAAA,CAAQ,cAAgB,EAAA,EAAE,CAAE,CAAA,OAAA,CAAQ,mBAAqB,EAAA,IAAI,CAAE,CAAA,OAAA,CAAQ,cAAgB,EAAA,IAAI,EAAE,IAAK,EAAA;AAChH;AACO,SAAS,gBAAgB,KAA2C,EAAA;AACzE,EAAI,IAAA,CAAC,OAAc,OAAA,KAAA;AACnB,EAAO,OAAA,gBAAA,CAAiB,KAAK,KAAK,CAAA;AACpC"}
@@ -20,6 +20,7 @@ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
20
  const COMPUTER_CONNECTOR = "yantra-computer";
21
21
  const GEAR_LINE_RE = /^[ \t]*[⚙🔧\u{1F6E0}]️?[ \t]+(.+?)[ \t]*$/u;
22
22
  const BARE_MCP_LINE_RE = /^[ \t]*(mcp__[^\s].*?)[ \t]*$/;
23
+ const BARE_GEAR_LINE_RE = /^[ \t]*[⚙🔧\u{1F6E0}]️?[ \t]*$/u;
23
24
  const TOOL_LABELS = [[/^tool[_-]?search$/i, "Searching tools"], [/^web[_-]?search$/i, "Searching the web"], [/^web[_-]?scrape$/i, "Reading a page"], [/^web[_-]?batch[_-]?scrape$/i, "Reading pages"], [/^reddit[_-]?search$/i, "Searching Reddit"], [/^x[_-]?search$/i, "Searching X"], [/^youtube[_-]?search$/i, "Searching YouTube"], [/^academic[_-]?search$/i, "Searching papers"], [/^(yantra[_-]?)?github[_-]?search$/i, "Searching GitHub"], [/^bash$/i, "Running a command"], [/^grep$/i, "Searching files"], [/^(str_replace_editor|edit|write)$/i, "Editing files"], [/^read$/i, "Reading files"]];
24
25
  const COMPUTER_ACTION_LABELS = [[/_(RESOLVE|STATUS|PROVIDERS)$/i, "Finding your Computer"], [/_(CREATE|RESUME)$/i, "Starting your Computer"], [/_BROWSE$/i, "Opening a page on your Computer"], [/_READ_PAGE$/i, "Reading the page on your Computer"], [/_SCREENSHOT$/i, "Looking at your Computer screen"], [/_(ACT|INPUT)$/i, "Clicking and typing on your Computer"], [/_ATTACH_FILE$/i, "Attaching a file on your Computer"], [/_(READ_FILE|WRITE_FILE)$/i, "Working with files on your Computer"], [/_EXEC$/i, "Running a command on your Computer"]];
25
26
  const CONNECTOR_LABELS = [[/image[_-]?gen/i, "Generating image"], [/video[_-]?gen/i, "Generating video"], [/(audio|tts|speech|voice)/i, "Generating audio"], [/search/i, "Searching"]];
@@ -122,7 +123,7 @@ function parseToolActivity(content, active = false) {
122
123
  };
123
124
  const steps = [];
124
125
  const kept = [];
125
- for (const line of content.split("\n")) {
126
+ for (const line of content.split("\n").filter((l) => !BARE_GEAR_LINE_RE.test(l))) {
126
127
  const gear = GEAR_LINE_RE.exec(line);
127
128
  const mcp = gear ? null : BARE_MCP_LINE_RE.exec(line);
128
129
  const body = (_a = gear == null ? void 0 : gear[1]) != null ? _a : mcp == null ? void 0 : mcp[1];
@@ -1 +1 @@
1
- {"version":3,"file":"toolActivity.js","sources":["../../../src/features/chat/toolActivity.ts"],"sourcesContent":["/**\n * Tool-activity parsing for the polished status timeline (mobile).\n *\n * A port of the web's `features/chat/toolActivity`, copied rather than imported:\n * mobile is a separate bundle and shares no code with the web shell. It is pure\n * string logic with no DOM or React dependency, so the two are kept in step by\n * having the same tests rather than by a shared module.\n *\n *\n * The cdecli/agent narrates tool use into the streamed assistant text as bare\n * rows, e.g.:\n *\n * ⚙ ToolSearch\n * ⚙ mcp__cdecli__execute_action — yantra-image-generation/Image\n *\n * Rendered raw, these read as debug noise. We parse them out of the prose into\n * structured steps that {@link ./ToolTimeline} renders as an animated,\n * collapsible timeline, and hand back the cleaned prose so the bubble shows only\n * the real answer.\n *\n * Parsing is deliberately conservative: a line only counts as tool activity when\n * it leads with the gear/wrench glyph the agent prefixes, or is a standalone\n * `mcp__…` invocation. Ordinary prose never matches, so a miss degrades to\n * \"leave the text as-is\" rather than eating real content.\n */\n\nexport interface ToolStep {\n /** Stable within a single parse pass — index-based, so identical tools don't collide. */\n id: string;\n /** Humanized present-tense label, e.g. \"Generating image\". */\n label: string;\n /** Optional detail, e.g. the connector slug or raw tool token. */\n detail?: string;\n /** The connector an execute_action row ran on (`yantra-computer`), when it was one. */\n connector?: string;\n /** 'running' only ever applies to the LAST step of an in-flight turn. */\n status: 'running' | 'done';\n}\n\n/** The connector behind the user's cloud Computer - the one browser they can watch and take over. */\nexport const COMPUTER_CONNECTOR = 'yantra-computer';\n\n/** Whether any step of a turn ran on the user's Computer. */\nexport function usesComputer(steps: ToolStep[]): boolean {\n return steps.some((step) => step.connector === COMPUTER_CONNECTOR);\n}\n\nexport interface ParsedToolActivity {\n steps: ToolStep[];\n /** Prose with every tool-activity line removed. */\n text: string;\n}\n\n// A tool-activity line: optional indent, a gear/wrench glyph (with optional\n// emoji variation selector), then the tool token + optional \"— detail\". The `m`\n// flag scopes ^/$ to each line; `u` so the glyph classes match correctly.\nconst GEAR_LINE_RE = /^[ \\t]*[⚙🔧\\u{1F6E0}]️?[ \\t]+(.+?)[ \\t]*$/u;\n// A bare MCP invocation on its own line — the agent sometimes omits the glyph.\n// `mcp__` is vanishingly unlikely in real prose, so this is safe to strip.\nconst BARE_MCP_LINE_RE = /^[ \\t]*(mcp__[^\\s].*?)[ \\t]*$/;\n\n/** Known tool tokens → present-tense labels. Anything unmapped falls back to a humanized token. */\nconst TOOL_LABELS: Array<[RegExp, string]> = [\n [/^tool[_-]?search$/i, 'Searching tools'],\n [/^web[_-]?search$/i, 'Searching the web'],\n [/^web[_-]?scrape$/i, 'Reading a page'],\n [/^web[_-]?batch[_-]?scrape$/i, 'Reading pages'],\n [/^reddit[_-]?search$/i, 'Searching Reddit'],\n [/^x[_-]?search$/i, 'Searching X'],\n [/^youtube[_-]?search$/i, 'Searching YouTube'],\n [/^academic[_-]?search$/i, 'Searching papers'],\n [/^(yantra[_-]?)?github[_-]?search$/i, 'Searching GitHub'],\n [/^bash$/i, 'Running a command'],\n [/^grep$/i, 'Searching files'],\n [/^(str_replace_editor|edit|write)$/i, 'Editing files'],\n [/^read$/i, 'Reading files'],\n];\n\n/**\n * The Computer's actions, by verb. The connector alone (\"Running computer\")\n * says nothing; what the person wants to know is that the agent is on their\n * browser and roughly what it is doing there.\n */\nconst COMPUTER_ACTION_LABELS: Array<[RegExp, string]> = [\n [/_(RESOLVE|STATUS|PROVIDERS)$/i, 'Finding your Computer'],\n [/_(CREATE|RESUME)$/i, 'Starting your Computer'],\n [/_BROWSE$/i, 'Opening a page on your Computer'],\n [/_READ_PAGE$/i, 'Reading the page on your Computer'],\n [/_SCREENSHOT$/i, 'Looking at your Computer screen'],\n [/_(ACT|INPUT)$/i, 'Clicking and typing on your Computer'],\n [/_ATTACH_FILE$/i, 'Attaching a file on your Computer'],\n [/_(READ_FILE|WRITE_FILE)$/i, 'Working with files on your Computer'],\n [/_EXEC$/i, 'Running a command on your Computer'],\n];\n\n/** Connector slug → verb phrase for `execute_action — <connector>/<action>` rows. */\nconst CONNECTOR_LABELS: Array<[RegExp, string]> = [\n [/image[_-]?gen/i, 'Generating image'],\n [/video[_-]?gen/i, 'Generating video'],\n [/(audio|tts|speech|voice)/i, 'Generating audio'],\n [/search/i, 'Searching'],\n];\n\n/** Title-case a slug/token: \"yantra-image-generation\" → \"Yantra image generation\". */\nfunction prettifySlug(slug: string): string {\n const words = slug\n .replace(/^yantra[_-]/i, '')\n .replace(/[_-]+/g, ' ')\n .trim();\n return words ? words.charAt(0).toUpperCase() + words.slice(1) : slug;\n}\n\n/** Shell / interpreter snippets the agent pastes after a bash/read row. */\nexport function isToolPayloadLine(line: string): boolean {\n const t = line.trim();\n if (!t) return false;\n if (/^```/.test(t)) return true;\n if (/^(import\\s+\\w+|from\\s+\\S+\\s+import)\\b/.test(t)) return true;\n if (/^(const|let|var)\\s+\\w+\\s*=/.test(t)) return true;\n if (/\\brequire\\s*\\(/.test(t)) return true;\n if (/^path\\s*=\\s*['\"`]/.test(t)) return true;\n if (/\\/(?:data\\/users\\/)?\\.cdecli-server\\b/.test(t)) return true;\n if (/^(mkdir|curl|chmod|rm |cp |mv |cat |echo |python3?\\b|node\\s+-e|npx |yarn |npm )\\b/.test(t)) return true;\n if (/^(fs\\.|JSON\\.parse|JSON\\.stringify)/.test(t)) return true;\n return false;\n}\n\nfunction looksLikeCommandDetail(detail: string): boolean {\n const t = detail.trim();\n if (!t) return false;\n if (isToolPayloadLine(t)) return true;\n if (/^(python3?|node|mkdir|curl|npx|bash|sh|zsh)\\b/.test(t)) return true;\n if (t.length > 72) return true;\n return false;\n}\n\nfunction sanitizeDetail(detail: string | undefined): string | undefined {\n if (!detail) return undefined;\n if (looksLikeCommandDetail(detail)) return undefined;\n return detail;\n}\n\nfunction stripUnfencedPayloadLines(chunk: string): string {\n return chunk\n .split('\\n')\n .filter((line) => !isToolPayloadLine(line))\n .join('\\n');\n}\n\n/**\n * Drop dumped tool scripts (unfenced bash/python after a gear row).\n * Never strip markdown fences — those are the answer's code examples.\n */\nexport function stripToolPayloads(text: string): string {\n const fenceRe = /```[\\s\\S]*?(?:```|$)/g;\n const out: string[] = [];\n let last = 0;\n let match: RegExpExecArray | null;\n while ((match = fenceRe.exec(text))) {\n out.push(stripUnfencedPayloadLines(text.slice(last, match.index)));\n const block = match[0];\n const isToolDump = /\\/(?:data\\/users\\/)?\\.cdecli-server\\b/.test(block);\n const keepWidget = /\\bpropsComponent\\b/.test(block);\n if (!isToolDump || keepWidget) out.push(block);\n last = match.index + block.length;\n }\n out.push(stripUnfencedPayloadLines(text.slice(last)));\n return out\n .join('')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n\n/**\n * Turn a raw tool row body into a { label, detail }. Handles the two shapes we\n * see: a bare tool name (\"ToolSearch\") and an MCP execute_action carrying a\n * \"connector/action\" detail after an em/en dash.\n */\nexport function humanizeToolLine(body: string): { label: string; detail?: string; connector?: string } {\n const trimmed = body.trim();\n\n // `mcp__cdecli__execute_action — yantra-image-generation/Image`\n const dashSplit = trimmed.split(/\\s+[—–-]\\s+/);\n const head = dashSplit[0]?.trim() ?? trimmed;\n const detailRaw = dashSplit.length > 1 ? dashSplit.slice(1).join(' — ').trim() : undefined;\n\n // Strip an `mcp__<server>__` prefix off the head token.\n const bareTool = head.replace(/^mcp__[^_]+__/i, '').replace(/^mcp__/i, '');\n\n // execute_action rows: the meaning is in the connector detail, not the verb.\n if (/^execute_action$/i.test(bareTool) && detailRaw) {\n const connector = detailRaw.split('/')[0]?.trim() ?? detailRaw;\n if (connector === COMPUTER_CONNECTOR) {\n const action = detailRaw.split('/')[1]?.trim() ?? '';\n const label = COMPUTER_ACTION_LABELS.find(([re]) => re.test(action))?.[1] ?? 'Using your Computer';\n return { label, detail: 'Computer', connector };\n }\n for (const [re, label] of CONNECTOR_LABELS) {\n if (re.test(connector)) return { label, detail: prettifySlug(connector) };\n }\n return { label: `Running ${prettifySlug(connector)}`, detail: sanitizeDetail(detailRaw) };\n }\n\n for (const [re, label] of TOOL_LABELS) {\n if (re.test(bareTool)) return { label, detail: sanitizeDetail(detailRaw) };\n }\n\n // Generic fallback: prettify the token (camelCase / snake_case → spaced).\n const spaced = bareTool\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .trim();\n // Lowercase the WHOLE token, not just its first character: the split above\n // turns \"someNewTool\" into \"some New Tool\", and lowercasing only the head\n // left the label reading \"Running some New Tool\" mid-sentence.\n const label = spaced ? `Running ${spaced.toLowerCase()}` : 'Working';\n return { label, detail: sanitizeDetail(detailRaw) };\n}\n\n/**\n * Extract tool-activity rows from streamed/persisted assistant content.\n *\n * @param content raw assistant text\n * @param active when true the turn is still streaming, so the LAST step is\n * marked 'running'; otherwise every step is 'done'.\n */\nexport function parseToolActivity(content: string, active = false): ParsedToolActivity {\n if (!content) return { steps: [], text: content ?? '' };\n\n const steps: ToolStep[] = [];\n const kept: string[] = [];\n\n for (const line of content.split('\\n')) {\n const gear = GEAR_LINE_RE.exec(line);\n const mcp = gear ? null : BARE_MCP_LINE_RE.exec(line);\n const body = gear?.[1] ?? mcp?.[1];\n if (body) {\n const { label, detail, connector } = humanizeToolLine(body);\n steps.push({ id: `tool-${steps.length}`, label, detail, ...(connector && { connector }), status: 'done' });\n continue;\n }\n kept.push(line);\n }\n\n const joined = kept\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n // Payload stripping is only for dumps that follow tool rows. Applying it\n // to every assistant turn deleted fenced JS/bash examples from the bubble.\n const text = steps.length > 0 ? stripToolPayloads(joined) : joined;\n\n if (active && steps.length > 0) {\n steps[steps.length - 1].status = 'running';\n }\n\n return { steps, text };\n}\n"],"names":["label"],"mappings":"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"}
1
+ {"version":3,"file":"toolActivity.js","sources":["../../../src/features/chat/toolActivity.ts"],"sourcesContent":["/**\n * Tool-activity parsing for the polished status timeline (mobile).\n *\n * A port of the web's `features/chat/toolActivity`, copied rather than imported:\n * mobile is a separate bundle and shares no code with the web shell. It is pure\n * string logic with no DOM or React dependency, so the two are kept in step by\n * having the same tests rather than by a shared module.\n *\n *\n * The cdecli/agent narrates tool use into the streamed assistant text as bare\n * rows, e.g.:\n *\n * ⚙ ToolSearch\n * ⚙ mcp__cdecli__execute_action — yantra-image-generation/Image\n *\n * Rendered raw, these read as debug noise. We parse them out of the prose into\n * structured steps that {@link ./ToolTimeline} renders as an animated,\n * collapsible timeline, and hand back the cleaned prose so the bubble shows only\n * the real answer.\n *\n * Parsing is deliberately conservative: a line only counts as tool activity when\n * it leads with the gear/wrench glyph the agent prefixes, or is a standalone\n * `mcp__…` invocation. Ordinary prose never matches, so a miss degrades to\n * \"leave the text as-is\" rather than eating real content.\n */\n\nexport interface ToolStep {\n /** Stable within a single parse pass — index-based, so identical tools don't collide. */\n id: string;\n /** Humanized present-tense label, e.g. \"Generating image\". */\n label: string;\n /** Optional detail, e.g. the connector slug or raw tool token. */\n detail?: string;\n /** The connector an execute_action row ran on (`yantra-computer`), when it was one. */\n connector?: string;\n /** 'running' only ever applies to the LAST step of an in-flight turn. */\n status: 'running' | 'done';\n}\n\n/** The connector behind the user's cloud Computer - the one browser they can watch and take over. */\nexport const COMPUTER_CONNECTOR = 'yantra-computer';\n\n/** Whether any step of a turn ran on the user's Computer. */\nexport function usesComputer(steps: ToolStep[]): boolean {\n return steps.some((step) => step.connector === COMPUTER_CONNECTOR);\n}\n\nexport interface ParsedToolActivity {\n steps: ToolStep[];\n /** Prose with every tool-activity line removed. */\n text: string;\n}\n\n// A tool-activity line: optional indent, a gear/wrench glyph (with optional\n// emoji variation selector), then the tool token + optional \"— detail\". The `m`\n// flag scopes ^/$ to each line; `u` so the glyph classes match correctly.\nconst GEAR_LINE_RE = /^[ \\t]*[⚙🔧\\u{1F6E0}]️?[ \\t]+(.+?)[ \\t]*$/u;\n// A bare MCP invocation on its own line — the agent sometimes omits the glyph.\n// `mcp__` is vanishingly unlikely in real prose, so this is safe to strip.\nconst BARE_MCP_LINE_RE = /^[ \\t]*(mcp__[^\\s].*?)[ \\t]*$/;\n// The glyph alone: a tool row whose name has not streamed in yet. It is not\n// prose, and painted as text it trails the answer as a stray gear.\nconst BARE_GEAR_LINE_RE = /^[ \\t]*[⚙🔧\\u{1F6E0}]️?[ \\t]*$/u;\n\n/** Known tool tokens → present-tense labels. Anything unmapped falls back to a humanized token. */\nconst TOOL_LABELS: Array<[RegExp, string]> = [\n [/^tool[_-]?search$/i, 'Searching tools'],\n [/^web[_-]?search$/i, 'Searching the web'],\n [/^web[_-]?scrape$/i, 'Reading a page'],\n [/^web[_-]?batch[_-]?scrape$/i, 'Reading pages'],\n [/^reddit[_-]?search$/i, 'Searching Reddit'],\n [/^x[_-]?search$/i, 'Searching X'],\n [/^youtube[_-]?search$/i, 'Searching YouTube'],\n [/^academic[_-]?search$/i, 'Searching papers'],\n [/^(yantra[_-]?)?github[_-]?search$/i, 'Searching GitHub'],\n [/^bash$/i, 'Running a command'],\n [/^grep$/i, 'Searching files'],\n [/^(str_replace_editor|edit|write)$/i, 'Editing files'],\n [/^read$/i, 'Reading files'],\n];\n\n/**\n * The Computer's actions, by verb. The connector alone (\"Running computer\")\n * says nothing; what the person wants to know is that the agent is on their\n * browser and roughly what it is doing there.\n */\nconst COMPUTER_ACTION_LABELS: Array<[RegExp, string]> = [\n [/_(RESOLVE|STATUS|PROVIDERS)$/i, 'Finding your Computer'],\n [/_(CREATE|RESUME)$/i, 'Starting your Computer'],\n [/_BROWSE$/i, 'Opening a page on your Computer'],\n [/_READ_PAGE$/i, 'Reading the page on your Computer'],\n [/_SCREENSHOT$/i, 'Looking at your Computer screen'],\n [/_(ACT|INPUT)$/i, 'Clicking and typing on your Computer'],\n [/_ATTACH_FILE$/i, 'Attaching a file on your Computer'],\n [/_(READ_FILE|WRITE_FILE)$/i, 'Working with files on your Computer'],\n [/_EXEC$/i, 'Running a command on your Computer'],\n];\n\n/** Connector slug → verb phrase for `execute_action — <connector>/<action>` rows. */\nconst CONNECTOR_LABELS: Array<[RegExp, string]> = [\n [/image[_-]?gen/i, 'Generating image'],\n [/video[_-]?gen/i, 'Generating video'],\n [/(audio|tts|speech|voice)/i, 'Generating audio'],\n [/search/i, 'Searching'],\n];\n\n/** Title-case a slug/token: \"yantra-image-generation\" → \"Yantra image generation\". */\nfunction prettifySlug(slug: string): string {\n const words = slug\n .replace(/^yantra[_-]/i, '')\n .replace(/[_-]+/g, ' ')\n .trim();\n return words ? words.charAt(0).toUpperCase() + words.slice(1) : slug;\n}\n\n/** Shell / interpreter snippets the agent pastes after a bash/read row. */\nexport function isToolPayloadLine(line: string): boolean {\n const t = line.trim();\n if (!t) return false;\n if (/^```/.test(t)) return true;\n if (/^(import\\s+\\w+|from\\s+\\S+\\s+import)\\b/.test(t)) return true;\n if (/^(const|let|var)\\s+\\w+\\s*=/.test(t)) return true;\n if (/\\brequire\\s*\\(/.test(t)) return true;\n if (/^path\\s*=\\s*['\"`]/.test(t)) return true;\n if (/\\/(?:data\\/users\\/)?\\.cdecli-server\\b/.test(t)) return true;\n if (/^(mkdir|curl|chmod|rm |cp |mv |cat |echo |python3?\\b|node\\s+-e|npx |yarn |npm )\\b/.test(t)) return true;\n if (/^(fs\\.|JSON\\.parse|JSON\\.stringify)/.test(t)) return true;\n return false;\n}\n\nfunction looksLikeCommandDetail(detail: string): boolean {\n const t = detail.trim();\n if (!t) return false;\n if (isToolPayloadLine(t)) return true;\n if (/^(python3?|node|mkdir|curl|npx|bash|sh|zsh)\\b/.test(t)) return true;\n if (t.length > 72) return true;\n return false;\n}\n\nfunction sanitizeDetail(detail: string | undefined): string | undefined {\n if (!detail) return undefined;\n if (looksLikeCommandDetail(detail)) return undefined;\n return detail;\n}\n\nfunction stripUnfencedPayloadLines(chunk: string): string {\n return chunk\n .split('\\n')\n .filter((line) => !isToolPayloadLine(line))\n .join('\\n');\n}\n\n/**\n * Drop dumped tool scripts (unfenced bash/python after a gear row).\n * Never strip markdown fences — those are the answer's code examples.\n */\nexport function stripToolPayloads(text: string): string {\n const fenceRe = /```[\\s\\S]*?(?:```|$)/g;\n const out: string[] = [];\n let last = 0;\n let match: RegExpExecArray | null;\n while ((match = fenceRe.exec(text))) {\n out.push(stripUnfencedPayloadLines(text.slice(last, match.index)));\n const block = match[0];\n const isToolDump = /\\/(?:data\\/users\\/)?\\.cdecli-server\\b/.test(block);\n const keepWidget = /\\bpropsComponent\\b/.test(block);\n if (!isToolDump || keepWidget) out.push(block);\n last = match.index + block.length;\n }\n out.push(stripUnfencedPayloadLines(text.slice(last)));\n return out\n .join('')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n\n/**\n * Turn a raw tool row body into a { label, detail }. Handles the two shapes we\n * see: a bare tool name (\"ToolSearch\") and an MCP execute_action carrying a\n * \"connector/action\" detail after an em/en dash.\n */\nexport function humanizeToolLine(body: string): { label: string; detail?: string; connector?: string } {\n const trimmed = body.trim();\n\n // `mcp__cdecli__execute_action — yantra-image-generation/Image`\n const dashSplit = trimmed.split(/\\s+[—–-]\\s+/);\n const head = dashSplit[0]?.trim() ?? trimmed;\n const detailRaw = dashSplit.length > 1 ? dashSplit.slice(1).join(' — ').trim() : undefined;\n\n // Strip an `mcp__<server>__` prefix off the head token.\n const bareTool = head.replace(/^mcp__[^_]+__/i, '').replace(/^mcp__/i, '');\n\n // execute_action rows: the meaning is in the connector detail, not the verb.\n if (/^execute_action$/i.test(bareTool) && detailRaw) {\n const connector = detailRaw.split('/')[0]?.trim() ?? detailRaw;\n if (connector === COMPUTER_CONNECTOR) {\n const action = detailRaw.split('/')[1]?.trim() ?? '';\n const label = COMPUTER_ACTION_LABELS.find(([re]) => re.test(action))?.[1] ?? 'Using your Computer';\n return { label, detail: 'Computer', connector };\n }\n for (const [re, label] of CONNECTOR_LABELS) {\n if (re.test(connector)) return { label, detail: prettifySlug(connector) };\n }\n return { label: `Running ${prettifySlug(connector)}`, detail: sanitizeDetail(detailRaw) };\n }\n\n for (const [re, label] of TOOL_LABELS) {\n if (re.test(bareTool)) return { label, detail: sanitizeDetail(detailRaw) };\n }\n\n // Generic fallback: prettify the token (camelCase / snake_case → spaced).\n const spaced = bareTool\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .trim();\n // Lowercase the WHOLE token, not just its first character: the split above\n // turns \"someNewTool\" into \"some New Tool\", and lowercasing only the head\n // left the label reading \"Running some New Tool\" mid-sentence.\n const label = spaced ? `Running ${spaced.toLowerCase()}` : 'Working';\n return { label, detail: sanitizeDetail(detailRaw) };\n}\n\n/**\n * Extract tool-activity rows from streamed/persisted assistant content.\n *\n * @param content raw assistant text\n * @param active when true the turn is still streaming, so the LAST step is\n * marked 'running'; otherwise every step is 'done'.\n */\nexport function parseToolActivity(content: string, active = false): ParsedToolActivity {\n if (!content) return { steps: [], text: content ?? '' };\n\n const steps: ToolStep[] = [];\n const kept: string[] = [];\n\n for (const line of content.split('\\n').filter((l) => !BARE_GEAR_LINE_RE.test(l))) {\n const gear = GEAR_LINE_RE.exec(line);\n const mcp = gear ? null : BARE_MCP_LINE_RE.exec(line);\n const body = gear?.[1] ?? mcp?.[1];\n if (body) {\n const { label, detail, connector } = humanizeToolLine(body);\n steps.push({ id: `tool-${steps.length}`, label, detail, ...(connector && { connector }), status: 'done' });\n continue;\n }\n kept.push(line);\n }\n\n const joined = kept\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n // Payload stripping is only for dumps that follow tool rows. Applying it\n // to every assistant turn deleted fenced JS/bash examples from the bubble.\n const text = steps.length > 0 ? stripToolPayloads(joined) : joined;\n\n if (active && steps.length > 0) {\n steps[steps.length - 1].status = 'running';\n }\n\n return { steps, text };\n}\n"],"names":["label"],"mappings":"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"}