@adminide-stack/yantra-mobile 12.0.54-alpha.0 → 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.
- package/lib/components/NavigationHeader/NavigationHeader.js +5 -2
- package/lib/components/NavigationHeader/NavigationHeader.js.map +1 -1
- package/lib/features/agent-chat/NativeAgentChat.js +354 -0
- package/lib/features/agent-chat/NativeAgentChat.js.map +1 -0
- package/lib/features/attachments/displayUserMessageText.js +3 -2
- package/lib/features/attachments/displayUserMessageText.js.map +1 -1
- package/lib/features/attachments/useImageAttachments.js +9 -7
- package/lib/features/attachments/useImageAttachments.js.map +1 -1
- package/lib/features/canvas/withRemoteSafeArea.js +46 -0
- package/lib/features/canvas/withRemoteSafeArea.js.map +1 -0
- package/lib/features/chat/ChatTranscript.js +23 -15
- package/lib/features/chat/ChatTranscript.js.map +1 -1
- package/lib/features/chat/toolActivity.js +42 -6
- package/lib/features/chat/toolActivity.js.map +1 -1
- package/lib/features/chat/transcriptGroups.js +11 -2
- package/lib/features/chat/transcriptGroups.js.map +1 -1
- package/lib/features/shopping/chat/homeShoppingIntent.js +58 -0
- package/lib/features/shopping/chat/homeShoppingIntent.js.map +1 -0
- package/lib/features/shopping/chat/openShopNative.js +60 -0
- package/lib/features/shopping/chat/openShopNative.js.map +1 -0
- package/lib/hooks/useCdecliChannel.js +10 -8
- package/lib/hooks/useCdecliChannel.js.map +1 -1
- package/lib/hooks/useChatStream.js +13 -6
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/screens/Chat/index.js +44 -5
- package/lib/screens/Chat/index.js.map +1 -1
- package/lib/screens/Home/HomeScreen.js +23 -3
- package/lib/screens/Home/HomeScreen.js.map +1 -1
- package/lib/screens/ShoppingNative/index.js +21 -2
- package/lib/screens/ShoppingNative/index.js.map +1 -1
- package/lib/screens/ShoppingNative/useShoppingHostChat.js +66 -0
- package/lib/screens/ShoppingNative/useShoppingHostChat.js.map +1 -0
- package/lib/services/brainQuickReply.js +72 -2
- package/lib/services/brainQuickReply.js.map +1 -1
- package/package.json +3 -3
|
@@ -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:
|
|
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
|
-
},
|
|
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 = (
|
|
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:
|
|
264
|
+
marginVertical: 4,
|
|
265
|
+
flexDirection: "row",
|
|
266
|
+
flexWrap: "wrap"
|
|
268
267
|
},
|
|
269
268
|
bullet_list: {
|
|
270
|
-
marginVertical:
|
|
269
|
+
marginVertical: 6
|
|
271
270
|
},
|
|
272
271
|
ordered_list: {
|
|
273
|
-
marginVertical:
|
|
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
|