@adminide-stack/yantra-mobile 12.0.54-alpha.1 → 12.0.54-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/lib/components/NavigationHeader/NavigationHeader.js +5 -2
  2. package/lib/components/NavigationHeader/NavigationHeader.js.map +1 -1
  3. package/lib/features/agent-chat/NativeAgentChat.js +354 -0
  4. package/lib/features/agent-chat/NativeAgentChat.js.map +1 -0
  5. package/lib/features/attachments/displayUserMessageText.js +3 -2
  6. package/lib/features/attachments/displayUserMessageText.js.map +1 -1
  7. package/lib/features/attachments/useImageAttachments.js +9 -7
  8. package/lib/features/attachments/useImageAttachments.js.map +1 -1
  9. package/lib/features/canvas/withRemoteSafeArea.js +46 -0
  10. package/lib/features/canvas/withRemoteSafeArea.js.map +1 -0
  11. package/lib/features/chat/ChatTranscript.js +23 -15
  12. package/lib/features/chat/ChatTranscript.js.map +1 -1
  13. package/lib/features/chat/toolActivity.js +42 -6
  14. package/lib/features/chat/toolActivity.js.map +1 -1
  15. package/lib/features/chat/transcriptGroups.js +11 -2
  16. package/lib/features/chat/transcriptGroups.js.map +1 -1
  17. package/lib/features/shopping/chat/homeShoppingIntent.js +58 -0
  18. package/lib/features/shopping/chat/homeShoppingIntent.js.map +1 -0
  19. package/lib/features/shopping/chat/openShopNative.js +60 -0
  20. package/lib/features/shopping/chat/openShopNative.js.map +1 -0
  21. package/lib/hooks/useCdecliChannel.js +10 -8
  22. package/lib/hooks/useCdecliChannel.js.map +1 -1
  23. package/lib/hooks/useChatStream.js +13 -6
  24. package/lib/hooks/useChatStream.js.map +1 -1
  25. package/lib/index.js +1 -1
  26. package/lib/index.js.map +1 -1
  27. package/lib/screens/Chat/index.js +44 -5
  28. package/lib/screens/Chat/index.js.map +1 -1
  29. package/lib/screens/Home/HomeScreen.js +23 -3
  30. package/lib/screens/Home/HomeScreen.js.map +1 -1
  31. package/lib/screens/ShoppingNative/index.js +21 -2
  32. package/lib/screens/ShoppingNative/index.js.map +1 -1
  33. package/lib/screens/ShoppingNative/useShoppingHostChat.js +66 -0
  34. package/lib/screens/ShoppingNative/useShoppingHostChat.js.map +1 -0
  35. package/lib/services/brainQuickReply.js +72 -2
  36. package/lib/services/brainQuickReply.js.map +1 -1
  37. package/package.json +2 -2
@@ -55,7 +55,7 @@ async function readUriBase64(uri) {
55
55
  })).trim();
56
56
  return base64 || null;
57
57
  }
58
- async function assetToAttachment(asset, index) {
58
+ async function assetToAttachment(asset, index, kind = "file") {
59
59
  var _a, _b, _c, _d;
60
60
  let base64 = ((_a = asset.base64) == null ? void 0 : _a.trim()) || "";
61
61
  if (!base64 && ((_b = asset.uri) == null ? void 0 : _b.trim())) {
@@ -69,7 +69,7 @@ async function assetToAttachment(asset, index) {
69
69
  return {
70
70
  id: `img-${Date.now()}-${index}`,
71
71
  name,
72
- type: "file",
72
+ type: kind,
73
73
  mimeType,
74
74
  dataUrl: `data:${mimeType};base64,${base64}`,
75
75
  url: ((_d = asset.uri) == null ? void 0 : _d.trim()) || void 0,
@@ -155,7 +155,7 @@ function useImageAttachments() {
155
155
  const [busyKind, setBusyKind] = useState(null);
156
156
  const [error, setError] = useState(null);
157
157
  const run = useCallback(async (mode) => {
158
- var _a, _b, _c;
158
+ var _a, _b, _c, _d;
159
159
  setError(null);
160
160
  try {
161
161
  const picker = loadPicker();
@@ -166,13 +166,15 @@ function useImageAttachments() {
166
166
  }
167
167
  setBusyKind(mode);
168
168
  const representation = (_b = (_a = picker.UIImagePickerPreferredAssetRepresentationMode) == null ? void 0 : _a.Current) != null ? _b : "current";
169
- const options = __spreadValues(__spreadValues({
169
+ const options = __spreadValues(__spreadValues(__spreadValues({
170
170
  base64: true,
171
171
  allowsMultipleSelection: mode === "library",
172
172
  mediaTypes: ["images"],
173
173
  selectionLimit: 4,
174
174
  exif: false
175
- }, mode === "camera" ? {
175
+ }, ((_c = picker.UIImagePickerPresentationStyle) == null ? void 0 : _c.OVER_FULL_SCREEN) ? {
176
+ presentationStyle: picker.UIImagePickerPresentationStyle.OVER_FULL_SCREEN
177
+ } : {}), mode === "camera" ? {
176
178
  quality: PICKER_QUALITY
177
179
  } : {}), mode === "library" ? {
178
180
  preferredAssetRepresentationMode: representation
@@ -189,10 +191,10 @@ function useImageAttachments() {
189
191
  }));
190
192
  }
191
193
  if (result.canceled) return;
192
- const picked = (_c = result.assets) != null ? _c : [];
194
+ const picked = (_d = result.assets) != null ? _d : [];
193
195
  const mapped = [];
194
196
  for (let i = 0; i < picked.length; i += 1) {
195
- const next = await assetToAttachment(picked[i], i);
197
+ const next = await assetToAttachment(picked[i], i, mode === "camera" ? "screenshot" : "file");
196
198
  if (next) mapped.push(next);
197
199
  }
198
200
  if (mapped.length === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"useImageAttachments.js","sources":["../../../src/features/attachments/useImageAttachments.ts"],"sourcesContent":["/**\n * Image attachments for the native composer.\n *\n * The transport was already there — `useChatStream.sendMessage(content,\n * attachments)` carries `MessageAttachment[]` and the gateway media lane is\n * covered by `aiwork/e2e-tests/gateway-media-upload.spec.ts`. What was missing\n * was any way to produce one on a phone: the composer shipped with\n * `image`/`camera`/`attach` hard-disabled, and nothing in the mobile package\n * touched `expo-image-picker` even though it is a dependency and the photo and\n * camera permission strings are already declared in `app.json`.\n *\n * Payload size is the whole game here. A modern iPhone photo is ~3-5 MB and\n * ~4000px wide; base64 inflates that by a third, and it travels inline on the\n * chat message. Camera captures can be JPEG-compressed via `quality`. Library\n * picks must NOT pass `quality`: that asks iOS for `public.jpeg` and fails on\n * HEIC / iCloud photos with \"Cannot load representation of type public.jpeg\".\n * The canvas picker already uses `Current` for that reason.\n */\nimport { useCallback, useMemo, useState } from 'react';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\n\n/** Longest edge, in px, we send. Plenty for a model to read; ~10x smaller than source. */\nconst MAX_EDGE = 1280;\n/** JPEG quality handed to the picker (0-1). */\nconst PICKER_QUALITY = 0.7;\n/** Refuse anything above this after re-encoding — a send is better failed than hung. */\nconst MAX_BYTES = 4 * 1024 * 1024;\n/** Nothing sensible is this small; treat as a failed decode. */\nconst MIN_BYTES = 64;\n\nexport type AttachmentBusyKind = 'camera' | 'library' | 'documents' | null;\n\nexport interface ImageAttachmentsApi {\n /** Attachments staged for the next send. */\n pending: MessageAttachment[];\n /** True while any picker/encode is in flight. */\n busy: boolean;\n /** Which toolbar control is waiting — only that one should spin. */\n busyKind: AttachmentBusyKind;\n /** Human-readable failure for the composer's error banner, or null. */\n error: string | null;\n pickFromLibrary: () => Promise<void>;\n captureFromCamera: () => Promise<void>;\n pickDocuments: () => Promise<void>;\n remove: (id: string) => void;\n clear: () => void;\n /** Pass to `sendMessage` — undefined when empty, which is what it expects. */\n forSend: () => MessageAttachment[] | undefined;\n}\n\ntype PickedAsset = {\n uri?: string | null;\n base64?: string | null;\n mimeType?: string | null;\n fileName?: string | null;\n fileSize?: number | null;\n width?: number | null;\n height?: number | null;\n};\n\n/** Base64 decodes to 3 bytes per 4 chars, minus `=` padding. */\nfunction base64Bytes(base64: string): number {\n const padding = base64.endsWith('==') ? 2 : base64.endsWith('=') ? 1 : 0;\n return Math.floor((base64.length * 3) / 4) - padding;\n}\n\nfunction isJpegRepresentationError(err: unknown): boolean {\n const msg = err instanceof Error ? err.message : String(err ?? '');\n return /public\\.jpeg|Failed to read picked image/i.test(msg);\n}\n\nfunction attachErrorMessage(err: unknown): string {\n if (isJpegRepresentationError(err)) {\n return 'Could not read that photo. Try another one, or save it to Recents first.';\n }\n return err instanceof Error ? err.message : 'Could not attach that image.';\n}\n\nfunction guessImageMime(asset: PickedAsset): string {\n const mime = asset.mimeType?.trim();\n if (mime) return mime;\n const name = `${asset.fileName ?? ''} ${asset.uri ?? ''}`;\n if (/\\.png(\\?|$)/i.test(name)) return 'image/png';\n if (/\\.gif(\\?|$)/i.test(name)) return 'image/gif';\n if (/\\.webp(\\?|$)/i.test(name)) return 'image/webp';\n if (/\\.(heic|heif)(\\?|$)/i.test(name)) return 'image/heic';\n return 'image/jpeg';\n}\n\nasync function readUriBase64(uri: string): Promise<string | null> {\n const fs = loadFileSystem() as {\n readAsStringAsync?: (uri: string, options?: { encoding?: string }) => Promise<string>;\n EncodingType?: { Base64?: string };\n };\n if (typeof fs.readAsStringAsync !== 'function') return null;\n const encoding = fs.EncodingType?.Base64 ?? 'base64';\n const base64 = (await fs.readAsStringAsync(uri, { encoding })).trim();\n return base64 || null;\n}\n\nasync function assetToAttachment(asset: PickedAsset, index: number): Promise<MessageAttachment | null> {\n let base64 = asset.base64?.trim() || '';\n if (!base64 && asset.uri?.trim()) {\n base64 = (await readUriBase64(asset.uri.trim())) || '';\n }\n if (!base64) return null;\n const size = base64Bytes(base64);\n if (size < MIN_BYTES || size > MAX_BYTES) return null;\n const mimeType = guessImageMime(asset);\n const name = asset.fileName?.trim() || `image-${Date.now()}-${index}.jpg`;\n return {\n id: `img-${Date.now()}-${index}`,\n name,\n type: 'file',\n mimeType,\n dataUrl: `data:${mimeType};base64,${base64}`,\n url: asset.uri?.trim() || undefined,\n size,\n };\n}\n\ntype PickedDocument = {\n uri?: string | null;\n name?: string | null;\n mimeType?: string | null;\n size?: number | null;\n};\n\n/**\n * Lazily required so a unit test or a non-Expo bundle importing this hook does\n * not hard-fail on the native module.\n */\nfunction loadPicker() {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n return require('expo-image-picker') as typeof import('expo-image-picker');\n}\n\nfunction loadDocumentPicker(): {\n getDocumentAsync: (options: {\n type?: string | string[];\n multiple?: boolean;\n copyToCacheDirectory?: boolean;\n }) => Promise<{\n canceled?: boolean;\n type?: string;\n uri?: string;\n name?: string;\n mimeType?: string;\n size?: number;\n assets?: PickedDocument[];\n }>;\n} {\n // Staging/dev-client binaries only include modules that are dependencies +\n // autolinked at EAS/prebuild time. Expo Go already ships ExpoDocumentPicker,\n // which is why paperclip works locally without that native rebuild.\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n const core = require('expo-modules-core') as {\n requireOptionalNativeModule?: (name: string) => { getDocumentAsync?: Function } | null;\n };\n const native = core.requireOptionalNativeModule?.('ExpoDocumentPicker');\n if (native?.getDocumentAsync) {\n return {\n getDocumentAsync: (options) =>\n native.getDocumentAsync({\n type: Array.isArray(options.type) ? options.type : [options.type ?? '*/*'],\n multiple: options.multiple ?? false,\n copyToCacheDirectory: options.copyToCacheDirectory ?? true,\n }) as Promise<{\n canceled?: boolean;\n type?: string;\n uri?: string;\n name?: string;\n mimeType?: string;\n size?: number;\n assets?: PickedDocument[];\n }>,\n };\n }\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n return require('expo-document-picker') as typeof import('expo-document-picker');\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err ?? '');\n if (/Cannot find native module|ExpoDocumentPicker|Cannot find module/i.test(msg)) {\n throw new Error(\n 'File picker is not included in this app build. Use Photos, or install a staging build that includes expo-document-picker.',\n );\n }\n throw err;\n }\n}\n\nfunction loadFileSystem() {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n return require('expo-file-system') as typeof import('expo-file-system');\n}\n\n/** Web `ACCEPTED_FILE_TYPES` — docs + images. */\nconst DOCUMENT_MIME_TYPES = [\n 'application/pdf',\n 'text/plain',\n 'text/markdown',\n 'text/csv',\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'image/*',\n];\nconst MAX_FILES = 6;\nconst MAX_DOCUMENT_BYTES = 15 * 1024 * 1024;\n\nfunction normalizeDocumentAssets(result: {\n canceled?: boolean;\n type?: string;\n uri?: string;\n name?: string;\n mimeType?: string;\n size?: number;\n assets?: PickedDocument[];\n}): PickedDocument[] {\n if (!result || result.canceled || result.type === 'cancel') return [];\n if (Array.isArray(result.assets) && result.assets.length > 0) return result.assets;\n if (result.uri) {\n return [{ uri: result.uri, name: result.name, mimeType: result.mimeType, size: result.size }];\n }\n return [];\n}\n\nasync function documentToAttachment(asset: PickedDocument, index: number): Promise<MessageAttachment | null> {\n const uri = asset.uri?.trim();\n if (!uri) return null;\n if (typeof asset.size === 'number' && asset.size > MAX_DOCUMENT_BYTES) return null;\n\n const fs = loadFileSystem() as {\n readAsStringAsync?: (uri: string, options?: { encoding?: string }) => Promise<string>;\n EncodingType?: { Base64?: string };\n };\n const encoding = fs.EncodingType?.Base64 ?? 'base64';\n if (typeof fs.readAsStringAsync !== 'function') return null;\n const base64 = (await fs.readAsStringAsync(uri, { encoding })).trim();\n if (!base64) return null;\n const size = typeof asset.size === 'number' && asset.size > 0 ? asset.size : base64Bytes(base64);\n if (size < MIN_BYTES || size > MAX_DOCUMENT_BYTES) return null;\n const mimeType = asset.mimeType?.trim() || 'application/octet-stream';\n const name = asset.name?.trim() || `file-${Date.now()}-${index}`;\n return {\n id: `file-${Date.now()}-${index}`,\n name,\n type: 'file',\n mimeType,\n dataUrl: `data:${mimeType};base64,${base64}`,\n url: uri,\n size,\n };\n}\n\nexport function useImageAttachments(): ImageAttachmentsApi {\n const [pending, setPending] = useState<MessageAttachment[]>([]);\n const [busyKind, setBusyKind] = useState<AttachmentBusyKind>(null);\n const [error, setError] = useState<string | null>(null);\n\n const run = useCallback(async (mode: 'library' | 'camera') => {\n setError(null);\n try {\n const picker = loadPicker();\n\n const permission =\n mode === 'camera'\n ? await picker.requestCameraPermissionsAsync()\n : await picker.requestMediaLibraryPermissionsAsync();\n if (!permission.granted) {\n setError(\n mode === 'camera'\n ? 'Camera access is off for Yantra. Turn it on in Settings to attach a photo.'\n : 'Photo access is off for Yantra. Turn it on in Settings to attach an image.',\n );\n return;\n }\n\n // Spin only the control that was tapped, and only after permission\n // so the iOS prompt does not turn camera+gallery+attach into loaders.\n setBusyKind(mode);\n const representation =\n picker.UIImagePickerPreferredAssetRepresentationMode?.Current ?? ('current' as never);\n const options: import('expo-image-picker').ImagePickerOptions = {\n base64: true,\n allowsMultipleSelection: mode === 'library',\n mediaTypes: ['images'],\n selectionLimit: 4,\n exif: false,\n // Library: never pass `quality` — that requests public.jpeg and fails\n // on HEIC / iCloud (see pickLocalMedia). Camera JPEGs can compress.\n ...(mode === 'camera' ? { quality: PICKER_QUALITY } : {}),\n ...(mode === 'library' ? { preferredAssetRepresentationMode: representation } : {}),\n };\n\n let result: import('expo-image-picker').ImagePickerResult;\n try {\n result =\n mode === 'camera'\n ? await picker.launchCameraAsync({ ...options, allowsMultipleSelection: false })\n : await picker.launchImageLibraryAsync(options);\n } catch (e) {\n if (mode !== 'library' || !isJpegRepresentationError(e)) throw e;\n result = await picker.launchImageLibraryAsync({ ...options, base64: false });\n }\n\n if (result.canceled) return;\n\n const picked = result.assets ?? [];\n const mapped: MessageAttachment[] = [];\n for (let i = 0; i < picked.length; i += 1) {\n const next = await assetToAttachment(picked[i] as PickedAsset, i);\n if (next) mapped.push(next);\n }\n\n if (mapped.length === 0) {\n setError('That image was too large to attach. Try a smaller one.');\n return;\n }\n setPending((prev) => [...prev, ...mapped].slice(0, MAX_FILES));\n } catch (e) {\n setError(attachErrorMessage(e));\n } finally {\n setBusyKind(null);\n }\n }, []);\n\n const pickFromLibrary = useCallback(() => run('library'), [run]);\n const captureFromCamera = useCallback(() => run('camera'), [run]);\n const pickDocuments = useCallback(async () => {\n setError(null);\n try {\n const picker = loadDocumentPicker();\n setBusyKind('documents');\n const result = await picker.getDocumentAsync({\n type: DOCUMENT_MIME_TYPES,\n multiple: true,\n copyToCacheDirectory: true,\n });\n const assets = normalizeDocumentAssets(result as Parameters<typeof normalizeDocumentAssets>[0]);\n if (assets.length === 0) return;\n\n const mapped: MessageAttachment[] = [];\n for (let i = 0; i < assets.length; i += 1) {\n const next = await documentToAttachment(assets[i], i);\n if (next) mapped.push(next);\n }\n if (mapped.length === 0) {\n setError('That file was too large to attach. Try a smaller one.');\n return;\n }\n setPending((prev) => [...prev, ...mapped].slice(0, MAX_FILES));\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Could not attach that file.');\n } finally {\n setBusyKind(null);\n }\n }, []);\n const remove = useCallback((id: string) => setPending((prev) => prev.filter((a) => a.id !== id)), []);\n const clear = useCallback(() => setPending([]), []);\n const forSend = useCallback(() => (pending.length > 0 ? pending.map((a) => ({ ...a })) : undefined), [pending]);\n const busy = busyKind !== null;\n\n return useMemo(\n () => ({\n pending,\n busy,\n busyKind,\n error,\n pickFromLibrary,\n captureFromCamera,\n pickDocuments,\n remove,\n clear,\n forSend,\n }),\n [pending, busy, busyKind, error, pickFromLibrary, captureFromCamera, pickDocuments, remove, clear, forSend],\n );\n}\n\nexport const IMAGE_ATTACHMENT_LIMITS = { MAX_EDGE, MAX_BYTES, PICKER_QUALITY } as const;\n"],"names":["_a"],"mappings":"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"}
1
+ {"version":3,"file":"useImageAttachments.js","sources":["../../../src/features/attachments/useImageAttachments.ts"],"sourcesContent":["/**\n * Image attachments for the native composer.\n *\n * The transport was already there — `useChatStream.sendMessage(content,\n * attachments)` carries `MessageAttachment[]` and the gateway media lane is\n * covered by `aiwork/e2e-tests/gateway-media-upload.spec.ts`. What was missing\n * was any way to produce one on a phone: the composer shipped with\n * `image`/`camera`/`attach` hard-disabled, and nothing in the mobile package\n * touched `expo-image-picker` even though it is a dependency and the photo and\n * camera permission strings are already declared in `app.json`.\n *\n * Payload size is the whole game here. A modern iPhone photo is ~3-5 MB and\n * ~4000px wide; base64 inflates that by a third, and it travels inline on the\n * chat message. Camera captures can be JPEG-compressed via `quality`. Library\n * picks must NOT pass `quality`: that asks iOS for `public.jpeg` and fails on\n * HEIC / iCloud photos with \"Cannot load representation of type public.jpeg\".\n * The canvas picker already uses `Current` for that reason.\n */\nimport { useCallback, useMemo, useState } from 'react';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\n\n/** Longest edge, in px, we send. Plenty for a model to read; ~10x smaller than source. */\nconst MAX_EDGE = 1280;\n/** JPEG quality handed to the picker (0-1). */\nconst PICKER_QUALITY = 0.7;\n/** Refuse anything above this after re-encoding — a send is better failed than hung. */\nconst MAX_BYTES = 4 * 1024 * 1024;\n/** Nothing sensible is this small; treat as a failed decode. */\nconst MIN_BYTES = 64;\n\nexport type AttachmentBusyKind = 'camera' | 'library' | 'documents' | null;\n\nexport interface ImageAttachmentsApi {\n /** Attachments staged for the next send. */\n pending: MessageAttachment[];\n /** True while any picker/encode is in flight. */\n busy: boolean;\n /** Which toolbar control is waiting — only that one should spin. */\n busyKind: AttachmentBusyKind;\n /** Human-readable failure for the composer's error banner, or null. */\n error: string | null;\n pickFromLibrary: () => Promise<void>;\n captureFromCamera: () => Promise<void>;\n pickDocuments: () => Promise<void>;\n remove: (id: string) => void;\n clear: () => void;\n /** Pass to `sendMessage` — undefined when empty, which is what it expects. */\n forSend: () => MessageAttachment[] | undefined;\n}\n\ntype PickedAsset = {\n uri?: string | null;\n base64?: string | null;\n mimeType?: string | null;\n fileName?: string | null;\n fileSize?: number | null;\n width?: number | null;\n height?: number | null;\n};\n\n/** Base64 decodes to 3 bytes per 4 chars, minus `=` padding. */\nfunction base64Bytes(base64: string): number {\n const padding = base64.endsWith('==') ? 2 : base64.endsWith('=') ? 1 : 0;\n return Math.floor((base64.length * 3) / 4) - padding;\n}\n\nfunction isJpegRepresentationError(err: unknown): boolean {\n const msg = err instanceof Error ? err.message : String(err ?? '');\n return /public\\.jpeg|Failed to read picked image/i.test(msg);\n}\n\nfunction attachErrorMessage(err: unknown): string {\n if (isJpegRepresentationError(err)) {\n return 'Could not read that photo. Try another one, or save it to Recents first.';\n }\n return err instanceof Error ? err.message : 'Could not attach that image.';\n}\n\nfunction guessImageMime(asset: PickedAsset): string {\n const mime = asset.mimeType?.trim();\n if (mime) return mime;\n const name = `${asset.fileName ?? ''} ${asset.uri ?? ''}`;\n if (/\\.png(\\?|$)/i.test(name)) return 'image/png';\n if (/\\.gif(\\?|$)/i.test(name)) return 'image/gif';\n if (/\\.webp(\\?|$)/i.test(name)) return 'image/webp';\n if (/\\.(heic|heif)(\\?|$)/i.test(name)) return 'image/heic';\n return 'image/jpeg';\n}\n\nasync function readUriBase64(uri: string): Promise<string | null> {\n const fs = loadFileSystem() as {\n readAsStringAsync?: (uri: string, options?: { encoding?: string }) => Promise<string>;\n EncodingType?: { Base64?: string };\n };\n if (typeof fs.readAsStringAsync !== 'function') return null;\n const encoding = fs.EncodingType?.Base64 ?? 'base64';\n const base64 = (await fs.readAsStringAsync(uri, { encoding })).trim();\n return base64 || null;\n}\n\nasync function assetToAttachment(\n asset: PickedAsset,\n index: number,\n kind: MessageAttachment['type'] = 'file',\n): Promise<MessageAttachment | null> {\n let base64 = asset.base64?.trim() || '';\n if (!base64 && asset.uri?.trim()) {\n base64 = (await readUriBase64(asset.uri.trim())) || '';\n }\n if (!base64) return null;\n const size = base64Bytes(base64);\n if (size < MIN_BYTES || size > MAX_BYTES) return null;\n const mimeType = guessImageMime(asset);\n const name = asset.fileName?.trim() || `image-${Date.now()}-${index}.jpg`;\n return {\n id: `img-${Date.now()}-${index}`,\n name,\n type: kind,\n mimeType,\n dataUrl: `data:${mimeType};base64,${base64}`,\n url: asset.uri?.trim() || undefined,\n size,\n };\n}\n\ntype PickedDocument = {\n uri?: string | null;\n name?: string | null;\n mimeType?: string | null;\n size?: number | null;\n};\n\n/**\n * Lazily required so a unit test or a non-Expo bundle importing this hook does\n * not hard-fail on the native module.\n */\nfunction loadPicker() {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n return require('expo-image-picker') as typeof import('expo-image-picker');\n}\n\nfunction loadDocumentPicker(): {\n getDocumentAsync: (options: {\n type?: string | string[];\n multiple?: boolean;\n copyToCacheDirectory?: boolean;\n }) => Promise<{\n canceled?: boolean;\n type?: string;\n uri?: string;\n name?: string;\n mimeType?: string;\n size?: number;\n assets?: PickedDocument[];\n }>;\n} {\n // Staging/dev-client binaries only include modules that are dependencies +\n // autolinked at EAS/prebuild time. Expo Go already ships ExpoDocumentPicker,\n // which is why paperclip works locally without that native rebuild.\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n const core = require('expo-modules-core') as {\n requireOptionalNativeModule?: (name: string) => { getDocumentAsync?: Function } | null;\n };\n const native = core.requireOptionalNativeModule?.('ExpoDocumentPicker');\n if (native?.getDocumentAsync) {\n return {\n getDocumentAsync: (options) =>\n native.getDocumentAsync({\n type: Array.isArray(options.type) ? options.type : [options.type ?? '*/*'],\n multiple: options.multiple ?? false,\n copyToCacheDirectory: options.copyToCacheDirectory ?? true,\n }) as Promise<{\n canceled?: boolean;\n type?: string;\n uri?: string;\n name?: string;\n mimeType?: string;\n size?: number;\n assets?: PickedDocument[];\n }>,\n };\n }\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n return require('expo-document-picker') as typeof import('expo-document-picker');\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err ?? '');\n if (/Cannot find native module|ExpoDocumentPicker|Cannot find module/i.test(msg)) {\n throw new Error(\n 'File picker is not included in this app build. Use Photos, or install a staging build that includes expo-document-picker.',\n );\n }\n throw err;\n }\n}\n\nfunction loadFileSystem() {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n return require('expo-file-system') as typeof import('expo-file-system');\n}\n\n/** Web `ACCEPTED_FILE_TYPES` — docs + images. */\nconst DOCUMENT_MIME_TYPES = [\n 'application/pdf',\n 'text/plain',\n 'text/markdown',\n 'text/csv',\n 'application/msword',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'image/*',\n];\nconst MAX_FILES = 6;\nconst MAX_DOCUMENT_BYTES = 15 * 1024 * 1024;\n\nfunction normalizeDocumentAssets(result: {\n canceled?: boolean;\n type?: string;\n uri?: string;\n name?: string;\n mimeType?: string;\n size?: number;\n assets?: PickedDocument[];\n}): PickedDocument[] {\n if (!result || result.canceled || result.type === 'cancel') return [];\n if (Array.isArray(result.assets) && result.assets.length > 0) return result.assets;\n if (result.uri) {\n return [{ uri: result.uri, name: result.name, mimeType: result.mimeType, size: result.size }];\n }\n return [];\n}\n\nasync function documentToAttachment(asset: PickedDocument, index: number): Promise<MessageAttachment | null> {\n const uri = asset.uri?.trim();\n if (!uri) return null;\n if (typeof asset.size === 'number' && asset.size > MAX_DOCUMENT_BYTES) return null;\n\n const fs = loadFileSystem() as {\n readAsStringAsync?: (uri: string, options?: { encoding?: string }) => Promise<string>;\n EncodingType?: { Base64?: string };\n };\n const encoding = fs.EncodingType?.Base64 ?? 'base64';\n if (typeof fs.readAsStringAsync !== 'function') return null;\n const base64 = (await fs.readAsStringAsync(uri, { encoding })).trim();\n if (!base64) return null;\n const size = typeof asset.size === 'number' && asset.size > 0 ? asset.size : base64Bytes(base64);\n if (size < MIN_BYTES || size > MAX_DOCUMENT_BYTES) return null;\n const mimeType = asset.mimeType?.trim() || 'application/octet-stream';\n const name = asset.name?.trim() || `file-${Date.now()}-${index}`;\n return {\n id: `file-${Date.now()}-${index}`,\n name,\n type: 'file',\n mimeType,\n dataUrl: `data:${mimeType};base64,${base64}`,\n url: uri,\n size,\n };\n}\n\nexport function useImageAttachments(): ImageAttachmentsApi {\n const [pending, setPending] = useState<MessageAttachment[]>([]);\n const [busyKind, setBusyKind] = useState<AttachmentBusyKind>(null);\n const [error, setError] = useState<string | null>(null);\n\n const run = useCallback(async (mode: 'library' | 'camera') => {\n setError(null);\n try {\n const picker = loadPicker();\n\n const permission =\n mode === 'camera'\n ? await picker.requestCameraPermissionsAsync()\n : await picker.requestMediaLibraryPermissionsAsync();\n if (!permission.granted) {\n setError(\n mode === 'camera'\n ? 'Camera access is off for Yantra. Turn it on in Settings to attach a photo.'\n : 'Photo access is off for Yantra. Turn it on in Settings to attach an image.',\n );\n return;\n }\n\n // Spin only the control that was tapped, and only after permission\n // so the iOS prompt does not turn camera+gallery+attach into loaders.\n setBusyKind(mode);\n const representation =\n picker.UIImagePickerPreferredAssetRepresentationMode?.Current ?? ('current' as never);\n const options: import('expo-image-picker').ImagePickerOptions = {\n base64: true,\n allowsMultipleSelection: mode === 'library',\n mediaTypes: ['images'],\n selectionLimit: 4,\n exif: false,\n ...(picker.UIImagePickerPresentationStyle?.OVER_FULL_SCREEN\n ? { presentationStyle: picker.UIImagePickerPresentationStyle.OVER_FULL_SCREEN }\n : {}),\n ...(mode === 'camera' ? { quality: PICKER_QUALITY } : {}),\n ...(mode === 'library' ? { preferredAssetRepresentationMode: representation } : {}),\n };\n\n let result: import('expo-image-picker').ImagePickerResult;\n try {\n result =\n mode === 'camera'\n ? await picker.launchCameraAsync({ ...options, allowsMultipleSelection: false })\n : await picker.launchImageLibraryAsync(options);\n } catch (e) {\n if (mode !== 'library' || !isJpegRepresentationError(e)) throw e;\n result = await picker.launchImageLibraryAsync({ ...options, base64: false });\n }\n\n if (result.canceled) return;\n\n const picked = result.assets ?? [];\n const mapped: MessageAttachment[] = [];\n for (let i = 0; i < picked.length; i += 1) {\n const next = await assetToAttachment(\n picked[i] as PickedAsset,\n i,\n mode === 'camera' ? 'screenshot' : 'file',\n );\n if (next) mapped.push(next);\n }\n\n if (mapped.length === 0) {\n setError('That image was too large to attach. Try a smaller one.');\n return;\n }\n setPending((prev) => [...prev, ...mapped].slice(0, MAX_FILES));\n } catch (e) {\n setError(attachErrorMessage(e));\n } finally {\n setBusyKind(null);\n }\n }, []);\n\n const pickFromLibrary = useCallback(() => run('library'), [run]);\n const captureFromCamera = useCallback(() => run('camera'), [run]);\n const pickDocuments = useCallback(async () => {\n setError(null);\n try {\n const picker = loadDocumentPicker();\n setBusyKind('documents');\n const result = await picker.getDocumentAsync({\n type: DOCUMENT_MIME_TYPES,\n multiple: true,\n copyToCacheDirectory: true,\n });\n const assets = normalizeDocumentAssets(result as Parameters<typeof normalizeDocumentAssets>[0]);\n if (assets.length === 0) return;\n\n const mapped: MessageAttachment[] = [];\n for (let i = 0; i < assets.length; i += 1) {\n const next = await documentToAttachment(assets[i], i);\n if (next) mapped.push(next);\n }\n if (mapped.length === 0) {\n setError('That file was too large to attach. Try a smaller one.');\n return;\n }\n setPending((prev) => [...prev, ...mapped].slice(0, MAX_FILES));\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Could not attach that file.');\n } finally {\n setBusyKind(null);\n }\n }, []);\n const remove = useCallback((id: string) => setPending((prev) => prev.filter((a) => a.id !== id)), []);\n const clear = useCallback(() => setPending([]), []);\n const forSend = useCallback(() => (pending.length > 0 ? pending.map((a) => ({ ...a })) : undefined), [pending]);\n const busy = busyKind !== null;\n\n return useMemo(\n () => ({\n pending,\n busy,\n busyKind,\n error,\n pickFromLibrary,\n captureFromCamera,\n pickDocuments,\n remove,\n clear,\n forSend,\n }),\n [pending, busy, busyKind, error, pickFromLibrary, captureFromCamera, pickDocuments, remove, clear, forSend],\n );\n}\n\nexport const IMAGE_ATTACHMENT_LIMITS = { MAX_EDGE, MAX_BYTES, PICKER_QUALITY } as const;\n"],"names":["_a"],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import {jsx}from'react/jsx-runtime';import React from'react';var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
4
+ const ZERO_INSETS = {
5
+ top: 0,
6
+ bottom: 0,
7
+ left: 0,
8
+ right: 0
9
+ };
10
+ let lib = null;
11
+ try {
12
+ lib = require("react-native-safe-area-context");
13
+ } catch (e) {
14
+ lib = null;
15
+ }
16
+ function useSafeInsets() {
17
+ if (lib) {
18
+ try {
19
+ return lib.useSafeAreaInsets();
20
+ } catch (e) {
21
+ return ZERO_INSETS;
22
+ }
23
+ }
24
+ return ZERO_INSETS;
25
+ }
26
+ class ProviderBoundary extends React.Component {
27
+ constructor() {
28
+ super(...arguments);
29
+ __publicField(this, "state", {
30
+ failed: false
31
+ });
32
+ }
33
+ static getDerivedStateFromError() {
34
+ return {
35
+ failed: true
36
+ };
37
+ }
38
+ render() {
39
+ var _a;
40
+ if (this.state.failed || !lib) {
41
+ return this.props.children;
42
+ }
43
+ const Provider = lib.SafeAreaProvider;
44
+ return /* @__PURE__ */ jsx(Provider, { initialMetrics: (_a = lib.initialWindowMetrics) != null ? _a : void 0, children: this.props.children });
45
+ }
46
+ }export{useSafeInsets};//# sourceMappingURL=withRemoteSafeArea.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withRemoteSafeArea.js","sources":["../../../src/features/canvas/withRemoteSafeArea.tsx"],"sourcesContent":["import React from 'react';\n\n/**\n * Safe-area access for code that ships inside a federated remote.\n *\n * react-native-safe-area-context is NOT a federation shared singleton (and\n * must not become one: a remote built `import: false` would fail with\n * RUNTIME-012 on every installed binary whose host bundle never shared it).\n * So the remote bundles its own copy, and two distinct failure modes follow,\n * both observed on device/sim:\n *\n * 1. The remote copy's SafeAreaInsetsContext is a different object from the\n * host's, so the host's <SafeAreaProvider> can never satisfy a\n * useSafeAreaInsets call made inside the remote - it throws \"No safe area\n * value available\" and the surface-level error boundary turns that into a\n * whole-surface \"Canvas surface unavailable\".\n * 2. On some hosts (bare-RN release harness), merely EVALUATING the remote's\n * copy is fatal: its native-component spec registers RNCSafeAreaProvider a\n * second time and React Native throws \"Tried to register two views with\n * the same name\".\n *\n * Therefore NOTHING in the remote may import the library top-level. This\n * module is the single guarded entry: the library is require'd once in a\n * try/catch, consumers take `useSafeInsets()` (never the library hook), and\n * `withRemoteSafeArea` mounts the remote copy's own provider when it loaded -\n * its native measurement works from any JS copy - or a pure-JS zero-inset\n * fallback when it did not.\n */\n\ntype Insets = { top: number; bottom: number; left: number; right: number };\n\nconst ZERO_INSETS: Insets = { top: 0, bottom: 0, left: 0, right: 0 };\n\ntype SafeAreaLib = {\n SafeAreaProvider: React.ComponentType<{ initialMetrics?: unknown; children?: React.ReactNode }>;\n useSafeAreaInsets: () => Insets;\n initialWindowMetrics?: unknown;\n};\n\nlet lib: SafeAreaLib | null = null;\ntry {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n lib = require('react-native-safe-area-context');\n} catch {\n lib = null;\n}\n\n/**\n * Insets that never throw: the library's value when its provider is reachable,\n * zero insets otherwise. Library presence is constant for the process, so the\n * hook call order is stable.\n */\nexport function useSafeInsets(): Insets {\n if (lib) {\n try {\n return lib.useSafeAreaInsets();\n } catch {\n return ZERO_INSETS;\n }\n }\n return ZERO_INSETS;\n}\n\n/** Probes whether the (remote-copy) insets context is already provided. */\nfunction insetsReachable(): boolean {\n try {\n lib!.useSafeAreaInsets();\n return true;\n } catch {\n return false;\n }\n}\n\nclass ProviderBoundary extends React.Component<{ children: React.ReactNode }, { failed: boolean }> {\n state = { failed: false };\n\n static getDerivedStateFromError() {\n return { failed: true };\n }\n\n render() {\n if (this.state.failed || !lib) {\n // Consumers reach insets via useSafeInsets(), which degrades to\n // zero on its own - a plain passthrough is enough here.\n return this.props.children;\n }\n const Provider = lib.SafeAreaProvider;\n return <Provider initialMetrics={lib.initialWindowMetrics ?? undefined}>{this.props.children}</Provider>;\n }\n}\n\nexport function withRemoteSafeArea<P extends object>(Component: React.ComponentType<P>): React.ComponentType<P> {\n function RemoteSafeArea(props: P) {\n // Library presence is process-constant, so the conditional hook call\n // inside insetsReachable keeps a stable order across renders.\n if (lib && insetsReachable()) {\n return <Component {...props} />;\n }\n return (\n <ProviderBoundary>\n <Component {...props} />\n </ProviderBoundary>\n );\n }\n RemoteSafeArea.displayName = `withRemoteSafeArea(${Component.displayName || Component.name || 'Component'})`;\n return RemoteSafeArea;\n}\n"],"names":[],"mappings":";;;AAmCA,MAAM,WAAsB,GAAA;AAAA,EAC1B,GAAK,EAAA,CAAA;AAAA,EACL,MAAQ,EAAA,CAAA;AAAA,EACR,IAAM,EAAA,CAAA;AAAA,EACN,KAAO,EAAA;AACT,CAAA;AASA,IAAI,GAA0B,GAAA,IAAA;AAC9B,IAAI;AAEF,EAAA,GAAA,GAAM,QAAQ,gCAAgC,CAAA;AAChD,CAAQ,CAAA,OAAA,CAAA,EAAA;AACN,EAAM,GAAA,GAAA,IAAA;AACR;AAOO,SAAS,aAAwB,GAAA;AACtC,EAAA,IAAI,GAAK,EAAA;AACP,IAAI,IAAA;AACF,MAAA,OAAO,IAAI,iBAAkB,EAAA;AAAA,KACvB,CAAA,OAAA,CAAA,EAAA;AACN,MAAO,OAAA,WAAA;AAAA;AACT;AAEF,EAAO,OAAA,WAAA;AACT;AAWA,MAAM,gBAAA,SAAyB,MAAM,SAIlC,CAAA;AAAA,EAJH,WAAA,GAAA;AAAA,IAAA,KAAA,CAAA,GAAA,SAAA,CAAA;AAKE,IAAQ,aAAA,CAAA,IAAA,EAAA,OAAA,EAAA;AAAA,MACN,MAAQ,EAAA;AAAA,KACV,CAAA;AAAA;AAAA,EACA,OAAO,wBAA2B,GAAA;AAChC,IAAO,OAAA;AAAA,MACL,MAAQ,EAAA;AAAA,KACV;AAAA;AACF,EACA,MAAS,GAAA;AA/FX,IAAA,IAAA,EAAA;AAgGI,IAAA,IAAI,IAAK,CAAA,KAAA,CAAM,MAAU,IAAA,CAAC,GAAK,EAAA;AAG7B,MAAA,OAAO,KAAK,KAAM,CAAA,QAAA;AAAA;AAEpB,IAAA,MAAM,WAAW,GAAI,CAAA,gBAAA;AACrB,IAAO,uBAAA,GAAA,CAAC,YAAS,cAAgB,EAAA,CAAA,EAAA,GAAA,GAAA,CAAI,yBAAJ,IAA4B,GAAA,EAAA,GAAA,MAAA,EAAY,QAAK,EAAA,IAAA,CAAA,KAAA,CAAM,QAAS,EAAA,CAAA;AAAA;AAEjG"}
@@ -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}from'react-native';import {dismissKeyboard}from'../../utils/keyboardController.js';import {useKeyboardLiftHeight}from'../../components/KeyboardComposerDock.js';import Markdown from'react-native-markdown-display';import {shouldRenderUserTranscriptTurn,displayUserMessageText}from'../attachments/displayUserMessageText.js';import {MessageAttachmentPreviews}from'../attachments/MessageAttachmentPreviews.js';import {ToolTimeline}from'./ToolTimeline.js';import {parseToolActivity}from'./toolActivity.js';import {parseAskUser,displayAskUserAssistantText,stripAskUser}from'./askUser.js';import {collapseDuplicateUserRows,groupConsecutiveRoleRows}from'./transcriptGroups.js';import {closeOpenMarkdownFences}from'./streamingMarkdown.js';var __defProp = Object.defineProperty;
1
+ import {jsx,Fragment,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useCallback,useEffect,memo}from'react';import {View,StyleSheet,FlatList,Platform,Pressable}from'react-native';import {dismissKeyboard}from'../../utils/keyboardController.js';import {useKeyboardLiftHeight}from'../../components/KeyboardComposerDock.js';import Markdown from'react-native-markdown-display';import {shouldRenderUserTranscriptTurn,displayUserMessageText}from'../attachments/displayUserMessageText.js';import {MessageAttachmentPreviews}from'../attachments/MessageAttachmentPreviews.js';import {ToolTimeline}from'./ToolTimeline.js';import {parseToolActivity}from'./toolActivity.js';import {parseAskUser,displayAskUserAssistantText,stripAskUser}from'./askUser.js';import {collapseDuplicateUserRows,groupConsecutiveRoleRows}from'./transcriptGroups.js';import {closeOpenMarkdownFences}from'./streamingMarkdown.js';var __defProp = Object.defineProperty;
2
2
  var __defProps = Object.defineProperties;
3
3
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
@@ -23,6 +23,7 @@ function ChatTranscript({
23
23
  streamingContent,
24
24
  renderMessageActions,
25
25
  listContentStyle,
26
+ listFooter,
26
27
  isDark = false
27
28
  }) {
28
29
  const listRef = useRef(null);
@@ -99,10 +100,6 @@ function ChatTranscript({
99
100
  list.scrollToEnd({
100
101
  animated
101
102
  });
102
- list.scrollToOffset({
103
- offset: Number.MAX_SAFE_INTEGER,
104
- animated
105
- });
106
103
  }, [groups.length]);
107
104
  useEffect(() => {
108
105
  if (groups.length === 0 || !stickToBottomRef.current) return void 0;
@@ -146,9 +143,9 @@ function ChatTranscript({
146
143
  const renderItem = useCallback(({
147
144
  item
148
145
  }) => /* @__PURE__ */ jsx(TranscriptGroupView, { group: item, isDark, renderMessageActions }), [isDark, renderMessageActions]);
149
- return /* @__PURE__ */ jsx(View, { style: styles.list, children: /* @__PURE__ */ jsx(FlatList, { ref: listRef, data: groups, keyExtractor: (item) => item.id, renderItem, extraData: streamingContent, initialNumToRender: 12, maxToRenderPerBatch: 8, updateCellsBatchingPeriod: 50, windowSize: 7, onScroll: handleScroll, onContentSizeChange: handleContentSizeChange, onLayout: () => {
146
+ return /* @__PURE__ */ jsx(View, { style: styles.list, children: /* @__PURE__ */ jsx(FlatList, { ref: listRef, data: groups, keyExtractor: (item) => item.id, renderItem, extraData: `${streamingContent != null ? streamingContent : ""}:${Boolean(listFooter)}`, ListFooterComponent: listFooter ? /* @__PURE__ */ jsx(Fragment, { children: listFooter }) : null, initialNumToRender: 12, maxToRenderPerBatch: 8, updateCellsBatchingPeriod: 50, windowSize: 7, nestedScrollEnabled: true, directionalLockEnabled: true, onScroll: handleScroll, onContentSizeChange: handleContentSizeChange, onLayout: () => {
150
147
  if (stickToBottomRef.current) scrollToLatest(false);
151
- }, 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 }) });
148
+ }, scrollEventThrottle: 16, keyboardShouldPersistTaps: "handled", keyboardDismissMode: Platform.OS === "ios" ? "interactive" : "on-drag", automaticallyAdjustKeyboardInsets: false, automaticallyAdjustContentInsets: false, contentInsetAdjustmentBehavior: "never", contentContainerStyle: [styles.listContent, listContentStyle, !listFooter && styles.listContentFill], style: styles.list }) });
152
149
  }
153
150
  const TranscriptGroupView = memo(function TranscriptGroupView2({
154
151
  group,
@@ -157,9 +154,9 @@ const TranscriptGroupView = memo(function TranscriptGroupView2({
157
154
  }) {
158
155
  if (group.role === "user") {
159
156
  return /* @__PURE__ */ jsx(Pressable, { style: styles.userCol, onPress: dismissKeyboard, accessible: false, children: group.items.map((item, index) => {
160
- var _a;
157
+ var _a, _b;
161
158
  const attachments = (_a = item.attachments) != null ? _a : [];
162
- const text = displayUserMessageText(item.content);
159
+ const text = displayUserMessageText((_b = item.displayContent) != null ? _b : item.content);
163
160
  if (!text && attachments.length === 0) return null;
164
161
  return /* @__PURE__ */ jsxs(View, { style: [styles.userTurn, index > 0 && styles.userFollow], children: [
165
162
  attachments.length > 0 ? /* @__PURE__ */ jsx(MessageAttachmentPreviews, { attachments, align: "end", isDark }) : null,
@@ -264,13 +261,24 @@ function assistantMarkdownStyle(markdownColor, codeBg) {
264
261
  marginBottom: 4
265
262
  },
266
263
  list_item: {
267
- marginVertical: 2
264
+ marginVertical: 4,
265
+ flexDirection: "row",
266
+ flexWrap: "wrap"
268
267
  },
269
268
  bullet_list: {
270
- marginVertical: 4
269
+ marginVertical: 6
271
270
  },
272
271
  ordered_list: {
273
- marginVertical: 4
272
+ marginVertical: 6
273
+ },
274
+ bullet_list_icon: {
275
+ marginRight: 8,
276
+ marginTop: 2
277
+ },
278
+ ordered_list_icon: {
279
+ minWidth: 22,
280
+ marginRight: 8,
281
+ marginTop: 2
274
282
  },
275
283
  code_inline: {
276
284
  backgroundColor: codeBg,
@@ -299,7 +307,8 @@ function assistantMarkdownStyle(markdownColor, codeBg) {
299
307
  }
300
308
  const styles = StyleSheet.create({
301
309
  list: {
302
- flex: 1
310
+ flex: 1,
311
+ overflow: "hidden"
303
312
  },
304
313
  listContent: {
305
314
  paddingTop: 8,
@@ -307,8 +316,7 @@ const styles = StyleSheet.create({
307
316
  paddingHorizontal: 8
308
317
  },
309
318
  listContentFill: {
310
- flexGrow: 1,
311
- justifyContent: "flex-end"
319
+ flexGrow: 1
312
320
  },
313
321
  userCol: {
314
322
  width: "100%",
@@ -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, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { dismissKeyboard } from '../../utils/keyboardController';\nimport { useKeyboardLiftHeight } from '../../components/KeyboardComposerDock';\nimport Markdown from 'react-native-markdown-display';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\nimport { displayUserMessageText, shouldRenderUserTranscriptTurn } from '../attachments/displayUserMessageText';\nimport { MessageAttachmentPreviews } from '../attachments/MessageAttachmentPreviews';\nimport { ToolTimeline } from './ToolTimeline';\nimport { parseToolActivity } from './toolActivity';\nimport { parseAskUser, stripAskUser, displayAskUserAssistantText } from './askUser';\nimport { collapseDuplicateUserRows, groupConsecutiveRoleRows, type RoleGroup } from './transcriptGroups';\nimport { closeOpenMarkdownFences } from './streamingMarkdown';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n attachments?: MessageAttachment[];\n};\n\ntype TranscriptRow = TranscriptMessage & { streaming?: boolean; askAnswered?: boolean };\ntype TranscriptGroup = RoleGroup<TranscriptRow>;\n\nconst USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n renderMessageActions,\n listContentStyle,\n isDark = false,\n}: {\n messages: TranscriptMessage[];\n streamingContent?: string;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n listContentStyle?: StyleProp<ViewStyle>;\n isDark?: boolean;\n}) {\n const listRef = useRef<FlatList<TranscriptGroup>>(null);\n const stickToBottomRef = useRef(true);\n const isProgrammaticScrollRef = useRef(false);\n const ignoreScrollUntilRef = useRef(0);\n const keyboardHeight = useKeyboardLiftHeight();\n\n const historyRows = useMemo<TranscriptRow[]>(() => {\n const rows: TranscriptRow[] = [];\n messages.forEach((msg, index) => {\n if (msg.role === 'user') {\n if (shouldRenderUserTranscriptTurn(msg.content, msg.attachments)) {\n rows.push({ ...msg, streaming: false });\n }\n return;\n }\n if (msg.role !== 'assistant') {\n rows.push({ ...msg, streaming: false });\n return;\n }\n const askAnswered =\n Boolean(parseAskUser(msg.content)) && messages.slice(index + 1).some((m) => m.role === 'user');\n const visible = displayAskUserAssistantText(msg.content, askAnswered).trim();\n const tools = parseToolActivity(stripAskUser(msg.content), false);\n const hasAsk = Boolean(parseAskUser(msg.content));\n if (hasAsk && !visible && tools.steps.length === 0 && !(msg.attachments?.length ?? 0)) {\n return;\n }\n if (!hasAsk) {\n const leftover = stripAskUser(msg.content).trim();\n if (!leftover && !(msg.attachments?.length ?? 0)) return;\n }\n rows.push({ ...msg, streaming: false, askAnswered });\n });\n return rows;\n }, [messages]);\n\n const rows = useMemo<TranscriptRow[]>(() => {\n const history = collapseDuplicateUserRows(historyRows);\n const rawStream = streamingContent ?? '';\n const last = history[history.length - 1];\n const streamAskAnswered = last?.role === 'user' && Boolean(parseAskUser(rawStream));\n const streamProse = displayAskUserAssistantText(rawStream, streamAskAnswered).trim();\n const streamTools = parseToolActivity(stripAskUser(rawStream), true);\n if (!streamProse && streamTools.steps.length === 0) return history;\n if (last?.role === 'assistant') {\n const lastText = last.content.trim();\n const streamText = rawStream.trim();\n if (lastText === streamText || lastText.startsWith(streamText) || streamText.startsWith(lastText)) {\n return history;\n }\n }\n return [\n ...history,\n {\n id: '__streaming__',\n role: 'assistant',\n content: rawStream,\n streaming: true,\n askAnswered: streamAskAnswered,\n },\n ];\n }, [historyRows, streamingContent]);\n\n const groups = useMemo(() => groupConsecutiveRoleRows(rows), [rows]);\n\n const scrollToLatest = useCallback(\n (animated: boolean) => {\n if (groups.length === 0) return;\n isProgrammaticScrollRef.current = true;\n stickToBottomRef.current = true;\n const list = listRef.current;\n if (!list) return;\n list.scrollToEnd({ animated });\n // Tall bubbles: scrollToEnd can stop at the start of the last cell.\n list.scrollToOffset({ offset: Number.MAX_SAFE_INTEGER, animated });\n },\n [groups.length],\n );\n\n useEffect(() => {\n if (groups.length === 0 || !stickToBottomRef.current) return undefined;\n const t = setTimeout(() => scrollToLatest(false), 0);\n return () => clearTimeout(t);\n }, [groups.length, streamingContent?.length, scrollToLatest]);\n\n // Spacer and list height update on the same keyboard frame. Scroll only\n // after that shrink, or we stay on the top of a long turn (image 1).\n useEffect(() => {\n if (keyboardHeight <= 0) return undefined;\n stickToBottomRef.current = true;\n ignoreScrollUntilRef.current = Date.now() + 400;\n const frame = requestAnimationFrame(() => scrollToLatest(false));\n const afterLayout = setTimeout(() => scrollToLatest(false), 64);\n const afterKeyboard = setTimeout(() => scrollToLatest(false), 280);\n return () => {\n cancelAnimationFrame(frame);\n clearTimeout(afterLayout);\n clearTimeout(afterKeyboard);\n };\n }, [keyboardHeight, scrollToLatest]);\n\n const handleScroll = useCallback(\n (e: {\n nativeEvent: {\n contentOffset: { y: number };\n contentSize: { height: number };\n layoutMeasurement: { height: number };\n };\n }) => {\n if (Date.now() < ignoreScrollUntilRef.current) {\n stickToBottomRef.current = true;\n return;\n }\n const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;\n const nearLatest = contentOffset.y + layoutMeasurement.height >= contentSize.height - 80;\n if (!isProgrammaticScrollRef.current) {\n stickToBottomRef.current = nearLatest;\n } else if (nearLatest) {\n isProgrammaticScrollRef.current = false;\n }\n },\n [],\n );\n\n const handleContentSizeChange = useCallback(() => {\n if (!stickToBottomRef.current) return;\n scrollToLatest(false);\n }, [scrollToLatest]);\n\n const renderItem = useCallback(\n ({ item }: { item: TranscriptGroup }) => (\n <TranscriptGroupView group={item} isDark={isDark} renderMessageActions={renderMessageActions} />\n ),\n [isDark, renderMessageActions],\n );\n\n return (\n <View style={styles.list}>\n <FlatList\n ref={listRef}\n data={groups}\n keyExtractor={(item) => item.id}\n renderItem={renderItem}\n extraData={streamingContent}\n initialNumToRender={12}\n maxToRenderPerBatch={8}\n updateCellsBatchingPeriod={50}\n windowSize={7}\n onScroll={handleScroll}\n onContentSizeChange={handleContentSizeChange}\n onLayout={() => {\n if (stickToBottomRef.current) scrollToLatest(false);\n }}\n scrollEventThrottle={16}\n keyboardShouldPersistTaps=\"handled\"\n keyboardDismissMode={Platform.OS === 'ios' ? 'interactive' : 'on-drag'}\n automaticallyAdjustKeyboardInsets={false}\n automaticallyAdjustContentInsets={false}\n contentInsetAdjustmentBehavior=\"never\"\n contentContainerStyle={[styles.listContent, listContentStyle, styles.listContentFill]}\n style={styles.list}\n />\n </View>\n );\n}\n\nconst TranscriptGroupView = memo(function TranscriptGroupView({\n group,\n isDark,\n renderMessageActions,\n}: {\n group: TranscriptGroup;\n isDark: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n if (group.role === 'user') {\n return (\n <Pressable style={styles.userCol} onPress={dismissKeyboard} accessible={false}>\n {group.items.map((item, index) => {\n const attachments = item.attachments ?? [];\n const text = displayUserMessageText(item.content, attachments);\n if (!text && attachments.length === 0) return null;\n return (\n <View key={item.id} style={[styles.userTurn, index > 0 && styles.userFollow]}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"end\" isDark={isDark} />\n ) : null}\n {text ? (\n <View style={styles.userBubble}>\n <Markdown style={USER_MARKDOWN}>{text}</Markdown>\n </View>\n ) : null}\n </View>\n );\n })}\n </Pressable>\n );\n }\n\n const markdownColor = isDark ? '#e2e8f0' : '#111827';\n const codeBg = isDark ? '#1e293b' : '#f3f4f6';\n const last = group.items[group.items.length - 1];\n\n return (\n <View style={styles.assistantGroup}>\n {group.items.map((item, index) => (\n <AssistantTurnView\n key={item.id}\n item={item}\n isDark={isDark}\n markdownColor={markdownColor}\n codeBg={codeBg}\n tightTop={index > 0}\n renderMessageActions={item.streaming || item !== last ? undefined : renderMessageActions}\n />\n ))}\n </View>\n );\n});\n\nconst AssistantTurnView = memo(function AssistantTurnView({\n item,\n isDark,\n markdownColor,\n codeBg,\n tightTop,\n renderMessageActions,\n}: {\n item: TranscriptRow;\n isDark: boolean;\n markdownColor: string;\n codeBg: string;\n tightTop: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n const attachments = item.attachments ?? [];\n const stripped = stripAskUser(item.content);\n const toolActivity = parseToolActivity(stripped, !!item.streaming);\n const text = parseToolActivity(\n displayAskUserAssistantText(item.content, !!item.askAnswered),\n !!item.streaming,\n ).text;\n\n return (\n <Pressable\n style={[styles.assistantCol, tightTop && styles.assistantColTight]}\n onPress={dismissKeyboard}\n accessible={false}\n >\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"start\" isDark={isDark} />\n ) : null}\n {toolActivity.steps.length > 0 ? (\n <ToolTimeline steps={toolActivity.steps} active={!!item.streaming} isDark={isDark} />\n ) : null}\n {text ? (\n <Markdown style={assistantMarkdownStyle(markdownColor, codeBg)}>\n {item.streaming ? closeOpenMarkdownFences(text) : text}\n </Markdown>\n ) : null}\n {renderMessageActions\n ? renderMessageActions({ id: item.id, role: 'assistant', content: item.content })\n : null}\n </Pressable>\n );\n});\n\nconst USER_MARKDOWN = {\n body: { color: '#ffffff', fontSize: 15, lineHeight: 22 },\n paragraph: { marginTop: 4, marginBottom: 4, color: '#ffffff' },\n strong: { fontWeight: '700' as const, color: '#ffffff' },\n em: { color: '#ffffff' },\n text: { color: '#ffffff' },\n bullet_list: { marginVertical: 4 },\n ordered_list: { marginVertical: 4 },\n list_item: { marginVertical: 2, color: '#ffffff' },\n};\n\nfunction assistantMarkdownStyle(markdownColor: string, codeBg: string) {\n return {\n body: { color: markdownColor, fontSize: 15, lineHeight: 22 },\n heading1: {\n fontSize: 18,\n fontWeight: '700' as const,\n marginTop: 12,\n marginBottom: 4,\n color: markdownColor,\n },\n heading2: {\n fontSize: 16,\n fontWeight: '700' as const,\n marginTop: 10,\n marginBottom: 4,\n color: markdownColor,\n },\n heading3: {\n fontSize: 15,\n fontWeight: '600' as const,\n marginTop: 8,\n marginBottom: 4,\n color: markdownColor,\n },\n strong: { fontWeight: '700' as const },\n paragraph: { marginTop: 4, marginBottom: 4 },\n list_item: { marginVertical: 2 },\n bullet_list: { marginVertical: 4 },\n ordered_list: { marginVertical: 4 },\n code_inline: {\n backgroundColor: codeBg,\n paddingHorizontal: 4,\n borderRadius: 4,\n fontSize: 14,\n color: markdownColor,\n },\n code_block: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n fence: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n };\n}\n\nconst styles = StyleSheet.create({\n list: {\n flex: 1,\n },\n listContent: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingHorizontal: 8,\n },\n listContentFill: {\n flexGrow: 1,\n justifyContent: 'flex-end',\n },\n userCol: {\n width: '100%',\n alignItems: 'flex-end',\n marginBottom: 12,\n paddingHorizontal: 4,\n },\n userTurn: {\n maxWidth: '92%',\n alignItems: 'flex-end',\n },\n userFollow: {\n marginTop: 4,\n },\n userBubble: {\n backgroundColor: USER_BUBBLE_BG,\n borderRadius: 16,\n paddingHorizontal: 12,\n paddingVertical: 8,\n },\n assistantGroup: {\n width: '100%',\n marginBottom: 4,\n },\n assistantCol: {\n width: '100%',\n alignItems: 'flex-start',\n marginBottom: 8,\n paddingHorizontal: 4,\n },\n assistantColTight: {\n marginTop: -4,\n },\n});\n"],"names":["rows","TranscriptGroupView","AssistantTurnView"],"mappings":"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"}
1
+ {"version":3,"file":"ChatTranscript.js","sources":["../../../src/features/chat/ChatTranscript.tsx"],"sourcesContent":["/**\n * Chat thread list that can show attachment previews.\n *\n * `@messenger-box/platform-mobile` PlanModeView maps each row to GiftedChat\n * `{ text }` only, so an image send becomes the gateway caption\n * \"Attached: IMG_0005.jpg\" with no thumbnail. This list keeps streaming\n * markdown for the assistant and draws Manus-style previews on the user side.\n *\n * Rows stay oldest → newest (same as web). A previous `inverted` FlatList\n * reversed that order on device. The composer dock already lifts with the\n * keyboard; this list just fills the leftover space and pins to the latest\n * turn.\n */\nimport React, { memo, useCallback, useEffect, useMemo, useRef, type ReactNode } from 'react';\nimport { FlatList, Platform, Pressable, StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { dismissKeyboard } from '../../utils/keyboardController';\nimport { useKeyboardLiftHeight } from '../../components/KeyboardComposerDock';\nimport Markdown from 'react-native-markdown-display';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\nimport { displayUserMessageText, shouldRenderUserTranscriptTurn } from '../attachments/displayUserMessageText';\nimport { MessageAttachmentPreviews } from '../attachments/MessageAttachmentPreviews';\nimport { ToolTimeline } from './ToolTimeline';\nimport { parseToolActivity } from './toolActivity';\nimport { parseAskUser, stripAskUser, displayAskUserAssistantText } from './askUser';\nimport { collapseDuplicateUserRows, groupConsecutiveRoleRows, type RoleGroup } from './transcriptGroups';\nimport { closeOpenMarkdownFences } from './streamingMarkdown';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n displayContent?: string;\n attachments?: MessageAttachment[];\n};\n\ntype TranscriptRow = TranscriptMessage & { streaming?: boolean; askAnswered?: boolean };\ntype TranscriptGroup = RoleGroup<TranscriptRow>;\n\nconst USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n renderMessageActions,\n listContentStyle,\n listFooter,\n isDark = false,\n}: {\n messages: TranscriptMessage[];\n streamingContent?: string;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n listContentStyle?: StyleProp<ViewStyle>;\n listFooter?: ReactNode;\n isDark?: boolean;\n}) {\n const listRef = useRef<FlatList<TranscriptGroup>>(null);\n const stickToBottomRef = useRef(true);\n const isProgrammaticScrollRef = useRef(false);\n const ignoreScrollUntilRef = useRef(0);\n const keyboardHeight = useKeyboardLiftHeight();\n\n const historyRows = useMemo<TranscriptRow[]>(() => {\n const rows: TranscriptRow[] = [];\n messages.forEach((msg, index) => {\n if (msg.role === 'user') {\n if (shouldRenderUserTranscriptTurn(msg.content, msg.attachments)) {\n rows.push({ ...msg, streaming: false });\n }\n return;\n }\n if (msg.role !== 'assistant') {\n rows.push({ ...msg, streaming: false });\n return;\n }\n const askAnswered =\n Boolean(parseAskUser(msg.content)) && messages.slice(index + 1).some((m) => m.role === 'user');\n const visible = displayAskUserAssistantText(msg.content, askAnswered).trim();\n const tools = parseToolActivity(stripAskUser(msg.content), false);\n const hasAsk = Boolean(parseAskUser(msg.content));\n if (hasAsk && !visible && tools.steps.length === 0 && !(msg.attachments?.length ?? 0)) {\n return;\n }\n if (!hasAsk) {\n const leftover = stripAskUser(msg.content).trim();\n if (!leftover && !(msg.attachments?.length ?? 0)) return;\n }\n rows.push({ ...msg, streaming: false, askAnswered });\n });\n return rows;\n }, [messages]);\n\n const rows = useMemo<TranscriptRow[]>(() => {\n const history = collapseDuplicateUserRows(historyRows);\n const rawStream = streamingContent ?? '';\n const last = history[history.length - 1];\n const streamAskAnswered = last?.role === 'user' && Boolean(parseAskUser(rawStream));\n const streamProse = displayAskUserAssistantText(rawStream, streamAskAnswered).trim();\n const streamTools = parseToolActivity(stripAskUser(rawStream), true);\n if (!streamProse && streamTools.steps.length === 0) return history;\n if (last?.role === 'assistant') {\n const lastText = last.content.trim();\n const streamText = rawStream.trim();\n if (lastText === streamText || lastText.startsWith(streamText) || streamText.startsWith(lastText)) {\n return history;\n }\n }\n return [\n ...history,\n {\n id: '__streaming__',\n role: 'assistant',\n content: rawStream,\n streaming: true,\n askAnswered: streamAskAnswered,\n },\n ];\n }, [historyRows, streamingContent]);\n\n const groups = useMemo(() => groupConsecutiveRoleRows(rows), [rows]);\n\n const scrollToLatest = useCallback(\n (animated: boolean) => {\n if (groups.length === 0) return;\n isProgrammaticScrollRef.current = true;\n stickToBottomRef.current = true;\n const list = listRef.current;\n if (!list) return;\n list.scrollToEnd({ animated });\n },\n [groups.length],\n );\n\n useEffect(() => {\n if (groups.length === 0 || !stickToBottomRef.current) return undefined;\n const t = setTimeout(() => scrollToLatest(false), 0);\n return () => clearTimeout(t);\n }, [groups.length, streamingContent?.length, scrollToLatest]);\n\n // Spacer and list height update on the same keyboard frame. Scroll only\n // after that shrink, or we stay on the top of a long turn (image 1).\n useEffect(() => {\n if (keyboardHeight <= 0) return undefined;\n stickToBottomRef.current = true;\n ignoreScrollUntilRef.current = Date.now() + 400;\n const frame = requestAnimationFrame(() => scrollToLatest(false));\n const afterLayout = setTimeout(() => scrollToLatest(false), 64);\n const afterKeyboard = setTimeout(() => scrollToLatest(false), 280);\n return () => {\n cancelAnimationFrame(frame);\n clearTimeout(afterLayout);\n clearTimeout(afterKeyboard);\n };\n }, [keyboardHeight, scrollToLatest]);\n\n const handleScroll = useCallback(\n (e: {\n nativeEvent: {\n contentOffset: { y: number };\n contentSize: { height: number };\n layoutMeasurement: { height: number };\n };\n }) => {\n if (Date.now() < ignoreScrollUntilRef.current) {\n stickToBottomRef.current = true;\n return;\n }\n const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;\n const nearLatest = contentOffset.y + layoutMeasurement.height >= contentSize.height - 80;\n if (!isProgrammaticScrollRef.current) {\n stickToBottomRef.current = nearLatest;\n } else if (nearLatest) {\n isProgrammaticScrollRef.current = false;\n }\n },\n [],\n );\n\n const handleContentSizeChange = useCallback(() => {\n if (!stickToBottomRef.current) return;\n scrollToLatest(false);\n }, [scrollToLatest]);\n\n const renderItem = useCallback(\n ({ item }: { item: TranscriptGroup }) => (\n <TranscriptGroupView group={item} isDark={isDark} renderMessageActions={renderMessageActions} />\n ),\n [isDark, renderMessageActions],\n );\n\n return (\n <View style={styles.list}>\n <FlatList\n ref={listRef}\n data={groups}\n keyExtractor={(item) => item.id}\n renderItem={renderItem}\n extraData={`${streamingContent ?? ''}:${Boolean(listFooter)}`}\n ListFooterComponent={listFooter ? <>{listFooter}</> : null}\n initialNumToRender={12}\n maxToRenderPerBatch={8}\n updateCellsBatchingPeriod={50}\n windowSize={7}\n nestedScrollEnabled\n directionalLockEnabled\n onScroll={handleScroll}\n onContentSizeChange={handleContentSizeChange}\n onLayout={() => {\n if (stickToBottomRef.current) scrollToLatest(false);\n }}\n scrollEventThrottle={16}\n keyboardShouldPersistTaps=\"handled\"\n keyboardDismissMode={Platform.OS === 'ios' ? 'interactive' : 'on-drag'}\n automaticallyAdjustKeyboardInsets={false}\n automaticallyAdjustContentInsets={false}\n contentInsetAdjustmentBehavior=\"never\"\n contentContainerStyle={[styles.listContent, listContentStyle, !listFooter && styles.listContentFill]}\n style={styles.list}\n />\n </View>\n );\n}\n\nconst TranscriptGroupView = memo(function TranscriptGroupView({\n group,\n isDark,\n renderMessageActions,\n}: {\n group: TranscriptGroup;\n isDark: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n if (group.role === 'user') {\n return (\n <Pressable style={styles.userCol} onPress={dismissKeyboard} accessible={false}>\n {group.items.map((item, index) => {\n const attachments = item.attachments ?? [];\n const text = displayUserMessageText(item.displayContent ?? item.content, attachments);\n if (!text && attachments.length === 0) return null;\n return (\n <View key={item.id} style={[styles.userTurn, index > 0 && styles.userFollow]}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"end\" isDark={isDark} />\n ) : null}\n {text ? (\n <View style={styles.userBubble}>\n <Markdown style={USER_MARKDOWN}>{text}</Markdown>\n </View>\n ) : null}\n </View>\n );\n })}\n </Pressable>\n );\n }\n\n const markdownColor = isDark ? '#e2e8f0' : '#111827';\n const codeBg = isDark ? '#1e293b' : '#f3f4f6';\n const last = group.items[group.items.length - 1];\n\n return (\n <View style={styles.assistantGroup}>\n {group.items.map((item, index) => (\n <AssistantTurnView\n key={item.id}\n item={item}\n isDark={isDark}\n markdownColor={markdownColor}\n codeBg={codeBg}\n tightTop={index > 0}\n renderMessageActions={item.streaming || item !== last ? undefined : renderMessageActions}\n />\n ))}\n </View>\n );\n});\n\nconst AssistantTurnView = memo(function AssistantTurnView({\n item,\n isDark,\n markdownColor,\n codeBg,\n tightTop,\n renderMessageActions,\n}: {\n item: TranscriptRow;\n isDark: boolean;\n markdownColor: string;\n codeBg: string;\n tightTop: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n const attachments = item.attachments ?? [];\n const stripped = stripAskUser(item.content);\n const toolActivity = parseToolActivity(stripped, !!item.streaming);\n const text = parseToolActivity(\n displayAskUserAssistantText(item.content, !!item.askAnswered),\n !!item.streaming,\n ).text;\n\n return (\n <Pressable\n style={[styles.assistantCol, tightTop && styles.assistantColTight]}\n onPress={dismissKeyboard}\n accessible={false}\n >\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"start\" isDark={isDark} />\n ) : null}\n {toolActivity.steps.length > 0 ? (\n <ToolTimeline steps={toolActivity.steps} active={!!item.streaming} isDark={isDark} />\n ) : null}\n {text ? (\n <Markdown style={assistantMarkdownStyle(markdownColor, codeBg)}>\n {item.streaming ? closeOpenMarkdownFences(text) : text}\n </Markdown>\n ) : null}\n {renderMessageActions\n ? renderMessageActions({ id: item.id, role: 'assistant', content: item.content })\n : null}\n </Pressable>\n );\n});\n\nconst USER_MARKDOWN = {\n body: { color: '#ffffff', fontSize: 15, lineHeight: 22 },\n paragraph: { marginTop: 4, marginBottom: 4, color: '#ffffff' },\n strong: { fontWeight: '700' as const, color: '#ffffff' },\n em: { color: '#ffffff' },\n text: { color: '#ffffff' },\n bullet_list: { marginVertical: 4 },\n ordered_list: { marginVertical: 4 },\n list_item: { marginVertical: 2, color: '#ffffff' },\n};\n\nfunction assistantMarkdownStyle(markdownColor: string, codeBg: string) {\n return {\n body: { color: markdownColor, fontSize: 15, lineHeight: 22 },\n heading1: {\n fontSize: 18,\n fontWeight: '700' as const,\n marginTop: 12,\n marginBottom: 4,\n color: markdownColor,\n },\n heading2: {\n fontSize: 16,\n fontWeight: '700' as const,\n marginTop: 10,\n marginBottom: 4,\n color: markdownColor,\n },\n heading3: {\n fontSize: 15,\n fontWeight: '600' as const,\n marginTop: 8,\n marginBottom: 4,\n color: markdownColor,\n },\n strong: { fontWeight: '700' as const },\n paragraph: { marginTop: 4, marginBottom: 4 },\n list_item: { marginVertical: 4, flexDirection: 'row', flexWrap: 'wrap' as const },\n bullet_list: { marginVertical: 6 },\n ordered_list: { marginVertical: 6 },\n bullet_list_icon: { marginRight: 8, marginTop: 2 },\n ordered_list_icon: { minWidth: 22, marginRight: 8, marginTop: 2 },\n code_inline: {\n backgroundColor: codeBg,\n paddingHorizontal: 4,\n borderRadius: 4,\n fontSize: 14,\n color: markdownColor,\n },\n code_block: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n fence: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n };\n}\n\nconst styles = StyleSheet.create({\n list: {\n flex: 1,\n overflow: 'hidden',\n },\n listContent: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingHorizontal: 8,\n },\n listContentFill: {\n flexGrow: 1,\n },\n userCol: {\n width: '100%',\n alignItems: 'flex-end',\n marginBottom: 12,\n paddingHorizontal: 4,\n },\n userTurn: {\n maxWidth: '92%',\n alignItems: 'flex-end',\n },\n userFollow: {\n marginTop: 4,\n },\n userBubble: {\n backgroundColor: USER_BUBBLE_BG,\n borderRadius: 16,\n paddingHorizontal: 12,\n paddingVertical: 8,\n },\n assistantGroup: {\n width: '100%',\n marginBottom: 4,\n },\n assistantCol: {\n width: '100%',\n alignItems: 'flex-start',\n marginBottom: 8,\n paddingHorizontal: 4,\n },\n assistantColTight: {\n marginTop: -4,\n },\n});\n"],"names":["rows","TranscriptGroupView","AssistantTurnView"],"mappings":"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"}
@@ -1,11 +1,47 @@
1
1
  const GEAR_LINE_RE = /^[ \t]*[⚙🔧\u{1F6E0}]️?[ \t]+(.+?)[ \t]*$/u;
2
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"]];
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"], [/^grep$/i, "Searching files"], [/^(str_replace_editor|edit|write)$/i, "Editing files"], [/^read$/i, "Reading files"]];
4
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
5
  function prettifySlug(slug) {
6
6
  const words = slug.replace(/^yantra[_-]/i, "").replace(/[_-]+/g, " ").trim();
7
7
  return words ? words.charAt(0).toUpperCase() + words.slice(1) : slug;
8
8
  }
9
+ function isToolPayloadLine(line) {
10
+ const t = line.trim();
11
+ if (!t) return false;
12
+ if (/^```/.test(t)) return true;
13
+ if (/^(import\s+\w+|from\s+\S+\s+import)\b/.test(t)) return true;
14
+ if (/^(const|let|var)\s+\w+\s*=/.test(t)) return true;
15
+ if (/\brequire\s*\(/.test(t)) return true;
16
+ if (/^path\s*=\s*['"`]/.test(t)) return true;
17
+ if (/\/(?:data\/users\/)?\.cdecli-server\b/.test(t)) return true;
18
+ if (/^(mkdir|curl|chmod|rm |cp |mv |cat |echo |python3?\b|node\s+-e|npx |yarn |npm )\b/.test(t)) return true;
19
+ if (/^(fs\.|JSON\.parse|JSON\.stringify)/.test(t)) return true;
20
+ return false;
21
+ }
22
+ function looksLikeCommandDetail(detail) {
23
+ const t = detail.trim();
24
+ if (!t) return false;
25
+ if (isToolPayloadLine(t)) return true;
26
+ if (/^(python3?|node|mkdir|curl|npx|bash|sh|zsh)\b/.test(t)) return true;
27
+ if (t.length > 72) return true;
28
+ return false;
29
+ }
30
+ function sanitizeDetail(detail) {
31
+ if (!detail) return void 0;
32
+ if (looksLikeCommandDetail(detail)) return void 0;
33
+ return detail;
34
+ }
35
+ function stripToolPayloads(text) {
36
+ const withoutFences = text.replace(/```([^\n]*)\n([\s\S]*?)(?:```|$)/g, (full, lang, body) => {
37
+ const language = String(lang).trim().toLowerCase();
38
+ if (/\bpropsComponent\b/.test(body)) return full;
39
+ if (/^(python|py|javascript|js|ts|tsx|bash|sh|shell|zsh)$/.test(language)) return "";
40
+ if (String(body).split("\n").some(isToolPayloadLine)) return "";
41
+ return full;
42
+ });
43
+ return withoutFences.split("\n").filter((line) => !isToolPayloadLine(line)).join("\n").replace(/\n{3,}/g, "\n\n").trim();
44
+ }
9
45
  function humanizeToolLine(body) {
10
46
  var _a, _b, _c, _d;
11
47
  const trimmed = body.trim();
@@ -23,20 +59,20 @@ function humanizeToolLine(body) {
23
59
  }
24
60
  return {
25
61
  label: `Running ${prettifySlug(connector)}`,
26
- detail: detailRaw
62
+ detail: sanitizeDetail(detailRaw)
27
63
  };
28
64
  }
29
65
  for (const [re, label2] of TOOL_LABELS) {
30
66
  if (re.test(bareTool)) return {
31
67
  label: label2,
32
- detail: detailRaw
68
+ detail: sanitizeDetail(detailRaw)
33
69
  };
34
70
  }
35
71
  const spaced = bareTool.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
36
72
  const label = spaced ? `Running ${spaced.toLowerCase()}` : "Working";
37
73
  return {
38
74
  label,
39
- detail: detailRaw
75
+ detail: sanitizeDetail(detailRaw)
40
76
  };
41
77
  }
42
78
  function parseToolActivity(content, active = false) {
@@ -66,7 +102,7 @@ function parseToolActivity(content, active = false) {
66
102
  }
67
103
  kept.push(line);
68
104
  }
69
- const text = kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
105
+ const text = stripToolPayloads(kept.join("\n").replace(/\n{3,}/g, "\n\n").trim());
70
106
  if (active && steps.length > 0) {
71
107
  steps[steps.length - 1].status = "running";
72
108
  }
@@ -74,4 +110,4 @@ function parseToolActivity(content, active = false) {
74
110
  steps,
75
111
  text
76
112
  };
77
- }export{humanizeToolLine,parseToolActivity};//# sourceMappingURL=toolActivity.js.map
113
+ }export{humanizeToolLine,isToolPayloadLine,parseToolActivity,stripToolPayloads};//# sourceMappingURL=toolActivity.js.map