@adminide-stack/yantra-mobile 12.0.44-alpha.15 → 12.0.44-alpha.18
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/bridge/handlers/NavigationBridgeHandler.js +3 -1
- package/lib/bridge/handlers/NavigationBridgeHandler.js.map +1 -1
- package/lib/components/CustomDrawer.js +122 -82
- package/lib/components/CustomDrawer.js.map +1 -1
- package/lib/components/NavigationHeader/NavigationHeader.js +16 -30
- package/lib/components/NavigationHeader/NavigationHeader.js.map +1 -1
- package/lib/compute.js +54 -5
- package/lib/compute.js.map +1 -1
- package/lib/features/apps/AppsCatalog.js +200 -0
- package/lib/features/apps/AppsCatalog.js.map +1 -0
- 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 +198 -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/routes.json +56 -4
- package/lib/screens/Apps/index.js +20 -0
- package/lib/screens/Apps/index.js.map +1 -0
- package/lib/screens/CanvasBoard/index.js +20 -15
- package/lib/screens/CanvasBoard/index.js.map +1 -1
- package/lib/screens/Chat/index.js +39 -62
- 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 +2 -2
- package/lib/components/AppsMenu/AppsMenu.js +0 -295
- package/lib/components/AppsMenu/AppsMenu.js.map +0 -1
- package/lib/components/NavigationHeader/ChatHistoryToggleMobile.js +0 -6
- package/lib/components/NavigationHeader/ChatHistoryToggleMobile.js.map +0 -1
- package/lib/features/canvas/canvasCore.js +0 -41
- package/lib/features/canvas/canvasCore.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveSurfaceUrl.js","sources":["../../../src/features/apps/resolveSurfaceUrl.ts"],"sourcesContent":["/**\n * Which URL the shared WebView surface shell should open for a launcher tile.\n *\n * Explicit env key wins (the surfaces with a dedicated var, which are the\n * ones run locally), then the `{slug}` template that covers every deployed\n * surface. Returning undefined means \"no surface\" — the caller falls back to\n * seeding the tile's chat prompt, which is what every tile did before.\n */\nimport type { MobileAppItem } from './mobileAppCatalog';\n\nexport interface SurfaceUrlConfig {\n WEB_BUILDER_URL?: string;\n CANVAS_SURFACE_URL?: string;\n DESIGN_SURFACE_URL?: string;\n SURFACE_URL_TEMPLATE?: string;\n}\n\nexport function resolveSurfaceUrl(app: MobileAppItem, config: SurfaceUrlConfig): string | undefined {\n // Only the shared WebView shell needs a URL - a tile with its own native\n // screen (nativeRoute 'canvas-native') resolves nothing by design.\n if (app.nativeRoute !== 'webbuilder') return undefined;\n\n if (app.surfaceUrlKey) {\n const explicit = config[app.surfaceUrlKey]?.trim();\n\n if (explicit) return explicit;\n }\n\n const template = config.SURFACE_URL_TEMPLATE?.trim();\n\n if (!template || !app.surfaceSlug) return undefined;\n // A template without the placeholder would silently point every surface at\n // the same origin, which is worse than opening none.\n if (!template.includes('{slug}')) return undefined;\n\n return template.replace(/\\{slug\\}/g, app.surfaceSlug);\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolveSurfaceUrl.js","sources":["../../../src/features/apps/resolveSurfaceUrl.ts"],"sourcesContent":["/**\n * Which URL the shared WebView surface shell should open for a launcher tile.\n *\n * Explicit env key wins (the surfaces with a dedicated var, which are the\n * ones run locally), then the `{slug}` template that covers every deployed\n * surface. Returning undefined means \"no surface\" — the caller falls back to\n * seeding the tile's chat prompt, which is what every tile did before.\n */\nimport type { MobileAppItem } from './mobileAppCatalog';\nimport { MOBILE_APP_CATALOG } from './mobileAppCatalog';\n\nexport interface SurfaceUrlConfig {\n WEB_BUILDER_URL?: string;\n CANVAS_SURFACE_URL?: string;\n DESIGN_SURFACE_URL?: string;\n SURFACE_URL_TEMPLATE?: string;\n}\n\nexport function resolveSurfaceUrl(app: MobileAppItem, config: SurfaceUrlConfig): string | undefined {\n // Only the shared WebView shell needs a URL - a tile with its own native\n // screen (nativeRoute 'canvas-native') resolves nothing by design.\n if (app.nativeRoute !== 'webbuilder') return undefined;\n\n if (app.surfaceUrlKey) {\n const explicit = config[app.surfaceUrlKey]?.trim();\n\n if (explicit) return explicit;\n }\n\n const template = config.SURFACE_URL_TEMPLATE?.trim();\n\n if (!template || !app.surfaceSlug) return undefined;\n // A template without the placeholder would silently point every surface at\n // the same origin, which is worse than opening none.\n if (!template.includes('{slug}')) return undefined;\n\n return template.replace(/\\{slug\\}/g, app.surfaceSlug);\n}\n\nfunction originKey(url: string): string | null {\n try {\n const parsed = new URL(url);\n return `${parsed.protocol}//${parsed.host}`.toLowerCase();\n } catch {\n const trimmed = url.trim().replace(/\\/+$/, '');\n return trimmed ? trimmed.toLowerCase() : null;\n }\n}\n\n/**\n * Header title for the shared WebView shell. Matches the launcher tile whose\n * surface URL is this origin (Canvas, Design, …) instead of always \"Web Builder\".\n */\nexport function resolveAppTitleFromSurfaceUrl(url: string | null | undefined, config: SurfaceUrlConfig): string | null {\n if (typeof url !== 'string' || !url.trim()) return null;\n const needle = originKey(url);\n if (!needle) return null;\n\n for (const app of MOBILE_APP_CATALOG) {\n const surface = resolveSurfaceUrl(app, config);\n if (!surface) continue;\n if (originKey(surface) === needle) return app.label;\n }\n return null;\n}\n"],"names":[],"mappings":"uDAgBgB,SAAA,iBAAA,CAAkB,KAAoB,MAA8C,EAAA;AAhBpG,EAAA,IAAA,EAAA,EAAA,EAAA;AAmBE,EAAI,IAAA,GAAA,CAAI,WAAgB,KAAA,YAAA,EAAqB,OAAA,MAAA;AAC7C,EAAA,IAAI,IAAI,aAAe,EAAA;AACrB,IAAA,MAAM,QAAW,GAAA,CAAA,EAAA,GAAA,MAAA,CAAO,GAAI,CAAA,aAAa,MAAxB,IAA2B,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AAC5C,IAAA,IAAI,UAAiB,OAAA,QAAA;AAAA;AAEvB,EAAM,MAAA,QAAA,GAAA,CAAW,EAAO,GAAA,MAAA,CAAA,oBAAA,KAAP,IAA6B,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AAC9C,EAAA,IAAI,CAAC,QAAA,IAAY,CAAC,GAAA,CAAI,aAAoB,OAAA,MAAA;AAG1C,EAAA,IAAI,CAAC,QAAA,CAAS,QAAS,CAAA,QAAQ,GAAU,OAAA,MAAA;AACzC,EAAA,OAAO,QAAS,CAAA,OAAA,CAAQ,WAAa,EAAA,GAAA,CAAI,WAAW,CAAA;AACtD;AACA,SAAS,UAAU,GAA4B,EAAA;AAC7C,EAAI,IAAA;AACF,IAAM,MAAA,MAAA,GAAS,IAAI,GAAA,CAAI,GAAG,CAAA;AAC1B,IAAA,OAAO,GAAG,MAAO,CAAA,QAAQ,KAAK,MAAO,CAAA,IAAI,GAAG,WAAY,EAAA;AAAA,GAClD,CAAA,OAAA,CAAA,EAAA;AACN,IAAA,MAAM,UAAU,GAAI,CAAA,IAAA,EAAO,CAAA,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAC7C,IAAO,OAAA,OAAA,GAAU,OAAQ,CAAA,WAAA,EAAgB,GAAA,IAAA;AAAA;AAE7C;AAMgB,SAAA,6BAAA,CAA8B,KAAgC,MAAyC,EAAA;AACrH,EAAA,IAAI,OAAO,GAAQ,KAAA,QAAA,IAAY,CAAC,GAAI,CAAA,IAAA,IAAe,OAAA,IAAA;AACnD,EAAM,MAAA,MAAA,GAAS,UAAU,GAAG,CAAA;AAC5B,EAAI,IAAA,CAAC,QAAe,OAAA,IAAA;AACpB,EAAA,KAAA,MAAW,OAAO,kBAAoB,EAAA;AACpC,IAAM,MAAA,OAAA,GAAU,iBAAkB,CAAA,GAAA,EAAK,MAAM,CAAA;AAC7C,IAAA,IAAI,CAAC,OAAS,EAAA;AACd,IAAA,IAAI,SAAU,CAAA,OAAO,CAAM,KAAA,MAAA,SAAe,GAAI,CAAA,KAAA;AAAA;AAEhD,EAAO,OAAA,IAAA;AACT"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {jsx,jsxs}from'react/jsx-runtime';import {ScrollView,StyleSheet,View,Image,Text,Pressable}from'react-native';import {Ionicons}from'@expo/vector-icons';import {attachmentPreviewUri}from'./attachmentPreviewUri.js';import {mobileTokens}from'../../theme/mobileTokens.js';function ComposerAttachmentBar({
|
|
2
|
+
attachments,
|
|
3
|
+
onRemove,
|
|
4
|
+
isDark
|
|
5
|
+
}) {
|
|
6
|
+
if (attachments.length === 0) return null;
|
|
7
|
+
const border = isDark ? "rgba(148,163,184,0.28)" : mobileTokens.color.border;
|
|
8
|
+
const surface = isDark ? "#1e293b" : mobileTokens.color.surface;
|
|
9
|
+
const ink = isDark ? "#e2e8f0" : mobileTokens.color.text;
|
|
10
|
+
const muted = isDark ? "#94a3b8" : mobileTokens.color.textMuted;
|
|
11
|
+
return /* @__PURE__ */ jsx(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, contentContainerStyle: styles.row, keyboardShouldPersistTaps: "handled", children: attachments.map((attachment) => {
|
|
12
|
+
const preview = attachmentPreviewUri(attachment);
|
|
13
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.chip, {
|
|
14
|
+
backgroundColor: surface,
|
|
15
|
+
borderColor: border
|
|
16
|
+
}], children: [
|
|
17
|
+
preview ? /* @__PURE__ */ jsx(Image, { source: {
|
|
18
|
+
uri: preview
|
|
19
|
+
}, style: styles.thumb }) : /* @__PURE__ */ jsx(View, { style: [styles.fileIcon, {
|
|
20
|
+
backgroundColor: isDark ? "#334155" : "#eef2ff"
|
|
21
|
+
}], children: /* @__PURE__ */ jsx(Ionicons, { name: "document-outline", size: 16, color: muted }) }),
|
|
22
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.name, {
|
|
23
|
+
color: ink
|
|
24
|
+
}], numberOfLines: 1, children: attachment.name }),
|
|
25
|
+
/* @__PURE__ */ jsx(Pressable, { onPress: () => onRemove(attachment.id), hitSlop: 8, accessibilityRole: "button", accessibilityLabel: `Remove ${attachment.name}`, children: /* @__PURE__ */ jsx(Ionicons, { name: "close-circle", size: 18, color: muted }) })
|
|
26
|
+
] }, attachment.id);
|
|
27
|
+
}) });
|
|
28
|
+
}
|
|
29
|
+
const styles = StyleSheet.create({
|
|
30
|
+
row: {
|
|
31
|
+
flexDirection: "row",
|
|
32
|
+
alignItems: "center",
|
|
33
|
+
gap: 8,
|
|
34
|
+
paddingBottom: 8
|
|
35
|
+
},
|
|
36
|
+
chip: {
|
|
37
|
+
flexDirection: "row",
|
|
38
|
+
alignItems: "center",
|
|
39
|
+
gap: 8,
|
|
40
|
+
maxWidth: 180,
|
|
41
|
+
paddingVertical: 6,
|
|
42
|
+
paddingLeft: 6,
|
|
43
|
+
paddingRight: 8,
|
|
44
|
+
borderRadius: 12,
|
|
45
|
+
borderWidth: StyleSheet.hairlineWidth
|
|
46
|
+
},
|
|
47
|
+
thumb: {
|
|
48
|
+
width: 32,
|
|
49
|
+
height: 32,
|
|
50
|
+
borderRadius: 8
|
|
51
|
+
},
|
|
52
|
+
fileIcon: {
|
|
53
|
+
width: 32,
|
|
54
|
+
height: 32,
|
|
55
|
+
borderRadius: 8,
|
|
56
|
+
alignItems: "center",
|
|
57
|
+
justifyContent: "center"
|
|
58
|
+
},
|
|
59
|
+
name: {
|
|
60
|
+
flexShrink: 1,
|
|
61
|
+
fontSize: 12,
|
|
62
|
+
fontWeight: "600"
|
|
63
|
+
}
|
|
64
|
+
});export{ComposerAttachmentBar};//# sourceMappingURL=ComposerAttachmentBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComposerAttachmentBar.js","sources":["../../../src/features/attachments/ComposerAttachmentBar.tsx"],"sourcesContent":["import React from 'react';\nimport { Image, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';\nimport { Ionicons } from '@expo/vector-icons';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\nimport { attachmentPreviewUri } from './attachmentPreviewUri';\nimport { mobileTokens } from '../../theme/mobileTokens';\n\nexport function ComposerAttachmentBar({\n attachments,\n onRemove,\n isDark,\n}: {\n attachments: MessageAttachment[];\n onRemove: (id: string) => void;\n isDark: boolean;\n}) {\n if (attachments.length === 0) return null;\n\n const border = isDark ? 'rgba(148,163,184,0.28)' : mobileTokens.color.border;\n const surface = isDark ? '#1e293b' : mobileTokens.color.surface;\n const ink = isDark ? '#e2e8f0' : mobileTokens.color.text;\n const muted = isDark ? '#94a3b8' : mobileTokens.color.textMuted;\n\n return (\n <ScrollView\n horizontal\n showsHorizontalScrollIndicator={false}\n contentContainerStyle={styles.row}\n keyboardShouldPersistTaps=\"handled\"\n >\n {attachments.map((attachment) => {\n const preview = attachmentPreviewUri(attachment);\n return (\n <View key={attachment.id} style={[styles.chip, { backgroundColor: surface, borderColor: border }]}>\n {preview ? (\n <Image source={{ uri: preview }} style={styles.thumb} />\n ) : (\n <View style={[styles.fileIcon, { backgroundColor: isDark ? '#334155' : '#eef2ff' }]}>\n <Ionicons name=\"document-outline\" size={16} color={muted} />\n </View>\n )}\n <Text style={[styles.name, { color: ink }]} numberOfLines={1}>\n {attachment.name}\n </Text>\n <Pressable\n onPress={() => onRemove(attachment.id)}\n hitSlop={8}\n accessibilityRole=\"button\"\n accessibilityLabel={`Remove ${attachment.name}`}\n >\n <Ionicons name=\"close-circle\" size={18} color={muted} />\n </Pressable>\n </View>\n );\n })}\n </ScrollView>\n );\n}\n\nconst styles = StyleSheet.create({\n row: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 8,\n paddingBottom: 8,\n },\n chip: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 8,\n maxWidth: 180,\n paddingVertical: 6,\n paddingLeft: 6,\n paddingRight: 8,\n borderRadius: 12,\n borderWidth: StyleSheet.hairlineWidth,\n },\n thumb: {\n width: 32,\n height: 32,\n borderRadius: 8,\n },\n fileIcon: {\n width: 32,\n height: 32,\n borderRadius: 8,\n alignItems: 'center',\n justifyContent: 'center',\n },\n name: {\n flexShrink: 1,\n fontSize: 12,\n fontWeight: '600',\n },\n});\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {jsx,jsxs}from'react/jsx-runtime';import {View,StyleSheet,Image,Text}from'react-native';import {Ionicons}from'@expo/vector-icons';import {attachmentPreviewUri}from'./attachmentPreviewUri.js';import {mobileTokens}from'../../theme/mobileTokens.js';function MessageAttachmentPreviews({
|
|
2
|
+
attachments,
|
|
3
|
+
align = "end",
|
|
4
|
+
isDark = false
|
|
5
|
+
}) {
|
|
6
|
+
if (attachments.length === 0) return null;
|
|
7
|
+
const border = isDark ? "rgba(148,163,184,0.28)" : mobileTokens.color.border;
|
|
8
|
+
const surface = isDark ? "#1e293b" : "#ffffff";
|
|
9
|
+
const ink = isDark ? "#e2e8f0" : mobileTokens.color.text;
|
|
10
|
+
const muted = isDark ? "#94a3b8" : mobileTokens.color.textMuted;
|
|
11
|
+
return /* @__PURE__ */ jsx(View, { style: [styles.wrap, align === "end" ? styles.wrapEnd : styles.wrapStart], children: attachments.map((attachment) => {
|
|
12
|
+
const uri = attachmentPreviewUri(attachment);
|
|
13
|
+
if (uri) {
|
|
14
|
+
return /* @__PURE__ */ jsx(Image, { source: {
|
|
15
|
+
uri
|
|
16
|
+
}, style: styles.image, resizeMode: "cover", accessibilityLabel: attachment.name }, attachment.id);
|
|
17
|
+
}
|
|
18
|
+
return /* @__PURE__ */ jsxs(View, { style: [styles.fileChip, {
|
|
19
|
+
backgroundColor: surface,
|
|
20
|
+
borderColor: border
|
|
21
|
+
}], accessibilityLabel: `Attached ${attachment.name}`, children: [
|
|
22
|
+
/* @__PURE__ */ jsx(View, { style: [styles.fileIcon, {
|
|
23
|
+
backgroundColor: isDark ? "#334155" : "#eef2ff"
|
|
24
|
+
}], children: /* @__PURE__ */ jsx(Ionicons, { name: "attach", size: 16, color: muted }) }),
|
|
25
|
+
/* @__PURE__ */ jsx(Text, { style: [styles.fileName, {
|
|
26
|
+
color: ink
|
|
27
|
+
}], numberOfLines: 1, children: attachment.name })
|
|
28
|
+
] }, attachment.id);
|
|
29
|
+
}) });
|
|
30
|
+
}
|
|
31
|
+
const styles = StyleSheet.create({
|
|
32
|
+
wrap: {
|
|
33
|
+
flexDirection: "row",
|
|
34
|
+
flexWrap: "wrap",
|
|
35
|
+
gap: 8,
|
|
36
|
+
marginBottom: 8,
|
|
37
|
+
maxWidth: "92%"
|
|
38
|
+
},
|
|
39
|
+
wrapEnd: {
|
|
40
|
+
justifyContent: "flex-end",
|
|
41
|
+
alignSelf: "flex-end"
|
|
42
|
+
},
|
|
43
|
+
wrapStart: {
|
|
44
|
+
justifyContent: "flex-start",
|
|
45
|
+
alignSelf: "flex-start"
|
|
46
|
+
},
|
|
47
|
+
image: {
|
|
48
|
+
width: 168,
|
|
49
|
+
height: 168,
|
|
50
|
+
borderRadius: 16,
|
|
51
|
+
backgroundColor: "#e2e8f0",
|
|
52
|
+
overflow: "hidden"
|
|
53
|
+
},
|
|
54
|
+
fileChip: {
|
|
55
|
+
flexDirection: "row",
|
|
56
|
+
alignItems: "center",
|
|
57
|
+
gap: 8,
|
|
58
|
+
maxWidth: 220,
|
|
59
|
+
paddingVertical: 8,
|
|
60
|
+
paddingLeft: 8,
|
|
61
|
+
paddingRight: 12,
|
|
62
|
+
borderRadius: 14,
|
|
63
|
+
borderWidth: StyleSheet.hairlineWidth
|
|
64
|
+
},
|
|
65
|
+
fileIcon: {
|
|
66
|
+
width: 28,
|
|
67
|
+
height: 28,
|
|
68
|
+
borderRadius: 8,
|
|
69
|
+
alignItems: "center",
|
|
70
|
+
justifyContent: "center"
|
|
71
|
+
},
|
|
72
|
+
fileName: {
|
|
73
|
+
flexShrink: 1,
|
|
74
|
+
fontSize: 13,
|
|
75
|
+
fontWeight: "600"
|
|
76
|
+
}
|
|
77
|
+
});export{MessageAttachmentPreviews};//# sourceMappingURL=MessageAttachmentPreviews.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageAttachmentPreviews.js","sources":["../../../src/features/attachments/MessageAttachmentPreviews.tsx"],"sourcesContent":["import React from 'react';\nimport { Image, StyleSheet, Text, View } from 'react-native';\nimport { Ionicons } from '@expo/vector-icons';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\nimport { attachmentPreviewUri } from './attachmentPreviewUri';\nimport { mobileTokens } from '../../theme/mobileTokens';\n\nexport function MessageAttachmentPreviews({\n attachments,\n align = 'end',\n isDark = false,\n}: {\n attachments: MessageAttachment[];\n align?: 'start' | 'end';\n isDark?: boolean;\n}) {\n if (attachments.length === 0) return null;\n\n const border = isDark ? 'rgba(148,163,184,0.28)' : mobileTokens.color.border;\n const surface = isDark ? '#1e293b' : '#ffffff';\n const ink = isDark ? '#e2e8f0' : mobileTokens.color.text;\n const muted = isDark ? '#94a3b8' : mobileTokens.color.textMuted;\n\n return (\n <View style={[styles.wrap, align === 'end' ? styles.wrapEnd : styles.wrapStart]}>\n {attachments.map((attachment) => {\n const uri = attachmentPreviewUri(attachment);\n if (uri) {\n return (\n <Image\n key={attachment.id}\n source={{ uri }}\n style={styles.image}\n resizeMode=\"cover\"\n accessibilityLabel={attachment.name}\n />\n );\n }\n return (\n <View\n key={attachment.id}\n style={[styles.fileChip, { backgroundColor: surface, borderColor: border }]}\n accessibilityLabel={`Attached ${attachment.name}`}\n >\n <View style={[styles.fileIcon, { backgroundColor: isDark ? '#334155' : '#eef2ff' }]}>\n <Ionicons name=\"attach\" size={16} color={muted} />\n </View>\n <Text style={[styles.fileName, { color: ink }]} numberOfLines={1}>\n {attachment.name}\n </Text>\n </View>\n );\n })}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n wrap: {\n flexDirection: 'row',\n flexWrap: 'wrap',\n gap: 8,\n marginBottom: 8,\n maxWidth: '92%',\n },\n wrapEnd: {\n justifyContent: 'flex-end',\n alignSelf: 'flex-end',\n },\n wrapStart: {\n justifyContent: 'flex-start',\n alignSelf: 'flex-start',\n },\n image: {\n width: 168,\n height: 168,\n borderRadius: 16,\n backgroundColor: '#e2e8f0',\n overflow: 'hidden',\n },\n fileChip: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 8,\n maxWidth: 220,\n paddingVertical: 8,\n paddingLeft: 8,\n paddingRight: 12,\n borderRadius: 14,\n borderWidth: StyleSheet.hairlineWidth,\n },\n fileIcon: {\n width: 28,\n height: 28,\n borderRadius: 8,\n alignItems: 'center',\n justifyContent: 'center',\n },\n fileName: {\n flexShrink: 1,\n fontSize: 13,\n fontWeight: '600',\n },\n});\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import {isImageAttachment}from'./isImageAttachment.js';function attachmentPreviewUri(attachment) {
|
|
2
|
+
var _a, _b;
|
|
3
|
+
if (!isImageAttachment(attachment)) return void 0;
|
|
4
|
+
const url = (_a = attachment.url) == null ? void 0 : _a.trim();
|
|
5
|
+
if (url && /^(file:|content:|ph:|assets-library:)/i.test(url)) return url;
|
|
6
|
+
if ((_b = attachment.dataUrl) == null ? void 0 : _b.startsWith("data:image/")) return attachment.dataUrl;
|
|
7
|
+
if (url && /^https?:\/\//i.test(url)) return url;
|
|
8
|
+
return void 0;
|
|
9
|
+
}export{attachmentPreviewUri};//# sourceMappingURL=attachmentPreviewUri.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachmentPreviewUri.js","sources":["../../../src/features/attachments/attachmentPreviewUri.ts"],"sourcesContent":["import type { MessageAttachment } from '../../hooks/useChatStream';\nimport { isImageAttachment } from './isImageAttachment';\n\n/** URI the native Image component can actually paint. */\nexport function attachmentPreviewUri(attachment: MessageAttachment): string | undefined {\n if (!isImageAttachment(attachment)) return undefined;\n const url = attachment.url?.trim();\n if (url && /^(file:|content:|ph:|assets-library:)/i.test(url)) return url;\n if (attachment.dataUrl?.startsWith('data:image/')) return attachment.dataUrl;\n if (url && /^https?:\\/\\//i.test(url)) return url;\n return undefined;\n}\n"],"names":[],"mappings":"uDAIO,SAAS,qBAAqB,UAAmD,EAAA;AAJxF,EAAA,IAAA,EAAA,EAAA,EAAA;AAKE,EAAA,IAAI,CAAC,iBAAA,CAAkB,UAAU,CAAA,EAAU,OAAA,MAAA;AAC3C,EAAM,MAAA,GAAA,GAAA,CAAM,EAAW,GAAA,UAAA,CAAA,GAAA,KAAX,IAAgB,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,EAAA;AAC5B,EAAA,IAAI,GAAO,IAAA,wCAAA,CAAyC,IAAK,CAAA,GAAG,GAAU,OAAA,GAAA;AACtE,EAAA,IAAA,CAAI,gBAAW,OAAX,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAoB,UAAW,CAAA,aAAA,CAAA,SAAuB,UAAW,CAAA,OAAA;AACrE,EAAA,IAAI,GAAO,IAAA,eAAA,CAAgB,IAAK,CAAA,GAAG,GAAU,OAAA,GAAA;AAC7C,EAAO,OAAA,MAAA;AACT"}
|
|
@@ -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
|