@adminide-stack/yantra-mobile 12.0.51-alpha.7 → 12.0.52-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/features/chat/AskUserCard.js +244 -0
- package/lib/features/chat/AskUserCard.js.map +1 -0
- package/lib/features/chat/ChatTranscript.js +38 -9
- package/lib/features/chat/ChatTranscript.js.map +1 -1
- package/lib/features/chat/ToolTimeline.js +118 -0
- package/lib/features/chat/ToolTimeline.js.map +1 -0
- package/lib/features/chat/askUser.js +40 -0
- package/lib/features/chat/askUser.js.map +1 -0
- package/lib/features/chat/toolActivity.js +77 -0
- package/lib/features/chat/toolActivity.js.map +1 -0
- package/lib/screens/Chat/index.js +6 -1
- package/lib/screens/Chat/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import {jsxs,jsx}from'react/jsx-runtime';import {memo,useState,useMemo,useCallback}from'react';import {View,StyleSheet,Text,Pressable,TextInput}from'react-native';import {formatAskUserAnswers}from'./askUser.js';var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
const ACCENT = "#2563eb";
|
|
21
|
+
const AskUserCard = memo(function AskUserCard2({
|
|
22
|
+
payload,
|
|
23
|
+
isDark = false,
|
|
24
|
+
disabled = false,
|
|
25
|
+
onAnswer,
|
|
26
|
+
onDismiss
|
|
27
|
+
}) {
|
|
28
|
+
const [answers, setAnswers] = useState({});
|
|
29
|
+
const palette = useMemo(() => ({
|
|
30
|
+
card: isDark ? "#111827" : "#ffffff",
|
|
31
|
+
border: isDark ? "#1f2937" : "#e5e7eb",
|
|
32
|
+
title: isDark ? "#f1f5f9" : "#111827",
|
|
33
|
+
muted: isDark ? "#94a3b8" : "#6b7280",
|
|
34
|
+
optionBg: isDark ? "#0b1220" : "#f9fafb",
|
|
35
|
+
optionBorder: isDark ? "#243045" : "#e5e7eb",
|
|
36
|
+
inputBg: isDark ? "#0b1220" : "#f9fafb"
|
|
37
|
+
}), [isDark]);
|
|
38
|
+
const selectedLabels = useCallback((q) => {
|
|
39
|
+
var _a;
|
|
40
|
+
const raw = (_a = answers[q.id]) != null ? _a : "";
|
|
41
|
+
if (!raw.trim()) return [];
|
|
42
|
+
if (!q.multiSelect) return [raw];
|
|
43
|
+
return raw.split(",").map((v) => v.trim()).filter(Boolean);
|
|
44
|
+
}, [answers]);
|
|
45
|
+
const toggle = useCallback((q, label) => {
|
|
46
|
+
if (disabled) return;
|
|
47
|
+
setAnswers((prev) => {
|
|
48
|
+
var _a;
|
|
49
|
+
const current = (_a = prev[q.id]) != null ? _a : "";
|
|
50
|
+
if (!q.multiSelect) {
|
|
51
|
+
return __spreadProps(__spreadValues({}, prev), {
|
|
52
|
+
[q.id]: current === label ? "" : label
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const parts = current.split(",").map((v) => v.trim()).filter(Boolean);
|
|
56
|
+
const next = parts.includes(label) ? parts.filter((v) => v !== label) : [...parts, label];
|
|
57
|
+
return __spreadProps(__spreadValues({}, prev), {
|
|
58
|
+
[q.id]: next.join(", ")
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}, [disabled]);
|
|
62
|
+
const setText = useCallback((q, value) => {
|
|
63
|
+
setAnswers((prev) => __spreadProps(__spreadValues({}, prev), {
|
|
64
|
+
[q.id]: value
|
|
65
|
+
}));
|
|
66
|
+
}, []);
|
|
67
|
+
const formatted = formatAskUserAnswers(payload.questions, answers);
|
|
68
|
+
const canSend = formatted.trim().length > 0 && !disabled;
|
|
69
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.card, {
|
|
70
|
+
backgroundColor: palette.card,
|
|
71
|
+
borderColor: palette.border
|
|
72
|
+
}], children: [
|
|
73
|
+
payload.questions.map((q) => {
|
|
74
|
+
var _a, _b, _c, _d;
|
|
75
|
+
const chosen = selectedLabels(q);
|
|
76
|
+
const optionLabels = new Set(((_a = q.options) != null ? _a : []).map((o) => o.label));
|
|
77
|
+
const textValue = chosen.every((c) => optionLabels.has(c)) ? "" : (_b = answers[q.id]) != null ? _b : "";
|
|
78
|
+
return /* @__PURE__ */ jsxs(View, { style: styles.question, children: [
|
|
79
|
+
q.header ? /* @__PURE__ */ jsx(Text, { style: [styles.eyebrow, {
|
|
80
|
+
color: palette.muted
|
|
81
|
+
}], children: q.header.toUpperCase() }) : null,
|
|
82
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.title, {
|
|
83
|
+
color: palette.title
|
|
84
|
+
}], children: q.title }),
|
|
85
|
+
q.multiSelect ? /* @__PURE__ */ jsx(Text, { style: [styles.hint, {
|
|
86
|
+
color: palette.muted
|
|
87
|
+
}], children: "Pick as many as apply." }) : null,
|
|
88
|
+
((_c = q.options) != null ? _c : []).map((o) => {
|
|
89
|
+
const active = chosen.includes(o.label);
|
|
90
|
+
return /* @__PURE__ */ jsxs(Pressable, { accessibilityRole: "button", accessibilityState: {
|
|
91
|
+
selected: active,
|
|
92
|
+
disabled
|
|
93
|
+
}, accessibilityLabel: o.detail ? `${o.label}. ${o.detail}` : o.label, disabled, onPress: () => toggle(q, o.label), style: ({
|
|
94
|
+
pressed
|
|
95
|
+
}) => [styles.option, {
|
|
96
|
+
backgroundColor: active ? `${ACCENT}14` : palette.optionBg,
|
|
97
|
+
borderColor: active ? ACCENT : palette.optionBorder,
|
|
98
|
+
opacity: pressed && !disabled ? 0.75 : 1
|
|
99
|
+
}], children: [
|
|
100
|
+
/* @__PURE__ */ jsxs(View, { style: styles.optionBody, children: [
|
|
101
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.optionLabel, {
|
|
102
|
+
color: palette.title
|
|
103
|
+
}], children: o.label }),
|
|
104
|
+
o.detail ? /* @__PURE__ */ jsx(Text, { style: [styles.optionDetail, {
|
|
105
|
+
color: palette.muted
|
|
106
|
+
}], children: o.detail }) : null
|
|
107
|
+
] }),
|
|
108
|
+
/* @__PURE__ */ jsx(View, { style: [styles.tick, {
|
|
109
|
+
borderColor: active ? ACCENT : palette.optionBorder,
|
|
110
|
+
backgroundColor: active ? ACCENT : "transparent"
|
|
111
|
+
}], children: active ? /* @__PURE__ */ jsx(Text, { style: styles.tickMark, children: "\u2713" }) : null })
|
|
112
|
+
] }, o.id);
|
|
113
|
+
}),
|
|
114
|
+
q.allowText !== false ? /* @__PURE__ */ jsx(TextInput, { value: textValue, onChangeText: (v) => setText(q, v), editable: !disabled, placeholder: (_d = q.textPlaceholder) != null ? _d : "Or type your own answer\u2026", placeholderTextColor: palette.muted, style: [styles.input, {
|
|
115
|
+
backgroundColor: palette.inputBg,
|
|
116
|
+
borderColor: palette.optionBorder,
|
|
117
|
+
color: palette.title
|
|
118
|
+
}] }) : null
|
|
119
|
+
] }, q.id);
|
|
120
|
+
}),
|
|
121
|
+
/* @__PURE__ */ jsxs(View, { style: styles.actions, children: [
|
|
122
|
+
/* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", onPress: onDismiss, disabled, style: ({
|
|
123
|
+
pressed
|
|
124
|
+
}) => [styles.skip, {
|
|
125
|
+
opacity: pressed ? 0.6 : 1
|
|
126
|
+
}], children: /* @__PURE__ */ jsx(Text, { style: [styles.skipText, {
|
|
127
|
+
color: palette.muted
|
|
128
|
+
}], children: "Skip" }) }),
|
|
129
|
+
/* @__PURE__ */ jsx(Pressable, { accessibilityRole: "button", accessibilityState: {
|
|
130
|
+
disabled: !canSend
|
|
131
|
+
}, onPress: () => canSend && onAnswer(formatted), disabled: !canSend, style: ({
|
|
132
|
+
pressed
|
|
133
|
+
}) => [styles.send, {
|
|
134
|
+
backgroundColor: canSend ? ACCENT : palette.optionBorder,
|
|
135
|
+
opacity: pressed ? 0.85 : 1
|
|
136
|
+
}], children: /* @__PURE__ */ jsx(Text, { style: [styles.sendText, {
|
|
137
|
+
color: canSend ? "#ffffff" : palette.muted
|
|
138
|
+
}], children: "Send" }) })
|
|
139
|
+
] })
|
|
140
|
+
] });
|
|
141
|
+
});
|
|
142
|
+
const styles = StyleSheet.create({
|
|
143
|
+
card: {
|
|
144
|
+
width: "100%",
|
|
145
|
+
borderWidth: 1,
|
|
146
|
+
borderRadius: 16,
|
|
147
|
+
padding: 14,
|
|
148
|
+
marginTop: 4,
|
|
149
|
+
marginBottom: 10
|
|
150
|
+
},
|
|
151
|
+
question: {
|
|
152
|
+
marginBottom: 12
|
|
153
|
+
},
|
|
154
|
+
eyebrow: {
|
|
155
|
+
fontSize: 11,
|
|
156
|
+
fontWeight: "600",
|
|
157
|
+
letterSpacing: 1,
|
|
158
|
+
marginBottom: 4
|
|
159
|
+
},
|
|
160
|
+
title: {
|
|
161
|
+
fontSize: 15,
|
|
162
|
+
fontWeight: "700",
|
|
163
|
+
lineHeight: 21,
|
|
164
|
+
marginBottom: 4
|
|
165
|
+
},
|
|
166
|
+
hint: {
|
|
167
|
+
fontSize: 12,
|
|
168
|
+
marginBottom: 6
|
|
169
|
+
},
|
|
170
|
+
option: {
|
|
171
|
+
flexDirection: "row",
|
|
172
|
+
alignItems: "center",
|
|
173
|
+
borderWidth: 1,
|
|
174
|
+
borderRadius: 12,
|
|
175
|
+
paddingHorizontal: 12,
|
|
176
|
+
paddingVertical: 10,
|
|
177
|
+
marginTop: 6,
|
|
178
|
+
minHeight: 44
|
|
179
|
+
// iOS minimum touch target
|
|
180
|
+
},
|
|
181
|
+
optionBody: {
|
|
182
|
+
flex: 1,
|
|
183
|
+
paddingRight: 10
|
|
184
|
+
},
|
|
185
|
+
optionLabel: {
|
|
186
|
+
fontSize: 14,
|
|
187
|
+
fontWeight: "600"
|
|
188
|
+
},
|
|
189
|
+
optionDetail: {
|
|
190
|
+
fontSize: 12,
|
|
191
|
+
marginTop: 2,
|
|
192
|
+
lineHeight: 16
|
|
193
|
+
},
|
|
194
|
+
tick: {
|
|
195
|
+
width: 22,
|
|
196
|
+
height: 22,
|
|
197
|
+
borderRadius: 11,
|
|
198
|
+
borderWidth: 1,
|
|
199
|
+
alignItems: "center",
|
|
200
|
+
justifyContent: "center"
|
|
201
|
+
},
|
|
202
|
+
tickMark: {
|
|
203
|
+
color: "#ffffff",
|
|
204
|
+
fontSize: 13,
|
|
205
|
+
fontWeight: "700",
|
|
206
|
+
lineHeight: 16
|
|
207
|
+
},
|
|
208
|
+
input: {
|
|
209
|
+
borderWidth: 1,
|
|
210
|
+
borderRadius: 12,
|
|
211
|
+
paddingHorizontal: 12,
|
|
212
|
+
paddingVertical: 10,
|
|
213
|
+
marginTop: 8,
|
|
214
|
+
fontSize: 14,
|
|
215
|
+
minHeight: 44
|
|
216
|
+
},
|
|
217
|
+
actions: {
|
|
218
|
+
flexDirection: "row",
|
|
219
|
+
alignItems: "center",
|
|
220
|
+
justifyContent: "flex-end",
|
|
221
|
+
gap: 8
|
|
222
|
+
},
|
|
223
|
+
skip: {
|
|
224
|
+
paddingHorizontal: 14,
|
|
225
|
+
paddingVertical: 10,
|
|
226
|
+
minHeight: 44,
|
|
227
|
+
justifyContent: "center"
|
|
228
|
+
},
|
|
229
|
+
skipText: {
|
|
230
|
+
fontSize: 14,
|
|
231
|
+
fontWeight: "600"
|
|
232
|
+
},
|
|
233
|
+
send: {
|
|
234
|
+
paddingHorizontal: 20,
|
|
235
|
+
paddingVertical: 10,
|
|
236
|
+
borderRadius: 12,
|
|
237
|
+
minHeight: 44,
|
|
238
|
+
justifyContent: "center"
|
|
239
|
+
},
|
|
240
|
+
sendText: {
|
|
241
|
+
fontSize: 14,
|
|
242
|
+
fontWeight: "700"
|
|
243
|
+
}
|
|
244
|
+
});export{AskUserCard};//# sourceMappingURL=AskUserCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AskUserCard.js","sources":["../../../src/features/chat/AskUserCard.tsx"],"sourcesContent":["/**\n * <AskUserCard> — the `[ASK_USER]` clarifying question, as a touch card.\n *\n * Deliberately NOT a port of the web card. The web pages through questions with\n * a Next/Skip footer and keyboard shortcuts; on a phone the whole card sits\n * above the keyboard, so every question is laid out in one scrollable column and\n * options are full-width rows with a 44pt minimum target. One \"Send\" commits the\n * lot, matching how the answer travels anyway: a single formatted message.\n *\n * The reply goes out through the ordinary send path (onAnswer -> sendMessage),\n * which is what releases cdecli's parked turn.\n */\nimport React, { memo, useCallback, useMemo, useState } from 'react';\nimport { Pressable, StyleSheet, Text, TextInput, View } from 'react-native';\nimport { formatAskUserAnswers, type AskUserPayload, type AskUserQuestion } from './askUser';\n\nconst ACCENT = '#2563eb';\n\nexport const AskUserCard = memo(function AskUserCard({\n payload,\n isDark = false,\n disabled = false,\n onAnswer,\n onDismiss,\n}: {\n payload: AskUserPayload;\n isDark?: boolean;\n disabled?: boolean;\n onAnswer: (formatted: string) => void;\n onDismiss: () => void;\n}) {\n // One entry per question: an option label, comma-joined labels for a\n // multi-select, or free text. Same string either way, so formatting and\n // \"has the user answered anything?\" stay trivial.\n const [answers, setAnswers] = useState<Record<string, string>>({});\n\n const palette = useMemo(\n () => ({\n card: isDark ? '#111827' : '#ffffff',\n border: isDark ? '#1f2937' : '#e5e7eb',\n title: isDark ? '#f1f5f9' : '#111827',\n muted: isDark ? '#94a3b8' : '#6b7280',\n optionBg: isDark ? '#0b1220' : '#f9fafb',\n optionBorder: isDark ? '#243045' : '#e5e7eb',\n inputBg: isDark ? '#0b1220' : '#f9fafb',\n }),\n [isDark],\n );\n\n const selectedLabels = useCallback(\n (q: AskUserQuestion): string[] => {\n const raw = answers[q.id] ?? '';\n if (!raw.trim()) return [];\n if (!q.multiSelect) return [raw];\n return raw\n .split(',')\n .map((v) => v.trim())\n .filter(Boolean);\n },\n [answers],\n );\n\n const toggle = useCallback(\n (q: AskUserQuestion, label: string) => {\n if (disabled) return;\n setAnswers((prev) => {\n const current = prev[q.id] ?? '';\n if (!q.multiSelect) {\n // Tapping the chosen option again clears it, so a mis-tap is\n // recoverable without a separate \"clear\" affordance.\n return { ...prev, [q.id]: current === label ? '' : label };\n }\n const parts = current\n .split(',')\n .map((v) => v.trim())\n .filter(Boolean);\n const next = parts.includes(label) ? parts.filter((v) => v !== label) : [...parts, label];\n return { ...prev, [q.id]: next.join(', ') };\n });\n },\n [disabled],\n );\n\n const setText = useCallback((q: AskUserQuestion, value: string) => {\n setAnswers((prev) => ({ ...prev, [q.id]: value }));\n }, []);\n\n const formatted = formatAskUserAnswers(payload.questions, answers);\n const canSend = formatted.trim().length > 0 && !disabled;\n\n return (\n <View style={[styles.card, { backgroundColor: palette.card, borderColor: palette.border }]}>\n {payload.questions.map((q) => {\n const chosen = selectedLabels(q);\n const optionLabels = new Set((q.options ?? []).map((o) => o.label));\n // Free text is whatever is not one of the offered labels, so\n // switching between an option and typing does not lose the draft.\n const textValue = chosen.every((c) => optionLabels.has(c)) ? '' : (answers[q.id] ?? '');\n return (\n <View key={q.id} style={styles.question}>\n {q.header ? (\n <Text style={[styles.eyebrow, { color: palette.muted }]}>{q.header.toUpperCase()}</Text>\n ) : null}\n <Text style={[styles.title, { color: palette.title }]}>{q.title}</Text>\n {q.multiSelect ? (\n <Text style={[styles.hint, { color: palette.muted }]}>Pick as many as apply.</Text>\n ) : null}\n\n {(q.options ?? []).map((o) => {\n const active = chosen.includes(o.label);\n return (\n <Pressable\n key={o.id}\n accessibilityRole=\"button\"\n accessibilityState={{ selected: active, disabled }}\n accessibilityLabel={o.detail ? `${o.label}. ${o.detail}` : o.label}\n disabled={disabled}\n onPress={() => toggle(q, o.label)}\n style={({ pressed }) => [\n styles.option,\n {\n backgroundColor: active ? `${ACCENT}14` : palette.optionBg,\n borderColor: active ? ACCENT : palette.optionBorder,\n opacity: pressed && !disabled ? 0.75 : 1,\n },\n ]}\n >\n <View style={styles.optionBody}>\n <Text style={[styles.optionLabel, { color: palette.title }]}>{o.label}</Text>\n {o.detail ? (\n <Text style={[styles.optionDetail, { color: palette.muted }]}>\n {o.detail}\n </Text>\n ) : null}\n </View>\n <View\n style={[\n styles.tick,\n {\n borderColor: active ? ACCENT : palette.optionBorder,\n backgroundColor: active ? ACCENT : 'transparent',\n },\n ]}\n >\n {active ? <Text style={styles.tickMark}>✓</Text> : null}\n </View>\n </Pressable>\n );\n })}\n\n {q.allowText !== false ? (\n <TextInput\n value={textValue}\n onChangeText={(v) => setText(q, v)}\n editable={!disabled}\n placeholder={q.textPlaceholder ?? 'Or type your own answer…'}\n placeholderTextColor={palette.muted}\n style={[\n styles.input,\n {\n backgroundColor: palette.inputBg,\n borderColor: palette.optionBorder,\n color: palette.title,\n },\n ]}\n />\n ) : null}\n </View>\n );\n })}\n\n <View style={styles.actions}>\n <Pressable\n accessibilityRole=\"button\"\n onPress={onDismiss}\n disabled={disabled}\n style={({ pressed }) => [styles.skip, { opacity: pressed ? 0.6 : 1 }]}\n >\n <Text style={[styles.skipText, { color: palette.muted }]}>Skip</Text>\n </Pressable>\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ disabled: !canSend }}\n onPress={() => canSend && onAnswer(formatted)}\n disabled={!canSend}\n style={({ pressed }) => [\n styles.send,\n { backgroundColor: canSend ? ACCENT : palette.optionBorder, opacity: pressed ? 0.85 : 1 },\n ]}\n >\n <Text style={[styles.sendText, { color: canSend ? '#ffffff' : palette.muted }]}>Send</Text>\n </Pressable>\n </View>\n </View>\n );\n});\n\nconst styles = StyleSheet.create({\n card: {\n width: '100%',\n borderWidth: 1,\n borderRadius: 16,\n padding: 14,\n marginTop: 4,\n marginBottom: 10,\n },\n question: { marginBottom: 12 },\n eyebrow: { fontSize: 11, fontWeight: '600', letterSpacing: 1, marginBottom: 4 },\n title: { fontSize: 15, fontWeight: '700', lineHeight: 21, marginBottom: 4 },\n hint: { fontSize: 12, marginBottom: 6 },\n option: {\n flexDirection: 'row',\n alignItems: 'center',\n borderWidth: 1,\n borderRadius: 12,\n paddingHorizontal: 12,\n paddingVertical: 10,\n marginTop: 6,\n minHeight: 44, // iOS minimum touch target\n },\n optionBody: { flex: 1, paddingRight: 10 },\n optionLabel: { fontSize: 14, fontWeight: '600' },\n optionDetail: { fontSize: 12, marginTop: 2, lineHeight: 16 },\n tick: {\n width: 22,\n height: 22,\n borderRadius: 11,\n borderWidth: 1,\n alignItems: 'center',\n justifyContent: 'center',\n },\n tickMark: { color: '#ffffff', fontSize: 13, fontWeight: '700', lineHeight: 16 },\n input: {\n borderWidth: 1,\n borderRadius: 12,\n paddingHorizontal: 12,\n paddingVertical: 10,\n marginTop: 8,\n fontSize: 14,\n minHeight: 44,\n },\n actions: { flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end', gap: 8 },\n skip: { paddingHorizontal: 14, paddingVertical: 10, minHeight: 44, justifyContent: 'center' },\n skipText: { fontSize: 14, fontWeight: '600' },\n send: {\n paddingHorizontal: 20,\n paddingVertical: 10,\n borderRadius: 12,\n minHeight: 44,\n justifyContent: 'center',\n },\n sendText: { fontSize: 14, fontWeight: '700' },\n});\n"],"names":["AskUserCard"],"mappings":"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"}
|
|
@@ -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 React,{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 {AskUserCard}from'./AskUserCard.js';import {ToolTimeline}from'./ToolTimeline.js';import {parseToolActivity}from'./toolActivity.js';import {pendingAskUser,stripAskUser}from'./askUser.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __defProps = Object.defineProperties;
|
|
3
3
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -23,7 +23,9 @@ function ChatTranscript({
|
|
|
23
23
|
streamingContent,
|
|
24
24
|
renderMessageActions,
|
|
25
25
|
listContentStyle,
|
|
26
|
-
isDark = false
|
|
26
|
+
isDark = false,
|
|
27
|
+
onAskUserAnswer,
|
|
28
|
+
sending = false
|
|
27
29
|
}) {
|
|
28
30
|
const listRef = useRef(null);
|
|
29
31
|
const stickToBottomRef = useRef(true);
|
|
@@ -35,14 +37,25 @@ function ChatTranscript({
|
|
|
35
37
|
})), [messages]);
|
|
36
38
|
const rows = useMemo(() => {
|
|
37
39
|
const stream = streamingContent == null ? void 0 : streamingContent.trim();
|
|
38
|
-
|
|
39
|
-
return [...historyRows, {
|
|
40
|
+
const withStream = stream ? [...historyRows, {
|
|
40
41
|
id: "__streaming__",
|
|
41
42
|
role: "assistant",
|
|
42
43
|
content: stream,
|
|
43
44
|
streaming: true
|
|
44
|
-
}];
|
|
45
|
-
|
|
45
|
+
}] : historyRows;
|
|
46
|
+
const pending = onAskUserAnswer ? pendingAskUser(withStream) : null;
|
|
47
|
+
if (!pending) return withStream;
|
|
48
|
+
for (let i = withStream.length - 1; i >= 0; i--) {
|
|
49
|
+
if (withStream[i].role === "assistant" && withStream[i].content.includes(pending.requestId)) {
|
|
50
|
+
const next = [...withStream];
|
|
51
|
+
next[i] = __spreadProps(__spreadValues({}, next[i]), {
|
|
52
|
+
askUser: pending
|
|
53
|
+
});
|
|
54
|
+
return next;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return withStream;
|
|
58
|
+
}, [historyRows, streamingContent, onAskUserAnswer]);
|
|
46
59
|
const scrollToLatest = useCallback((animated) => {
|
|
47
60
|
if (rows.length === 0) return;
|
|
48
61
|
isProgrammaticScrollRef.current = true;
|
|
@@ -96,9 +109,10 @@ function ChatTranscript({
|
|
|
96
109
|
if (!stickToBottomRef.current) return;
|
|
97
110
|
scrollToLatest(false);
|
|
98
111
|
}, [scrollToLatest]);
|
|
112
|
+
const [dismissedAsk, setDismissedAsk] = React.useState(null);
|
|
99
113
|
const renderItem = useCallback(({
|
|
100
114
|
item
|
|
101
|
-
}) => /* @__PURE__ */ jsx(TranscriptRowView, { item, isDark, renderMessageActions: item.streaming ? void 0 : renderMessageActions }), [isDark, renderMessageActions]);
|
|
115
|
+
}) => /* @__PURE__ */ jsx(TranscriptRowView, { item, isDark, renderMessageActions: item.streaming ? void 0 : renderMessageActions, askUserDismissed: dismissedAsk, onAskUserAnswer, onAskUserDismiss: setDismissedAsk, sending }), [isDark, renderMessageActions, dismissedAsk, onAskUserAnswer, sending]);
|
|
102
116
|
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: () => {
|
|
103
117
|
if (stickToBottomRef.current) scrollToLatest(false);
|
|
104
118
|
}, 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 }) });
|
|
@@ -106,12 +120,22 @@ function ChatTranscript({
|
|
|
106
120
|
const TranscriptRowView = memo(function TranscriptRowView2({
|
|
107
121
|
item,
|
|
108
122
|
isDark,
|
|
109
|
-
renderMessageActions
|
|
123
|
+
renderMessageActions,
|
|
124
|
+
askUserDismissed,
|
|
125
|
+
onAskUserAnswer,
|
|
126
|
+
onAskUserDismiss,
|
|
127
|
+
sending
|
|
110
128
|
}) {
|
|
111
129
|
var _a;
|
|
112
130
|
const isUser = item.role === "user";
|
|
113
131
|
const attachments = (_a = item.attachments) != null ? _a : [];
|
|
114
|
-
const
|
|
132
|
+
const assistantRaw = isUser ? "" : stripAskUser(item.content);
|
|
133
|
+
const toolActivity = isUser ? {
|
|
134
|
+
steps: [],
|
|
135
|
+
text: ""
|
|
136
|
+
} : parseToolActivity(assistantRaw, !!item.streaming);
|
|
137
|
+
const text = isUser ? displayUserMessageText(item.content) : toolActivity.text;
|
|
138
|
+
const askUser = !isUser && item.askUser && item.askUser.requestId !== askUserDismissed ? item.askUser : null;
|
|
115
139
|
if (isUser) {
|
|
116
140
|
return /* @__PURE__ */ jsxs(Pressable, { style: styles.userCol, onPress: dismissKeyboard, accessible: false, children: [
|
|
117
141
|
attachments.length > 0 ? /* @__PURE__ */ jsx(MessageAttachmentPreviews, { attachments, align: "end", isDark }) : null,
|
|
@@ -122,6 +146,11 @@ const TranscriptRowView = memo(function TranscriptRowView2({
|
|
|
122
146
|
const codeBg = isDark ? "#1e293b" : "#f3f4f6";
|
|
123
147
|
return /* @__PURE__ */ jsxs(Pressable, { style: styles.assistantCol, onPress: dismissKeyboard, accessible: false, children: [
|
|
124
148
|
attachments.length > 0 ? /* @__PURE__ */ jsx(MessageAttachmentPreviews, { attachments, align: "start", isDark }) : null,
|
|
149
|
+
toolActivity.steps.length > 0 ? /* @__PURE__ */ jsx(ToolTimeline, { steps: toolActivity.steps, active: !!item.streaming, isDark }) : null,
|
|
150
|
+
askUser && onAskUserAnswer ? /* @__PURE__ */ jsx(AskUserCard, { payload: askUser, isDark, disabled: !!sending, onAnswer: (formatted) => {
|
|
151
|
+
onAskUserDismiss == null ? void 0 : onAskUserDismiss(askUser.requestId);
|
|
152
|
+
onAskUserAnswer(formatted);
|
|
153
|
+
}, onDismiss: () => onAskUserDismiss == null ? void 0 : onAskUserDismiss(askUser.requestId) }) : null,
|
|
125
154
|
text ? item.streaming ? /* @__PURE__ */ jsx(Text, { style: [styles.assistantStreamText, {
|
|
126
155
|
color: markdownColor
|
|
127
156
|
}], children: text }) : /* @__PURE__ */ jsx(Markdown, { style: {
|
|
@@ -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":";;;;;;;;;;;;;;;;;;;AA8BA,MAAM,cAAiB,GAAA,SAAA;AAChB,SAAS,cAAe,CAAA;AAAA,EAC7B,QAAA;AAAA,EACA,gBAAA;AAAA,EACA,oBAAA;AAAA,EACA,gBAAA;AAAA,EACA,MAAS,GAAA;AACX,CAUG,EAAA;AACD,EAAM,MAAA,OAAA,GAAU,OAAgC,IAAI,CAAA;AACpD,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,EAAA,MAAM,cAAc,OAAyB,CAAA,MAAM,SAAS,MAAO,CAAA,CAAA,GAAA,KAAO,IAAI,IAAS,KAAA,MAAA,IAAU,+BAA+B,GAAI,CAAA,OAAA,EAAS,IAAI,WAAW,CAAC,EAAE,GAAI,CAAA,CAAA,GAAA,KAAQ,iCACtK,GADsK,CAAA,EAAA;AAAA,IAEzK,SAAW,EAAA;AAAA,GACX,CAAA,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AACf,EAAM,MAAA,IAAA,GAAO,QAAyB,MAAM;AAC1C,IAAA,MAAM,SAAS,gBAAkB,IAAA,IAAA,GAAA,MAAA,GAAA,gBAAA,CAAA,IAAA,EAAA;AACjC,IAAI,IAAA,CAAC,QAAe,OAAA,WAAA;AACpB,IAAO,OAAA,CAAC,GAAG,WAAa,EAAA;AAAA,MACtB,EAAI,EAAA,eAAA;AAAA,MACJ,IAAM,EAAA,WAAA;AAAA,MACN,OAAS,EAAA,MAAA;AAAA,MACT,SAAW,EAAA;AAAA,KACZ,CAAA;AAAA,GACA,EAAA,CAAC,WAAa,EAAA,gBAAgB,CAAC,CAAA;AAClC,EAAM,MAAA,cAAA,GAAiB,WAAY,CAAA,CAAC,QAAsB,KAAA;AACxD,IAAI,IAAA,IAAA,CAAK,WAAW,CAAG,EAAA;AACvB,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;AAED,IAAA,IAAA,CAAK,cAAe,CAAA;AAAA,MAClB,QAAQ,MAAO,CAAA,gBAAA;AAAA,MACf;AAAA,KACD,CAAA;AAAA,GACA,EAAA,CAAC,IAAK,CAAA,MAAM,CAAC,CAAA;AAChB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,KAAK,MAAW,KAAA,CAAA,IAAK,CAAC,gBAAA,CAAiB,SAAgB,OAAA,MAAA;AAC3D,IAAA,MAAM,IAAI,UAAW,CAAA,MAAM,cAAe,CAAA,KAAK,GAAG,CAAC,CAAA;AACnD,IAAO,OAAA,MAAM,aAAa,CAAC,CAAA;AAAA,KAC1B,CAAC,IAAA,CAAK,QAAQ,gBAAkB,IAAA,IAAA,GAAA,MAAA,GAAA,gBAAA,CAAA,MAAA,EAAQ,cAAc,CAAC,CAAA;AAI1D,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,GAGI,qBAAA,GAAA,CAAC,iBAAkB,EAAA,EAAA,IAAA,EAAY,QAAgB,oBAAsB,EAAA,IAAA,CAAK,SAAY,GAAA,MAAA,GAAY,oBAAsB,EAAA,CAAA,EAAI,CAAC,MAAA,EAAQ,oBAAoB,CAAC,CAAA;AAChK,EAAA,uBAAQ,GAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,MAAA,CAAO,IACjB,EAAA,QAAA,kBAAA,GAAA,CAAC,QAAS,EAAA,EAAA,GAAA,EAAK,OAAS,EAAA,IAAA,EAAM,IAAM,EAAA,YAAA,EAAc,UAAQ,IAAK,CAAA,EAAA,EAAI,UAAwB,EAAA,SAAA,EAAW,gBAAkB,EAAA,kBAAA,EAAoB,EAAI,EAAA,mBAAA,EAAqB,GAAG,yBAA2B,EAAA,EAAA,EAAI,UAAY,EAAA,CAAA,EAAG,QAAU,EAAA,YAAA,EAAc,mBAAqB,EAAA,uBAAA,EAAyB,UAAU,MAAM;AAClT,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,KAAO,EAAA,gCAAA,EAAkC,KAAO,EAAA,8BAAA,EAA+B,OAAQ,EAAA,qBAAA,EAAuB,CAAC,MAAA,CAAO,WAAa,EAAA,gBAAA,EAAkB,MAAO,CAAA,eAAe,CAAG,EAAA,KAAA,EAAO,MAAO,CAAA,IAAA,EAAM,CACzW,EAAA,CAAA;AACR;AACA,MAAM,iBAAA,GAAoB,IAAK,CAAA,SAASA,kBAAkB,CAAA;AAAA,EACxD,IAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAQG,EAAA;AA/JH,EAAA,IAAA,EAAA;AAgKE,EAAM,MAAA,MAAA,GAAS,KAAK,IAAS,KAAA,MAAA;AAC7B,EAAA,MAAM,WAAc,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,WAAL,KAAA,IAAA,GAAA,EAAA,GAAoB,EAAC;AACzC,EAAA,MAAM,OAAO,MAAS,GAAA,sBAAA,CAAuB,KAAK,OAAoB,IAAI,IAAK,CAAA,OAAA;AAC/E,EAAA,IAAI,MAAQ,EAAA;AACV,IAAO,uBAAA,IAAA,CAAC,aAAU,KAAO,EAAA,MAAA,CAAO,SAAS,OAAS,EAAA,eAAA,EAAiB,YAAY,KAClE,EAAA,QAAA,EAAA;AAAA,MAAY,WAAA,CAAA,MAAA,GAAS,oBAAK,GAAA,CAAA,yBAAA,EAAA,EAA0B,aAA0B,KAAM,EAAA,KAAA,EAAM,QAAgB,CAAK,GAAA,IAAA;AAAA,MAC/G,IAAO,mBAAA,GAAA,CAAC,IAAK,EAAA,EAAA,KAAA,EAAO,MAAO,CAAA,UAAA,EACpB,QAAC,kBAAA,GAAA,CAAA,IAAA,EAAA,EAAK,KAAO,EAAA,MAAA,CAAO,QAAW,EAAA,QAAA,EAAA,IAAA,EAAK,GACxC,CAAU,GAAA;AAAA,KAClB,EAAA,CAAA;AAAA;AAEV,EAAM,MAAA,aAAA,GAAgB,SAAS,SAAY,GAAA,SAAA;AAC3C,EAAM,MAAA,MAAA,GAAS,SAAS,SAAY,GAAA,SAAA;AACpC,EAAO,uBAAA,IAAA,CAAC,aAAU,KAAO,EAAA,MAAA,CAAO,cAAc,OAAS,EAAA,eAAA,EAAiB,YAAY,KACzE,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,IAAA,GAAO,KAAK,SAAY,mBAAA,GAAA,CAAC,QAAK,KAAO,EAAA,CAAC,OAAO,mBAAqB,EAAA;AAAA,MACzE,KAAO,EAAA;AAAA,KACR,CAAI,EAAA,QAAA,EAAA,IAAA,EAAK,CAAU,mBAAA,GAAA,CAAC,YAAS,KAAO,EAAA;AAAA,MACnC,IAAM,EAAA;AAAA,QACJ,KAAO,EAAA,aAAA;AAAA,QACP,QAAU,EAAA,EAAA;AAAA,QACV,UAAY,EAAA;AAAA,OACd;AAAA,MACA,QAAU,EAAA;AAAA,QACR,QAAU,EAAA,EAAA;AAAA,QACV,UAAY,EAAA,KAAA;AAAA,QACZ,SAAW,EAAA,EAAA;AAAA,QACX,YAAc,EAAA,CAAA;AAAA,QACd,KAAO,EAAA;AAAA,OACT;AAAA,MACA,QAAU,EAAA;AAAA,QACR,QAAU,EAAA,EAAA;AAAA,QACV,UAAY,EAAA,KAAA;AAAA,QACZ,SAAW,EAAA,EAAA;AAAA,QACX,YAAc,EAAA,CAAA;AAAA,QACd,KAAO,EAAA;AAAA,OACT;AAAA,MACA,QAAU,EAAA;AAAA,QACR,QAAU,EAAA,EAAA;AAAA,QACV,UAAY,EAAA,KAAA;AAAA,QACZ,SAAW,EAAA,CAAA;AAAA,QACX,YAAc,EAAA,CAAA;AAAA,QACd,KAAO,EAAA;AAAA,OACT;AAAA,MACA,MAAQ,EAAA;AAAA,QACN,UAAY,EAAA;AAAA,OACd;AAAA,MACA,SAAW,EAAA;AAAA,QACT,SAAW,EAAA,CAAA;AAAA,QACX,YAAc,EAAA;AAAA,OAChB;AAAA,MACA,SAAW,EAAA;AAAA,QACT,cAAgB,EAAA;AAAA,OAClB;AAAA,MACA,WAAa,EAAA;AAAA,QACX,cAAgB,EAAA;AAAA,OAClB;AAAA,MACA,YAAc,EAAA;AAAA,QACZ,cAAgB,EAAA;AAAA,OAClB;AAAA,MACA,WAAa,EAAA;AAAA,QACX,eAAiB,EAAA,MAAA;AAAA,QACjB,iBAAmB,EAAA,CAAA;AAAA,QACnB,YAAc,EAAA,CAAA;AAAA,QACd,QAAU,EAAA,EAAA;AAAA,QACV,KAAO,EAAA;AAAA,OACT;AAAA,MACA,UAAY,EAAA;AAAA,QACV,eAAiB,EAAA,MAAA;AAAA,QACjB,OAAS,EAAA,EAAA;AAAA,QACT,YAAc,EAAA,CAAA;AAAA,QACd,cAAgB,EAAA,CAAA;AAAA,QAChB,QAAU,EAAA,EAAA;AAAA,QACV,KAAO,EAAA;AAAA;AACT,KACF,EACqB,gBACL,CAAc,GAAA,IAAA;AAAA,IACrB,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,MAAA,GAAS,WAAW,MAAO,CAAA;AAAA,EAC/B,IAAM,EAAA;AAAA,IACJ,IAAM,EAAA;AAAA,GACR;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,CAAA;AAAA,IACV,cAAgB,EAAA;AAAA,GAClB;AAAA,EACA,OAAS,EAAA;AAAA,IACP,KAAO,EAAA,MAAA;AAAA,IACP,UAAY,EAAA,UAAA;AAAA,IACZ,YAAc,EAAA,CAAA;AAAA,IACd,iBAAmB,EAAA;AAAA,GACrB;AAAA,EACA,UAAY,EAAA;AAAA,IACV,eAAiB,EAAA,cAAA;AAAA,IACjB,YAAc,EAAA,EAAA;AAAA,IACd,OAAS,EAAA,EAAA;AAAA,IACT,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,QAAU,EAAA;AAAA,IACR,KAAO,EAAA,SAAA;AAAA,IACP,QAAU,EAAA,EAAA;AAAA,IACV,UAAY,EAAA;AAAA,GACd;AAAA,EACA,YAAc,EAAA;AAAA,IACZ,KAAO,EAAA,MAAA;AAAA,IACP,UAAY,EAAA,YAAA;AAAA,IACZ,YAAc,EAAA,CAAA;AAAA,IACd,iBAAmB,EAAA;AAAA,GACrB;AAAA,EACA,mBAAqB,EAAA;AAAA,IACnB,QAAU,EAAA,EAAA;AAAA,IACV,UAAY,EAAA;AAAA;AAEhB,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 } 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 { AskUserCard } from './AskUserCard';\nimport { ToolTimeline } from './ToolTimeline';\nimport { parseToolActivity } from './toolActivity';\nimport { pendingAskUser, stripAskUser, type AskUserPayload } from './askUser';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n attachments?: MessageAttachment[];\n};\n\ntype TranscriptRow = TranscriptMessage & { streaming?: boolean; askUser?: AskUserPayload };\n\nconst USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n renderMessageActions,\n listContentStyle,\n isDark = false,\n onAskUserAnswer,\n sending = 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 * Send the card's formatted answer as an ordinary user message. That send is\n * what releases cdecli's parked turn — without it the agent waits forever.\n * Omit to render questions as plain (stripped) text.\n */\n onAskUserAnswer?: (formatted: string) => void;\n /** A turn is in flight: the card stays visible but is not actionable. */\n sending?: 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 const withStream = stream\n ? [\n ...historyRows,\n {\n id: '__streaming__',\n role: 'assistant',\n content: stream,\n streaming: true,\n },\n ]\n : historyRows;\n\n // Only the LAST unanswered question is actionable, and only the row that\n // carries it renders the card — so a card from earlier in the thread\n // never reappears, and answering makes it disappear on the next turn\n // (pendingAskUser stops at the first user message it meets).\n const pending = onAskUserAnswer ? pendingAskUser(withStream) : null;\n if (!pending) return withStream;\n for (let i = withStream.length - 1; i >= 0; i--) {\n if (withStream[i].role === 'assistant' && withStream[i].content.includes(pending.requestId)) {\n const next = [...withStream];\n next[i] = { ...next[i], askUser: pending };\n return next;\n }\n }\n return withStream;\n }, [historyRows, streamingContent, onAskUserAnswer]);\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 [dismissedAsk, setDismissedAsk] = React.useState<string | null>(null);\n\n const renderItem = useCallback(\n ({ item }: { item: TranscriptRow }) => (\n <TranscriptRowView\n item={item}\n isDark={isDark}\n renderMessageActions={item.streaming ? undefined : renderMessageActions}\n askUserDismissed={dismissedAsk}\n onAskUserAnswer={onAskUserAnswer}\n onAskUserDismiss={setDismissedAsk}\n sending={sending}\n />\n ),\n [isDark, renderMessageActions, dismissedAsk, onAskUserAnswer, sending],\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 askUserDismissed,\n onAskUserAnswer,\n onAskUserDismiss,\n sending,\n}: {\n item: TranscriptRow;\n isDark: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n askUserDismissed?: string | null;\n onAskUserAnswer?: (formatted: string) => void;\n onAskUserDismiss?: (requestId: string) => void;\n sending?: boolean;\n}) {\n const isUser = item.role === 'user';\n const attachments = item.attachments ?? [];\n // The marker is STRIPPED, never used to hide the message: cdecli emits it\n // mid-turn and streams its reply into the same message once the answer\n // releases the parked run, so dropping the message would swallow that reply.\n // Order matters: pull the ASK_USER block out first (it can contain an em\n // dash and a `⚙` line right above it), then lift the agent's narrated\n // `⚙ ToolSearch` rows out of what remains. What is left is the real answer.\n const assistantRaw = isUser ? '' : stripAskUser(item.content);\n const toolActivity = isUser ? { steps: [], text: '' } : parseToolActivity(assistantRaw, !!item.streaming);\n const text = isUser ? displayUserMessageText(item.content, attachments) : toolActivity.text;\n const askUser = !isUser && item.askUser && item.askUser.requestId !== askUserDismissed ? item.askUser : null;\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 {toolActivity.steps.length > 0 ? (\n <ToolTimeline steps={toolActivity.steps} active={!!item.streaming} isDark={isDark} />\n ) : null}\n {askUser && onAskUserAnswer ? (\n <AskUserCard\n payload={askUser}\n isDark={isDark}\n disabled={!!sending}\n onAnswer={(formatted) => {\n onAskUserDismiss?.(askUser.requestId);\n onAskUserAnswer(formatted);\n }}\n onDismiss={() => onAskUserDismiss?.(askUser.requestId)}\n />\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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"}
|
|
@@ -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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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const MAX_ASK_USER_QUESTIONS = 4;
|
|
2
|
+
const ASK_USER_RE = /\[ASK_USER\]([\s\S]*?)\[\/ASK_USER\]/;
|
|
3
|
+
function parseAskUser(raw) {
|
|
4
|
+
if (!(raw == null ? void 0 : raw.trim())) return null;
|
|
5
|
+
const m = ASK_USER_RE.exec(raw);
|
|
6
|
+
if (!m) return null;
|
|
7
|
+
try {
|
|
8
|
+
const parsed = JSON.parse(m[1]);
|
|
9
|
+
if (!(parsed == null ? void 0 : parsed.requestId) || !Array.isArray(parsed.questions)) return null;
|
|
10
|
+
const questions = parsed.questions.filter((q) => q && typeof q.id === "string" && typeof q.title === "string" && q.title.trim()).slice(0, MAX_ASK_USER_QUESTIONS);
|
|
11
|
+
return questions.length ? {
|
|
12
|
+
requestId: parsed.requestId,
|
|
13
|
+
questions
|
|
14
|
+
} : null;
|
|
15
|
+
} catch (e) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function stripAskUser(raw) {
|
|
20
|
+
if (!raw) return "";
|
|
21
|
+
const stripped = raw.replace(new RegExp(ASK_USER_RE.source, "g"), "");
|
|
22
|
+
if (stripped === raw) return raw;
|
|
23
|
+
return stripped.replace(/\n{3,}/g, "\n\n").trim();
|
|
24
|
+
}
|
|
25
|
+
function pendingAskUser(messages) {
|
|
26
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
27
|
+
const msg = messages[i];
|
|
28
|
+
if (msg.role === "user") return null;
|
|
29
|
+
const parsed = msg.role === "assistant" ? parseAskUser(msg.content) : null;
|
|
30
|
+
if (parsed) return parsed;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
function formatAskUserAnswers(questions, answers) {
|
|
35
|
+
return questions.map((q) => {
|
|
36
|
+
var _a;
|
|
37
|
+
const answer = (_a = answers[q.id]) == null ? void 0 : _a.trim();
|
|
38
|
+
return answer ? `**${q.title.replace(/:\s*$/, "")}:** ${answer}` : null;
|
|
39
|
+
}).filter(Boolean).join("\n\n");
|
|
40
|
+
}export{MAX_ASK_USER_QUESTIONS,formatAskUserAnswers,parseAskUser,pendingAskUser,stripAskUser};//# sourceMappingURL=askUser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"askUser.js","sources":["../../../src/features/chat/askUser.ts"],"sourcesContent":["/**\n * The `[ASK_USER]` clarifying-question protocol, mobile side (Clockbook #1643).\n *\n * cdecli emits the block itself — from Claude's AskUserQuestion and its own\n * ask_user_question tool — and PARKS the run until the answer arrives:\n *\n * [ASK_USER]{\"requestId\":\"ask-…\",\"questions\":[{\"id\":\"q1\",\"title\":\"Which\n * database?\",\"header\":\"Database\",\"options\":[{\"id\":\"o1\",\"label\":\"Postgres\",\n * \"detail\":\"…\"}],\"allowText\":true}]}[/ASK_USER]\n *\n * The reply is deliberately NOT a marker: answers are formatted into one visible\n * user message (`**Title:** answer` lines) and sent through the normal chat path,\n * so the producer needs no reply-protocol support. Only the question side is\n * structured.\n *\n * Deliberately a copy of the web's `responsePromptProtocol` rather than an\n * import — mobile is a separate bundle and shares no code with the web shell.\n * The reply side is plain text, so the two stay compatible without a shared\n * version.\n */\n\nexport interface AskUserOption {\n id: string;\n label: string;\n /** Secondary line under the label. */\n detail?: string;\n /** \"Something else\": focuses the free-text row instead of answering. */\n other?: boolean;\n}\n\nexport interface AskUserQuestion {\n id: string;\n /** Also the bold prefix of the formatted answer. */\n title: string;\n /** Short category eyebrow above the title, e.g. \"Database\". */\n header?: string;\n options?: AskUserOption[];\n /** Show the free-text row (default true). */\n allowText?: boolean;\n textPlaceholder?: string;\n /** Pick several options; the answer is sent as comma-joined labels. */\n multiSelect?: boolean;\n}\n\nexport interface AskUserPayload {\n requestId: string;\n questions: AskUserQuestion[];\n}\n\n/** Anything longer is a form, not a prompt — the web caps it the same way. */\nexport const MAX_ASK_USER_QUESTIONS = 4;\n\n// Not anchored: the channel layer may prepend a stream envelope or cost banner.\nconst ASK_USER_RE = /\\[ASK_USER\\]([\\s\\S]*?)\\[\\/ASK_USER\\]/;\n\n/** Parse an `[ASK_USER]…[/ASK_USER]` message, or null if it isn't one. */\nexport function parseAskUser(raw: string | null | undefined): AskUserPayload | null {\n if (!raw?.trim()) return null;\n const m = ASK_USER_RE.exec(raw);\n if (!m) return null;\n try {\n const parsed = JSON.parse(m[1]) as AskUserPayload;\n if (!parsed?.requestId || !Array.isArray(parsed.questions)) return null;\n const questions = parsed.questions\n .filter((q) => q && typeof q.id === 'string' && typeof q.title === 'string' && q.title.trim())\n .slice(0, MAX_ASK_USER_QUESTIONS);\n return questions.length ? { requestId: parsed.requestId, questions } : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Remove every `[ASK_USER]` block, leaving the rest of the message intact.\n *\n * cdecli emits the block MID-turn and keeps streaming its reply into the SAME\n * message once the answer releases the parked run, so the block and the reply it\n * was asked for share one message. Hiding the whole message — which is what a\n * naive implementation does — would swallow that reply.\n */\nexport function stripAskUser(raw: string | null | undefined): string {\n if (!raw) return '';\n const stripped = raw.replace(new RegExp(ASK_USER_RE.source, 'g'), '');\n if (stripped === raw) return raw;\n return stripped.replace(/\\n{3,}/g, '\\n\\n').trim();\n}\n\n/**\n * The question still awaiting an answer: the last assistant block, unless the\n * user has already replied after it. Scanning backwards means a stale card from\n * earlier in the thread never reappears.\n */\nexport function pendingAskUser(messages: ReadonlyArray<{ role: string; content: string }>): AskUserPayload | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.role === 'user') return null;\n const parsed = msg.role === 'assistant' ? parseAskUser(msg.content) : null;\n if (parsed) return parsed;\n }\n return null;\n}\n\n/**\n * Fold the answers into the single visible reply message:\n *\n * **Which database?:** Postgres\n *\n * Skipped questions are omitted; an empty result means \"nothing to send\", which\n * the card treats as a dismiss.\n */\nexport function formatAskUserAnswers(questions: readonly AskUserQuestion[], answers: Record<string, string>): string {\n return questions\n .map((q) => {\n const answer = answers[q.id]?.trim();\n return answer ? `**${q.title.replace(/:\\s*$/, '')}:** ${answer}` : null;\n })\n .filter(Boolean)\n .join('\\n\\n');\n}\n"],"names":[],"mappings":"AAgDO,MAAM,sBAAyB,GAAA;AAGtC,MAAM,WAAc,GAAA,sCAAA;AAGb,SAAS,aAAa,GAAuD,EAAA;AAClF,EAAI,IAAA,EAAC,GAAK,IAAA,IAAA,GAAA,MAAA,GAAA,GAAA,CAAA,IAAA,EAAA,CAAA,EAAe,OAAA,IAAA;AACzB,EAAM,MAAA,CAAA,GAAI,WAAY,CAAA,IAAA,CAAK,GAAG,CAAA;AAC9B,EAAI,IAAA,CAAC,GAAU,OAAA,IAAA;AACf,EAAI,IAAA;AACF,IAAA,MAAM,MAAS,GAAA,IAAA,CAAK,KAAM,CAAA,CAAA,CAAE,CAAC,CAAC,CAAA;AAC9B,IAAI,IAAA,EAAC,iCAAQ,SAAa,CAAA,IAAA,CAAC,MAAM,OAAQ,CAAA,MAAA,CAAO,SAAS,CAAA,EAAU,OAAA,IAAA;AACnE,IAAM,MAAA,SAAA,GAAY,OAAO,SAAU,CAAA,MAAA,CAAO,OAAK,CAAK,IAAA,OAAO,EAAE,EAAO,KAAA,QAAA,IAAY,OAAO,CAAE,CAAA,KAAA,KAAU,YAAY,CAAE,CAAA,KAAA,CAAM,MAAM,CAAA,CAAE,KAAM,CAAA,CAAA,EAAG,sBAAsB,CAAA;AAC9J,IAAA,OAAO,UAAU,MAAS,GAAA;AAAA,MACxB,WAAW,MAAO,CAAA,SAAA;AAAA,MAClB;AAAA,KACE,GAAA,IAAA;AAAA,GACE,CAAA,OAAA,CAAA,EAAA;AACN,IAAO,OAAA,IAAA;AAAA;AAEX;AAUO,SAAS,aAAa,GAAwC,EAAA;AACnE,EAAI,IAAA,CAAC,KAAY,OAAA,EAAA;AACjB,EAAM,MAAA,QAAA,GAAW,IAAI,OAAQ,CAAA,IAAI,OAAO,WAAY,CAAA,MAAA,EAAQ,GAAG,CAAA,EAAG,EAAE,CAAA;AACpE,EAAI,IAAA,QAAA,KAAa,KAAY,OAAA,GAAA;AAC7B,EAAA,OAAO,QAAS,CAAA,OAAA,CAAQ,SAAW,EAAA,MAAM,EAAE,IAAK,EAAA;AAClD;AAOO,SAAS,eAAe,QAGJ,EAAA;AACzB,EAAA,KAAA,IAAS,IAAI,QAAS,CAAA,MAAA,GAAS,CAAG,EAAA,CAAA,IAAK,GAAG,CAAK,EAAA,EAAA;AAC7C,IAAM,MAAA,GAAA,GAAM,SAAS,CAAC,CAAA;AACtB,IAAI,IAAA,GAAA,CAAI,IAAS,KAAA,MAAA,EAAe,OAAA,IAAA;AAChC,IAAA,MAAM,SAAS,GAAI,CAAA,IAAA,KAAS,cAAc,YAAa,CAAA,GAAA,CAAI,OAAO,CAAI,GAAA,IAAA;AACtE,IAAA,IAAI,QAAe,OAAA,MAAA;AAAA;AAErB,EAAO,OAAA,IAAA;AACT;AAUgB,SAAA,oBAAA,CAAqB,WAAuC,OAAyC,EAAA;AACnH,EAAO,OAAA,SAAA,CAAU,IAAI,CAAK,CAAA,KAAA;AAjH5B,IAAA,IAAA,EAAA;AAkHI,IAAA,MAAM,MAAS,GAAA,CAAA,EAAA,GAAA,OAAA,CAAQ,CAAE,CAAA,EAAE,MAAZ,IAAe,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AAC9B,IAAO,OAAA,MAAA,GAAS,CAAK,EAAA,EAAA,CAAA,CAAE,KAAM,CAAA,OAAA,CAAQ,SAAS,EAAE,CAAC,CAAO,IAAA,EAAA,MAAM,CAAK,CAAA,GAAA,IAAA;AAAA,GACpE,CAAE,CAAA,MAAA,CAAO,OAAO,CAAA,CAAE,KAAK,MAAM,CAAA;AAChC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const GEAR_LINE_RE = /^[ \t]*[⚙🔧\u{1F6E0}]️?[ \t]+(.+?)[ \t]*$/u;
|
|
2
|
+
const BARE_MCP_LINE_RE = /^[ \t]*(mcp__[^\s].*?)[ \t]*$/;
|
|
3
|
+
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"], [/^(str_replace_editor|edit|write)$/i, "Editing files"], [/^read$/i, "Reading files"]];
|
|
4
|
+
const CONNECTOR_LABELS = [[/image[_-]?gen/i, "Generating image"], [/video[_-]?gen/i, "Generating video"], [/(audio|tts|speech|voice)/i, "Generating audio"], [/search/i, "Searching"]];
|
|
5
|
+
function prettifySlug(slug) {
|
|
6
|
+
const words = slug.replace(/^yantra[_-]/i, "").replace(/[_-]+/g, " ").trim();
|
|
7
|
+
return words ? words.charAt(0).toUpperCase() + words.slice(1) : slug;
|
|
8
|
+
}
|
|
9
|
+
function humanizeToolLine(body) {
|
|
10
|
+
var _a, _b, _c, _d;
|
|
11
|
+
const trimmed = body.trim();
|
|
12
|
+
const dashSplit = trimmed.split(/\s+[—–-]\s+/);
|
|
13
|
+
const head = (_b = (_a = dashSplit[0]) == null ? void 0 : _a.trim()) != null ? _b : trimmed;
|
|
14
|
+
const detailRaw = dashSplit.length > 1 ? dashSplit.slice(1).join(" \u2014 ").trim() : void 0;
|
|
15
|
+
const bareTool = head.replace(/^mcp__[^_]+__/i, "").replace(/^mcp__/i, "");
|
|
16
|
+
if (/^execute_action$/i.test(bareTool) && detailRaw) {
|
|
17
|
+
const connector = (_d = (_c = detailRaw.split("/")[0]) == null ? void 0 : _c.trim()) != null ? _d : detailRaw;
|
|
18
|
+
for (const [re, label2] of CONNECTOR_LABELS) {
|
|
19
|
+
if (re.test(connector)) return {
|
|
20
|
+
label: label2,
|
|
21
|
+
detail: prettifySlug(connector)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
label: `Running ${prettifySlug(connector)}`,
|
|
26
|
+
detail: detailRaw
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
for (const [re, label2] of TOOL_LABELS) {
|
|
30
|
+
if (re.test(bareTool)) return {
|
|
31
|
+
label: label2,
|
|
32
|
+
detail: detailRaw
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const spaced = bareTool.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
|
|
36
|
+
const label = spaced ? `Running ${spaced.toLowerCase()}` : "Working";
|
|
37
|
+
return {
|
|
38
|
+
label,
|
|
39
|
+
detail: detailRaw
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function parseToolActivity(content, active = false) {
|
|
43
|
+
var _a;
|
|
44
|
+
if (!content) return {
|
|
45
|
+
steps: [],
|
|
46
|
+
text: content != null ? content : ""
|
|
47
|
+
};
|
|
48
|
+
const steps = [];
|
|
49
|
+
const kept = [];
|
|
50
|
+
for (const line of content.split("\n")) {
|
|
51
|
+
const gear = GEAR_LINE_RE.exec(line);
|
|
52
|
+
const mcp = gear ? null : BARE_MCP_LINE_RE.exec(line);
|
|
53
|
+
const body = (_a = gear == null ? void 0 : gear[1]) != null ? _a : mcp == null ? void 0 : mcp[1];
|
|
54
|
+
if (body) {
|
|
55
|
+
const {
|
|
56
|
+
label,
|
|
57
|
+
detail
|
|
58
|
+
} = humanizeToolLine(body);
|
|
59
|
+
steps.push({
|
|
60
|
+
id: `tool-${steps.length}`,
|
|
61
|
+
label,
|
|
62
|
+
detail,
|
|
63
|
+
status: "done"
|
|
64
|
+
});
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
kept.push(line);
|
|
68
|
+
}
|
|
69
|
+
const text = kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
70
|
+
if (active && steps.length > 0) {
|
|
71
|
+
steps[steps.length - 1].status = "running";
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
steps,
|
|
75
|
+
text
|
|
76
|
+
};
|
|
77
|
+
}export{humanizeToolLine,parseToolActivity};//# sourceMappingURL=toolActivity.js.map
|
|
@@ -0,0 +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 /** 'running' only ever applies to the LAST step of an in-flight turn. */\n status: 'running' | 'done';\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 [/^(str_replace_editor|edit|write)$/i, 'Editing files'],\n [/^read$/i, 'Reading files'],\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/**\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 } {\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 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: detailRaw };\n }\n\n for (const [re, label] of TOOL_LABELS) {\n if (re.test(bareTool)) return { label, detail: 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: 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 } = humanizeToolLine(body);\n steps.push({ id: `tool-${steps.length}`, label, detail, status: 'done' });\n continue;\n }\n kept.push(line);\n }\n\n // Collapse the blank lines the removed rows leave behind (3+ newlines → 2).\n const text = kept\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\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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"}
|
|
@@ -124,6 +124,11 @@ function ChatScreen() {
|
|
|
124
124
|
} catch (e) {
|
|
125
125
|
}
|
|
126
126
|
}, [channelId, initialQuery, initialAttachments, sendMessage, navigation, cdecli.status, isDeepSearchMode]);
|
|
127
|
+
const handleAskUserAnswer = useCallback((formatted) => {
|
|
128
|
+
const text = formatted.trim();
|
|
129
|
+
if (!text) return;
|
|
130
|
+
void sendMessage(text, void 0, channelId);
|
|
131
|
+
}, [sendMessage, channelId]);
|
|
127
132
|
const handleSend = useCallback((text) => {
|
|
128
133
|
var _a2;
|
|
129
134
|
const pending = imageAttachments.forSend();
|
|
@@ -360,7 +365,7 @@ function ChatScreen() {
|
|
|
360
365
|
/* @__PURE__ */ jsx(Text, { style: [styles.statusText, {
|
|
361
366
|
color: secondaryTextColor
|
|
362
367
|
}], children: "Connecting gateway\u2026" })
|
|
363
|
-
] }) : /* @__PURE__ */ jsx(Box, { flex: 1, width: "100%", alignSelf: "stretch", children: /* @__PURE__ */ jsx(ChatTranscript, { messages: transcriptRows, streamingContent: response, renderMessageActions, isDark, listContentStyle: {
|
|
368
|
+
] }) : /* @__PURE__ */ jsx(Box, { flex: 1, width: "100%", alignSelf: "stretch", children: /* @__PURE__ */ jsx(ChatTranscript, { messages: transcriptRows, streamingContent: response, renderMessageActions, isDark, onAskUserAnswer: handleAskUserAnswer, sending: chatBusy, listContentStyle: {
|
|
364
369
|
paddingTop: 12,
|
|
365
370
|
paddingBottom: 8,
|
|
366
371
|
margin: 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/screens/Chat/index.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { Keyboard, StyleSheet, TextInput, View, useColorScheme, useWindowDimensions } from 'react-native';\nimport { Box, InputToolBar, Text, getDefaultLeftItems, getDefaultRightItems } from '@admin-layout/gluestack-ui-mobile';\nimport { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';\nimport { useNavigation, useRoute, CommonActions } from '@react-navigation/native';\nimport { MessageActionBar } from '../../features/chat/MessageActionBar';\nimport { ChatTranscript } from '../../features/chat/ChatTranscript';\nimport { useCdecliConnection } from '../../contexts/CdecliConnectionContext';\nimport { useChatStream, type UseChatStreamRouting } from '../../hooks/useChatStream';\nimport { KeyboardAwareScreen, KeyboardComposerDock } from '../../components/KeyboardComposerDock';\nimport { YantraBrandLoader, YANTRA_LOADER_SIZE_COMPACT } from '../../components/YantraBrandLoader';\nimport ThinkingIndicator from '../../components/ThinkingIndicator';\nimport { mobileTokens } from '../../theme/mobileTokens';\nimport DeepSearchModal from '../Home/components/DeepSearchModal';\nimport { extractDeepSearchSources, normalizeSummaryText, type DeepSearchSourceItem } from '../Home/deepSearchUtils';\nimport type { ModeSubmission, RoutingMode } from '../Home/types';\nimport { AudioRecorderPanel } from '../../features/audio-input/AudioRecorderPanel';\nimport { NativeDictationBar } from '../../features/audio-input/NativeDictationBar';\nimport { isNativeDictationAvailable } from '../../features/audio-input/nativeDictation';\nimport { useImageAttachments } from '../../features/attachments/useImageAttachments';\nimport { ComposerAttachmentBar } from '../../features/attachments/ComposerAttachmentBar';\nimport { hasComposerAttachments, takeComposerAttachments } from '../../features/attachments/composerAttachmentHandoff';\nimport { MicErrorBoundary } from '../../features/audio-input/MicErrorBoundary';\nimport { requestMicPermission } from '../../features/audio-input/useAudioPermission';\n\n/** Route params passed by Home (or by deep-link) when entering the Chat surface. */\nexport interface ChatScreenRouteParams {\n /** Channel id is required — the screen is keyed on the conversation. */\n channelId: string;\n /** Org slug used by gateway/account context. */\n orgName?: string | null;\n /**\n * First user message to auto-fire when the screen mounts. Home generates\n * this when the user submits from the empty composer so the assistant\n * starts streaming on Chat without an extra round trip.\n */\n initialQuery?: string | null;\n /**\n * Seed for the composer's mode (`'chat'` | `'deep-search'` | `'build'`).\n *\n * Set by `HomeScreen` to carry the user's last selection across the\n * navigation hop (\"research from home → research-shaped composer on chat\").\n * `ChatHistoryScreen` does NOT forward this — revisits always land on the\n * default `'chat'` mode, matching Home's defaults, and the user is free to\n * switch from there. Build-mode send hands off to Web Builder.\n *\n * After mount, this param is purely a seed: the screen keeps its own\n * `activeMode` so the user can toggle freely while inside the thread.\n */\n initialMode?: RoutingMode;\n /** Optional attachments forwarded with the initial message. */\n initialAttachments?: ModeSubmission['attachments'];\n}\n\nexport default function ChatScreen() {\n const navigation = useNavigation<any>();\n const route = useRoute<any>();\n\n const params = (route?.params ?? {}) as Partial<ChatScreenRouteParams>;\n const channelId = useMemo(() => {\n const raw = typeof params.channelId === 'string' ? params.channelId.trim() : '';\n return raw.length > 0 ? raw : null;\n }, [params.channelId]);\n const initialQuery = useMemo(() => {\n const raw = typeof params.initialQuery === 'string' ? params.initialQuery.trim() : '';\n return raw.length > 0 ? raw : null;\n }, [params.initialQuery]);\n const initialAttachments = params.initialAttachments;\n\n const colorScheme = useColorScheme();\n const isDark = colorScheme === 'dark';\n const { width: screenWidth } = useWindowDimensions();\n const contentMaxWidth = Math.min(720, Math.max(360, screenWidth - 24));\n\n const surfaceColor = isDark ? '#0f172a' : mobileTokens.color.surface;\n const secondaryTextColor = isDark ? '#94a3b8' : mobileTokens.color.textMuted;\n\n const insets = useSafeAreaInsets();\n\n /**\n * Gateway + chat stream routing.\n *\n * The CDeCLI lifecycle is owned app-wide by `CdecliConnectionProvider`,\n * so we just register this screen's channelId and read the shared status.\n * This avoids racing `gatewayConnect` mutations with the header's\n * `GatewayConnector` (which previously ran its own `useCdecliAutoConnect`\n * and could clobber the chat session binding).\n */\n const cdecli = useCdecliConnection();\n const effectivePersistenceMode = cdecli.persistenceMode ?? 'backend';\n\n useEffect(() => {\n if (!channelId) return undefined;\n cdecli.setActiveChannelId(channelId);\n return () => {\n cdecli.setActiveChannelId(undefined);\n };\n }, [channelId, cdecli.setActiveChannelId]);\n\n const chatRouting: UseChatStreamRouting = useMemo(\n () => ({\n channelConnected: cdecli.channelConnected,\n accountId: cdecli.accountId,\n persistenceMode: effectivePersistenceMode,\n }),\n [cdecli.channelConnected, cdecli.accountId, effectivePersistenceMode],\n );\n\n const chat = useChatStream(channelId, chatRouting);\n\n /**\n * Gifted-shaped transcript rows, memoised on `messages` alone.\n *\n * This screen re-renders on every streamed token — `useChatStream` does\n * `setResponse((r) => r + text)` per delta. Building this array inline in\n * the JSX therefore handed `PlanModeView` a fresh `messages` identity on\n * every token, which invalidated its own `useMemo([messages, …])`, re-mapped\n * every row, and re-rendered every bubble. Assistant bubbles render through\n * `react-native-markdown-display`, so that meant re-parsing the markdown of\n * the whole thread once per token — O(messages) parses per delta, on the JS\n * thread, which is exactly when the UI has to stay responsive.\n *\n * `messages` is already stable upstream (`useChatApi` memoises it on the\n * Apollo result), so this only rebuilds when a message is actually added or\n * changed.\n */\n const transcriptRows = useMemo(\n () =>\n chat.messages.map((msg, index) => ({\n id: `msg-${index}-${msg.role}`,\n role: msg.role,\n content: msg.content,\n attachments: msg.attachments,\n metadata: (msg as { metadata?: unknown }).metadata,\n })),\n [chat.messages],\n );\n const { messages, response, error: chatError, isLoading, isStreaming, messagesLoading, sendMessage, cancel } = chat;\n\n /**\n * \"Try again\" on an assistant message: resend the nearest preceding user\n * prompt on the same channel. Row ids are `msg-<index>-<role>` (see\n * transcriptRows), so the transcript index comes straight out of the id.\n */\n const regenerateFrom = useCallback(\n (messageId: string) => {\n const match = /^msg-(\\d+)-assistant$/.exec(messageId);\n if (!match || !channelId) return;\n for (let i = Number(match[1]) - 1; i >= 0; i -= 1) {\n const prior = chat.messages[i];\n if (prior?.role === 'user' && (prior.content?.trim() || prior.attachments?.length)) {\n void chat.sendMessage(prior.content, prior.attachments, channelId);\n return;\n }\n }\n },\n [chat, channelId],\n );\n\n const chatBusy = isLoading || isStreaming;\n const renderMessageActions = useCallback(\n (message: { id: string; role: string; content: string }) =>\n message.role === 'assistant' ? (\n <MessageActionBar\n text={message.content}\n onRegenerate={chatBusy ? undefined : () => regenerateFrom(message.id)}\n />\n ) : null,\n [chatBusy, regenerateFrom],\n );\n\n const imageAttachments = useImageAttachments();\n\n /*\n * Composer state — kept local so it doesn't leak across navigations.\n *\n * `valueRef` mirrors the controlled `value` so `handleSend` can read the\n * latest text without taking a dependency on it. Without this, `handleSend`\n * (and therefore `micSendButton.onSend`) would be recreated on every\n * keystroke, which is unnecessary churn for `InputToolBar`.\n *\n * `inputRef` is forwarded to InputToolBar's TextInput so we can call\n * `.clear()` imperatively after sending. On iOS, the native text buffer\n * can stay desynced from the controlled `value` for a frame when a state\n * update is followed by an async dispatch in the same gesture — the\n * \"send-and-clear\" race. The imperative clear closes that gap deterministically.\n */\n const [value, setValue] = useState('');\n const valueRef = useRef(value);\n valueRef.current = value;\n const inputRef = useRef<TextInput>(null);\n const trimmed = value.trim();\n const hasQuery = trimmed.length > 0;\n const hasAttachments = imageAttachments.pending.length > 0;\n const canSubmit = (hasQuery || hasAttachments) && Boolean(channelId);\n\n /**\n * Composer mode is FREELY user-controlled, mirroring `HomeScreen`.\n *\n * Seed:\n * • From Home (which forwards `initialMode` alongside `initialQuery`):\n * start in whatever mode the user picked there so the \"research from\n * home → research-shaped chat\" hand-off feels continuous.\n * • From ChatHistory (which intentionally does NOT pass `initialMode`):\n * default to `'chat'`, same as Home's empty composer.\n *\n * Post-mount this is a normal local state — the search/zap pills toggle\n * it on tap, and every place that previously branched on\n * `params.initialMode` now branches on `activeMode` instead.\n */\n const [activeMode, setActiveMode] = useState<RoutingMode>(() =>\n params.initialMode === 'deep-search' ? 'deep-search' : params.initialMode === 'build' ? 'build' : 'chat',\n );\n const handleModeSwitch = useCallback((mode: RoutingMode) => {\n setActiveMode(mode);\n }, []);\n const isDeepSearchMode = activeMode === 'deep-search';\n const isBuildMode = activeMode === 'build';\n\n /**\n * Inline voice recorder state — parity with HomeScreen and the web\n * `YantraSearchComposer.showAudioRecorder` flow. When active, the panel\n * fully replaces the InputToolBar so the toolbar's mic→send button\n * doesn't compete with the panel's own send/cancel affordances.\n */\n const [showAudioRecorder, setShowAudioRecorder] = useState(false);\n // Apple on-device dictation (the iOS-built-in mic) is preferred when the\n // binary has it; the Whisper recorder above is the fallback.\n const [showNativeDictation, setShowNativeDictation] = useState(false);\n const [voiceError, setVoiceError] = useState<string | null>(null);\n\n /**\n * Auto-fire the initial query once when the screen mounts on a fresh channel.\n * Home creates the channel optimistically and forwards the user prompt here so\n * the first assistant token arrives without an extra interaction.\n *\n * Wait for CDeCLI to finish connecting (or fail) before auto-firing — chat is\n * cdecli-only and `sendMessage` rejects sends while the gateway is disconnected.\n */\n const autoFiredRef = useRef(false);\n useEffect(() => {\n if (autoFiredRef.current) return;\n if (!channelId) return;\n if (cdecli.status !== 'connected' && cdecli.status !== 'error') return;\n const stashed = hasComposerAttachments(channelId) ? takeComposerAttachments(channelId) : undefined;\n if (!initialQuery && !stashed?.length) return;\n\n autoFiredRef.current = true;\n // Deep-search auto-fire from Home: open the structured modal at the\n // same instant we kick off the send so the user sees the research view\n // streaming in rather than raw assistant turns. Tied to the\n // sendMessage call (same event) to avoid a race where the modal opens\n // before/after the actual send.\n //\n // We branch on the LOCAL `isDeepSearchMode` instead of\n // `params.initialMode`. At auto-fire time they're equal (the local\n // state was seeded from the param), and using the local state means\n // future toggles drive the same code path consistently.\n if (isDeepSearchMode) {\n setIsDeepSearchModalOpen(true);\n }\n void sendMessage(initialQuery ?? '', stashed, channelId);\n try {\n navigation.setParams({ initialQuery: null, initialAttachments: null });\n } catch {\n /* setParams may be unavailable in tests; clearing is best-effort. */\n }\n }, [channelId, initialQuery, initialAttachments, sendMessage, navigation, cdecli.status, isDeepSearchMode]);\n\n const handleSend = useCallback(\n (text?: string) => {\n const pending = imageAttachments.forSend();\n const t = (text ?? valueRef.current).trim();\n if ((!t && !pending?.length) || !channelId) return;\n // Clear both ways: the controlled state (so React's model stays\n // consistent on next render) AND the native buffer via the ref\n // (so the visible text disappears on the same frame the user\n // pressed send, even before React flushes).\n setValue('');\n inputRef.current?.clear();\n\n // Build mode: hand off to Web Builder on this channel (web `/c/:id/webbuilder`).\n if (isBuildMode) {\n navigation.dispatch(\n CommonActions.navigate({\n name: 'MainStack.WebBuilder',\n params: {\n ...(params.orgName ? { orgName: params.orgName } : {}),\n channelId,\n message: t,\n },\n }),\n );\n return;\n }\n\n // Deep-search mode: every new query pops the structured research\n // modal back open (parity with the previous Home flow, where every\n // submit re-opened the DeepSearchModal). Without this, a user who\n // closed the modal once would see plain assistant turns for every\n // subsequent send. Reads the LOCAL `isDeepSearchMode` so the user\n // can toggle modes mid-thread and have sends behave accordingly.\n if (isDeepSearchMode) {\n setIsDeepSearchModalOpen(true);\n }\n void sendMessage(t, pending, channelId);\n imageAttachments.clear();\n },\n [channelId, sendMessage, isDeepSearchMode, isBuildMode, navigation, params.orgName, imageAttachments],\n );\n\n const handleValueChange = useCallback((e: any) => {\n setValue(e?.nativeEvent?.text ?? '');\n }, []);\n\n /**\n * Mic affordance — web parity (`handleMicClick` in `YantraSearchComposer.tsx`).\n * The shared `MicSendButton` in `InputToolBar.tsx` only routes to `onMic`\n * when the input has no content, so we only need to handle the empty-input\n * case here.\n *\n * Permission is requested HERE, before the panel mounts. The previous flow\n * flipped `showAudioRecorder=true` immediately, which mounted\n * `AudioRecorderPanel` and constructed the native `AVAudioRecorder` via\n * `useAudioRecorder()` during the panel's render phase — before iOS had\n * granted mic access. On SDK 53 + `newArchEnabled: true` + Hermes that\n * ordering hard-crashes TestFlight builds even though it works in Expo Go\n * (Expo Go pre-caches permission). Asking up front means iOS shows the\n * system alert first and the panel only mounts (and therefore the native\n * recorder is only allocated) once permission is granted.\n *\n * `expo-audio` is lazy-imported so a missing/broken native module surfaces\n * as a user-visible banner instead of a synchronous TurboModule crash.\n */\n /** Open the record -> Whisper recorder (the fallback mic lane). */\n const openWhisperRecorder = useCallback(() => {\n setVoiceError(null);\n void (async () => {\n const { granted, error } = await requestMicPermission();\n if (!granted) {\n setVoiceError(error || 'Microphone is not available.');\n return;\n }\n setShowAudioRecorder(true);\n })();\n }, []);\n\n const handleMicPress = useCallback(() => {\n if (hasQuery) return;\n setVoiceError(null);\n Keyboard.dismiss();\n // Prefer Apple's on-device dictation (the iOS-built-in mic): it streams\n // text live and needs no upload. The bar requests its own permission\n // and calls back to the Whisper recorder if the native path is\n // unusable. Only reachable on a build that linked the native module.\n if (isNativeDictationAvailable()) {\n setShowNativeDictation(true);\n return;\n }\n openWhisperRecorder();\n }, [hasQuery, openWhisperRecorder]);\n\n /**\n * On a successful Whisper round-trip, paste the transcript into the input\n * field, close the recorder, and let the user review/send manually. Matches\n * web's `handleTranscriptionComplete`: we deliberately do NOT auto-fire\n * `sendMessage` because the user often wants to edit the dictation first.\n */\n /** Append a finished transcript to the composer and re-focus it. Shared by\n * the Whisper recorder and native dictation. */\n const pasteTranscript = useCallback((text: string) => {\n const cleaned = (text ?? '').trim();\n if (!cleaned) return;\n setValue((prev) => {\n const next = prev.trim().length > 0 ? `${prev.trim()} ${cleaned}` : cleaned;\n /*\n * Keep `inputRef`'s native text in sync with our controlled value\n * for the same reason `handleSend` does (the iOS native buffer\n * can lag the React state across a single gesture). `setNativeProps`\n * is the safe way to do this without re-rendering.\n */\n requestAnimationFrame(() => {\n inputRef.current?.setNativeProps?.({ text: next });\n inputRef.current?.focus?.();\n });\n return next;\n });\n }, []);\n\n const handleTranscriptionComplete = useCallback(\n (text: string) => {\n setShowAudioRecorder(false);\n pasteTranscript(text);\n },\n [pasteTranscript],\n );\n\n const handleRecorderCancel = useCallback(() => {\n setShowAudioRecorder(false);\n }, []);\n\n const handleDictationComplete = useCallback(\n (text: string) => {\n setShowNativeDictation(false);\n pasteTranscript(text);\n },\n [pasteTranscript],\n );\n\n const handleDictationCancel = useCallback(() => setShowNativeDictation(false), []);\n\n /** Native dictation could not run - drop to the Whisper recorder, which\n * requests its own permission and surfaces its own errors. */\n const handleDictationFallback = useCallback(() => {\n setShowNativeDictation(false);\n openWhisperRecorder();\n }, [openWhisperRecorder]);\n\n const handleRecorderError = useCallback((error: string) => {\n setVoiceError(error);\n }, []);\n\n const waitingForAssistant = useMemo(() => {\n if ((!isLoading && !isStreaming) || messages.length === 0) return false;\n if ((response ?? '').trim()) return false;\n return messages[messages.length - 1]?.role === 'user';\n }, [isLoading, isStreaming, messages, response]);\n\n /*\n * NOTE: the previous read-only `isDeepSearchChannel` derivation has been\n * removed. Mode is now a freely-toggleable local state (`activeMode`)\n * driven by tapping the search/zap pills, mirroring HomeScreen. All\n * downstream branches use `isDeepSearchMode` declared with the rest of\n * the composer state above.\n */\n\n /**\n * Deep-search overlay state.\n *\n * Restored from the previous Home-screen flow (commit history): a deep-search\n * channel renders the structured `DeepSearchModal` (query / live summary /\n * sources / research process) on top of the regular Chat conversation. Closing\n * the modal reveals the same channel's transcript underneath so the\n * user can still see the raw turns.\n *\n * Trigger semantics — IMPORTANT:\n * • Opens ONLY when a request is actually fired in this session:\n * - Home → Chat auto-fire of `initialQuery`\n * - User sends a new message in a deep-search channel from the composer\n * - Explicit retry from inside the modal\n * • Does NOT open on plain entry from ChatHistoryScreen. That's a revisit\n * of an already-completed thread — no new API call, nothing live to\n * stream, so popping the modal would just be a confusing flash. The\n * user still sees the deep-search rendering in the chat conversation\n * underneath, and any new send re-opens the live view.\n *\n * Local UI state (accordions, open flag) lives here; data state (query /\n * summary / sources) is DERIVED from the chat stream, not stored separately,\n * so there's no drift between the modal and the underlying conversation.\n */\n const [isDeepSearchModalOpen, setIsDeepSearchModalOpen] = useState(false);\n const [researchProcessOpen, setResearchProcessOpen] = useState(true);\n const [sourcesAccordionOpen, setSourcesAccordionOpen] = useState(true);\n\n /**\n * Modal data is always scoped to the CURRENT run, not the whole channel.\n * In a multi-turn deep-search channel each send is its own \"run\": one user\n * query → one assistant summary. If we sourced from \"first user message\" /\n * \"any assistant message\", a follow-up send would keep showing the previous\n * run's query and stale sources during the transient window between the new\n * user message being appended and the first stream token arriving.\n *\n * Cursor: the LAST user message in the thread. The assistant content for\n * the current run is either:\n * • `response` while the stream is filling (token-by-token), OR\n * • the assistant message that comes AFTER the last user (run complete), OR\n * • empty — covers the brief \"user message appended, no response yet\"\n * window so the modal flips to \"Running...\" with no stale body.\n */\n const lastUserIndex = useMemo(() => {\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n if (messages[i]?.role === 'user') return i;\n }\n return -1;\n }, [messages]);\n\n const deepSearchQuery = useMemo(() => {\n if (lastUserIndex >= 0) {\n const txt = messages[lastUserIndex]?.content?.trim();\n if (txt) return txt;\n }\n return initialQuery ?? null;\n }, [messages, lastUserIndex, initialQuery]);\n\n const latestAssistantContent = useMemo(() => {\n if (response && response.trim()) return response;\n if (lastUserIndex < 0) return '';\n // Look for an assistant turn strictly AFTER the last user message.\n // Anything before it belongs to a prior run and must not leak into\n // the modal for the current one.\n for (let i = lastUserIndex + 1; i < messages.length; i += 1) {\n if (messages[i]?.role === 'assistant') {\n return messages[i]?.content ?? '';\n }\n }\n return '';\n }, [response, messages, lastUserIndex]);\n\n const deepSearchSummary = useMemo(() => normalizeSummaryText(latestAssistantContent), [latestAssistantContent]);\n const deepSearchSources: DeepSearchSourceItem[] = useMemo(\n () => extractDeepSearchSources(latestAssistantContent),\n [latestAssistantContent],\n );\n\n const handleDeepSearchClose = useCallback(() => {\n if (isStreaming) cancel();\n setIsDeepSearchModalOpen(false);\n }, [isStreaming, cancel]);\n\n const handleDeepSearchRetry = useCallback(() => {\n if (!deepSearchQuery || isStreaming || !channelId) return;\n void sendMessage(deepSearchQuery, undefined, channelId);\n }, [deepSearchQuery, isStreaming, channelId, sendMessage]);\n const leftItems = useMemo(\n () =>\n getDefaultLeftItems({\n search: { active: activeMode === 'chat', onClick: () => handleModeSwitch('chat') },\n zap: { active: activeMode === 'deep-search', onClick: () => handleModeSwitch('deep-search') },\n lightbulb: {\n active: activeMode === 'build',\n onClick: () => handleModeSwitch('build'),\n },\n }),\n [activeMode, handleModeSwitch],\n );\n const rightItems = useMemo(\n () =>\n getDefaultRightItems({\n tag: { enabled: false },\n chip: { enabled: false },\n // Attaching an image was disabled here even though the transport\n // (`sendMessage(content, attachments)`) and the gateway media lane\n // already carried it, and `expo-image-picker` was already a\n // dependency with its permission strings declared in app.json.\n camera: {\n enabled: true,\n loading: imageAttachments.busyKind === 'camera',\n onClick: () => {\n void imageAttachments.captureFromCamera();\n },\n },\n image: {\n enabled: true,\n loading: imageAttachments.busyKind === 'library',\n onClick: () => {\n void imageAttachments.pickFromLibrary();\n },\n },\n attach: {\n enabled: true,\n loading: imageAttachments.busyKind === 'documents',\n onClick: () => {\n void imageAttachments.pickDocuments();\n },\n },\n }),\n [imageAttachments],\n );\n /**\n * Composer placeholder.\n *\n * Mirrors Home so the composer feels like the same affordance across the\n * \"first message\" and \"continuing thread\" surfaces — only the surrounding\n * UI changes, the input stays a stable anchor. We intentionally do NOT\n * surface a \"Loading conversation…\" state in the placeholder: the message\n * list already shows a dedicated hydrating loader, and switching the\n * placeholder for ~1 frame creates a visible flash on every entry.\n */\n const inputPlaceholder = isDeepSearchMode\n ? 'Research anything...'\n : isBuildMode\n ? 'Describe an app to build...'\n : 'Ask anything...';\n\n const inputConfig = useMemo(\n () => ({\n value,\n onChange: handleValueChange,\n placeholder: inputPlaceholder,\n disabled: !channelId,\n inputRef,\n returnKeyType: 'send',\n blurOnSubmit: false,\n onSubmitEditing: () => handleSend(),\n }),\n [value, handleValueChange, inputPlaceholder, channelId, handleSend],\n );\n const micSendButton = useMemo(\n () => ({\n hasContent: hasQuery || hasAttachments,\n onSend: () => handleSend(),\n onMic: handleMicPress,\n disabled: !channelId,\n // Empty + streaming → Stop. Typed follow-up → Send (steer). Never lock the composer.\n isLoading: canSubmit ? false : isLoading,\n onStop: isStreaming && !canSubmit ? cancel : undefined,\n steerable: isLoading || isStreaming,\n }),\n [canSubmit, handleSend, handleMicPress, isLoading, channelId, isStreaming, cancel, hasQuery, hasAttachments],\n );\n\n /**\n * Loader visibility:\n * - `isHydrating`: Apollo channel history is being fetched and we have nothing to show yet.\n * - `isWaitingForGateway`: Home navigated us here with an `initialQuery`, but CDeCLI is still\n * establishing the workspace session, so the auto-fire is deferred. Show a \"Connecting…\"\n * placeholder instead of an empty thread so the user doesn't think their send was dropped.\n */\n const isHydrating = messagesLoading && messages.length === 0 && !response;\n const isWaitingForGateway =\n !autoFiredRef.current &&\n !!channelId &&\n (!!initialQuery || hasComposerAttachments(channelId)) &&\n cdecli.status !== 'connected' &&\n cdecli.status !== 'error';\n const showSendingLoader = waitingForAssistant;\n const pinSendingLoaderNearComposer = messages.length > 0 || Boolean((response ?? '').trim());\n\n return (\n <SafeAreaView edges={['left', 'right']} style={{ flex: 1, backgroundColor: surfaceColor }}>\n <KeyboardAwareScreen>\n <Box flex={1} width=\"100%\" position=\"relative\">\n {(chatError || cdecli.error) && (\n <Box\n mb=\"$2\"\n mx=\"$4\"\n p=\"$3\"\n borderRadius=\"$md\"\n style={{\n backgroundColor: isDark ? '#2b1a1d' : '#fef2f2',\n borderWidth: 1,\n borderColor: isDark ? '#7f1d1d' : '#fecaca',\n }}\n >\n <Text fontSize=\"$sm\" style={{ color: isDark ? '#fecaca' : '#991b1b' }}>\n {chatError || cdecli.error}\n </Text>\n </Box>\n )}\n {!channelId ? (\n <Box flex={1} alignItems=\"center\" justifyContent=\"center\">\n <YantraBrandLoader size={YANTRA_LOADER_SIZE_COMPACT} />\n <Text style={[styles.statusText, { color: secondaryTextColor }]}>No channel selected</Text>\n </Box>\n ) : isHydrating ? (\n /**\n * Channel hydration (entering Chat from history or a deep\n * link) reuses the same `ThinkingIndicator` row as the\n * chat-history list — thin spinning arc + muted caption.\n * Keeps the loading vocabulary consistent across screens\n * and avoids the previous branded loader + \"Loading\n * conversation\" copy that read as a different state.\n */\n <Box flex={1} alignItems=\"center\" justifyContent=\"center\">\n <ThinkingIndicator color={secondaryTextColor} />\n </Box>\n ) : isWaitingForGateway ? (\n <Box flex={1} alignItems=\"center\" justifyContent=\"center\">\n <YantraBrandLoader size={YANTRA_LOADER_SIZE_COMPACT} />\n <Text style={[styles.statusText, { color: secondaryTextColor }]}>Connecting gateway…</Text>\n </Box>\n ) : (\n <Box flex={1} width=\"100%\" alignSelf=\"stretch\">\n <ChatTranscript\n messages={transcriptRows}\n streamingContent={response}\n renderMessageActions={renderMessageActions}\n isDark={isDark}\n listContentStyle={{\n paddingTop: 12,\n paddingBottom: 8,\n margin: 0,\n width: contentMaxWidth,\n alignSelf: 'center',\n }}\n />\n </Box>\n )}\n {showSendingLoader && !pinSendingLoaderNearComposer ? (\n <View pointerEvents=\"none\" style={styles.sendingLoaderEmptyState}>\n <ThinkingIndicator color={secondaryTextColor} />\n </View>\n ) : null}\n <KeyboardComposerDock closedInset={insets.bottom} onSwipeUp={() => inputRef.current?.focus()}>\n {showSendingLoader && pinSendingLoaderNearComposer ? (\n <View pointerEvents=\"none\" style={styles.sendingLoaderInDock}>\n <ThinkingIndicator color={secondaryTextColor} />\n </View>\n ) : null}\n <View style={[styles.bottomComposerInner, { width: contentMaxWidth }]}>\n {voiceError || imageAttachments.error ? (\n <View\n style={[\n styles.voiceErrorBanner,\n {\n backgroundColor: isDark ? '#2b1a1d' : '#fef2f2',\n borderColor: isDark ? '#7f1d1d' : '#fecaca',\n },\n ]}\n >\n <Text style={[styles.voiceErrorText, { color: isDark ? '#fecaca' : '#991b1b' }]}>\n {voiceError || imageAttachments.error}\n </Text>\n </View>\n ) : null}\n {showNativeDictation ? (\n // A throw inside the native recognizer degrades to\n // the Whisper recorder rather than killing the JS\n // thread (same guard rationale as the recorder).\n <MicErrorBoundary onCancel={handleDictationFallback} onError={handleDictationFallback}>\n <NativeDictationBar\n isDark={isDark}\n onInterim={() => undefined}\n onTranscriptionComplete={handleDictationComplete}\n onCancel={handleDictationCancel}\n onFallback={handleDictationFallback}\n />\n </MicErrorBoundary>\n ) : showAudioRecorder ? (\n /*\n * Render-time JS errors thrown by `useAudioRecorder`\n * (e.g. expo-audio native module missing, recorder\n * constructor throws in a release build) are caught\n * by the boundary and converted into a soft cancel\n * via the same handlers the panel itself uses on\n * runtime errors. Without this, a throw during the\n * panel's render phase kills the JS thread in\n * release / TestFlight.\n */\n <MicErrorBoundary onCancel={handleRecorderCancel} onError={handleRecorderError}>\n <AudioRecorderPanel\n isDark={isDark}\n onTranscriptionComplete={handleTranscriptionComplete}\n onCancel={handleRecorderCancel}\n onError={handleRecorderError}\n />\n </MicErrorBoundary>\n ) : (\n <>\n <ComposerAttachmentBar\n attachments={imageAttachments.pending}\n onRemove={imageAttachments.remove}\n isDark={isDark}\n />\n <InputToolBar\n inputConfig={inputConfig}\n leftItems={leftItems}\n rightItems={rightItems}\n templateButton={null}\n templateModalConfig={null}\n micSendButton={micSendButton}\n />\n </>\n )}\n </View>\n </KeyboardComposerDock>\n </Box>\n </KeyboardAwareScreen>\n {isDeepSearchMode ? (\n <DeepSearchModal\n visible={isDeepSearchModalOpen}\n query={deepSearchQuery}\n summaryText={deepSearchSummary}\n sources={deepSearchSources}\n isStreaming={isStreaming}\n researchProcessOpen={researchProcessOpen}\n sourcesAccordionOpen={sourcesAccordionOpen}\n onToggleResearchProcess={() => setResearchProcessOpen((prev) => !prev)}\n onToggleSourcesAccordion={() => setSourcesAccordionOpen((prev) => !prev)}\n onRetry={handleDeepSearchRetry}\n onStop={cancel}\n onClose={handleDeepSearchClose}\n />\n ) : null}\n </SafeAreaView>\n );\n}\n\nconst styles = StyleSheet.create({\n statusText: {\n marginTop: 14,\n fontSize: 13,\n fontWeight: '500',\n },\n bottomComposerInner: {\n paddingHorizontal: 14,\n paddingTop: 10,\n paddingBottom: 0,\n },\n voiceErrorBanner: {\n marginBottom: 8,\n paddingHorizontal: 12,\n paddingVertical: 8,\n borderRadius: 10,\n borderWidth: 1,\n },\n voiceErrorText: {\n fontSize: 12,\n fontWeight: '500',\n },\n sendingLoaderEmptyState: {\n position: 'absolute',\n left: 0,\n right: 0,\n top: 0,\n bottom: 0,\n justifyContent: 'center',\n alignItems: 'center',\n zIndex: 40,\n },\n sendingLoaderInDock: {\n alignItems: 'center',\n justifyContent: 'flex-end',\n paddingBottom: 8,\n },\n});\n"],"names":["_a","_b"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/screens/Chat/index.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { Keyboard, StyleSheet, TextInput, View, useColorScheme, useWindowDimensions } from 'react-native';\nimport { Box, InputToolBar, Text, getDefaultLeftItems, getDefaultRightItems } from '@admin-layout/gluestack-ui-mobile';\nimport { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';\nimport { useNavigation, useRoute, CommonActions } from '@react-navigation/native';\nimport { MessageActionBar } from '../../features/chat/MessageActionBar';\nimport { ChatTranscript } from '../../features/chat/ChatTranscript';\nimport { useCdecliConnection } from '../../contexts/CdecliConnectionContext';\nimport { useChatStream, type UseChatStreamRouting } from '../../hooks/useChatStream';\nimport { KeyboardAwareScreen, KeyboardComposerDock } from '../../components/KeyboardComposerDock';\nimport { YantraBrandLoader, YANTRA_LOADER_SIZE_COMPACT } from '../../components/YantraBrandLoader';\nimport ThinkingIndicator from '../../components/ThinkingIndicator';\nimport { mobileTokens } from '../../theme/mobileTokens';\nimport DeepSearchModal from '../Home/components/DeepSearchModal';\nimport { extractDeepSearchSources, normalizeSummaryText, type DeepSearchSourceItem } from '../Home/deepSearchUtils';\nimport type { ModeSubmission, RoutingMode } from '../Home/types';\nimport { AudioRecorderPanel } from '../../features/audio-input/AudioRecorderPanel';\nimport { NativeDictationBar } from '../../features/audio-input/NativeDictationBar';\nimport { isNativeDictationAvailable } from '../../features/audio-input/nativeDictation';\nimport { useImageAttachments } from '../../features/attachments/useImageAttachments';\nimport { ComposerAttachmentBar } from '../../features/attachments/ComposerAttachmentBar';\nimport { hasComposerAttachments, takeComposerAttachments } from '../../features/attachments/composerAttachmentHandoff';\nimport { MicErrorBoundary } from '../../features/audio-input/MicErrorBoundary';\nimport { requestMicPermission } from '../../features/audio-input/useAudioPermission';\n\n/** Route params passed by Home (or by deep-link) when entering the Chat surface. */\nexport interface ChatScreenRouteParams {\n /** Channel id is required — the screen is keyed on the conversation. */\n channelId: string;\n /** Org slug used by gateway/account context. */\n orgName?: string | null;\n /**\n * First user message to auto-fire when the screen mounts. Home generates\n * this when the user submits from the empty composer so the assistant\n * starts streaming on Chat without an extra round trip.\n */\n initialQuery?: string | null;\n /**\n * Seed for the composer's mode (`'chat'` | `'deep-search'` | `'build'`).\n *\n * Set by `HomeScreen` to carry the user's last selection across the\n * navigation hop (\"research from home → research-shaped composer on chat\").\n * `ChatHistoryScreen` does NOT forward this — revisits always land on the\n * default `'chat'` mode, matching Home's defaults, and the user is free to\n * switch from there. Build-mode send hands off to Web Builder.\n *\n * After mount, this param is purely a seed: the screen keeps its own\n * `activeMode` so the user can toggle freely while inside the thread.\n */\n initialMode?: RoutingMode;\n /** Optional attachments forwarded with the initial message. */\n initialAttachments?: ModeSubmission['attachments'];\n}\n\nexport default function ChatScreen() {\n const navigation = useNavigation<any>();\n const route = useRoute<any>();\n\n const params = (route?.params ?? {}) as Partial<ChatScreenRouteParams>;\n const channelId = useMemo(() => {\n const raw = typeof params.channelId === 'string' ? params.channelId.trim() : '';\n return raw.length > 0 ? raw : null;\n }, [params.channelId]);\n const initialQuery = useMemo(() => {\n const raw = typeof params.initialQuery === 'string' ? params.initialQuery.trim() : '';\n return raw.length > 0 ? raw : null;\n }, [params.initialQuery]);\n const initialAttachments = params.initialAttachments;\n\n const colorScheme = useColorScheme();\n const isDark = colorScheme === 'dark';\n const { width: screenWidth } = useWindowDimensions();\n const contentMaxWidth = Math.min(720, Math.max(360, screenWidth - 24));\n\n const surfaceColor = isDark ? '#0f172a' : mobileTokens.color.surface;\n const secondaryTextColor = isDark ? '#94a3b8' : mobileTokens.color.textMuted;\n\n const insets = useSafeAreaInsets();\n\n /**\n * Gateway + chat stream routing.\n *\n * The CDeCLI lifecycle is owned app-wide by `CdecliConnectionProvider`,\n * so we just register this screen's channelId and read the shared status.\n * This avoids racing `gatewayConnect` mutations with the header's\n * `GatewayConnector` (which previously ran its own `useCdecliAutoConnect`\n * and could clobber the chat session binding).\n */\n const cdecli = useCdecliConnection();\n const effectivePersistenceMode = cdecli.persistenceMode ?? 'backend';\n\n useEffect(() => {\n if (!channelId) return undefined;\n cdecli.setActiveChannelId(channelId);\n return () => {\n cdecli.setActiveChannelId(undefined);\n };\n }, [channelId, cdecli.setActiveChannelId]);\n\n const chatRouting: UseChatStreamRouting = useMemo(\n () => ({\n channelConnected: cdecli.channelConnected,\n accountId: cdecli.accountId,\n persistenceMode: effectivePersistenceMode,\n }),\n [cdecli.channelConnected, cdecli.accountId, effectivePersistenceMode],\n );\n\n const chat = useChatStream(channelId, chatRouting);\n\n /**\n * Gifted-shaped transcript rows, memoised on `messages` alone.\n *\n * This screen re-renders on every streamed token — `useChatStream` does\n * `setResponse((r) => r + text)` per delta. Building this array inline in\n * the JSX therefore handed `PlanModeView` a fresh `messages` identity on\n * every token, which invalidated its own `useMemo([messages, …])`, re-mapped\n * every row, and re-rendered every bubble. Assistant bubbles render through\n * `react-native-markdown-display`, so that meant re-parsing the markdown of\n * the whole thread once per token — O(messages) parses per delta, on the JS\n * thread, which is exactly when the UI has to stay responsive.\n *\n * `messages` is already stable upstream (`useChatApi` memoises it on the\n * Apollo result), so this only rebuilds when a message is actually added or\n * changed.\n */\n const transcriptRows = useMemo(\n () =>\n chat.messages.map((msg, index) => ({\n id: `msg-${index}-${msg.role}`,\n role: msg.role,\n content: msg.content,\n attachments: msg.attachments,\n metadata: (msg as { metadata?: unknown }).metadata,\n })),\n [chat.messages],\n );\n const { messages, response, error: chatError, isLoading, isStreaming, messagesLoading, sendMessage, cancel } = chat;\n\n /**\n * \"Try again\" on an assistant message: resend the nearest preceding user\n * prompt on the same channel. Row ids are `msg-<index>-<role>` (see\n * transcriptRows), so the transcript index comes straight out of the id.\n */\n const regenerateFrom = useCallback(\n (messageId: string) => {\n const match = /^msg-(\\d+)-assistant$/.exec(messageId);\n if (!match || !channelId) return;\n for (let i = Number(match[1]) - 1; i >= 0; i -= 1) {\n const prior = chat.messages[i];\n if (prior?.role === 'user' && (prior.content?.trim() || prior.attachments?.length)) {\n void chat.sendMessage(prior.content, prior.attachments, channelId);\n return;\n }\n }\n },\n [chat, channelId],\n );\n\n const chatBusy = isLoading || isStreaming;\n const renderMessageActions = useCallback(\n (message: { id: string; role: string; content: string }) =>\n message.role === 'assistant' ? (\n <MessageActionBar\n text={message.content}\n onRegenerate={chatBusy ? undefined : () => regenerateFrom(message.id)}\n />\n ) : null,\n [chatBusy, regenerateFrom],\n );\n\n const imageAttachments = useImageAttachments();\n\n /*\n * Composer state — kept local so it doesn't leak across navigations.\n *\n * `valueRef` mirrors the controlled `value` so `handleSend` can read the\n * latest text without taking a dependency on it. Without this, `handleSend`\n * (and therefore `micSendButton.onSend`) would be recreated on every\n * keystroke, which is unnecessary churn for `InputToolBar`.\n *\n * `inputRef` is forwarded to InputToolBar's TextInput so we can call\n * `.clear()` imperatively after sending. On iOS, the native text buffer\n * can stay desynced from the controlled `value` for a frame when a state\n * update is followed by an async dispatch in the same gesture — the\n * \"send-and-clear\" race. The imperative clear closes that gap deterministically.\n */\n const [value, setValue] = useState('');\n const valueRef = useRef(value);\n valueRef.current = value;\n const inputRef = useRef<TextInput>(null);\n const trimmed = value.trim();\n const hasQuery = trimmed.length > 0;\n const hasAttachments = imageAttachments.pending.length > 0;\n const canSubmit = (hasQuery || hasAttachments) && Boolean(channelId);\n\n /**\n * Composer mode is FREELY user-controlled, mirroring `HomeScreen`.\n *\n * Seed:\n * • From Home (which forwards `initialMode` alongside `initialQuery`):\n * start in whatever mode the user picked there so the \"research from\n * home → research-shaped chat\" hand-off feels continuous.\n * • From ChatHistory (which intentionally does NOT pass `initialMode`):\n * default to `'chat'`, same as Home's empty composer.\n *\n * Post-mount this is a normal local state — the search/zap pills toggle\n * it on tap, and every place that previously branched on\n * `params.initialMode` now branches on `activeMode` instead.\n */\n const [activeMode, setActiveMode] = useState<RoutingMode>(() =>\n params.initialMode === 'deep-search' ? 'deep-search' : params.initialMode === 'build' ? 'build' : 'chat',\n );\n const handleModeSwitch = useCallback((mode: RoutingMode) => {\n setActiveMode(mode);\n }, []);\n const isDeepSearchMode = activeMode === 'deep-search';\n const isBuildMode = activeMode === 'build';\n\n /**\n * Inline voice recorder state — parity with HomeScreen and the web\n * `YantraSearchComposer.showAudioRecorder` flow. When active, the panel\n * fully replaces the InputToolBar so the toolbar's mic→send button\n * doesn't compete with the panel's own send/cancel affordances.\n */\n const [showAudioRecorder, setShowAudioRecorder] = useState(false);\n // Apple on-device dictation (the iOS-built-in mic) is preferred when the\n // binary has it; the Whisper recorder above is the fallback.\n const [showNativeDictation, setShowNativeDictation] = useState(false);\n const [voiceError, setVoiceError] = useState<string | null>(null);\n\n /**\n * Auto-fire the initial query once when the screen mounts on a fresh channel.\n * Home creates the channel optimistically and forwards the user prompt here so\n * the first assistant token arrives without an extra interaction.\n *\n * Wait for CDeCLI to finish connecting (or fail) before auto-firing — chat is\n * cdecli-only and `sendMessage` rejects sends while the gateway is disconnected.\n */\n const autoFiredRef = useRef(false);\n useEffect(() => {\n if (autoFiredRef.current) return;\n if (!channelId) return;\n if (cdecli.status !== 'connected' && cdecli.status !== 'error') return;\n const stashed = hasComposerAttachments(channelId) ? takeComposerAttachments(channelId) : undefined;\n if (!initialQuery && !stashed?.length) return;\n\n autoFiredRef.current = true;\n // Deep-search auto-fire from Home: open the structured modal at the\n // same instant we kick off the send so the user sees the research view\n // streaming in rather than raw assistant turns. Tied to the\n // sendMessage call (same event) to avoid a race where the modal opens\n // before/after the actual send.\n //\n // We branch on the LOCAL `isDeepSearchMode` instead of\n // `params.initialMode`. At auto-fire time they're equal (the local\n // state was seeded from the param), and using the local state means\n // future toggles drive the same code path consistently.\n if (isDeepSearchMode) {\n setIsDeepSearchModalOpen(true);\n }\n void sendMessage(initialQuery ?? '', stashed, channelId);\n try {\n navigation.setParams({ initialQuery: null, initialAttachments: null });\n } catch {\n /* setParams may be unavailable in tests; clearing is best-effort. */\n }\n }, [channelId, initialQuery, initialAttachments, sendMessage, navigation, cdecli.status, isDeepSearchMode]);\n\n /**\n * Answer a parked `[ASK_USER]` question. The formatted `**Title:** answer`\n * block goes out as an ordinary user message — cdecli absorbs it into the\n * turn it parked, which is what releases the run. Deliberately NOT routed\n * through handleSend: no deep-search/build-mode rewriting, no attachments,\n * and it must send even while the turn is still streaming.\n */\n const handleAskUserAnswer = useCallback(\n (formatted: string) => {\n const text = formatted.trim();\n if (!text) return;\n void sendMessage(text, undefined, channelId);\n },\n [sendMessage, channelId],\n );\n\n const handleSend = useCallback(\n (text?: string) => {\n const pending = imageAttachments.forSend();\n const t = (text ?? valueRef.current).trim();\n if ((!t && !pending?.length) || !channelId) return;\n // Clear both ways: the controlled state (so React's model stays\n // consistent on next render) AND the native buffer via the ref\n // (so the visible text disappears on the same frame the user\n // pressed send, even before React flushes).\n setValue('');\n inputRef.current?.clear();\n\n // Build mode: hand off to Web Builder on this channel (web `/c/:id/webbuilder`).\n if (isBuildMode) {\n navigation.dispatch(\n CommonActions.navigate({\n name: 'MainStack.WebBuilder',\n params: {\n ...(params.orgName ? { orgName: params.orgName } : {}),\n channelId,\n message: t,\n },\n }),\n );\n return;\n }\n\n // Deep-search mode: every new query pops the structured research\n // modal back open (parity with the previous Home flow, where every\n // submit re-opened the DeepSearchModal). Without this, a user who\n // closed the modal once would see plain assistant turns for every\n // subsequent send. Reads the LOCAL `isDeepSearchMode` so the user\n // can toggle modes mid-thread and have sends behave accordingly.\n if (isDeepSearchMode) {\n setIsDeepSearchModalOpen(true);\n }\n void sendMessage(t, pending, channelId);\n imageAttachments.clear();\n },\n [channelId, sendMessage, isDeepSearchMode, isBuildMode, navigation, params.orgName, imageAttachments],\n );\n\n const handleValueChange = useCallback((e: any) => {\n setValue(e?.nativeEvent?.text ?? '');\n }, []);\n\n /**\n * Mic affordance — web parity (`handleMicClick` in `YantraSearchComposer.tsx`).\n * The shared `MicSendButton` in `InputToolBar.tsx` only routes to `onMic`\n * when the input has no content, so we only need to handle the empty-input\n * case here.\n *\n * Permission is requested HERE, before the panel mounts. The previous flow\n * flipped `showAudioRecorder=true` immediately, which mounted\n * `AudioRecorderPanel` and constructed the native `AVAudioRecorder` via\n * `useAudioRecorder()` during the panel's render phase — before iOS had\n * granted mic access. On SDK 53 + `newArchEnabled: true` + Hermes that\n * ordering hard-crashes TestFlight builds even though it works in Expo Go\n * (Expo Go pre-caches permission). Asking up front means iOS shows the\n * system alert first and the panel only mounts (and therefore the native\n * recorder is only allocated) once permission is granted.\n *\n * `expo-audio` is lazy-imported so a missing/broken native module surfaces\n * as a user-visible banner instead of a synchronous TurboModule crash.\n */\n /** Open the record -> Whisper recorder (the fallback mic lane). */\n const openWhisperRecorder = useCallback(() => {\n setVoiceError(null);\n void (async () => {\n const { granted, error } = await requestMicPermission();\n if (!granted) {\n setVoiceError(error || 'Microphone is not available.');\n return;\n }\n setShowAudioRecorder(true);\n })();\n }, []);\n\n const handleMicPress = useCallback(() => {\n if (hasQuery) return;\n setVoiceError(null);\n Keyboard.dismiss();\n // Prefer Apple's on-device dictation (the iOS-built-in mic): it streams\n // text live and needs no upload. The bar requests its own permission\n // and calls back to the Whisper recorder if the native path is\n // unusable. Only reachable on a build that linked the native module.\n if (isNativeDictationAvailable()) {\n setShowNativeDictation(true);\n return;\n }\n openWhisperRecorder();\n }, [hasQuery, openWhisperRecorder]);\n\n /**\n * On a successful Whisper round-trip, paste the transcript into the input\n * field, close the recorder, and let the user review/send manually. Matches\n * web's `handleTranscriptionComplete`: we deliberately do NOT auto-fire\n * `sendMessage` because the user often wants to edit the dictation first.\n */\n /** Append a finished transcript to the composer and re-focus it. Shared by\n * the Whisper recorder and native dictation. */\n const pasteTranscript = useCallback((text: string) => {\n const cleaned = (text ?? '').trim();\n if (!cleaned) return;\n setValue((prev) => {\n const next = prev.trim().length > 0 ? `${prev.trim()} ${cleaned}` : cleaned;\n /*\n * Keep `inputRef`'s native text in sync with our controlled value\n * for the same reason `handleSend` does (the iOS native buffer\n * can lag the React state across a single gesture). `setNativeProps`\n * is the safe way to do this without re-rendering.\n */\n requestAnimationFrame(() => {\n inputRef.current?.setNativeProps?.({ text: next });\n inputRef.current?.focus?.();\n });\n return next;\n });\n }, []);\n\n const handleTranscriptionComplete = useCallback(\n (text: string) => {\n setShowAudioRecorder(false);\n pasteTranscript(text);\n },\n [pasteTranscript],\n );\n\n const handleRecorderCancel = useCallback(() => {\n setShowAudioRecorder(false);\n }, []);\n\n const handleDictationComplete = useCallback(\n (text: string) => {\n setShowNativeDictation(false);\n pasteTranscript(text);\n },\n [pasteTranscript],\n );\n\n const handleDictationCancel = useCallback(() => setShowNativeDictation(false), []);\n\n /** Native dictation could not run - drop to the Whisper recorder, which\n * requests its own permission and surfaces its own errors. */\n const handleDictationFallback = useCallback(() => {\n setShowNativeDictation(false);\n openWhisperRecorder();\n }, [openWhisperRecorder]);\n\n const handleRecorderError = useCallback((error: string) => {\n setVoiceError(error);\n }, []);\n\n const waitingForAssistant = useMemo(() => {\n if ((!isLoading && !isStreaming) || messages.length === 0) return false;\n if ((response ?? '').trim()) return false;\n return messages[messages.length - 1]?.role === 'user';\n }, [isLoading, isStreaming, messages, response]);\n\n /*\n * NOTE: the previous read-only `isDeepSearchChannel` derivation has been\n * removed. Mode is now a freely-toggleable local state (`activeMode`)\n * driven by tapping the search/zap pills, mirroring HomeScreen. All\n * downstream branches use `isDeepSearchMode` declared with the rest of\n * the composer state above.\n */\n\n /**\n * Deep-search overlay state.\n *\n * Restored from the previous Home-screen flow (commit history): a deep-search\n * channel renders the structured `DeepSearchModal` (query / live summary /\n * sources / research process) on top of the regular Chat conversation. Closing\n * the modal reveals the same channel's transcript underneath so the\n * user can still see the raw turns.\n *\n * Trigger semantics — IMPORTANT:\n * • Opens ONLY when a request is actually fired in this session:\n * - Home → Chat auto-fire of `initialQuery`\n * - User sends a new message in a deep-search channel from the composer\n * - Explicit retry from inside the modal\n * • Does NOT open on plain entry from ChatHistoryScreen. That's a revisit\n * of an already-completed thread — no new API call, nothing live to\n * stream, so popping the modal would just be a confusing flash. The\n * user still sees the deep-search rendering in the chat conversation\n * underneath, and any new send re-opens the live view.\n *\n * Local UI state (accordions, open flag) lives here; data state (query /\n * summary / sources) is DERIVED from the chat stream, not stored separately,\n * so there's no drift between the modal and the underlying conversation.\n */\n const [isDeepSearchModalOpen, setIsDeepSearchModalOpen] = useState(false);\n const [researchProcessOpen, setResearchProcessOpen] = useState(true);\n const [sourcesAccordionOpen, setSourcesAccordionOpen] = useState(true);\n\n /**\n * Modal data is always scoped to the CURRENT run, not the whole channel.\n * In a multi-turn deep-search channel each send is its own \"run\": one user\n * query → one assistant summary. If we sourced from \"first user message\" /\n * \"any assistant message\", a follow-up send would keep showing the previous\n * run's query and stale sources during the transient window between the new\n * user message being appended and the first stream token arriving.\n *\n * Cursor: the LAST user message in the thread. The assistant content for\n * the current run is either:\n * • `response` while the stream is filling (token-by-token), OR\n * • the assistant message that comes AFTER the last user (run complete), OR\n * • empty — covers the brief \"user message appended, no response yet\"\n * window so the modal flips to \"Running...\" with no stale body.\n */\n const lastUserIndex = useMemo(() => {\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n if (messages[i]?.role === 'user') return i;\n }\n return -1;\n }, [messages]);\n\n const deepSearchQuery = useMemo(() => {\n if (lastUserIndex >= 0) {\n const txt = messages[lastUserIndex]?.content?.trim();\n if (txt) return txt;\n }\n return initialQuery ?? null;\n }, [messages, lastUserIndex, initialQuery]);\n\n const latestAssistantContent = useMemo(() => {\n if (response && response.trim()) return response;\n if (lastUserIndex < 0) return '';\n // Look for an assistant turn strictly AFTER the last user message.\n // Anything before it belongs to a prior run and must not leak into\n // the modal for the current one.\n for (let i = lastUserIndex + 1; i < messages.length; i += 1) {\n if (messages[i]?.role === 'assistant') {\n return messages[i]?.content ?? '';\n }\n }\n return '';\n }, [response, messages, lastUserIndex]);\n\n const deepSearchSummary = useMemo(() => normalizeSummaryText(latestAssistantContent), [latestAssistantContent]);\n const deepSearchSources: DeepSearchSourceItem[] = useMemo(\n () => extractDeepSearchSources(latestAssistantContent),\n [latestAssistantContent],\n );\n\n const handleDeepSearchClose = useCallback(() => {\n if (isStreaming) cancel();\n setIsDeepSearchModalOpen(false);\n }, [isStreaming, cancel]);\n\n const handleDeepSearchRetry = useCallback(() => {\n if (!deepSearchQuery || isStreaming || !channelId) return;\n void sendMessage(deepSearchQuery, undefined, channelId);\n }, [deepSearchQuery, isStreaming, channelId, sendMessage]);\n const leftItems = useMemo(\n () =>\n getDefaultLeftItems({\n search: { active: activeMode === 'chat', onClick: () => handleModeSwitch('chat') },\n zap: { active: activeMode === 'deep-search', onClick: () => handleModeSwitch('deep-search') },\n lightbulb: {\n active: activeMode === 'build',\n onClick: () => handleModeSwitch('build'),\n },\n }),\n [activeMode, handleModeSwitch],\n );\n const rightItems = useMemo(\n () =>\n getDefaultRightItems({\n tag: { enabled: false },\n chip: { enabled: false },\n // Attaching an image was disabled here even though the transport\n // (`sendMessage(content, attachments)`) and the gateway media lane\n // already carried it, and `expo-image-picker` was already a\n // dependency with its permission strings declared in app.json.\n camera: {\n enabled: true,\n loading: imageAttachments.busyKind === 'camera',\n onClick: () => {\n void imageAttachments.captureFromCamera();\n },\n },\n image: {\n enabled: true,\n loading: imageAttachments.busyKind === 'library',\n onClick: () => {\n void imageAttachments.pickFromLibrary();\n },\n },\n attach: {\n enabled: true,\n loading: imageAttachments.busyKind === 'documents',\n onClick: () => {\n void imageAttachments.pickDocuments();\n },\n },\n }),\n [imageAttachments],\n );\n /**\n * Composer placeholder.\n *\n * Mirrors Home so the composer feels like the same affordance across the\n * \"first message\" and \"continuing thread\" surfaces — only the surrounding\n * UI changes, the input stays a stable anchor. We intentionally do NOT\n * surface a \"Loading conversation…\" state in the placeholder: the message\n * list already shows a dedicated hydrating loader, and switching the\n * placeholder for ~1 frame creates a visible flash on every entry.\n */\n const inputPlaceholder = isDeepSearchMode\n ? 'Research anything...'\n : isBuildMode\n ? 'Describe an app to build...'\n : 'Ask anything...';\n\n const inputConfig = useMemo(\n () => ({\n value,\n onChange: handleValueChange,\n placeholder: inputPlaceholder,\n disabled: !channelId,\n inputRef,\n returnKeyType: 'send',\n blurOnSubmit: false,\n onSubmitEditing: () => handleSend(),\n }),\n [value, handleValueChange, inputPlaceholder, channelId, handleSend],\n );\n const micSendButton = useMemo(\n () => ({\n hasContent: hasQuery || hasAttachments,\n onSend: () => handleSend(),\n onMic: handleMicPress,\n disabled: !channelId,\n // Empty + streaming → Stop. Typed follow-up → Send (steer). Never lock the composer.\n isLoading: canSubmit ? false : isLoading,\n onStop: isStreaming && !canSubmit ? cancel : undefined,\n steerable: isLoading || isStreaming,\n }),\n [canSubmit, handleSend, handleMicPress, isLoading, channelId, isStreaming, cancel, hasQuery, hasAttachments],\n );\n\n /**\n * Loader visibility:\n * - `isHydrating`: Apollo channel history is being fetched and we have nothing to show yet.\n * - `isWaitingForGateway`: Home navigated us here with an `initialQuery`, but CDeCLI is still\n * establishing the workspace session, so the auto-fire is deferred. Show a \"Connecting…\"\n * placeholder instead of an empty thread so the user doesn't think their send was dropped.\n */\n const isHydrating = messagesLoading && messages.length === 0 && !response;\n const isWaitingForGateway =\n !autoFiredRef.current &&\n !!channelId &&\n (!!initialQuery || hasComposerAttachments(channelId)) &&\n cdecli.status !== 'connected' &&\n cdecli.status !== 'error';\n const showSendingLoader = waitingForAssistant;\n const pinSendingLoaderNearComposer = messages.length > 0 || Boolean((response ?? '').trim());\n\n return (\n <SafeAreaView edges={['left', 'right']} style={{ flex: 1, backgroundColor: surfaceColor }}>\n <KeyboardAwareScreen>\n <Box flex={1} width=\"100%\" position=\"relative\">\n {(chatError || cdecli.error) && (\n <Box\n mb=\"$2\"\n mx=\"$4\"\n p=\"$3\"\n borderRadius=\"$md\"\n style={{\n backgroundColor: isDark ? '#2b1a1d' : '#fef2f2',\n borderWidth: 1,\n borderColor: isDark ? '#7f1d1d' : '#fecaca',\n }}\n >\n <Text fontSize=\"$sm\" style={{ color: isDark ? '#fecaca' : '#991b1b' }}>\n {chatError || cdecli.error}\n </Text>\n </Box>\n )}\n {!channelId ? (\n <Box flex={1} alignItems=\"center\" justifyContent=\"center\">\n <YantraBrandLoader size={YANTRA_LOADER_SIZE_COMPACT} />\n <Text style={[styles.statusText, { color: secondaryTextColor }]}>No channel selected</Text>\n </Box>\n ) : isHydrating ? (\n /**\n * Channel hydration (entering Chat from history or a deep\n * link) reuses the same `ThinkingIndicator` row as the\n * chat-history list — thin spinning arc + muted caption.\n * Keeps the loading vocabulary consistent across screens\n * and avoids the previous branded loader + \"Loading\n * conversation\" copy that read as a different state.\n */\n <Box flex={1} alignItems=\"center\" justifyContent=\"center\">\n <ThinkingIndicator color={secondaryTextColor} />\n </Box>\n ) : isWaitingForGateway ? (\n <Box flex={1} alignItems=\"center\" justifyContent=\"center\">\n <YantraBrandLoader size={YANTRA_LOADER_SIZE_COMPACT} />\n <Text style={[styles.statusText, { color: secondaryTextColor }]}>Connecting gateway…</Text>\n </Box>\n ) : (\n <Box flex={1} width=\"100%\" alignSelf=\"stretch\">\n <ChatTranscript\n messages={transcriptRows}\n streamingContent={response}\n renderMessageActions={renderMessageActions}\n isDark={isDark}\n onAskUserAnswer={handleAskUserAnswer}\n sending={chatBusy}\n listContentStyle={{\n paddingTop: 12,\n paddingBottom: 8,\n margin: 0,\n width: contentMaxWidth,\n alignSelf: 'center',\n }}\n />\n </Box>\n )}\n {showSendingLoader && !pinSendingLoaderNearComposer ? (\n <View pointerEvents=\"none\" style={styles.sendingLoaderEmptyState}>\n <ThinkingIndicator color={secondaryTextColor} />\n </View>\n ) : null}\n <KeyboardComposerDock closedInset={insets.bottom} onSwipeUp={() => inputRef.current?.focus()}>\n {showSendingLoader && pinSendingLoaderNearComposer ? (\n <View pointerEvents=\"none\" style={styles.sendingLoaderInDock}>\n <ThinkingIndicator color={secondaryTextColor} />\n </View>\n ) : null}\n <View style={[styles.bottomComposerInner, { width: contentMaxWidth }]}>\n {voiceError || imageAttachments.error ? (\n <View\n style={[\n styles.voiceErrorBanner,\n {\n backgroundColor: isDark ? '#2b1a1d' : '#fef2f2',\n borderColor: isDark ? '#7f1d1d' : '#fecaca',\n },\n ]}\n >\n <Text style={[styles.voiceErrorText, { color: isDark ? '#fecaca' : '#991b1b' }]}>\n {voiceError || imageAttachments.error}\n </Text>\n </View>\n ) : null}\n {showNativeDictation ? (\n // A throw inside the native recognizer degrades to\n // the Whisper recorder rather than killing the JS\n // thread (same guard rationale as the recorder).\n <MicErrorBoundary onCancel={handleDictationFallback} onError={handleDictationFallback}>\n <NativeDictationBar\n isDark={isDark}\n onInterim={() => undefined}\n onTranscriptionComplete={handleDictationComplete}\n onCancel={handleDictationCancel}\n onFallback={handleDictationFallback}\n />\n </MicErrorBoundary>\n ) : showAudioRecorder ? (\n /*\n * Render-time JS errors thrown by `useAudioRecorder`\n * (e.g. expo-audio native module missing, recorder\n * constructor throws in a release build) are caught\n * by the boundary and converted into a soft cancel\n * via the same handlers the panel itself uses on\n * runtime errors. Without this, a throw during the\n * panel's render phase kills the JS thread in\n * release / TestFlight.\n */\n <MicErrorBoundary onCancel={handleRecorderCancel} onError={handleRecorderError}>\n <AudioRecorderPanel\n isDark={isDark}\n onTranscriptionComplete={handleTranscriptionComplete}\n onCancel={handleRecorderCancel}\n onError={handleRecorderError}\n />\n </MicErrorBoundary>\n ) : (\n <>\n <ComposerAttachmentBar\n attachments={imageAttachments.pending}\n onRemove={imageAttachments.remove}\n isDark={isDark}\n />\n <InputToolBar\n inputConfig={inputConfig}\n leftItems={leftItems}\n rightItems={rightItems}\n templateButton={null}\n templateModalConfig={null}\n micSendButton={micSendButton}\n />\n </>\n )}\n </View>\n </KeyboardComposerDock>\n </Box>\n </KeyboardAwareScreen>\n {isDeepSearchMode ? (\n <DeepSearchModal\n visible={isDeepSearchModalOpen}\n query={deepSearchQuery}\n summaryText={deepSearchSummary}\n sources={deepSearchSources}\n isStreaming={isStreaming}\n researchProcessOpen={researchProcessOpen}\n sourcesAccordionOpen={sourcesAccordionOpen}\n onToggleResearchProcess={() => setResearchProcessOpen((prev) => !prev)}\n onToggleSourcesAccordion={() => setSourcesAccordionOpen((prev) => !prev)}\n onRetry={handleDeepSearchRetry}\n onStop={cancel}\n onClose={handleDeepSearchClose}\n />\n ) : null}\n </SafeAreaView>\n );\n}\n\nconst styles = StyleSheet.create({\n statusText: {\n marginTop: 14,\n fontSize: 13,\n fontWeight: '500',\n },\n bottomComposerInner: {\n paddingHorizontal: 14,\n paddingTop: 10,\n paddingBottom: 0,\n },\n voiceErrorBanner: {\n marginBottom: 8,\n paddingHorizontal: 12,\n paddingVertical: 8,\n borderRadius: 10,\n borderWidth: 1,\n },\n voiceErrorText: {\n fontSize: 12,\n fontWeight: '500',\n },\n sendingLoaderEmptyState: {\n position: 'absolute',\n left: 0,\n right: 0,\n top: 0,\n bottom: 0,\n justifyContent: 'center',\n alignItems: 'center',\n zIndex: 40,\n },\n sendingLoaderInDock: {\n alignItems: 'center',\n justifyContent: 'flex-end',\n paddingBottom: 8,\n },\n});\n"],"names":["_a","_b"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminide-stack/yantra-mobile",
|
|
3
|
-
"version": "12.0.
|
|
3
|
+
"version": "12.0.52-alpha.0",
|
|
4
4
|
"description": "Sample core for higher packages to depend on",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "CDMBase LLC",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"typescript": {
|
|
58
58
|
"definition": "lib/index.d.ts"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "b3274a01090c298296402606b7d5e51b94bffa4c"
|
|
61
61
|
}
|