@adminide-stack/yantra-mobile 12.0.44-alpha.17 → 12.0.44-alpha.20

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 (62) hide show
  1. package/lib/components/KeyboardComposerDock.js +10 -1
  2. package/lib/components/KeyboardComposerDock.js.map +1 -1
  3. package/lib/components/NavigationHeader/NavigationHeader.js +15 -16
  4. package/lib/components/NavigationHeader/NavigationHeader.js.map +1 -1
  5. package/lib/features/apps/AppsCatalog.js +2 -1
  6. package/lib/features/apps/AppsCatalog.js.map +1 -1
  7. package/lib/features/apps/resolveSurfaceUrl.js +22 -2
  8. package/lib/features/apps/resolveSurfaceUrl.js.map +1 -1
  9. package/lib/features/attachments/ComposerAttachmentBar.js +64 -0
  10. package/lib/features/attachments/ComposerAttachmentBar.js.map +1 -0
  11. package/lib/features/attachments/MessageAttachmentPreviews.js +77 -0
  12. package/lib/features/attachments/MessageAttachmentPreviews.js.map +1 -0
  13. package/lib/features/attachments/attachmentPreviewUri.js +9 -0
  14. package/lib/features/attachments/attachmentPreviewUri.js.map +1 -0
  15. package/lib/features/attachments/buildGatewayMedia.js +118 -0
  16. package/lib/features/attachments/buildGatewayMedia.js.map +1 -0
  17. package/lib/features/attachments/composerAttachmentHandoff.js +15 -0
  18. package/lib/features/attachments/composerAttachmentHandoff.js.map +1 -0
  19. package/lib/features/attachments/displayUserMessageText.js +19 -0
  20. package/lib/features/attachments/displayUserMessageText.js.map +1 -0
  21. package/lib/features/attachments/historyAttachmentLabel.js +51 -0
  22. package/lib/features/attachments/historyAttachmentLabel.js.map +1 -0
  23. package/lib/features/attachments/isImageAttachment.js +7 -0
  24. package/lib/features/attachments/isImageAttachment.js.map +1 -0
  25. package/lib/features/attachments/preserveAttachmentPreviews.js +41 -0
  26. package/lib/features/attachments/preserveAttachmentPreviews.js.map +1 -0
  27. package/lib/features/attachments/uploadChatAttachments.js +102 -0
  28. package/lib/features/attachments/uploadChatAttachments.js.map +1 -0
  29. package/lib/features/attachments/useImageAttachments.js +102 -7
  30. package/lib/features/attachments/useImageAttachments.js.map +1 -1
  31. package/lib/features/canvas/nativeViewerRegistry.js +8 -1
  32. package/lib/features/canvas/nativeViewerRegistry.js.map +1 -1
  33. package/lib/features/canvas/surfaceIndex.js +25 -0
  34. package/lib/features/canvas/surfaceIndex.js.map +1 -0
  35. package/lib/features/canvas/useCanvasSession.js +57 -0
  36. package/lib/features/canvas/useCanvasSession.js.map +1 -0
  37. package/lib/features/chat/ChatTranscript.js +202 -0
  38. package/lib/features/chat/ChatTranscript.js.map +1 -0
  39. package/lib/hooks/useCdecliChannel.js +5 -3
  40. package/lib/hooks/useCdecliChannel.js.map +1 -1
  41. package/lib/hooks/useChatApi.js +32 -15
  42. package/lib/hooks/useChatApi.js.map +1 -1
  43. package/lib/hooks/useChatStream.js +35 -8
  44. package/lib/hooks/useChatStream.js.map +1 -1
  45. package/lib/index.js +1 -1
  46. package/lib/index.js.map +1 -1
  47. package/lib/screens/CanvasBoard/index.js +20 -15
  48. package/lib/screens/CanvasBoard/index.js.map +1 -1
  49. package/lib/screens/Chat/index.js +39 -66
  50. package/lib/screens/Chat/index.js.map +1 -1
  51. package/lib/screens/ChatHistory/index.js +2 -8
  52. package/lib/screens/ChatHistory/index.js.map +1 -1
  53. package/lib/screens/Home/HomeScreen.js +57 -21
  54. package/lib/screens/Home/HomeScreen.js.map +1 -1
  55. package/lib/screens/Home/components/ChatHistoryLanding.js +15 -9
  56. package/lib/screens/Home/components/ChatHistoryLanding.js.map +1 -1
  57. package/lib/screens/WebBuilder/index.js.map +1 -1
  58. package/lib/utils/navigateToNewChatHome.js +40 -0
  59. package/lib/utils/navigateToNewChatHome.js.map +1 -0
  60. package/package.json +3 -3
  61. package/lib/features/canvas/canvasCore.js +0 -41
  62. package/lib/features/canvas/canvasCore.js.map +0 -1
@@ -0,0 +1,118 @@
1
+ import {uploadChatAttachments}from'./uploadChatAttachments.js';var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ function isImageMedia(attachment) {
21
+ var _a, _b;
22
+ const dataUrl = (_a = attachment.dataUrl) != null ? _a : "";
23
+ return dataUrl.startsWith("data:image/") || Boolean((_b = attachment.mimeType) == null ? void 0 : _b.startsWith("image/")) || attachment.type === "screenshot";
24
+ }
25
+ function mimeOf(attachment) {
26
+ var _a;
27
+ const dataUrl = (_a = attachment.dataUrl) != null ? _a : "";
28
+ return attachment.mimeType || (dataUrl.startsWith("data:") ? dataUrl.slice(5).split(/[;,]/)[0] : "");
29
+ }
30
+ function buildGatewayMedia(attachments, opts = {
31
+ includeData: true
32
+ }) {
33
+ return (attachments != null ? attachments : []).map((a) => {
34
+ var _a, _b;
35
+ const dataUrl = (_b = (_a = a.dataUrl) != null ? _a : a.url) != null ? _b : "";
36
+ const isImage = isImageMedia(a) || dataUrl.startsWith("data:image/");
37
+ const base64 = dataUrl.includes("base64,") ? dataUrl.split("base64,")[1] : "";
38
+ const mimeType = mimeOf(a);
39
+ return __spreadProps(__spreadValues(__spreadValues({
40
+ type: isImage ? "IMAGE" : "DOCUMENT",
41
+ url: dataUrl
42
+ }, opts.includeData && base64 ? {
43
+ data: base64
44
+ } : {}), mimeType ? {
45
+ mimeType
46
+ } : {}), {
47
+ filename: a.name
48
+ });
49
+ }).filter((m) => m.url.startsWith("data:"));
50
+ }
51
+ const GATEWAY_MEDIA_INLINE_MAX_B64 = 14e6;
52
+ async function buildGatewayMediaPreferUrls(attachments, createFileUploadLinks, createFileDownloadLinks) {
53
+ if (!(attachments == null ? void 0 : attachments.length)) return [];
54
+ let uploaded;
55
+ try {
56
+ uploaded = await uploadChatAttachments(attachments, createFileUploadLinks);
57
+ } catch (error) {
58
+ console.warn("[mobile] S3 upload for gateway media failed \u2014 falling back to inline base64:", error);
59
+ return buildGatewayMedia(attachments, {
60
+ includeData: true
61
+ });
62
+ }
63
+ const bareUrls = uploaded.map((a) => a.objectUrl).filter((u) => Boolean(u));
64
+ const presignedByBareUrl = /* @__PURE__ */ new Map();
65
+ if (createFileDownloadLinks && bareUrls.length > 0) {
66
+ try {
67
+ const signed = await createFileDownloadLinks(bareUrls);
68
+ if (signed.length === bareUrls.length) {
69
+ bareUrls.forEach((u, i) => presignedByBareUrl.set(u, signed[i]));
70
+ }
71
+ } catch (error) {
72
+ console.warn("[mobile] download-link mint failed \u2014 falling back to inline bytes on media:", error);
73
+ }
74
+ }
75
+ return uploaded.map((a) => {
76
+ var _a;
77
+ const type = isImageMedia(a) ? "IMAGE" : "DOCUMENT";
78
+ const mimeType = mimeOf(a);
79
+ const s3Url = a.objectUrl;
80
+ const dataUrl = (_a = a.dataUrl) != null ? _a : "";
81
+ const base64Payload = dataUrl.includes("base64,") ? dataUrl.split("base64,")[1] : "";
82
+ if (s3Url) {
83
+ const presigned = presignedByBareUrl.get(s3Url);
84
+ if (presigned) {
85
+ return __spreadProps(__spreadValues({
86
+ type,
87
+ url: presigned
88
+ }, mimeType ? {
89
+ mimeType
90
+ } : {}), {
91
+ filename: a.name
92
+ });
93
+ }
94
+ const inline = base64Payload && base64Payload.length <= GATEWAY_MEDIA_INLINE_MAX_B64 ? {
95
+ data: base64Payload
96
+ } : {};
97
+ return __spreadProps(__spreadValues(__spreadValues({
98
+ type,
99
+ url: s3Url
100
+ }, inline), mimeType ? {
101
+ mimeType
102
+ } : {}), {
103
+ filename: a.name
104
+ });
105
+ }
106
+ if (!dataUrl.startsWith("data:")) return null;
107
+ return __spreadProps(__spreadValues(__spreadValues({
108
+ type,
109
+ url: dataUrl
110
+ }, base64Payload ? {
111
+ data: base64Payload
112
+ } : {}), mimeType ? {
113
+ mimeType
114
+ } : {}), {
115
+ filename: a.name
116
+ });
117
+ }).filter((m) => m !== null);
118
+ }export{buildGatewayMedia,buildGatewayMediaPreferUrls};//# sourceMappingURL=buildGatewayMedia.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildGatewayMedia.js","sources":["../../../src/features/attachments/buildGatewayMedia.ts"],"sourcesContent":["import type { MessageAttachment } from '../../hooks/useChatStream';\nimport { uploadChatAttachments } from './uploadChatAttachments';\n\nexport type GatewayMediaItem = {\n type: string;\n url: string;\n data?: string;\n mimeType?: string;\n filename?: string;\n};\n\nfunction isImageMedia(attachment: MessageAttachment): boolean {\n const dataUrl = attachment.dataUrl ?? '';\n return (\n dataUrl.startsWith('data:image/') ||\n Boolean(attachment.mimeType?.startsWith('image/')) ||\n attachment.type === 'screenshot'\n );\n}\n\nfunction mimeOf(attachment: MessageAttachment): string {\n const dataUrl = attachment.dataUrl ?? '';\n return attachment.mimeType || (dataUrl.startsWith('data:') ? dataUrl.slice(5).split(/[;,]/)[0] : '');\n}\n\n/**\n * Browser-parity gateway `media` payload (`ChatLayout.buildGatewayMedia`).\n * Images → IMAGE, everything else → DOCUMENT. Only `data:` URLs are forwarded.\n */\nexport function buildGatewayMedia(\n attachments: MessageAttachment[] | undefined,\n opts: { includeData?: boolean } = { includeData: true },\n): GatewayMediaItem[] {\n return (attachments ?? [])\n .map((a) => {\n const dataUrl = a.dataUrl ?? a.url ?? '';\n const isImage = isImageMedia(a) || dataUrl.startsWith('data:image/');\n const base64 = dataUrl.includes('base64,') ? dataUrl.split('base64,')[1] : '';\n const mimeType = mimeOf(a);\n return {\n type: isImage ? 'IMAGE' : 'DOCUMENT',\n url: dataUrl,\n ...(opts.includeData && base64 ? { data: base64 } : {}),\n ...(mimeType ? { mimeType } : {}),\n filename: a.name,\n };\n })\n .filter((m) => m.url.startsWith('data:'));\n}\n\n/** Inline-bytes cap matching web (~10 MB decoded). */\nconst GATEWAY_MEDIA_INLINE_MAX_B64 = 14_000_000;\n\n/**\n * Browser `buildGatewayMediaPreferUrls` (yantra-app#607): upload to S3, then\n * send presigned GET URLs so the GraphQL send is not a multi-MB base64 blob.\n * Falls back to inline `data:` when upload/presign fails.\n */\nexport async function buildGatewayMediaPreferUrls(\n attachments: MessageAttachment[] | undefined,\n createFileUploadLinks: (filenames: string[]) => Promise<string[]>,\n createFileDownloadLinks?: (urls: string[]) => Promise<string[]>,\n): Promise<GatewayMediaItem[]> {\n if (!attachments?.length) return [];\n\n let uploaded: Array<MessageAttachment & { objectUrl?: string }>;\n try {\n uploaded = await uploadChatAttachments(attachments, createFileUploadLinks);\n } catch (error) {\n console.warn('[mobile] S3 upload for gateway media failed — falling back to inline base64:', error);\n return buildGatewayMedia(attachments, { includeData: true });\n }\n\n const bareUrls = uploaded.map((a) => a.objectUrl).filter((u): u is string => Boolean(u));\n const presignedByBareUrl = new Map<string, string>();\n if (createFileDownloadLinks && bareUrls.length > 0) {\n try {\n const signed = await createFileDownloadLinks(bareUrls);\n if (signed.length === bareUrls.length) {\n bareUrls.forEach((u, i) => presignedByBareUrl.set(u, signed[i]));\n }\n } catch (error) {\n console.warn('[mobile] download-link mint failed — falling back to inline bytes on media:', error);\n }\n }\n\n return uploaded\n .map((a) => {\n const type = isImageMedia(a) ? 'IMAGE' : 'DOCUMENT';\n const mimeType = mimeOf(a);\n const s3Url = a.objectUrl;\n const dataUrl = a.dataUrl ?? '';\n const base64Payload = dataUrl.includes('base64,') ? dataUrl.split('base64,')[1] : '';\n if (s3Url) {\n const presigned = presignedByBareUrl.get(s3Url);\n if (presigned) {\n return { type, url: presigned, ...(mimeType ? { mimeType } : {}), filename: a.name };\n }\n const inline =\n base64Payload && base64Payload.length <= GATEWAY_MEDIA_INLINE_MAX_B64\n ? { data: base64Payload }\n : {};\n return { type, url: s3Url, ...inline, ...(mimeType ? { mimeType } : {}), filename: a.name };\n }\n if (!dataUrl.startsWith('data:')) return null;\n return {\n type,\n url: dataUrl,\n ...(base64Payload ? { data: base64Payload } : {}),\n ...(mimeType ? { mimeType } : {}),\n filename: a.name,\n };\n })\n .filter((m): m is GatewayMediaItem => m !== null);\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ const stash = /* @__PURE__ */ new Map();
2
+ function stashComposerAttachments(channelId, attachments) {
3
+ if (!channelId || !(attachments == null ? void 0 : attachments.length)) return;
4
+ stash.set(channelId, attachments);
5
+ }
6
+ function hasComposerAttachments(channelId) {
7
+ return Boolean(channelId && stash.has(channelId));
8
+ }
9
+ function takeComposerAttachments(channelId) {
10
+ if (!channelId) return void 0;
11
+ const next = stash.get(channelId);
12
+ if (!next) return void 0;
13
+ stash.delete(channelId);
14
+ return next;
15
+ }export{hasComposerAttachments,stashComposerAttachments,takeComposerAttachments};//# sourceMappingURL=composerAttachmentHandoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composerAttachmentHandoff.js","sources":["../../../src/features/attachments/composerAttachmentHandoff.ts"],"sourcesContent":["import type { MessageAttachment } from '../../hooks/useChatStream';\n\n/**\n * Home → Chat cannot put base64 payloads in navigation params (size limits).\n * Stash by channel id, then Chat consumes once CDeCLI is ready to auto-fire.\n */\nconst stash = new Map<string, MessageAttachment[]>();\n\nexport function stashComposerAttachments(channelId: string, attachments: MessageAttachment[] | undefined): void {\n if (!channelId || !attachments?.length) return;\n stash.set(channelId, attachments);\n}\n\nexport function hasComposerAttachments(channelId: string | null | undefined): boolean {\n return Boolean(channelId && stash.has(channelId));\n}\n\nexport function takeComposerAttachments(channelId: string | null | undefined): MessageAttachment[] | undefined {\n if (!channelId) return undefined;\n const next = stash.get(channelId);\n if (!next) return undefined;\n stash.delete(channelId);\n return next;\n}\n"],"names":[],"mappings":"AAMA,MAAM,KAAA,uBAAY,GAAiC,EAAA;AACnC,SAAA,wBAAA,CAAyB,WAAmB,WAAoD,EAAA;AAC9G,EAAA,IAAI,CAAC,SAAA,IAAa,EAAC,WAAA,IAAA,IAAA,GAAA,MAAA,GAAA,WAAA,CAAa,MAAQ,CAAA,EAAA;AACxC,EAAM,KAAA,CAAA,GAAA,CAAI,WAAW,WAAW,CAAA;AAClC;AACO,SAAS,uBAAuB,SAA+C,EAAA;AACpF,EAAA,OAAO,OAAQ,CAAA,SAAA,IAAa,KAAM,CAAA,GAAA,CAAI,SAAS,CAAC,CAAA;AAClD;AACO,SAAS,wBAAwB,SAAuE,EAAA;AAC7G,EAAI,IAAA,CAAC,WAAkB,OAAA,MAAA;AACvB,EAAM,MAAA,IAAA,GAAO,KAAM,CAAA,GAAA,CAAI,SAAS,CAAA;AAChC,EAAI,IAAA,CAAC,MAAa,OAAA,MAAA;AAClB,EAAA,KAAA,CAAM,OAAO,SAAS,CAAA;AACtB,EAAO,OAAA,IAAA;AACT"}
@@ -0,0 +1,19 @@
1
+ import {isAttachedCaption}from'./historyAttachmentLabel.js';function displayUserMessageText(content, _attachments) {
2
+ const trimmed = (content != null ? content : "").trim();
3
+ if (!trimmed) return "";
4
+ if (isAttachedCaption(trimmed)) return "";
5
+ return trimmed;
6
+ }
7
+ function hasLiveLocalPreview(attachments) {
8
+ return (attachments != null ? attachments : []).some((attachment) => {
9
+ var _a, _b;
10
+ if ((_a = attachment.dataUrl) == null ? void 0 : _a.startsWith("data:")) return true;
11
+ const url = (_b = attachment.url) == null ? void 0 : _b.trim();
12
+ return Boolean(url && /^(file:|content:|ph:|assets-library:)/i.test(url));
13
+ });
14
+ }
15
+ function shouldRenderUserTranscriptTurn(content, attachments) {
16
+ if (displayUserMessageText(content)) return true;
17
+ if (hasLiveLocalPreview(attachments)) return true;
18
+ return false;
19
+ }export{displayUserMessageText,shouldRenderUserTranscriptTurn};//# sourceMappingURL=displayUserMessageText.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"displayUserMessageText.js","sources":["../../../src/features/attachments/displayUserMessageText.ts"],"sourcesContent":["/**\n * Text that belongs in the user bubble after attachments are drawn visually.\n *\n * Attachment-only sends still go to the gateway as `Attached: filename` so the\n * agent has a caption — that string must not appear in the bubble (web hides\n * this turn entirely when there is no live preview).\n */\nimport { isAttachedCaption } from './historyAttachmentLabel';\n\nexport function displayUserMessageText(\n content: string | undefined,\n _attachments?: Array<{ name?: string }> | null,\n): string {\n const trimmed = (content ?? '').trim();\n if (!trimmed) return '';\n if (isAttachedCaption(trimmed)) return '';\n return trimmed;\n}\n\nfunction hasLiveLocalPreview(attachments?: Array<{ url?: string; dataUrl?: string }> | null): boolean {\n return (attachments ?? []).some((attachment) => {\n if (attachment.dataUrl?.startsWith('data:')) return true;\n const url = attachment.url?.trim();\n return Boolean(url && /^(file:|content:|ph:|assets-library:)/i.test(url));\n });\n}\n\n/**\n * Web omits the user bubble for attachment-only turns (empty query + files).\n * Mobile still shows a live thumbnail while sending; from history, skip the\n * `Attached: filename` row and keep the assistant reply.\n */\nexport function shouldRenderUserTranscriptTurn(\n content: string | undefined,\n attachments?: Array<{ url?: string; dataUrl?: string; name?: string }> | null,\n): boolean {\n if (displayUserMessageText(content, attachments)) return true;\n if (hasLiveLocalPreview(attachments)) return true;\n return false;\n}\n"],"names":[],"mappings":"4DAQgB,SAAA,sBAAA,CAAuB,SAA6B,YAEjD,EAAA;AACjB,EAAM,MAAA,OAAA,GAAA,CAAW,OAAW,IAAA,IAAA,GAAA,OAAA,GAAA,EAAA,EAAI,IAAK,EAAA;AACrC,EAAI,IAAA,CAAC,SAAgB,OAAA,EAAA;AACrB,EAAI,IAAA,iBAAA,CAAkB,OAAO,CAAA,EAAU,OAAA,EAAA;AACvC,EAAO,OAAA,OAAA;AACT;AACA,SAAS,oBAAoB,WAGT,EAAA;AAClB,EAAA,OAAA,CAAQ,WAAe,IAAA,IAAA,GAAA,WAAA,GAAA,EAAI,EAAA,IAAA,CAAK,CAAc,UAAA,KAAA;AApBhD,IAAA,IAAA,EAAA,EAAA,EAAA;AAqBI,IAAA,IAAA,CAAI,EAAW,GAAA,UAAA,CAAA,OAAA,KAAX,IAAoB,GAAA,MAAA,GAAA,EAAA,CAAA,UAAA,CAAW,UAAiB,OAAA,IAAA;AACpD,IAAM,MAAA,GAAA,GAAA,CAAM,EAAW,GAAA,UAAA,CAAA,GAAA,KAAX,IAAgB,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AAC5B,IAAA,OAAO,OAAQ,CAAA,GAAA,IAAO,wCAAyC,CAAA,IAAA,CAAK,GAAG,CAAC,CAAA;AAAA,GACzE,CAAA;AACH;AAOgB,SAAA,8BAAA,CAA+B,SAA6B,WAIxD,EAAA;AAClB,EAAA,IAAI,sBAAuB,CAAA,OAAoB,CAAA,EAAU,OAAA,IAAA;AACzD,EAAI,IAAA,mBAAA,CAAoB,WAAW,CAAA,EAAU,OAAA,IAAA;AAC7C,EAAO,OAAA,KAAA;AACT"}
@@ -0,0 +1,51 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __spreadValues = (a, b) => {
7
+ for (var prop in b || (b = {}))
8
+ if (__hasOwnProp.call(b, prop))
9
+ __defNormalProp(a, prop, b[prop]);
10
+ if (__getOwnPropSymbols)
11
+ for (var prop of __getOwnPropSymbols(b)) {
12
+ if (__propIsEnum.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ }
15
+ return a;
16
+ };
17
+ const ATTACHED_CAPTION_RE = /^Attached:\s*(.+)$/i;
18
+ function isAttachedCaption(text) {
19
+ return ATTACHED_CAPTION_RE.test((text != null ? text : "").trim());
20
+ }
21
+ function historyAttachmentTitle(title) {
22
+ const trimmed = (title != null ? title : "").trim();
23
+ if (!trimmed || isAttachedCaption(trimmed)) return "New chat";
24
+ return trimmed;
25
+ }
26
+ function mimeFromName(name) {
27
+ if (/\.pdf$/i.test(name)) return "application/pdf";
28
+ if (/\.png$/i.test(name)) return "image/png";
29
+ if (/\.jpe?g$/i.test(name)) return "image/jpeg";
30
+ if (/\.gif$/i.test(name)) return "image/gif";
31
+ if (/\.webp$/i.test(name)) return "image/webp";
32
+ if (/\.(heic|heif)$/i.test(name)) return "image/heic";
33
+ if (/\.svg$/i.test(name)) return "image/svg+xml";
34
+ return void 0;
35
+ }
36
+ function attachmentsFromUserContent(content, existing) {
37
+ if (existing == null ? void 0 : existing.length) return existing;
38
+ const match = ATTACHED_CAPTION_RE.exec((content != null ? content : "").trim());
39
+ if (!match) return [];
40
+ const names = match[1].split(",").map((part) => part.trim()).filter(Boolean);
41
+ return names.map((name, index) => {
42
+ const mimeType = mimeFromName(name);
43
+ return __spreadValues({
44
+ id: `caption-${index}-${name}`,
45
+ name,
46
+ type: "file"
47
+ }, mimeType ? {
48
+ mimeType
49
+ } : {});
50
+ });
51
+ }export{ATTACHED_CAPTION_RE,attachmentsFromUserContent,historyAttachmentTitle,isAttachedCaption};//# sourceMappingURL=historyAttachmentLabel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historyAttachmentLabel.js","sources":["../../../src/features/attachments/historyAttachmentLabel.ts"],"sourcesContent":["/** Gateway caption used when the user sends files with no typed message. */\nexport const ATTACHED_CAPTION_RE = /^Attached:\\s*(.+)$/i;\n\nexport function isAttachedCaption(text: string | undefined | null): boolean {\n return ATTACHED_CAPTION_RE.test((text ?? '').trim());\n}\n\n/** History-list heading — web uses \"New chat\" for attachment-only first turns. */\nexport function historyAttachmentTitle(title: string): string {\n const trimmed = (title ?? '').trim();\n if (!trimmed || isAttachedCaption(trimmed)) return 'New chat';\n return trimmed;\n}\n\nfunction mimeFromName(name: string): string | undefined {\n if (/\\.pdf$/i.test(name)) return 'application/pdf';\n if (/\\.png$/i.test(name)) return 'image/png';\n if (/\\.jpe?g$/i.test(name)) return 'image/jpeg';\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 if (/\\.svg$/i.test(name)) return 'image/svg+xml';\n return undefined;\n}\n\nexport type CaptionAttachment = {\n id: string;\n name: string;\n type: 'file' | 'screenshot';\n mimeType?: string;\n size?: number;\n url?: string;\n dataUrl?: string;\n};\n\n/**\n * When history has the gateway caption but no `files[]`, still render a\n * paperclip chip (and a thumbnail if a stored URL is present).\n */\nexport function attachmentsFromUserContent<T extends CaptionAttachment>(\n content: string | undefined,\n existing?: T[] | null,\n): T[] {\n if (existing?.length) return existing;\n const match = ATTACHED_CAPTION_RE.exec((content ?? '').trim());\n if (!match) return [];\n const names = match[1]\n .split(',')\n .map((part) => part.trim())\n .filter(Boolean);\n return names.map((name, index) => {\n const mimeType = mimeFromName(name);\n return {\n id: `caption-${index}-${name}`,\n name,\n type: 'file',\n ...(mimeType ? { mimeType } : {}),\n } as T;\n });\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AACO,MAAM,mBAAsB,GAAA;AAC5B,SAAS,kBAAkB,IAA0C,EAAA;AAC1E,EAAA,OAAO,mBAAoB,CAAA,IAAA,CAAA,CAAM,IAAQ,IAAA,IAAA,GAAA,IAAA,GAAA,EAAA,EAAI,MAAM,CAAA;AACrD;AAGO,SAAS,uBAAuB,KAAuB,EAAA;AAC5D,EAAM,MAAA,OAAA,GAAA,CAAW,KAAS,IAAA,IAAA,GAAA,KAAA,GAAA,EAAA,EAAI,IAAK,EAAA;AACnC,EAAA,IAAI,CAAC,OAAA,IAAW,iBAAkB,CAAA,OAAO,GAAU,OAAA,UAAA;AACnD,EAAO,OAAA,OAAA;AACT;AACA,SAAS,aAAa,IAAkC,EAAA;AACtD,EAAA,IAAI,SAAU,CAAA,IAAA,CAAK,IAAI,CAAA,EAAU,OAAA,iBAAA;AACjC,EAAA,IAAI,SAAU,CAAA,IAAA,CAAK,IAAI,CAAA,EAAU,OAAA,WAAA;AACjC,EAAA,IAAI,WAAY,CAAA,IAAA,CAAK,IAAI,CAAA,EAAU,OAAA,YAAA;AACnC,EAAA,IAAI,SAAU,CAAA,IAAA,CAAK,IAAI,CAAA,EAAU,OAAA,WAAA;AACjC,EAAA,IAAI,UAAW,CAAA,IAAA,CAAK,IAAI,CAAA,EAAU,OAAA,YAAA;AAClC,EAAA,IAAI,iBAAkB,CAAA,IAAA,CAAK,IAAI,CAAA,EAAU,OAAA,YAAA;AACzC,EAAA,IAAI,SAAU,CAAA,IAAA,CAAK,IAAI,CAAA,EAAU,OAAA,eAAA;AACjC,EAAO,OAAA,MAAA;AACT;AAegB,SAAA,0BAAA,CAAwD,SAA6B,QAA4B,EAAA;AAC/H,EAAI,IAAA,QAAA,IAAA,IAAA,GAAA,MAAA,GAAA,QAAA,CAAU,QAAe,OAAA,QAAA;AAC7B,EAAA,MAAM,QAAQ,mBAAoB,CAAA,IAAA,CAAA,CAAM,OAAW,IAAA,IAAA,GAAA,OAAA,GAAA,EAAA,EAAI,MAAM,CAAA;AAC7D,EAAI,IAAA,CAAC,KAAO,EAAA,OAAO,EAAC;AACpB,EAAA,MAAM,KAAQ,GAAA,KAAA,CAAM,CAAC,CAAA,CAAE,MAAM,GAAG,CAAA,CAAE,GAAI,CAAA,CAAA,IAAA,KAAQ,IAAK,CAAA,IAAA,EAAM,CAAA,CAAE,OAAO,OAAO,CAAA;AACzE,EAAA,OAAO,KAAM,CAAA,GAAA,CAAI,CAAC,IAAA,EAAM,KAAU,KAAA;AAChC,IAAM,MAAA,QAAA,GAAW,aAAa,IAAI,CAAA;AAClC,IAAO,OAAA,cAAA,CAAA;AAAA,MACL,EAAI,EAAA,CAAA,QAAA,EAAW,KAAK,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AAAA,MAC5B,IAAA;AAAA,MACA,IAAM,EAAA;AAAA,KAAA,EACF,QAAW,GAAA;AAAA,MACb;AAAA,QACE,EAAC,CAAA;AAAA,GAER,CAAA;AACH"}
@@ -0,0 +1,7 @@
1
+ function isImageAttachment(attachment) {
2
+ var _a, _b;
3
+ if (attachment.type === "screenshot") return true;
4
+ if ((_a = attachment.mimeType) == null ? void 0 : _a.startsWith("image/")) return true;
5
+ if ((_b = attachment.dataUrl) == null ? void 0 : _b.startsWith("data:image/")) return true;
6
+ return Boolean(attachment.url && /\.(png|jpe?g|gif|webp|svg)(\?|$)/i.test(attachment.url));
7
+ }export{isImageAttachment};//# sourceMappingURL=isImageAttachment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isImageAttachment.js","sources":["../../../src/features/attachments/isImageAttachment.ts"],"sourcesContent":["import type { MessageAttachment } from '../../hooks/useChatStream';\n\n/** True when the attachment can be shown as a photo rather than a file chip. */\nexport function isImageAttachment(attachment: MessageAttachment): boolean {\n if (attachment.type === 'screenshot') return true;\n if (attachment.mimeType?.startsWith('image/')) return true;\n if (attachment.dataUrl?.startsWith('data:image/')) return true;\n return Boolean(attachment.url && /\\.(png|jpe?g|gif|webp|svg)(\\?|$)/i.test(attachment.url));\n}\n"],"names":[],"mappings":"AAGO,SAAS,kBAAkB,UAAwC,EAAA;AAH1E,EAAA,IAAA,EAAA,EAAA,EAAA;AAIE,EAAI,IAAA,UAAA,CAAW,IAAS,KAAA,YAAA,EAAqB,OAAA,IAAA;AAC7C,EAAA,IAAA,CAAI,EAAW,GAAA,UAAA,CAAA,QAAA,KAAX,IAAqB,GAAA,MAAA,GAAA,EAAA,CAAA,UAAA,CAAW,WAAkB,OAAA,IAAA;AACtD,EAAA,IAAA,CAAI,EAAW,GAAA,UAAA,CAAA,OAAA,KAAX,IAAoB,GAAA,MAAA,GAAA,EAAA,CAAA,UAAA,CAAW,gBAAuB,OAAA,IAAA;AAC1D,EAAA,OAAO,QAAQ,UAAW,CAAA,GAAA,IAAO,oCAAoC,IAAK,CAAA,UAAA,CAAW,GAAG,CAAC,CAAA;AAC3F"}
@@ -0,0 +1,41 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ function attachmentHasPreview(attachment) {
21
+ return Boolean(attachment.dataUrl || attachment.url);
22
+ }
23
+ function preserveAttachmentPreviews(prev, incoming) {
24
+ const unused = [...prev];
25
+ return incoming.map((msg) => {
26
+ var _a;
27
+ if (msg.role !== "user") return msg;
28
+ if (((_a = msg.attachments) != null ? _a : []).some(attachmentHasPreview)) return msg;
29
+ const idx = unused.findIndex((local) => {
30
+ var _a2;
31
+ return local.role === "user" && local.content === msg.content && ((_a2 = local.attachments) != null ? _a2 : []).some(attachmentHasPreview);
32
+ });
33
+ if (idx >= 0) {
34
+ const [local] = unused.splice(idx, 1);
35
+ return __spreadProps(__spreadValues({}, msg), {
36
+ attachments: local.attachments
37
+ });
38
+ }
39
+ return msg;
40
+ });
41
+ }export{preserveAttachmentPreviews};//# sourceMappingURL=preserveAttachmentPreviews.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preserveAttachmentPreviews.js","sources":["../../../src/features/attachments/preserveAttachmentPreviews.ts"],"sourcesContent":["import type { MessageAttachment } from '../../hooks/useChatStream';\n\ntype MessageWithAttachments = {\n role: string;\n content: string;\n attachments?: MessageAttachment[];\n};\n\nfunction attachmentHasPreview(attachment: MessageAttachment): boolean {\n return Boolean(attachment.dataUrl || attachment.url);\n}\n\n/**\n * Backend hydration often has the filename but not the in-memory data URL.\n * Keep the local preview so the bubble doesn't fall back to \"Attached: file.jpg\".\n */\nexport function preserveAttachmentPreviews<T extends MessageWithAttachments>(prev: T[], incoming: T[]): T[] {\n const unused = [...prev];\n return incoming.map((msg) => {\n if (msg.role !== 'user') return msg;\n if ((msg.attachments ?? []).some(attachmentHasPreview)) return msg;\n const idx = unused.findIndex(\n (local) =>\n local.role === 'user' &&\n local.content === msg.content &&\n (local.attachments ?? []).some(attachmentHasPreview),\n );\n if (idx >= 0) {\n const [local] = unused.splice(idx, 1);\n return { ...msg, attachments: local.attachments };\n }\n return msg;\n });\n}\n"],"names":["_a"],"mappings":";;;;;;;;;;;;;;;;;;;AAMA,SAAS,qBAAqB,UAAwC,EAAA;AACpE,EAAA,OAAO,OAAQ,CAAA,UAAA,CAAW,OAAW,IAAA,UAAA,CAAW,GAAG,CAAA;AACrD;AAMgB,SAAA,0BAAA,CAA6D,MAAW,QAAoB,EAAA;AAC1G,EAAM,MAAA,MAAA,GAAS,CAAC,GAAG,IAAI,CAAA;AACvB,EAAO,OAAA,QAAA,CAAS,IAAI,CAAO,GAAA,KAAA;AAhB7B,IAAA,IAAA,EAAA;AAiBI,IAAI,IAAA,GAAA,CAAI,IAAS,KAAA,MAAA,EAAe,OAAA,GAAA;AAChC,IAAK,IAAA,CAAA,CAAA,EAAA,GAAA,GAAA,CAAI,gBAAJ,IAAmB,GAAA,EAAA,GAAA,IAAI,IAAK,CAAA,oBAAoB,GAAU,OAAA,GAAA;AAC/D,IAAM,MAAA,GAAA,GAAM,MAAO,CAAA,SAAA,CAAU,CAAM,KAAA,KAAA;AAnBvC,MAAAA,IAAAA,GAAAA;AAmB0C,MAAA,OAAA,KAAA,CAAM,IAAS,KAAA,MAAA,IAAU,KAAM,CAAA,OAAA,KAAY,IAAI,OAAYA,IAAAA,CAAAA,CAAAA,GAAAA,GAAA,KAAM,CAAA,WAAA,KAAN,IAAAA,GAAAA,GAAAA,GAAqB,EAAC,EAAG,KAAK,oBAAoB,CAAA;AAAA,KAAC,CAAA;AACpJ,IAAA,IAAI,OAAO,CAAG,EAAA;AACZ,MAAA,MAAM,CAAC,KAAK,CAAA,GAAI,MAAO,CAAA,MAAA,CAAO,KAAK,CAAC,CAAA;AACpC,MAAA,OAAO,iCACF,GADE,CAAA,EAAA;AAAA,QAEL,aAAa,KAAM,CAAA;AAAA,OACrB,CAAA;AAAA;AAEF,IAAO,OAAA,GAAA;AAAA,GACR,CAAA;AACH"}
@@ -0,0 +1,102 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ function getBaseUrlFromSignedUrl(signedUrl) {
21
+ return signedUrl.split("?")[0];
22
+ }
23
+ function isRemoteObjectUrl(url) {
24
+ return Boolean(url && /^https:\/\//i.test(url) && !url.startsWith("data:"));
25
+ }
26
+ function loadFileSystem() {
27
+ return require("expo-file-system");
28
+ }
29
+ async function localFileForUpload(attachment) {
30
+ var _a, _b, _c, _d;
31
+ const url = (_a = attachment.url) == null ? void 0 : _a.trim();
32
+ if (url && /^(file:|content:)/i.test(url)) return url;
33
+ const dataUrl = (_b = attachment.dataUrl) != null ? _b : "";
34
+ const base64 = dataUrl.includes("base64,") ? dataUrl.split("base64,")[1] : "";
35
+ if (!base64) return null;
36
+ const fs = loadFileSystem();
37
+ const dir = fs.cacheDirectory;
38
+ if (!dir || typeof fs.writeAsStringAsync !== "function") return null;
39
+ const safeName = (attachment.name || "upload").replace(/[^\w.-]+/g, "_");
40
+ const dest = `${dir}yantra-upload-${Date.now()}-${safeName}`;
41
+ const encoding = (_d = (_c = fs.EncodingType) == null ? void 0 : _c.Base64) != null ? _d : "base64";
42
+ await fs.writeAsStringAsync(dest, base64, {
43
+ encoding
44
+ });
45
+ return dest;
46
+ }
47
+ async function putObject(signedUrl, attachment) {
48
+ var _a, _b;
49
+ const fileUri = await localFileForUpload(attachment);
50
+ if (!fileUri) {
51
+ throw new Error(`No bytes to upload for ${attachment.name}`);
52
+ }
53
+ const mimeType = attachment.mimeType || "application/octet-stream";
54
+ const fs = loadFileSystem();
55
+ if (typeof fs.uploadAsync === "function") {
56
+ const result = await fs.uploadAsync(signedUrl, fileUri, {
57
+ httpMethod: "PUT",
58
+ headers: {
59
+ "Content-Type": mimeType
60
+ },
61
+ uploadType: (_b = (_a = fs.FileSystemUploadType) == null ? void 0 : _a.BINARY_CONTENT) != null ? _b : 0
62
+ });
63
+ if (typeof result.status === "number" && result.status >= 400) {
64
+ throw new Error(`S3 upload failed for "${attachment.name}" with status ${result.status}`);
65
+ }
66
+ return;
67
+ }
68
+ const body = await fetch(fileUri).then((res2) => res2.blob());
69
+ const res = await fetch(signedUrl, {
70
+ method: "PUT",
71
+ headers: {
72
+ "Content-Type": mimeType
73
+ },
74
+ body
75
+ });
76
+ if (!res.ok) {
77
+ throw new Error(`S3 upload failed for "${attachment.name}" with status ${res.status}`);
78
+ }
79
+ }
80
+ async function uploadChatAttachments(attachments, createFileUploadLinks) {
81
+ const uploadEntries = attachments.map((attachment, index) => ({
82
+ attachment,
83
+ index
84
+ })).filter(({
85
+ attachment
86
+ }) => !isRemoteObjectUrl(attachment.url));
87
+ let signedUrls = [];
88
+ if (uploadEntries.length > 0) {
89
+ const filenames = uploadEntries.map(({
90
+ attachment
91
+ }) => attachment.name || `file-${Date.now()}`);
92
+ signedUrls = await createFileUploadLinks(filenames);
93
+ if (signedUrls.length !== uploadEntries.length) {
94
+ throw new Error(`Expected ${uploadEntries.length} signed URLs but received ${signedUrls.length}`);
95
+ }
96
+ await Promise.all(uploadEntries.map((entry, i) => putObject(signedUrls[i], entry.attachment)));
97
+ }
98
+ const urlByIndex = new Map(uploadEntries.map((entry, i) => [entry.index, getBaseUrlFromSignedUrl(signedUrls[i])]));
99
+ return attachments.map((attachment, index) => __spreadProps(__spreadValues({}, attachment), {
100
+ objectUrl: isRemoteObjectUrl(attachment.url) ? attachment.url : urlByIndex.get(index)
101
+ }));
102
+ }export{uploadChatAttachments};//# sourceMappingURL=uploadChatAttachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadChatAttachments.js","sources":["../../../src/features/attachments/uploadChatAttachments.ts"],"sourcesContent":["import type { MessageAttachment } from '../../hooks/useChatStream';\n\nfunction getBaseUrlFromSignedUrl(signedUrl: string): string {\n return signedUrl.split('?')[0];\n}\n\nfunction isRemoteObjectUrl(url: string | undefined): boolean {\n return Boolean(url && /^https:\\/\\//i.test(url) && !url.startsWith('data:'));\n}\n\nfunction loadFileSystem(): {\n cacheDirectory?: string | null;\n writeAsStringAsync?: (uri: string, data: string, options?: { encoding?: string }) => Promise<void>;\n uploadAsync?: (\n url: string,\n fileUri: string,\n options?: { httpMethod?: string; headers?: Record<string, string>; uploadType?: number },\n ) => Promise<{ status?: number }>;\n EncodingType?: { Base64?: string };\n FileSystemUploadType?: { BINARY_CONTENT?: number };\n} {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n return require('expo-file-system') as ReturnType<typeof loadFileSystem>;\n}\n\nasync function localFileForUpload(attachment: MessageAttachment): Promise<string | null> {\n const url = attachment.url?.trim();\n if (url && /^(file:|content:)/i.test(url)) return url;\n\n const dataUrl = attachment.dataUrl ?? '';\n const base64 = dataUrl.includes('base64,') ? dataUrl.split('base64,')[1] : '';\n if (!base64) return null;\n\n const fs = loadFileSystem();\n const dir = fs.cacheDirectory;\n if (!dir || typeof fs.writeAsStringAsync !== 'function') return null;\n const safeName = (attachment.name || 'upload').replace(/[^\\w.-]+/g, '_');\n const dest = `${dir}yantra-upload-${Date.now()}-${safeName}`;\n const encoding = fs.EncodingType?.Base64 ?? 'base64';\n await fs.writeAsStringAsync(dest, base64, { encoding });\n return dest;\n}\n\nasync function putObject(signedUrl: string, attachment: MessageAttachment): Promise<void> {\n const fileUri = await localFileForUpload(attachment);\n if (!fileUri) {\n throw new Error(`No bytes to upload for ${attachment.name}`);\n }\n const mimeType = attachment.mimeType || 'application/octet-stream';\n const fs = loadFileSystem();\n if (typeof fs.uploadAsync === 'function') {\n const result = await fs.uploadAsync(signedUrl, fileUri, {\n httpMethod: 'PUT',\n headers: { 'Content-Type': mimeType },\n uploadType: fs.FileSystemUploadType?.BINARY_CONTENT ?? 0,\n });\n if (typeof result.status === 'number' && result.status >= 400) {\n throw new Error(`S3 upload failed for \"${attachment.name}\" with status ${result.status}`);\n }\n return;\n }\n const body = await fetch(fileUri).then((res) => res.blob());\n const res = await fetch(signedUrl, {\n method: 'PUT',\n headers: { 'Content-Type': mimeType },\n body,\n });\n if (!res.ok) {\n throw new Error(`S3 upload failed for \"${attachment.name}\" with status ${res.status}`);\n }\n}\n\n/**\n * Browser `buildAttachmentsForMessage`: PUT each local file to a presigned\n * chat-attachment URL and return copies with the bare S3 object URL set.\n * Local `file:` / `data:` preview fields are left intact on the original objects.\n */\nexport async function uploadChatAttachments(\n attachments: MessageAttachment[],\n createFileUploadLinks: (filenames: string[]) => Promise<string[]>,\n): Promise<Array<MessageAttachment & { objectUrl?: string }>> {\n const uploadEntries = attachments\n .map((attachment, index) => ({ attachment, index }))\n .filter(({ attachment }) => !isRemoteObjectUrl(attachment.url));\n\n let signedUrls: string[] = [];\n if (uploadEntries.length > 0) {\n const filenames = uploadEntries.map(({ attachment }) => attachment.name || `file-${Date.now()}`);\n signedUrls = await createFileUploadLinks(filenames);\n if (signedUrls.length !== uploadEntries.length) {\n throw new Error(`Expected ${uploadEntries.length} signed URLs but received ${signedUrls.length}`);\n }\n await Promise.all(uploadEntries.map((entry, i) => putObject(signedUrls[i], entry.attachment)));\n }\n\n const urlByIndex = new Map(uploadEntries.map((entry, i) => [entry.index, getBaseUrlFromSignedUrl(signedUrls[i])]));\n\n return attachments.map((attachment, index) => ({\n ...attachment,\n objectUrl: isRemoteObjectUrl(attachment.url) ? attachment.url : urlByIndex.get(index),\n }));\n}\n"],"names":["res"],"mappings":"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"}
@@ -25,7 +25,7 @@ function base64Bytes(base64) {
25
25
  return Math.floor(base64.length * 3 / 4) - padding;
26
26
  }
27
27
  function toAttachment(asset, index) {
28
- var _a, _b, _c;
28
+ var _a, _b, _c, _d;
29
29
  const base64 = (_a = asset.base64) == null ? void 0 : _a.trim();
30
30
  if (!base64) return null;
31
31
  const size = base64Bytes(base64);
@@ -39,20 +39,83 @@ function toAttachment(asset, index) {
39
39
  type: "file",
40
40
  mimeType,
41
41
  dataUrl: `data:${mimeType};base64,${base64}`,
42
+ url: ((_d = asset.uri) == null ? void 0 : _d.trim()) || void 0,
42
43
  size
43
44
  };
44
45
  }
45
46
  function loadPicker() {
46
47
  return require("expo-image-picker");
47
48
  }
49
+ function loadDocumentPicker() {
50
+ var _a, _b, _c;
51
+ const core = require("expo-modules-core");
52
+ 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");
53
+ if (!(native == null ? void 0 : native.getDocumentAsync)) {
54
+ throw new Error("File picker is not available in this build.");
55
+ }
56
+ return {
57
+ getDocumentAsync: (options) => {
58
+ var _a2, _b2, _c2;
59
+ return native.getDocumentAsync({
60
+ type: Array.isArray(options.type) ? options.type : [(_a2 = options.type) != null ? _a2 : "*/*"],
61
+ multiple: (_b2 = options.multiple) != null ? _b2 : false,
62
+ copyToCacheDirectory: (_c2 = options.copyToCacheDirectory) != null ? _c2 : true
63
+ });
64
+ }
65
+ };
66
+ }
67
+ function loadFileSystem() {
68
+ return require("expo-file-system");
69
+ }
70
+ const DOCUMENT_MIME_TYPES = ["application/pdf", "text/plain", "text/markdown", "text/csv", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "image/*"];
71
+ const MAX_FILES = 6;
72
+ const MAX_DOCUMENT_BYTES = 15 * 1024 * 1024;
73
+ function normalizeDocumentAssets(result) {
74
+ if (!result || result.canceled || result.type === "cancel") return [];
75
+ if (Array.isArray(result.assets) && result.assets.length > 0) return result.assets;
76
+ if (result.uri) {
77
+ return [{
78
+ uri: result.uri,
79
+ name: result.name,
80
+ mimeType: result.mimeType,
81
+ size: result.size
82
+ }];
83
+ }
84
+ return [];
85
+ }
86
+ async function documentToAttachment(asset, index) {
87
+ var _a, _b, _c, _d, _e;
88
+ const uri = (_a = asset.uri) == null ? void 0 : _a.trim();
89
+ if (!uri) return null;
90
+ if (typeof asset.size === "number" && asset.size > MAX_DOCUMENT_BYTES) return null;
91
+ const fs = loadFileSystem();
92
+ const encoding = (_c = (_b = fs.EncodingType) == null ? void 0 : _b.Base64) != null ? _c : "base64";
93
+ if (typeof fs.readAsStringAsync !== "function") return null;
94
+ const base64 = (await fs.readAsStringAsync(uri, {
95
+ encoding
96
+ })).trim();
97
+ if (!base64) return null;
98
+ const size = typeof asset.size === "number" && asset.size > 0 ? asset.size : base64Bytes(base64);
99
+ if (size < MIN_BYTES || size > MAX_DOCUMENT_BYTES) return null;
100
+ const mimeType = ((_d = asset.mimeType) == null ? void 0 : _d.trim()) || "application/octet-stream";
101
+ const name = ((_e = asset.name) == null ? void 0 : _e.trim()) || `file-${Date.now()}-${index}`;
102
+ return {
103
+ id: `file-${Date.now()}-${index}`,
104
+ name,
105
+ type: "file",
106
+ mimeType,
107
+ dataUrl: `data:${mimeType};base64,${base64}`,
108
+ url: uri,
109
+ size
110
+ };
111
+ }
48
112
  function useImageAttachments() {
49
113
  const [pending, setPending] = useState([]);
50
- const [busy, setBusy] = useState(false);
114
+ const [busyKind, setBusyKind] = useState(null);
51
115
  const [error, setError] = useState(null);
52
116
  const run = useCallback(async (mode) => {
53
117
  var _a;
54
118
  setError(null);
55
- setBusy(true);
56
119
  try {
57
120
  const picker = loadPicker();
58
121
  const permission = mode === "camera" ? await picker.requestCameraPermissionsAsync() : await picker.requestMediaLibraryPermissionsAsync();
@@ -60,6 +123,7 @@ function useImageAttachments() {
60
123
  setError(mode === "camera" ? "Camera access is off for Yantra. Turn it on in Settings to attach a photo." : "Photo access is off for Yantra. Turn it on in Settings to attach an image.");
61
124
  return;
62
125
  }
126
+ setBusyKind(mode);
63
127
  const options = {
64
128
  // `base64` is what the message carries; `quality` + the picker's
65
129
  // own resize keep it small enough to travel inline.
@@ -81,26 +145,57 @@ function useImageAttachments() {
81
145
  setError("That image was too large to attach. Try a smaller one.");
82
146
  return;
83
147
  }
84
- setPending((prev) => [...prev, ...mapped]);
148
+ setPending((prev) => [...prev, ...mapped].slice(0, MAX_FILES));
85
149
  } catch (e) {
86
150
  setError(e instanceof Error ? e.message : "Could not attach that image.");
87
151
  } finally {
88
- setBusy(false);
152
+ setBusyKind(null);
89
153
  }
90
154
  }, []);
91
155
  const pickFromLibrary = useCallback(() => run("library"), [run]);
92
156
  const captureFromCamera = useCallback(() => run("camera"), [run]);
157
+ const pickDocuments = useCallback(async () => {
158
+ setError(null);
159
+ try {
160
+ const picker = loadDocumentPicker();
161
+ setBusyKind("documents");
162
+ const result = await picker.getDocumentAsync({
163
+ type: DOCUMENT_MIME_TYPES,
164
+ multiple: true,
165
+ copyToCacheDirectory: true
166
+ });
167
+ const assets = normalizeDocumentAssets(result);
168
+ if (assets.length === 0) return;
169
+ const mapped = [];
170
+ for (let i = 0; i < assets.length; i += 1) {
171
+ const next = await documentToAttachment(assets[i], i);
172
+ if (next) mapped.push(next);
173
+ }
174
+ if (mapped.length === 0) {
175
+ setError("That file was too large to attach. Try a smaller one.");
176
+ return;
177
+ }
178
+ setPending((prev) => [...prev, ...mapped].slice(0, MAX_FILES));
179
+ } catch (e) {
180
+ setError(e instanceof Error ? e.message : "Could not attach that file.");
181
+ } finally {
182
+ setBusyKind(null);
183
+ }
184
+ }, []);
93
185
  const remove = useCallback((id) => setPending((prev) => prev.filter((a) => a.id !== id)), []);
94
186
  const clear = useCallback(() => setPending([]), []);
95
- const forSend = useCallback(() => pending.length > 0 ? pending : void 0, [pending]);
187
+ const forSend = useCallback(() => pending.length > 0 ? pending.map((a) => __spreadValues({}, a)) : void 0, [pending]);
188
+ const busy = busyKind !== null;
96
189
  return useMemo(() => ({
97
190
  pending,
98
191
  busy,
192
+ busyKind,
99
193
  error,
100
194
  pickFromLibrary,
101
195
  captureFromCamera,
196
+ pickDocuments,
102
197
  remove,
103
198
  clear,
104
199
  forSend
105
- }), [pending, busy, error, pickFromLibrary, captureFromCamera, remove, clear, forSend]);
200
+ }), [pending, busy, busyKind, error, pickFromLibrary, captureFromCamera, pickDocuments, remove, clear, forSend]);
106
201
  }export{useImageAttachments};//# sourceMappingURL=useImageAttachments.js.map