@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.
- package/lib/components/KeyboardComposerDock.js +10 -1
- package/lib/components/KeyboardComposerDock.js.map +1 -1
- package/lib/components/NavigationHeader/NavigationHeader.js +15 -16
- package/lib/components/NavigationHeader/NavigationHeader.js.map +1 -1
- package/lib/features/apps/AppsCatalog.js +2 -1
- package/lib/features/apps/AppsCatalog.js.map +1 -1
- package/lib/features/apps/resolveSurfaceUrl.js +22 -2
- package/lib/features/apps/resolveSurfaceUrl.js.map +1 -1
- package/lib/features/attachments/ComposerAttachmentBar.js +64 -0
- package/lib/features/attachments/ComposerAttachmentBar.js.map +1 -0
- package/lib/features/attachments/MessageAttachmentPreviews.js +77 -0
- package/lib/features/attachments/MessageAttachmentPreviews.js.map +1 -0
- package/lib/features/attachments/attachmentPreviewUri.js +9 -0
- package/lib/features/attachments/attachmentPreviewUri.js.map +1 -0
- package/lib/features/attachments/buildGatewayMedia.js +118 -0
- package/lib/features/attachments/buildGatewayMedia.js.map +1 -0
- package/lib/features/attachments/composerAttachmentHandoff.js +15 -0
- package/lib/features/attachments/composerAttachmentHandoff.js.map +1 -0
- package/lib/features/attachments/displayUserMessageText.js +19 -0
- package/lib/features/attachments/displayUserMessageText.js.map +1 -0
- package/lib/features/attachments/historyAttachmentLabel.js +51 -0
- package/lib/features/attachments/historyAttachmentLabel.js.map +1 -0
- package/lib/features/attachments/isImageAttachment.js +7 -0
- package/lib/features/attachments/isImageAttachment.js.map +1 -0
- package/lib/features/attachments/preserveAttachmentPreviews.js +41 -0
- package/lib/features/attachments/preserveAttachmentPreviews.js.map +1 -0
- package/lib/features/attachments/uploadChatAttachments.js +102 -0
- package/lib/features/attachments/uploadChatAttachments.js.map +1 -0
- package/lib/features/attachments/useImageAttachments.js +102 -7
- package/lib/features/attachments/useImageAttachments.js.map +1 -1
- package/lib/features/canvas/nativeViewerRegistry.js +8 -1
- package/lib/features/canvas/nativeViewerRegistry.js.map +1 -1
- package/lib/features/canvas/surfaceIndex.js +25 -0
- package/lib/features/canvas/surfaceIndex.js.map +1 -0
- package/lib/features/canvas/useCanvasSession.js +57 -0
- package/lib/features/canvas/useCanvasSession.js.map +1 -0
- package/lib/features/chat/ChatTranscript.js +202 -0
- package/lib/features/chat/ChatTranscript.js.map +1 -0
- package/lib/hooks/useCdecliChannel.js +5 -3
- package/lib/hooks/useCdecliChannel.js.map +1 -1
- package/lib/hooks/useChatApi.js +32 -15
- package/lib/hooks/useChatApi.js.map +1 -1
- package/lib/hooks/useChatStream.js +35 -8
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/screens/CanvasBoard/index.js +20 -15
- package/lib/screens/CanvasBoard/index.js.map +1 -1
- package/lib/screens/Chat/index.js +39 -66
- package/lib/screens/Chat/index.js.map +1 -1
- package/lib/screens/ChatHistory/index.js +2 -8
- package/lib/screens/ChatHistory/index.js.map +1 -1
- package/lib/screens/Home/HomeScreen.js +57 -21
- package/lib/screens/Home/HomeScreen.js.map +1 -1
- package/lib/screens/Home/components/ChatHistoryLanding.js +15 -9
- package/lib/screens/Home/components/ChatHistoryLanding.js.map +1 -1
- package/lib/screens/WebBuilder/index.js.map +1 -1
- package/lib/utils/navigateToNewChatHome.js +40 -0
- package/lib/utils/navigateToNewChatHome.js.map +1 -0
- package/package.json +3 -3
- package/lib/features/canvas/canvasCore.js +0 -41
- 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 [
|
|
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
|
-
|
|
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
|