@adminide-stack/yantra-mobile 12.0.53-alpha.1 → 12.0.53-alpha.12

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 (43) hide show
  1. package/lib/components/KeyboardComposerDock.js +55 -12
  2. package/lib/components/KeyboardComposerDock.js.map +1 -1
  3. package/lib/components/NavigationHeader/NavigationHeader.js +3 -1
  4. package/lib/components/NavigationHeader/NavigationHeader.js.map +1 -1
  5. package/lib/features/apps/AppsCatalog.js +1 -1
  6. package/lib/features/apps/AppsCatalog.js.map +1 -1
  7. package/lib/features/attachments/ComposerAttachMenu.js +189 -0
  8. package/lib/features/attachments/ComposerAttachMenu.js.map +1 -0
  9. package/lib/features/attachments/useImageAttachments.js +23 -15
  10. package/lib/features/attachments/useImageAttachments.js.map +1 -1
  11. package/lib/features/canvas/canvasBoardsSnapshot.js +45 -0
  12. package/lib/features/canvas/canvasBoardsSnapshot.js.map +1 -0
  13. package/lib/features/canvas/nativeViewerRegistry.js +9 -1
  14. package/lib/features/canvas/nativeViewerRegistry.js.map +1 -1
  15. package/lib/features/chat/ChatTranscript.js +7 -0
  16. package/lib/features/chat/ChatTranscript.js.map +1 -1
  17. package/lib/features/chat/chatHistorySnapshot.js +103 -0
  18. package/lib/features/chat/chatHistorySnapshot.js.map +1 -0
  19. package/lib/graphql/typePolicies.js +139 -0
  20. package/lib/graphql/typePolicies.js.map +1 -0
  21. package/lib/hooks/useCdecliChannel.js +63 -7
  22. package/lib/hooks/useCdecliChannel.js.map +1 -1
  23. package/lib/hooks/useChatApi.js +232 -26
  24. package/lib/hooks/useChatApi.js.map +1 -1
  25. package/lib/hooks/useChatStream.js +105 -36
  26. package/lib/hooks/useChatStream.js.map +1 -1
  27. package/lib/index.js +1 -1
  28. package/lib/index.js.map +1 -1
  29. package/lib/module.js +5 -2
  30. package/lib/module.js.map +1 -1
  31. package/lib/screens/CanvasBoard/index.js +1 -1
  32. package/lib/screens/CanvasBoard/index.js.map +1 -1
  33. package/lib/screens/Chat/index.js +8 -38
  34. package/lib/screens/Chat/index.js.map +1 -1
  35. package/lib/screens/Home/HomeScreen.js +18 -31
  36. package/lib/screens/Home/HomeScreen.js.map +1 -1
  37. package/lib/screens/Home/components/CanvasPrewarm.js +3 -2
  38. package/lib/screens/Home/components/CanvasPrewarm.js.map +1 -1
  39. package/lib/screens/Home/components/ChatHistoryLanding.js +60 -12
  40. package/lib/screens/Home/components/ChatHistoryLanding.js.map +1 -1
  41. package/lib/state/chatThreadMerge.js +76 -2
  42. package/lib/state/chatThreadMerge.js.map +1 -1
  43. package/package.json +4 -4
@@ -80,22 +80,30 @@ function loadPicker() {
80
80
  return require("expo-image-picker");
81
81
  }
82
82
  function loadDocumentPicker() {
83
- var _a, _b, _c;
84
- const core = require("expo-modules-core");
85
- const native = (_c = (_a = core.requireOptionalNativeModule) == null ? void 0 : _a.call(core, "ExpoDocumentPicker")) != null ? _c : (_b = core.requireNativeModule) == null ? void 0 : _b.call(core, "ExpoDocumentPicker");
86
- if (!(native == null ? void 0 : native.getDocumentAsync)) {
87
- throw new Error("File picker is not available in this build.");
88
- }
89
- return {
90
- getDocumentAsync: (options) => {
91
- var _a2, _b2, _c2;
92
- return native.getDocumentAsync({
93
- type: Array.isArray(options.type) ? options.type : [(_a2 = options.type) != null ? _a2 : "*/*"],
94
- multiple: (_b2 = options.multiple) != null ? _b2 : false,
95
- copyToCacheDirectory: (_c2 = options.copyToCacheDirectory) != null ? _c2 : true
96
- });
83
+ var _a;
84
+ try {
85
+ const core = require("expo-modules-core");
86
+ const native = (_a = core.requireOptionalNativeModule) == null ? void 0 : _a.call(core, "ExpoDocumentPicker");
87
+ if (native == null ? void 0 : native.getDocumentAsync) {
88
+ return {
89
+ getDocumentAsync: (options) => {
90
+ var _a2, _b, _c;
91
+ return native.getDocumentAsync({
92
+ type: Array.isArray(options.type) ? options.type : [(_a2 = options.type) != null ? _a2 : "*/*"],
93
+ multiple: (_b = options.multiple) != null ? _b : false,
94
+ copyToCacheDirectory: (_c = options.copyToCacheDirectory) != null ? _c : true
95
+ });
96
+ }
97
+ };
97
98
  }
98
- };
99
+ return require("expo-document-picker");
100
+ } catch (err) {
101
+ const msg = err instanceof Error ? err.message : String(err != null ? err : "");
102
+ if (/Cannot find native module|ExpoDocumentPicker|Cannot find module/i.test(msg)) {
103
+ throw new Error("File picker is not included in this app build. Use Photos, or install a staging build that includes expo-document-picker.");
104
+ }
105
+ throw err;
106
+ }
99
107
  }
100
108
  function loadFileSystem() {
101
109
  return require("expo-file-system");
@@ -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 // Use the native module Expo Go already ships. Do not `require('expo-document-picker')`\n // — that package's published `main` is `build/index.js`, which is often missing\n // from this workspace install, and Metro then fails the whole bundle.\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 requireNativeModule?: (name: string) => { getDocumentAsync?: Function };\n };\n const native =\n core.requireOptionalNativeModule?.('ExpoDocumentPicker') ?? core.requireNativeModule?.('ExpoDocumentPicker');\n if (!native?.getDocumentAsync) {\n throw new Error('File picker is not available in this build.');\n }\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\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","_b","_c"],"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(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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"}
@@ -0,0 +1,45 @@
1
+ import {getCachedCanvasBoards,setCachedCanvasBoards}from'./surfaceIndex.js';var _a;
2
+ let AsyncStorage = null;
3
+ try {
4
+ AsyncStorage = (_a = require("@react-native-async-storage/async-storage").default) != null ? _a : null;
5
+ } catch (e) {
6
+ AsyncStorage = null;
7
+ }
8
+ const SNAPSHOT_KEY = "yantra.canvas-boards.snapshot.v1";
9
+ let lastWritten = "";
10
+ function isBoardSummary(value) {
11
+ if (!value || typeof value !== "object") return false;
12
+ const row = value;
13
+ return typeof row.channelId === "string" && row.channelId.length > 0 && Boolean(row.board);
14
+ }
15
+ function parseCanvasBoardsSnapshot(raw) {
16
+ if (!raw) return null;
17
+ try {
18
+ const parsed = JSON.parse(raw);
19
+ if (!(parsed == null ? void 0 : parsed.url) || !Array.isArray(parsed.boards)) return null;
20
+ const boards = parsed.boards.filter(isBoardSummary);
21
+ return {
22
+ url: parsed.url,
23
+ boards
24
+ };
25
+ } catch (e) {
26
+ return null;
27
+ }
28
+ }
29
+ async function hydrateCanvasBoardsSnapshot(indexUrl) {
30
+ const trimmed = (indexUrl || "").trim();
31
+ if (!trimmed) return null;
32
+ const memory = getCachedCanvasBoards(trimmed);
33
+ if (memory) return memory;
34
+ if (!AsyncStorage) return null;
35
+ try {
36
+ const raw = await AsyncStorage.getItem(SNAPSHOT_KEY);
37
+ const parsed = parseCanvasBoardsSnapshot(raw);
38
+ if (!parsed || parsed.url !== trimmed) return null;
39
+ setCachedCanvasBoards(trimmed, parsed.boards);
40
+ lastWritten = raw != null ? raw : lastWritten;
41
+ return parsed.boards;
42
+ } catch (e) {
43
+ return null;
44
+ }
45
+ }export{hydrateCanvasBoardsSnapshot,parseCanvasBoardsSnapshot};//# sourceMappingURL=canvasBoardsSnapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvasBoardsSnapshot.js","sources":["../../../src/features/canvas/canvasBoardsSnapshot.ts"],"sourcesContent":["import { getCachedCanvasBoards, setCachedCanvasBoards, type CanvasBoardSummary } from './surfaceIndex';\n\n// Guarded require, not a top-level import: this module ships OTA inside the\n// federated canvas remote onto host binaries we do not control, and\n// @react-native-async-storage/async-storage THROWS AT MODULE EVAL when its\n// native module is absent (\"NativeModule: AsyncStorage is null\") - which took\n// the whole remote down on binaries built before the dependency existed. Same\n// lesson as the expo-video renderer (ViewerExpose try/require): storage is a\n// best-effort cache, so degrade to memory-only instead of crashing.\ntype StorageLike = { getItem(key: string): Promise<string | null>; setItem(key: string, value: string): Promise<void> };\nlet AsyncStorage: StorageLike | null = null;\ntry {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n AsyncStorage = require('@react-native-async-storage/async-storage').default ?? null;\n} catch {\n AsyncStorage = null;\n}\n\nconst SNAPSHOT_KEY = 'yantra.canvas-boards.snapshot.v1';\n\ntype StoredSnapshot = {\n url: string;\n boards: CanvasBoardSummary[];\n};\n\nlet lastWritten = '';\n\nfunction isBoardSummary(value: unknown): value is CanvasBoardSummary {\n if (!value || typeof value !== 'object') return false;\n const row = value as CanvasBoardSummary;\n return typeof row.channelId === 'string' && row.channelId.length > 0 && Boolean(row.board);\n}\n\nexport function parseCanvasBoardsSnapshot(raw: string | null | undefined): StoredSnapshot | null {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as StoredSnapshot;\n if (!parsed?.url || !Array.isArray(parsed.boards)) return null;\n const boards = parsed.boards.filter(isBoardSummary);\n return { url: parsed.url, boards };\n } catch {\n return null;\n }\n}\n\nexport async function hydrateCanvasBoardsSnapshot(indexUrl: string): Promise<CanvasBoardSummary[] | null> {\n const trimmed = (indexUrl || '').trim();\n if (!trimmed) return null;\n const memory = getCachedCanvasBoards(trimmed);\n if (memory) return memory;\n if (!AsyncStorage) return null;\n try {\n const raw = await AsyncStorage.getItem(SNAPSHOT_KEY);\n const parsed = parseCanvasBoardsSnapshot(raw);\n if (!parsed || parsed.url !== trimmed) return null;\n setCachedCanvasBoards(trimmed, parsed.boards);\n lastWritten = raw ?? lastWritten;\n return parsed.boards;\n } catch {\n return null;\n }\n}\n\nexport function persistCanvasBoardsSnapshot(indexUrl: string, boards: CanvasBoardSummary[]): void {\n const trimmed = (indexUrl || '').trim();\n if (!trimmed) return;\n setCachedCanvasBoards(trimmed, boards);\n const raw = JSON.stringify({ url: trimmed, boards } satisfies StoredSnapshot);\n if (raw === lastWritten) return;\n lastWritten = raw;\n void AsyncStorage?.setItem(SNAPSHOT_KEY, raw).catch(() => {\n /* disk write is best-effort */\n });\n}\n"],"names":[],"mappings":"4EAAA,IAAA,EAAA;AAaA,IAAI,YAAmC,GAAA,IAAA;AACvC,IAAI;AAEF,EAAA,YAAA,GAAA,CAAe,EAAQ,GAAA,OAAA,CAAA,2CAA2C,CAAE,CAAA,OAAA,KAArD,IAAgE,GAAA,EAAA,GAAA,IAAA;AACjF,CAAQ,CAAA,OAAA,CAAA,EAAA;AACN,EAAe,YAAA,GAAA,IAAA;AACjB;AACA,MAAM,YAAe,GAAA,kCAAA;AAKrB,IAAI,WAAc,GAAA,EAAA;AAClB,SAAS,eAAe,KAA6C,EAAA;AACnE,EAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,UAAiB,OAAA,KAAA;AAChD,EAAA,MAAM,GAAM,GAAA,KAAA;AACZ,EAAO,OAAA,OAAO,GAAI,CAAA,SAAA,KAAc,QAAY,IAAA,GAAA,CAAI,UAAU,MAAS,GAAA,CAAA,IAAK,OAAQ,CAAA,GAAA,CAAI,KAAK,CAAA;AAC3F;AACO,SAAS,0BAA0B,GAAuD,EAAA;AAC/F,EAAI,IAAA,CAAC,KAAY,OAAA,IAAA;AACjB,EAAI,IAAA;AACF,IAAM,MAAA,MAAA,GAAS,IAAK,CAAA,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAI,IAAA,EAAC,iCAAQ,GAAO,CAAA,IAAA,CAAC,MAAM,OAAQ,CAAA,MAAA,CAAO,MAAM,CAAA,EAAU,OAAA,IAAA;AAC1D,IAAA,MAAM,MAAS,GAAA,MAAA,CAAO,MAAO,CAAA,MAAA,CAAO,cAAc,CAAA;AAClD,IAAO,OAAA;AAAA,MACL,KAAK,MAAO,CAAA,GAAA;AAAA,MACZ;AAAA,KACF;AAAA,GACM,CAAA,OAAA,CAAA,EAAA;AACN,IAAO,OAAA,IAAA;AAAA;AAEX;AACA,eAAsB,4BAA4B,QAAwD,EAAA;AACxG,EAAM,MAAA,OAAA,GAAA,CAAW,QAAY,IAAA,EAAA,EAAI,IAAK,EAAA;AACtC,EAAI,IAAA,CAAC,SAAgB,OAAA,IAAA;AACrB,EAAM,MAAA,MAAA,GAAS,sBAAsB,OAAO,CAAA;AAC5C,EAAA,IAAI,QAAe,OAAA,MAAA;AACnB,EAAI,IAAA,CAAC,cAAqB,OAAA,IAAA;AAC1B,EAAI,IAAA;AACF,IAAA,MAAM,GAAM,GAAA,MAAM,YAAa,CAAA,OAAA,CAAQ,YAAY,CAAA;AACnD,IAAM,MAAA,MAAA,GAAS,0BAA0B,GAAG,CAAA;AAC5C,IAAA,IAAI,CAAC,MAAA,IAAU,MAAO,CAAA,GAAA,KAAQ,SAAgB,OAAA,IAAA;AAC9C,IAAsB,qBAAA,CAAA,OAAA,EAAS,OAAO,MAAM,CAAA;AAC5C,IAAA,WAAA,GAAc,GAAO,IAAA,IAAA,GAAA,GAAA,GAAA,WAAA;AACrB,IAAA,OAAO,MAAO,CAAA,MAAA;AAAA,GACR,CAAA,OAAA,CAAA,EAAA;AACN,IAAO,OAAA,IAAA;AAAA;AAEX"}
@@ -6,9 +6,17 @@ function getNativeCanvasViewer() {
6
6
  return registered;
7
7
  }
8
8
  let experience = null;
9
+ let experiencePreload = null;
9
10
  function registerNativeCanvasExperience(component) {
10
11
  experience = component;
11
12
  }
12
13
  function getNativeCanvasExperience() {
13
14
  return experience;
14
- }export{getNativeCanvasExperience,getNativeCanvasViewer,registerNativeCanvasExperience,registerNativeCanvasViewer};//# sourceMappingURL=nativeViewerRegistry.js.map
15
+ }
16
+ function registerCanvasExperiencePreload(fn) {
17
+ experiencePreload = fn;
18
+ }
19
+ function preloadNativeCanvasExperience() {
20
+ var _a;
21
+ return (_a = experiencePreload == null ? void 0 : experiencePreload()) != null ? _a : Promise.resolve();
22
+ }export{getNativeCanvasExperience,getNativeCanvasViewer,preloadNativeCanvasExperience,registerCanvasExperiencePreload,registerNativeCanvasExperience,registerNativeCanvasViewer};//# sourceMappingURL=nativeViewerRegistry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nativeViewerRegistry.js","sources":["../../../src/features/canvas/nativeViewerRegistry.ts"],"sourcesContent":["/**\n * Seam between the app shell and this package for the FEDERATED canvas viewer.\n *\n * The federated loader (`import('canvas_native/Viewer')`) must live in the app\n * (portable-devices/mobile): this package also builds through rollup for lib/,\n * and rollup must never see a bare `canvas_native/...` specifier. The screen,\n * however, lives HERE, where routes register. So the app injects its loader at\n * startup and the screen reads it back - the same inversion the surfaces use\n * for host services, scaled down to one component.\n */\nimport type { ComponentType } from 'react';\nimport type { NativeCanvasViewerProps } from './NativeCanvasViewer';\n\nlet registered: ComponentType<NativeCanvasViewerProps> | null = null;\n\nexport function registerNativeCanvasViewer(component: ComponentType<NativeCanvasViewerProps>): void {\n registered = component;\n}\n\n/** Null when the host app has not injected a loader (e.g. a build without federation). */\nexport function getNativeCanvasViewer(): ComponentType<NativeCanvasViewerProps> | null {\n return registered;\n}\n\n/**\n * The federated canvas EXPERIENCE (boards + viewer), registered by the app the\n * same way the viewer is. Kept as a separate seam so a host that only wants the\n * raw renderer - a preview, a test - is unaffected.\n */\nexport type CanvasExperienceComponent = ComponentType<{\n token?: string | null;\n indexUrl: string;\n channelId?: string;\n sessionError?: string | null;\n}>;\n\nlet experience: CanvasExperienceComponent | null = null;\n\nexport function registerNativeCanvasExperience(component: CanvasExperienceComponent): void {\n experience = component;\n}\n\nexport function getNativeCanvasExperience(): CanvasExperienceComponent | null {\n return experience;\n}\n"],"names":[],"mappings":"AAYA,IAAI,UAA4D,GAAA,IAAA;AACzD,SAAS,2BAA2B,SAAyD,EAAA;AAClG,EAAa,UAAA,GAAA,SAAA;AACf;AAGO,SAAS,qBAAuE,GAAA;AACrF,EAAO,OAAA,UAAA;AACT;AAaA,IAAI,UAA+C,GAAA,IAAA;AAC5C,SAAS,+BAA+B,SAA4C,EAAA;AACzF,EAAa,UAAA,GAAA,SAAA;AACf;AACO,SAAS,yBAA8D,GAAA;AAC5E,EAAO,OAAA,UAAA;AACT"}
1
+ {"version":3,"file":"nativeViewerRegistry.js","sources":["../../../src/features/canvas/nativeViewerRegistry.ts"],"sourcesContent":["/**\n * Seam between the app shell and this package for the FEDERATED canvas viewer.\n *\n * The federated loader (`import('canvas_native/Viewer')`) must live in the app\n * (portable-devices/mobile): this package also builds through rollup for lib/,\n * and rollup must never see a bare `canvas_native/...` specifier. The screen,\n * however, lives HERE, where routes register. So the app injects its loader at\n * startup and the screen reads it back - the same inversion the surfaces use\n * for host services, scaled down to one component.\n */\nimport type { ComponentType } from 'react';\nimport type { NativeCanvasViewerProps } from './NativeCanvasViewer';\n\nlet registered: ComponentType<NativeCanvasViewerProps> | null = null;\n\nexport function registerNativeCanvasViewer(component: ComponentType<NativeCanvasViewerProps>): void {\n registered = component;\n}\n\n/** Null when the host app has not injected a loader (e.g. a build without federation). */\nexport function getNativeCanvasViewer(): ComponentType<NativeCanvasViewerProps> | null {\n return registered;\n}\n\n/**\n * The federated canvas EXPERIENCE (boards + viewer), registered by the app the\n * same way the viewer is. Kept as a separate seam so a host that only wants the\n * raw renderer - a preview, a test - is unaffected.\n */\nexport type CanvasExperienceComponent = ComponentType<{\n token?: string | null;\n indexUrl: string;\n channelId?: string;\n sessionError?: string | null;\n}>;\n\nlet experience: CanvasExperienceComponent | null = null;\nlet experiencePreload: (() => Promise<void>) | null = null;\n\nexport function registerNativeCanvasExperience(component: CanvasExperienceComponent): void {\n experience = component;\n}\n\nexport function getNativeCanvasExperience(): CanvasExperienceComponent | null {\n return experience;\n}\n\n/**\n * Host registers the federated `import('canvas_native/Viewer')` so Home can\n * start the download before the user opens Canvas Native.\n */\nexport function registerCanvasExperiencePreload(fn: () => Promise<void>): void {\n experiencePreload = fn;\n}\n\nexport function preloadNativeCanvasExperience(): Promise<void> {\n return experiencePreload?.() ?? Promise.resolve();\n}\n"],"names":[],"mappings":"AAYA,IAAI,UAA4D,GAAA,IAAA;AACzD,SAAS,2BAA2B,SAAyD,EAAA;AAClG,EAAa,UAAA,GAAA,SAAA;AACf;AAGO,SAAS,qBAAuE,GAAA;AACrF,EAAO,OAAA,UAAA;AACT;AAaA,IAAI,UAA+C,GAAA,IAAA;AACnD,IAAI,iBAAkD,GAAA,IAAA;AAC/C,SAAS,+BAA+B,SAA4C,EAAA;AACzF,EAAa,UAAA,GAAA,SAAA;AACf;AACO,SAAS,yBAA8D,GAAA;AAC5E,EAAO,OAAA,UAAA;AACT;AAMO,SAAS,gCAAgC,EAA+B,EAAA;AAC7E,EAAoB,iBAAA,GAAA,EAAA;AACtB;AACO,SAAS,6BAA+C,GAAA;AAjD/D,EAAA,IAAA,EAAA;AAkDE,EAAO,OAAA,CAAA,EAAA,GAAA,iBAAA,IAAA,IAAA,GAAA,MAAA,GAAA,iBAAA,EAAA,KAAA,IAAA,GAAA,EAAA,GAAyB,QAAQ,OAAQ,EAAA;AAClD"}
@@ -74,6 +74,13 @@ function ChatTranscript({
74
74
  const streamProse = displayAskUserAssistantText(rawStream, streamAskAnswered).trim();
75
75
  const streamTools = parseToolActivity(stripAskUser(rawStream), true);
76
76
  if (!streamProse && streamTools.steps.length === 0) return history;
77
+ if ((last == null ? void 0 : last.role) === "assistant") {
78
+ const lastText = last.content.trim();
79
+ const streamText = rawStream.trim();
80
+ if (lastText === streamText || lastText.startsWith(streamText) || streamText.startsWith(lastText)) {
81
+ return history;
82
+ }
83
+ }
77
84
  return [...history, {
78
85
  id: "__streaming__",
79
86
  role: "assistant",
@@ -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 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 } 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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"}
@@ -0,0 +1,103 @@
1
+ import AsyncStorage from'@react-native-async-storage/async-storage';const SNAPSHOT_KEY = "yantra.chat-history.snapshot.v1";
2
+ const SNAPSHOT_LIMIT = 10;
3
+ let memory = null;
4
+ let lastWritten = "";
5
+ let hydratePromise = null;
6
+ const listeners = /* @__PURE__ */ new Set();
7
+ function toSession(row) {
8
+ if (!(row == null ? void 0 : row.channelId)) return null;
9
+ const updatedAt = new Date(row.updatedAt);
10
+ const createdAt = new Date(row.createdAt);
11
+ if (Number.isNaN(updatedAt.getTime()) || Number.isNaN(createdAt.getTime())) return null;
12
+ return {
13
+ channelId: row.channelId,
14
+ title: row.title || "New chat",
15
+ preview: row.preview || "",
16
+ mode: row.mode === "deep-search" || row.mode === "build" ? row.mode : "chat",
17
+ updatedAt,
18
+ createdAt,
19
+ isPlaceholder: Boolean(row.isPlaceholder),
20
+ isAttachment: row.isAttachment
21
+ };
22
+ }
23
+ function serializeChatHistorySnapshot(orgName, sessions) {
24
+ const payload = {
25
+ orgName,
26
+ rows: sessions.slice(0, SNAPSHOT_LIMIT).map((row) => ({
27
+ channelId: row.channelId,
28
+ title: row.title,
29
+ preview: row.preview,
30
+ mode: row.mode,
31
+ updatedAt: row.updatedAt.toISOString(),
32
+ createdAt: row.createdAt.toISOString(),
33
+ isPlaceholder: row.isPlaceholder,
34
+ isAttachment: row.isAttachment
35
+ }))
36
+ };
37
+ return JSON.stringify(payload);
38
+ }
39
+ function parseChatHistorySnapshot(raw) {
40
+ if (!raw) return null;
41
+ try {
42
+ const parsed = JSON.parse(raw);
43
+ if (!(parsed == null ? void 0 : parsed.orgName) || !Array.isArray(parsed.rows)) return null;
44
+ const rows = parsed.rows.map(toSession).filter((row) => row !== null);
45
+ if (rows.length === 0) return {
46
+ orgName: parsed.orgName,
47
+ rows: []
48
+ };
49
+ return {
50
+ orgName: parsed.orgName,
51
+ rows
52
+ };
53
+ } catch (e) {
54
+ return null;
55
+ }
56
+ }
57
+ function notify() {
58
+ for (const listener of listeners) listener(memory);
59
+ }
60
+ function peekChatHistorySnapshot(orgName) {
61
+ var _a;
62
+ if (!((_a = memory == null ? void 0 : memory.rows) == null ? void 0 : _a.length)) return [];
63
+ if (orgName && memory.orgName && memory.orgName !== orgName) return [];
64
+ return memory.rows;
65
+ }
66
+ function peekChatHistorySnapshotOrgName() {
67
+ var _a;
68
+ return (_a = memory == null ? void 0 : memory.orgName) != null ? _a : null;
69
+ }
70
+ function subscribeChatHistorySnapshot(listener) {
71
+ listeners.add(listener);
72
+ return () => {
73
+ listeners.delete(listener);
74
+ };
75
+ }
76
+ function persistChatHistorySnapshot(orgName, sessions) {
77
+ const trimmed = typeof orgName === "string" ? orgName.trim() : "";
78
+ if (!trimmed) return;
79
+ const raw = serializeChatHistorySnapshot(trimmed, sessions);
80
+ if (raw === lastWritten) return;
81
+ lastWritten = raw;
82
+ memory = {
83
+ orgName: trimmed,
84
+ rows: sessions.slice(0, SNAPSHOT_LIMIT)
85
+ };
86
+ notify();
87
+ void AsyncStorage.setItem(SNAPSHOT_KEY, raw).catch(() => {
88
+ });
89
+ }
90
+ function hydrateChatHistorySnapshot() {
91
+ if (!hydratePromise) {
92
+ hydratePromise = AsyncStorage.getItem(SNAPSHOT_KEY).then((raw) => {
93
+ const parsed = parseChatHistorySnapshot(raw);
94
+ if (parsed) {
95
+ memory = parsed;
96
+ lastWritten = raw != null ? raw : lastWritten;
97
+ notify();
98
+ }
99
+ return parsed;
100
+ }).catch(() => null);
101
+ }
102
+ return hydratePromise;
103
+ }export{hydrateChatHistorySnapshot,parseChatHistorySnapshot,peekChatHistorySnapshot,peekChatHistorySnapshotOrgName,persistChatHistorySnapshot,serializeChatHistorySnapshot,subscribeChatHistorySnapshot};//# sourceMappingURL=chatHistorySnapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chatHistorySnapshot.js","sources":["../../../src/features/chat/chatHistorySnapshot.ts"],"sourcesContent":["import AsyncStorage from '@react-native-async-storage/async-storage';\nimport type { ChatHistoryMode, ChatHistorySession } from '../../hooks/useChatApi';\n\nconst SNAPSHOT_KEY = 'yantra.chat-history.snapshot.v1';\nconst SNAPSHOT_LIMIT = 10;\n\ntype StoredSession = {\n channelId: string;\n title: string;\n preview: string;\n mode: ChatHistoryMode;\n updatedAt: string;\n createdAt: string;\n isPlaceholder: boolean;\n isAttachment?: boolean;\n};\n\ntype StoredSnapshot = {\n orgName: string;\n rows: StoredSession[];\n};\n\nexport type ChatHistorySnapshot = {\n orgName: string;\n rows: ChatHistorySession[];\n};\n\nlet memory: ChatHistorySnapshot | null = null;\nlet lastWritten = '';\nlet hydratePromise: Promise<ChatHistorySnapshot | null> | null = null;\nconst listeners = new Set<(snap: ChatHistorySnapshot | null) => void>();\n\nfunction toSession(row: StoredSession): ChatHistorySession | null {\n if (!row?.channelId) return null;\n const updatedAt = new Date(row.updatedAt);\n const createdAt = new Date(row.createdAt);\n if (Number.isNaN(updatedAt.getTime()) || Number.isNaN(createdAt.getTime())) return null;\n return {\n channelId: row.channelId,\n title: row.title || 'New chat',\n preview: row.preview || '',\n mode: row.mode === 'deep-search' || row.mode === 'build' ? row.mode : 'chat',\n updatedAt,\n createdAt,\n isPlaceholder: Boolean(row.isPlaceholder),\n isAttachment: row.isAttachment,\n };\n}\n\nexport function serializeChatHistorySnapshot(orgName: string, sessions: ChatHistorySession[]): string {\n const payload: StoredSnapshot = {\n orgName,\n rows: sessions.slice(0, SNAPSHOT_LIMIT).map((row) => ({\n channelId: row.channelId,\n title: row.title,\n preview: row.preview,\n mode: row.mode,\n updatedAt: row.updatedAt.toISOString(),\n createdAt: row.createdAt.toISOString(),\n isPlaceholder: row.isPlaceholder,\n isAttachment: row.isAttachment,\n })),\n };\n return JSON.stringify(payload);\n}\n\nexport function parseChatHistorySnapshot(raw: string | null | undefined): ChatHistorySnapshot | null {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as StoredSnapshot;\n if (!parsed?.orgName || !Array.isArray(parsed.rows)) return null;\n const rows = parsed.rows.map(toSession).filter((row): row is ChatHistorySession => row !== null);\n if (rows.length === 0) return { orgName: parsed.orgName, rows: [] };\n return { orgName: parsed.orgName, rows };\n } catch {\n return null;\n }\n}\n\nfunction notify() {\n for (const listener of listeners) listener(memory);\n}\n\nexport function peekChatHistorySnapshot(orgName?: string | null): ChatHistorySession[] {\n if (!memory?.rows?.length) return [];\n if (orgName && memory.orgName && memory.orgName !== orgName) return [];\n return memory.rows;\n}\n\nexport function peekChatHistorySnapshotOrgName(): string | null {\n return memory?.orgName ?? null;\n}\n\nexport function subscribeChatHistorySnapshot(listener: (snap: ChatHistorySnapshot | null) => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nexport function persistChatHistorySnapshot(orgName: string, sessions: ChatHistorySession[]): void {\n const trimmed = typeof orgName === 'string' ? orgName.trim() : '';\n if (!trimmed) return;\n const raw = serializeChatHistorySnapshot(trimmed, sessions);\n if (raw === lastWritten) return;\n lastWritten = raw;\n memory = { orgName: trimmed, rows: sessions.slice(0, SNAPSHOT_LIMIT) };\n notify();\n void AsyncStorage.setItem(SNAPSHOT_KEY, raw).catch(() => {\n /* disk write is best-effort */\n });\n}\n\nexport function hydrateChatHistorySnapshot(): Promise<ChatHistorySnapshot | null> {\n if (!hydratePromise) {\n hydratePromise = AsyncStorage.getItem(SNAPSHOT_KEY)\n .then((raw) => {\n const parsed = parseChatHistorySnapshot(raw);\n if (parsed) {\n memory = parsed;\n lastWritten = raw ?? lastWritten;\n notify();\n }\n return parsed;\n })\n .catch(() => null);\n }\n return hydratePromise;\n}\n"],"names":[],"mappings":"oEAEA,MAAM,YAAe,GAAA,iCAAA;AACrB,MAAM,cAAiB,GAAA,EAAA;AAmBvB,IAAI,MAAqC,GAAA,IAAA;AACzC,IAAI,WAAc,GAAA,EAAA;AAClB,IAAI,cAA6D,GAAA,IAAA;AACjE,MAAM,SAAA,uBAAgB,GAAgD,EAAA;AACtE,SAAS,UAAU,GAA+C,EAAA;AAChE,EAAI,IAAA,EAAC,GAAK,IAAA,IAAA,GAAA,MAAA,GAAA,GAAA,CAAA,SAAA,CAAA,EAAkB,OAAA,IAAA;AAC5B,EAAA,MAAM,SAAY,GAAA,IAAI,IAAK,CAAA,GAAA,CAAI,SAAS,CAAA;AACxC,EAAA,MAAM,SAAY,GAAA,IAAI,IAAK,CAAA,GAAA,CAAI,SAAS,CAAA;AACxC,EAAA,IAAI,MAAO,CAAA,KAAA,CAAM,SAAU,CAAA,OAAA,EAAS,CAAA,IAAK,MAAO,CAAA,KAAA,CAAM,SAAU,CAAA,OAAA,EAAS,CAAA,EAAU,OAAA,IAAA;AACnF,EAAO,OAAA;AAAA,IACL,WAAW,GAAI,CAAA,SAAA;AAAA,IACf,KAAA,EAAO,IAAI,KAAS,IAAA,UAAA;AAAA,IACpB,OAAA,EAAS,IAAI,OAAW,IAAA,EAAA;AAAA,IACxB,IAAA,EAAM,IAAI,IAAS,KAAA,aAAA,IAAiB,IAAI,IAAS,KAAA,OAAA,GAAU,IAAI,IAAO,GAAA,MAAA;AAAA,IACtE,SAAA;AAAA,IACA,SAAA;AAAA,IACA,aAAA,EAAe,OAAQ,CAAA,GAAA,CAAI,aAAa,CAAA;AAAA,IACxC,cAAc,GAAI,CAAA;AAAA,GACpB;AACF;AACgB,SAAA,4BAAA,CAA6B,SAAiB,QAAwC,EAAA;AACpG,EAAA,MAAM,OAA0B,GAAA;AAAA,IAC9B,OAAA;AAAA,IACA,MAAM,QAAS,CAAA,KAAA,CAAM,GAAG,cAAc,CAAA,CAAE,IAAI,CAAQ,GAAA,MAAA;AAAA,MAClD,WAAW,GAAI,CAAA,SAAA;AAAA,MACf,OAAO,GAAI,CAAA,KAAA;AAAA,MACX,SAAS,GAAI,CAAA,OAAA;AAAA,MACb,MAAM,GAAI,CAAA,IAAA;AAAA,MACV,SAAA,EAAW,GAAI,CAAA,SAAA,CAAU,WAAY,EAAA;AAAA,MACrC,SAAA,EAAW,GAAI,CAAA,SAAA,CAAU,WAAY,EAAA;AAAA,MACrC,eAAe,GAAI,CAAA,aAAA;AAAA,MACnB,cAAc,GAAI,CAAA;AAAA,KAClB,CAAA;AAAA,GACJ;AACA,EAAO,OAAA,IAAA,CAAK,UAAU,OAAO,CAAA;AAC/B;AACO,SAAS,yBAAyB,GAA4D,EAAA;AACnG,EAAI,IAAA,CAAC,KAAY,OAAA,IAAA;AACjB,EAAI,IAAA;AACF,IAAM,MAAA,MAAA,GAAS,IAAK,CAAA,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAI,IAAA,EAAC,iCAAQ,OAAW,CAAA,IAAA,CAAC,MAAM,OAAQ,CAAA,MAAA,CAAO,IAAI,CAAA,EAAU,OAAA,IAAA;AAC5D,IAAM,MAAA,IAAA,GAAO,MAAO,CAAA,IAAA,CAAK,GAAI,CAAA,SAAS,EAAE,MAAO,CAAA,CAAC,GAAmC,KAAA,GAAA,KAAQ,IAAI,CAAA;AAC/F,IAAI,IAAA,IAAA,CAAK,MAAW,KAAA,CAAA,EAAU,OAAA;AAAA,MAC5B,SAAS,MAAO,CAAA,OAAA;AAAA,MAChB,MAAM;AAAC,KACT;AACA,IAAO,OAAA;AAAA,MACL,SAAS,MAAO,CAAA,OAAA;AAAA,MAChB;AAAA,KACF;AAAA,GACM,CAAA,OAAA,CAAA,EAAA;AACN,IAAO,OAAA,IAAA;AAAA;AAEX;AACA,SAAS,MAAS,GAAA;AAChB,EAAW,KAAA,MAAA,QAAA,IAAY,SAAW,EAAA,QAAA,CAAS,MAAM,CAAA;AACnD;AACO,SAAS,wBAAwB,OAA+C,EAAA;AA/EvF,EAAA,IAAA,EAAA;AAgFE,EAAA,IAAI,GAAC,EAAQ,GAAA,MAAA,IAAA,IAAA,GAAA,MAAA,GAAA,MAAA,CAAA,IAAA,KAAR,IAAc,GAAA,MAAA,GAAA,EAAA,CAAA,MAAA,CAAA,SAAe,EAAC;AACnC,EAAA,IAAI,WAAW,MAAO,CAAA,OAAA,IAAW,OAAO,OAAY,KAAA,OAAA,SAAgB,EAAC;AACrE,EAAA,OAAO,MAAO,CAAA,IAAA;AAChB;AACO,SAAS,8BAAgD,GAAA;AApFhE,EAAA,IAAA,EAAA;AAqFE,EAAO,OAAA,CAAA,EAAA,GAAA,MAAA,IAAA,IAAA,GAAA,MAAA,GAAA,MAAA,CAAQ,YAAR,IAAmB,GAAA,EAAA,GAAA,IAAA;AAC5B;AACO,SAAS,6BAA6B,QAAkE,EAAA;AAC7G,EAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,EAAA,OAAO,MAAM;AACX,IAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,GAC3B;AACF;AACgB,SAAA,0BAAA,CAA2B,SAAiB,QAAsC,EAAA;AAChG,EAAA,MAAM,UAAU,OAAO,OAAA,KAAY,QAAW,GAAA,OAAA,CAAQ,MAAS,GAAA,EAAA;AAC/D,EAAA,IAAI,CAAC,OAAS,EAAA;AACd,EAAM,MAAA,GAAA,GAAM,4BAA6B,CAAA,OAAA,EAAS,QAAQ,CAAA;AAC1D,EAAA,IAAI,QAAQ,WAAa,EAAA;AACzB,EAAc,WAAA,GAAA,GAAA;AACd,EAAS,MAAA,GAAA;AAAA,IACP,OAAS,EAAA,OAAA;AAAA,IACT,IAAM,EAAA,QAAA,CAAS,KAAM,CAAA,CAAA,EAAG,cAAc;AAAA,GACxC;AACA,EAAO,MAAA,EAAA;AACP,EAAA,KAAK,aAAa,OAAQ,CAAA,YAAA,EAAc,GAAG,CAAA,CAAE,MAAM,MAAM;AAAA,GAExD,CAAA;AACH;AACO,SAAS,0BAAkE,GAAA;AAChF,EAAA,IAAI,CAAC,cAAgB,EAAA;AACnB,IAAA,cAAA,GAAiB,YAAa,CAAA,OAAA,CAAQ,YAAY,CAAA,CAAE,KAAK,CAAO,GAAA,KAAA;AAC9D,MAAM,MAAA,MAAA,GAAS,yBAAyB,GAAG,CAAA;AAC3C,MAAA,IAAI,MAAQ,EAAA;AACV,QAAS,MAAA,GAAA,MAAA;AACT,QAAA,WAAA,GAAc,GAAO,IAAA,IAAA,GAAA,GAAA,GAAA,WAAA;AACrB,QAAO,MAAA,EAAA;AAAA;AAET,MAAO,OAAA,MAAA;AAAA,KACR,CAAA,CAAE,KAAM,CAAA,MAAM,IAAI,CAAA;AAAA;AAErB,EAAO,OAAA,cAAA;AACT"}