@sagepilot-ai/react-native-sdk 0.2.5 → 0.3.1
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/README.md +144 -33
- package/android/build.gradle +13 -4
- package/android/src/main/java/ai/sagepilot/reactnativesdk/SagepilotReactNativeSdkPackage.java +2 -0
- package/dist/index.d.mts +25 -12
- package/dist/index.d.ts +25 -12
- package/dist/index.js +511 -138
- package/dist/index.mjs +480 -112
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -30,6 +30,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
SAGEPILOT_DEFAULT_IMAGE_MAX_DIMENSION: () => SAGEPILOT_DEFAULT_IMAGE_MAX_DIMENSION,
|
|
34
|
+
SAGEPILOT_DEFAULT_IMAGE_MAX_FILE_SIZE_BYTES: () => SAGEPILOT_DEFAULT_IMAGE_MAX_FILE_SIZE_BYTES,
|
|
35
|
+
SAGEPILOT_DEFAULT_IMAGE_QUALITY: () => SAGEPILOT_DEFAULT_IMAGE_QUALITY,
|
|
33
36
|
SagepilotChat: () => SagepilotChat,
|
|
34
37
|
SagepilotChatError: () => SagepilotChatError,
|
|
35
38
|
SagepilotChatProvider: () => SagepilotChatProvider,
|
|
@@ -38,10 +41,15 @@ __export(index_exports, {
|
|
|
38
41
|
createKeychainTokenStorage: () => createKeychainTokenStorage,
|
|
39
42
|
createSagepilotFilePicker: () => createSagepilotFilePicker,
|
|
40
43
|
createSagepilotFileStore: () => createSagepilotFileStore,
|
|
44
|
+
ensureSagepilotAndroidCameraPermission: () => ensureSagepilotAndroidCameraPermission,
|
|
45
|
+
promptOpenSagepilotCameraSettings: () => promptOpenSagepilotCameraSettings,
|
|
41
46
|
useSagepilotChat: () => useSagepilotChat
|
|
42
47
|
});
|
|
43
48
|
module.exports = __toCommonJS(index_exports);
|
|
44
49
|
|
|
50
|
+
// src/public/SdkClient.ts
|
|
51
|
+
var import_react_native = require("react-native");
|
|
52
|
+
|
|
45
53
|
// src/core/errors/SagepilotChatError.ts
|
|
46
54
|
var SagepilotChatError = class extends Error {
|
|
47
55
|
constructor(code, message, options = {}) {
|
|
@@ -59,7 +67,7 @@ var SagepilotChatError = class extends Error {
|
|
|
59
67
|
|
|
60
68
|
// src/core/config/constants.ts
|
|
61
69
|
var SDK_NAME = "@sagepilot-ai/react-native-sdk";
|
|
62
|
-
var SDK_VERSION = "0.
|
|
70
|
+
var SDK_VERSION = "0.3.1";
|
|
63
71
|
var DEFAULT_HOST = "https://app.sagepilot.ai";
|
|
64
72
|
var DEFAULT_WIDGET_HOST = "https://app.sagepilot.ai";
|
|
65
73
|
var CUSTOMER_API_PREFIX = "/customer-api/v1";
|
|
@@ -368,6 +376,34 @@ async function resolveDeviceInfo(adapter) {
|
|
|
368
376
|
return adapter.getDeviceInfo();
|
|
369
377
|
}
|
|
370
378
|
|
|
379
|
+
// src/core/storage/cache.ts
|
|
380
|
+
function createAsyncStorageCacheStorage(asyncStorage) {
|
|
381
|
+
return {
|
|
382
|
+
getItem: (key) => asyncStorage.getItem(key),
|
|
383
|
+
setItem: (key, value) => asyncStorage.setItem(key, value),
|
|
384
|
+
removeItem: (key) => asyncStorage.removeItem(key)
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function createJsonCache(storage, namespace) {
|
|
388
|
+
return {
|
|
389
|
+
async get(key) {
|
|
390
|
+
const value = await storage.getItem(`${namespace}:${key}`);
|
|
391
|
+
if (!value) return null;
|
|
392
|
+
try {
|
|
393
|
+
return JSON.parse(value);
|
|
394
|
+
} catch {
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
async set(key, value) {
|
|
399
|
+
await storage.setItem(`${namespace}:${key}`, JSON.stringify(value));
|
|
400
|
+
},
|
|
401
|
+
async remove(key) {
|
|
402
|
+
await storage.removeItem(`${namespace}:${key}`);
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
371
407
|
// src/resources/channels.ts
|
|
372
408
|
function bootstrapChannel(http, host, channelId) {
|
|
373
409
|
return http.request(
|
|
@@ -451,6 +487,9 @@ function fetchUnreadCount(http, host, channelId, sessionId, authorizationHeader)
|
|
|
451
487
|
}
|
|
452
488
|
|
|
453
489
|
// src/public/SdkClient.ts
|
|
490
|
+
var PRESENTATION_CACHE_NAMESPACE = "sagepilot_rn_presentation";
|
|
491
|
+
var PRESENTATION_CACHE_KEY = "state";
|
|
492
|
+
var PRESENTATION_RESTORE_MAX_AGE_MS = 15 * 60 * 1e3;
|
|
454
493
|
var DEFAULT_MOBILE_LAUNCHER_CONFIG = {
|
|
455
494
|
label: "Chat",
|
|
456
495
|
buttonColor: "#173c2d",
|
|
@@ -520,6 +559,10 @@ function readRecordField(input, key) {
|
|
|
520
559
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
521
560
|
return value;
|
|
522
561
|
}
|
|
562
|
+
function isFreshPresentationState(updatedAt) {
|
|
563
|
+
if (typeof updatedAt !== "number" || !Number.isFinite(updatedAt)) return false;
|
|
564
|
+
return Date.now() - updatedAt <= PRESENTATION_RESTORE_MAX_AGE_MS;
|
|
565
|
+
}
|
|
523
566
|
function toPublicSessionState(session) {
|
|
524
567
|
return {
|
|
525
568
|
session_id: session.session_id,
|
|
@@ -609,6 +652,7 @@ var SagepilotReactNativeChat = class {
|
|
|
609
652
|
this.channel = channel;
|
|
610
653
|
this.session = session;
|
|
611
654
|
this.isConfigured = true;
|
|
655
|
+
await this.restorePresentationFromCache();
|
|
612
656
|
this.emitReady();
|
|
613
657
|
this.emitState();
|
|
614
658
|
if (config.behavior?.enableUnreadPolling ?? true) {
|
|
@@ -748,12 +792,14 @@ var SagepilotReactNativeChat = class {
|
|
|
748
792
|
this.hostedChatView = { screen: "home" };
|
|
749
793
|
if (this.presented) {
|
|
750
794
|
this.emitState();
|
|
795
|
+
this.persistPresentationState();
|
|
751
796
|
return true;
|
|
752
797
|
}
|
|
753
798
|
this.presented = true;
|
|
754
799
|
this.setUnreadCount(0);
|
|
755
800
|
this.presentCallbacks.forEach((callback) => callback(this.getLifecycleState()));
|
|
756
801
|
this.emitState();
|
|
802
|
+
this.persistPresentationState();
|
|
757
803
|
return true;
|
|
758
804
|
}
|
|
759
805
|
presentMessages() {
|
|
@@ -781,6 +827,7 @@ var SagepilotReactNativeChat = class {
|
|
|
781
827
|
this.emitUnreadChange();
|
|
782
828
|
this.dismissCallbacks.forEach((callback) => callback(this.getLifecycleState()));
|
|
783
829
|
this.emitState();
|
|
830
|
+
this.persistPresentationState();
|
|
784
831
|
return true;
|
|
785
832
|
}
|
|
786
833
|
hide() {
|
|
@@ -915,6 +962,10 @@ var SagepilotReactNativeChat = class {
|
|
|
915
962
|
}
|
|
916
963
|
return this.session?.conversation_id ?? void 0;
|
|
917
964
|
}
|
|
965
|
+
/** Returns the chat id that is explicitly part of the current hosted URL. */
|
|
966
|
+
getHostedRouteChatId() {
|
|
967
|
+
return this.hostedChatView.screen === "composer" ? this.hostedChatView.chatId : void 0;
|
|
968
|
+
}
|
|
918
969
|
getHostedIdentityMessage() {
|
|
919
970
|
if (!this.legacyWidgetJwt) return null;
|
|
920
971
|
return {
|
|
@@ -925,6 +976,34 @@ var SagepilotReactNativeChat = class {
|
|
|
925
976
|
}
|
|
926
977
|
};
|
|
927
978
|
}
|
|
979
|
+
/** Captures the hosted route that should receive a picked attachment batch. */
|
|
980
|
+
getHostedAttachmentTarget() {
|
|
981
|
+
return {
|
|
982
|
+
chatId: this.getActiveHostedChatId(),
|
|
983
|
+
routeChatId: this.getHostedRouteChatId(),
|
|
984
|
+
hostedChatView: this.serializeHostedChatView(this.hostedChatView)
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
/** Restores the hosted route for a queued attachment batch before delivery. */
|
|
988
|
+
restoreHostedAttachmentTarget(target) {
|
|
989
|
+
if (import_react_native.Platform.OS !== "android") return false;
|
|
990
|
+
if (!target) return false;
|
|
991
|
+
const hostedChatView = this.readPersistedHostedChatView(target.hostedChatView);
|
|
992
|
+
if (!hostedChatView) return false;
|
|
993
|
+
const currentChatId = this.getActiveHostedChatId();
|
|
994
|
+
const targetChatId = normalizeOptionalString(target.chatId);
|
|
995
|
+
const routeChatId = normalizeOptionalString(target.routeChatId) ?? (hostedChatView.screen === "composer" ? normalizeOptionalString(hostedChatView.chatId) : void 0);
|
|
996
|
+
const nextHostedChatView = routeChatId && hostedChatView.screen === "composer" ? this.withPinnedChatId(hostedChatView, routeChatId) : hostedChatView;
|
|
997
|
+
if (this.presented && (!targetChatId || currentChatId === targetChatId) && this.areHostedChatViewsEqual(this.hostedChatView, nextHostedChatView)) {
|
|
998
|
+
return false;
|
|
999
|
+
}
|
|
1000
|
+
this.hostedChatView = nextHostedChatView;
|
|
1001
|
+
this.presented = true;
|
|
1002
|
+
this.setUnreadCount(0);
|
|
1003
|
+
this.emitState();
|
|
1004
|
+
this.persistPresentationState();
|
|
1005
|
+
return true;
|
|
1006
|
+
}
|
|
928
1007
|
handleHostedBridgeMessage(message) {
|
|
929
1008
|
if (!message) return false;
|
|
930
1009
|
if (message.type === "close_widget") {
|
|
@@ -992,6 +1071,7 @@ var SagepilotReactNativeChat = class {
|
|
|
992
1071
|
}
|
|
993
1072
|
destroy() {
|
|
994
1073
|
this.stopUnreadPolling();
|
|
1074
|
+
this.persistPresentationState({ cleanShutdown: true, presented: false });
|
|
995
1075
|
this.resetRuntimeState();
|
|
996
1076
|
this.emitState();
|
|
997
1077
|
this.identifyCallbacks.clear();
|
|
@@ -1147,8 +1227,86 @@ var SagepilotReactNativeChat = class {
|
|
|
1147
1227
|
this.presentCallbacks.forEach((callback) => callback(this.getLifecycleState()));
|
|
1148
1228
|
}
|
|
1149
1229
|
this.emitState();
|
|
1230
|
+
this.persistPresentationState();
|
|
1150
1231
|
return true;
|
|
1151
1232
|
}
|
|
1233
|
+
/** Returns the dedicated cache used for hosted-chat presentation recovery. */
|
|
1234
|
+
getPresentationCache() {
|
|
1235
|
+
const cacheStorage = this.runtimeOptions?.cacheStorage;
|
|
1236
|
+
if (!cacheStorage) return null;
|
|
1237
|
+
return createJsonCache(cacheStorage, PRESENTATION_CACHE_NAMESPACE);
|
|
1238
|
+
}
|
|
1239
|
+
/** Removes callback-only fields so the hosted route can be safely serialized. */
|
|
1240
|
+
serializeHostedChatView(view) {
|
|
1241
|
+
if (view.screen !== "composer") return view;
|
|
1242
|
+
return {
|
|
1243
|
+
screen: "composer",
|
|
1244
|
+
message: view.message,
|
|
1245
|
+
mode: view.mode,
|
|
1246
|
+
chatId: view.chatId,
|
|
1247
|
+
metadata: view.metadata
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
/** Validates a persisted hosted view before using it to rebuild a widget URL. */
|
|
1251
|
+
readPersistedHostedChatView(view) {
|
|
1252
|
+
if (!view || typeof view !== "object") return null;
|
|
1253
|
+
const record = view;
|
|
1254
|
+
if (record.screen === "home") return { screen: "home" };
|
|
1255
|
+
if (record.screen === "messages") return { screen: "messages" };
|
|
1256
|
+
if (record.screen !== "composer") return null;
|
|
1257
|
+
const mode = record.mode === "new" ? "new" : "auto";
|
|
1258
|
+
return {
|
|
1259
|
+
screen: "composer",
|
|
1260
|
+
message: typeof record.message === "string" ? record.message : void 0,
|
|
1261
|
+
mode,
|
|
1262
|
+
chatId: typeof record.chatId === "string" && record.chatId ? record.chatId : void 0,
|
|
1263
|
+
metadata: readRecordField(record, "metadata")
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
/** Forces a persisted hosted route to target the exact chat that owns a pending attachment. */
|
|
1267
|
+
withPinnedChatId(view, chatId) {
|
|
1268
|
+
if (view.screen === "composer") {
|
|
1269
|
+
return {
|
|
1270
|
+
...view,
|
|
1271
|
+
chatId
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
return {
|
|
1275
|
+
screen: "composer",
|
|
1276
|
+
mode: "auto",
|
|
1277
|
+
chatId
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
/** Compares hosted route state without relying on object identity. */
|
|
1281
|
+
areHostedChatViewsEqual(first, second) {
|
|
1282
|
+
return JSON.stringify(this.serializeHostedChatView(first)) === JSON.stringify(this.serializeHostedChatView(second));
|
|
1283
|
+
}
|
|
1284
|
+
/** Persists presentation state without blocking customer-facing SDK methods. */
|
|
1285
|
+
persistPresentationState(options) {
|
|
1286
|
+
const cache = this.getPresentationCache();
|
|
1287
|
+
if (!cache) return;
|
|
1288
|
+
const state = {
|
|
1289
|
+
presented: options?.presented ?? this.presented,
|
|
1290
|
+
hostedChatView: this.serializeHostedChatView(this.hostedChatView),
|
|
1291
|
+
updatedAt: Date.now(),
|
|
1292
|
+
cleanShutdown: options?.cleanShutdown
|
|
1293
|
+
};
|
|
1294
|
+
void cache.set(PRESENTATION_CACHE_KEY, state).catch(() => void 0);
|
|
1295
|
+
}
|
|
1296
|
+
/** Restores Android presentation state after configure without resetting the hosted route to Home. */
|
|
1297
|
+
async restorePresentationFromCache() {
|
|
1298
|
+
const cache = this.getPresentationCache();
|
|
1299
|
+
if (!cache || import_react_native.Platform.OS !== "android") return;
|
|
1300
|
+
const persisted = await cache.get(PRESENTATION_CACHE_KEY).catch(() => null);
|
|
1301
|
+
if (persisted?.cleanShutdown) return;
|
|
1302
|
+
if (!isFreshPresentationState(persisted?.updatedAt)) return;
|
|
1303
|
+
if (!persisted?.presented) return;
|
|
1304
|
+
const hostedChatView = this.readPersistedHostedChatView(persisted.hostedChatView);
|
|
1305
|
+
if (!hostedChatView) return;
|
|
1306
|
+
this.hostedChatView = hostedChatView;
|
|
1307
|
+
this.presented = true;
|
|
1308
|
+
this.setUnreadCount(0);
|
|
1309
|
+
}
|
|
1152
1310
|
};
|
|
1153
1311
|
var internalSagepilotChat = new SagepilotReactNativeChat();
|
|
1154
1312
|
var SagepilotChat = {
|
|
@@ -1185,34 +1343,6 @@ var SagepilotChat = {
|
|
|
1185
1343
|
destroy: () => internalSagepilotChat.destroy()
|
|
1186
1344
|
};
|
|
1187
1345
|
|
|
1188
|
-
// src/core/storage/cache.ts
|
|
1189
|
-
function createAsyncStorageCacheStorage(asyncStorage) {
|
|
1190
|
-
return {
|
|
1191
|
-
getItem: (key) => asyncStorage.getItem(key),
|
|
1192
|
-
setItem: (key, value) => asyncStorage.setItem(key, value),
|
|
1193
|
-
removeItem: (key) => asyncStorage.removeItem(key)
|
|
1194
|
-
};
|
|
1195
|
-
}
|
|
1196
|
-
function createJsonCache(storage, namespace) {
|
|
1197
|
-
return {
|
|
1198
|
-
async get(key) {
|
|
1199
|
-
const value = await storage.getItem(`${namespace}:${key}`);
|
|
1200
|
-
if (!value) return null;
|
|
1201
|
-
try {
|
|
1202
|
-
return JSON.parse(value);
|
|
1203
|
-
} catch {
|
|
1204
|
-
return null;
|
|
1205
|
-
}
|
|
1206
|
-
},
|
|
1207
|
-
async set(key, value) {
|
|
1208
|
-
await storage.setItem(`${namespace}:${key}`, JSON.stringify(value));
|
|
1209
|
-
},
|
|
1210
|
-
async remove(key) {
|
|
1211
|
-
await storage.removeItem(`${namespace}:${key}`);
|
|
1212
|
-
}
|
|
1213
|
-
};
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
1346
|
// src/core/storage/fileStore.ts
|
|
1217
1347
|
var DEFAULT_DIRECTORY_NAME = "sagepilot-attachments";
|
|
1218
1348
|
function sanitizeKey(key) {
|
|
@@ -1270,21 +1400,29 @@ function createSagepilotFileStore(blobUtil, options = {}) {
|
|
|
1270
1400
|
}
|
|
1271
1401
|
|
|
1272
1402
|
// src/core/native/filePicker.ts
|
|
1273
|
-
var
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
var DEFAULT_IMAGE_MIME_TYPE = "image/jpeg";
|
|
1277
|
-
var DEFAULT_DOCUMENT_MIME_TYPE = "application/octet-stream";
|
|
1278
|
-
var DEFAULT_IMAGE_SELECTION_LIMIT = 5;
|
|
1279
|
-
var DEFAULT_DOCUMENT_MAX_FILE_SIZE_BYTES = 20 * 1024 * 1024;
|
|
1280
|
-
var DEFAULT_IMAGE_MAX_FILE_SIZE_BYTES = 15 * 1024 * 1024;
|
|
1403
|
+
var import_react_native2 = require("react-native");
|
|
1404
|
+
|
|
1405
|
+
// src/core/native/filePickerShared.ts
|
|
1281
1406
|
var SagepilotFilePickerError = class extends Error {
|
|
1407
|
+
/** Creates a typed picker error that can be surfaced over the widget bridge. */
|
|
1282
1408
|
constructor(code, message) {
|
|
1283
1409
|
super(message);
|
|
1284
1410
|
this.name = "SagepilotFilePickerError";
|
|
1285
1411
|
this.code = code;
|
|
1286
1412
|
}
|
|
1287
1413
|
};
|
|
1414
|
+
var SAGEPILOT_DEFAULT_IMAGE_MAX_DIMENSION = 1920;
|
|
1415
|
+
var SAGEPILOT_DEFAULT_IMAGE_QUALITY = 0.8;
|
|
1416
|
+
var SAGEPILOT_DEFAULT_IMAGE_MAX_FILE_SIZE_BYTES = 15 * 1024 * 1024;
|
|
1417
|
+
|
|
1418
|
+
// src/core/native/filePicker.ts
|
|
1419
|
+
var DEFAULT_IMAGE_MAX_DIMENSION = SAGEPILOT_DEFAULT_IMAGE_MAX_DIMENSION;
|
|
1420
|
+
var DEFAULT_IMAGE_QUALITY = SAGEPILOT_DEFAULT_IMAGE_QUALITY;
|
|
1421
|
+
var DEFAULT_IMAGE_MIME_TYPE = "image/jpeg";
|
|
1422
|
+
var DEFAULT_DOCUMENT_MIME_TYPE = "application/octet-stream";
|
|
1423
|
+
var DEFAULT_IMAGE_SELECTION_LIMIT = 5;
|
|
1424
|
+
var DEFAULT_DOCUMENT_MAX_FILE_SIZE_BYTES = 20 * 1024 * 1024;
|
|
1425
|
+
var DEFAULT_IMAGE_MAX_FILE_SIZE_BYTES = SAGEPILOT_DEFAULT_IMAGE_MAX_FILE_SIZE_BYTES;
|
|
1288
1426
|
function bytesToMb(bytes) {
|
|
1289
1427
|
return Math.round(bytes / (1024 * 1024) * 10) / 10;
|
|
1290
1428
|
}
|
|
@@ -1294,27 +1432,53 @@ function estimateBase64ByteSize(dataBase64) {
|
|
|
1294
1432
|
function stripFileScheme(uri) {
|
|
1295
1433
|
return uri.startsWith("file://") ? uri.replace("file://", "") : uri;
|
|
1296
1434
|
}
|
|
1297
|
-
async function
|
|
1298
|
-
if (
|
|
1435
|
+
async function promptOpenSagepilotCameraSettings() {
|
|
1436
|
+
if (import_react_native2.Platform.OS !== "android") return;
|
|
1437
|
+
await new Promise((resolve) => {
|
|
1438
|
+
import_react_native2.Alert.alert(
|
|
1439
|
+
"Camera access is off",
|
|
1440
|
+
"Enable camera access for this app in Settings to take a photo.",
|
|
1441
|
+
[
|
|
1442
|
+
{
|
|
1443
|
+
text: "Not now",
|
|
1444
|
+
style: "cancel",
|
|
1445
|
+
onPress: resolve
|
|
1446
|
+
},
|
|
1447
|
+
{
|
|
1448
|
+
text: "Open Settings",
|
|
1449
|
+
onPress: () => {
|
|
1450
|
+
import_react_native2.Linking.openSettings().finally(resolve);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
]
|
|
1454
|
+
);
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
async function ensureSagepilotAndroidCameraPermission() {
|
|
1458
|
+
if (import_react_native2.Platform.OS !== "android") return;
|
|
1299
1459
|
let result;
|
|
1300
1460
|
try {
|
|
1301
|
-
result = await
|
|
1461
|
+
result = await import_react_native2.PermissionsAndroid.request(import_react_native2.PermissionsAndroid.PERMISSIONS.CAMERA);
|
|
1302
1462
|
} catch {
|
|
1303
1463
|
return;
|
|
1304
1464
|
}
|
|
1305
|
-
if (result ===
|
|
1465
|
+
if (result === import_react_native2.PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
|
|
1466
|
+
await promptOpenSagepilotCameraSettings();
|
|
1306
1467
|
throw new SagepilotFilePickerError(
|
|
1307
1468
|
"permission_denied",
|
|
1308
1469
|
"Camera access is turned off. Enable it for this app in Settings to take a photo."
|
|
1309
1470
|
);
|
|
1310
1471
|
}
|
|
1311
|
-
if (result ===
|
|
1472
|
+
if (result === import_react_native2.PermissionsAndroid.RESULTS.DENIED) {
|
|
1312
1473
|
throw new SagepilotFilePickerError(
|
|
1313
1474
|
"permission_denied",
|
|
1314
1475
|
"Camera permission is required to take a photo."
|
|
1315
1476
|
);
|
|
1316
1477
|
}
|
|
1317
1478
|
}
|
|
1479
|
+
function canUseCameraSource(imagePicker) {
|
|
1480
|
+
return Boolean(imagePicker);
|
|
1481
|
+
}
|
|
1318
1482
|
function mapImagePickerErrorCode(errorCode) {
|
|
1319
1483
|
switch (errorCode) {
|
|
1320
1484
|
case "permission":
|
|
@@ -1391,7 +1555,8 @@ function createSagepilotFilePicker(options) {
|
|
|
1391
1555
|
const documentMaxFileSizeBytes = options.documentMaxFileSizeBytes ?? DEFAULT_DOCUMENT_MAX_FILE_SIZE_BYTES;
|
|
1392
1556
|
const imageMaxFileSizeBytes = options.imageMaxFileSizeBytes ?? DEFAULT_IMAGE_MAX_FILE_SIZE_BYTES;
|
|
1393
1557
|
const sources = [];
|
|
1394
|
-
if (imagePicker) sources.push("camera"
|
|
1558
|
+
if (canUseCameraSource(imagePicker)) sources.push("camera");
|
|
1559
|
+
if (imagePicker) sources.push("library");
|
|
1395
1560
|
if (documentsPicker) sources.push("documents");
|
|
1396
1561
|
if (sources.length === 0) return void 0;
|
|
1397
1562
|
const imageOptions = {
|
|
@@ -1403,8 +1568,10 @@ function createSagepilotFilePicker(options) {
|
|
|
1403
1568
|
saveToPhotos: false
|
|
1404
1569
|
};
|
|
1405
1570
|
async function pickFromCamera() {
|
|
1406
|
-
|
|
1407
|
-
|
|
1571
|
+
await ensureSagepilotAndroidCameraPermission();
|
|
1572
|
+
if (!imagePicker) {
|
|
1573
|
+
throw new SagepilotFilePickerError("camera_unavailable", "The camera picker is not available in this build.");
|
|
1574
|
+
}
|
|
1408
1575
|
return imageAssetsToPickedFiles(await imagePicker.launchCamera(imageOptions), imageMaxFileSizeBytes);
|
|
1409
1576
|
}
|
|
1410
1577
|
async function pickFromLibrary(multiple) {
|
|
@@ -1475,13 +1642,17 @@ function createSagepilotFilePicker(options) {
|
|
|
1475
1642
|
|
|
1476
1643
|
// src/ui/SagepilotChatProvider.ts
|
|
1477
1644
|
var import_react = require("react");
|
|
1478
|
-
var
|
|
1645
|
+
var import_lucide_react_native = require("lucide-react-native");
|
|
1646
|
+
var import_react_native3 = require("react-native");
|
|
1479
1647
|
var import_react_native_webview = require("react-native-webview");
|
|
1480
1648
|
|
|
1481
1649
|
// src/core/webview/mobileBridge.ts
|
|
1482
1650
|
var FILE_PICKER_PROTOCOL_VERSION = 2;
|
|
1651
|
+
function buildBridgeCapabilitiesPayload(capabilities) {
|
|
1652
|
+
return { ...capabilities, filePickerProtocol: FILE_PICKER_PROTOCOL_VERSION };
|
|
1653
|
+
}
|
|
1483
1654
|
function buildBridgeCapabilitiesScript(capabilities) {
|
|
1484
|
-
const payload =
|
|
1655
|
+
const payload = buildBridgeCapabilitiesPayload(capabilities);
|
|
1485
1656
|
return [
|
|
1486
1657
|
"(function(){",
|
|
1487
1658
|
"try {",
|
|
@@ -1530,9 +1701,23 @@ function parseHostedBridgeMessage(rawData) {
|
|
|
1530
1701
|
return null;
|
|
1531
1702
|
}
|
|
1532
1703
|
}
|
|
1533
|
-
|
|
1704
|
+
function buildMobileWebViewBridgeScript(capabilities) {
|
|
1705
|
+
const payload = buildBridgeCapabilitiesPayload(capabilities);
|
|
1706
|
+
return `
|
|
1534
1707
|
(function () {
|
|
1535
|
-
|
|
1708
|
+
var bridgeCapabilities = ${JSON.stringify(payload)};
|
|
1709
|
+
var applyBridgeCapabilities = function () {
|
|
1710
|
+
try {
|
|
1711
|
+
if (window.SagepilotMobileBridge) {
|
|
1712
|
+
window.SagepilotMobileBridge.capabilities = Object.assign({}, window.SagepilotMobileBridge.capabilities, bridgeCapabilities);
|
|
1713
|
+
}
|
|
1714
|
+
} catch (error) {}
|
|
1715
|
+
};
|
|
1716
|
+
|
|
1717
|
+
if (window.__sagepilotRnBridgeInstalled) {
|
|
1718
|
+
applyBridgeCapabilities();
|
|
1719
|
+
return true;
|
|
1720
|
+
}
|
|
1536
1721
|
window.__sagepilotRnBridgeInstalled = true;
|
|
1537
1722
|
|
|
1538
1723
|
var ensureViewport = function () {
|
|
@@ -1573,9 +1758,11 @@ var mobileWebViewBridgeScript = `
|
|
|
1573
1758
|
};
|
|
1574
1759
|
|
|
1575
1760
|
window.SagepilotMobileBridge = {
|
|
1761
|
+
capabilities: bridgeCapabilities,
|
|
1576
1762
|
postMessage: sendToReactNative,
|
|
1577
1763
|
ready: function () { sendToReactNative({ type: "sagepilot:ready" }); }
|
|
1578
1764
|
};
|
|
1765
|
+
applyBridgeCapabilities();
|
|
1579
1766
|
|
|
1580
1767
|
document.addEventListener("click", function (event) {
|
|
1581
1768
|
try {
|
|
@@ -1616,6 +1803,7 @@ var mobileWebViewBridgeScript = `
|
|
|
1616
1803
|
return true;
|
|
1617
1804
|
})();
|
|
1618
1805
|
`;
|
|
1806
|
+
}
|
|
1619
1807
|
|
|
1620
1808
|
// src/specs/SagepilotInsetsViewNativeComponent.ts
|
|
1621
1809
|
var import_codegenNativeComponent = __toESM(require("react-native/Libraries/Utilities/codegenNativeComponent"));
|
|
@@ -1632,13 +1820,17 @@ function readPresentationState() {
|
|
|
1632
1820
|
presentation: internalSagepilotChat.getConfig()?.presentation
|
|
1633
1821
|
};
|
|
1634
1822
|
}
|
|
1823
|
+
function getBridgeCapabilities() {
|
|
1824
|
+
return {
|
|
1825
|
+
nativeFilePicker: Boolean(internalSagepilotChat.getConfig()?.filePicker)
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1635
1828
|
function getInjectedWebViewScript() {
|
|
1829
|
+
const capabilities = getBridgeCapabilities();
|
|
1636
1830
|
return [
|
|
1637
1831
|
internalSagepilotChat.getHostedAuthScript(),
|
|
1638
|
-
|
|
1639
|
-
buildBridgeCapabilitiesScript(
|
|
1640
|
-
nativeFilePicker: Boolean(internalSagepilotChat.getConfig()?.filePicker)
|
|
1641
|
-
})
|
|
1832
|
+
buildMobileWebViewBridgeScript(capabilities),
|
|
1833
|
+
buildBridgeCapabilitiesScript(capabilities)
|
|
1642
1834
|
].filter(Boolean).join("\n");
|
|
1643
1835
|
}
|
|
1644
1836
|
var FILE_PICKER_SOURCE_LABELS = {
|
|
@@ -1646,6 +1838,21 @@ var FILE_PICKER_SOURCE_LABELS = {
|
|
|
1646
1838
|
library: "Choose from gallery",
|
|
1647
1839
|
documents: "Browse files"
|
|
1648
1840
|
};
|
|
1841
|
+
var FILE_PICKER_SOURCE_DESCRIPTIONS = {
|
|
1842
|
+
camera: "Use the in-app camera",
|
|
1843
|
+
library: "Photos and videos on this device",
|
|
1844
|
+
documents: "PDFs, documents, and Drive files"
|
|
1845
|
+
};
|
|
1846
|
+
var FILE_PICKER_SOURCE_ICONS = {
|
|
1847
|
+
camera: import_lucide_react_native.Camera,
|
|
1848
|
+
library: import_lucide_react_native.Images,
|
|
1849
|
+
documents: import_lucide_react_native.FileText
|
|
1850
|
+
};
|
|
1851
|
+
var FILE_PICKER_SOURCE_ICON_COLORS = {
|
|
1852
|
+
camera: "#0284c7",
|
|
1853
|
+
library: "#16a34a",
|
|
1854
|
+
documents: "#7c3aed"
|
|
1855
|
+
};
|
|
1649
1856
|
var DELIVERY_RETRY_MS = 1500;
|
|
1650
1857
|
var DELIVERY_LEGACY_FALLBACK_ATTEMPTS = 2;
|
|
1651
1858
|
var DELIVERY_MAX_ATTEMPTS = 8;
|
|
@@ -1655,6 +1862,7 @@ var PENDING_FILES_CACHE_KEY = "pending_batch";
|
|
|
1655
1862
|
var LIVENESS_HEARTBEAT_STALE_MS = 4e3;
|
|
1656
1863
|
var LIVENESS_PONG_TIMEOUT_MS = 1500;
|
|
1657
1864
|
var LIVENESS_MAX_PING_ATTEMPTS = 2;
|
|
1865
|
+
var LIVENESS_PICKER_GRACE_MS = 8e3;
|
|
1658
1866
|
var LIVENESS_MAX_REMOUNTS = 2;
|
|
1659
1867
|
var batchSequence = 0;
|
|
1660
1868
|
function nextBatchId() {
|
|
@@ -1668,6 +1876,29 @@ function totalBase64Bytes(files) {
|
|
|
1668
1876
|
function storageKeyFor(batchId, index) {
|
|
1669
1877
|
return `${batchId}_${index}.bin`;
|
|
1670
1878
|
}
|
|
1879
|
+
function renderFilePickerSourceIcon(source) {
|
|
1880
|
+
const Icon = FILE_PICKER_SOURCE_ICONS[source];
|
|
1881
|
+
return (0, import_react.createElement)(
|
|
1882
|
+
import_react_native3.View,
|
|
1883
|
+
{ style: getFilePickerSourceIconStyle(source) },
|
|
1884
|
+
(0, import_react.createElement)(Icon, {
|
|
1885
|
+
color: FILE_PICKER_SOURCE_ICON_COLORS[source],
|
|
1886
|
+
size: 23,
|
|
1887
|
+
strokeWidth: 2.35
|
|
1888
|
+
})
|
|
1889
|
+
);
|
|
1890
|
+
}
|
|
1891
|
+
function getFilePickerSourceIconStyle(source) {
|
|
1892
|
+
if (source === "camera") return [styles.sheetOptionIcon, styles.cameraOptionIcon];
|
|
1893
|
+
if (source === "library") return [styles.sheetOptionIcon, styles.libraryOptionIcon];
|
|
1894
|
+
return [styles.sheetOptionIcon, styles.documentsOptionIcon];
|
|
1895
|
+
}
|
|
1896
|
+
function getSheetOptionStyle(pressed) {
|
|
1897
|
+
return [styles.sheetOption, pressed && styles.sheetOptionPressed];
|
|
1898
|
+
}
|
|
1899
|
+
function getSheetCancelStyle(pressed) {
|
|
1900
|
+
return [styles.sheetCancelButton, pressed && styles.sheetCancelButtonPressed];
|
|
1901
|
+
}
|
|
1671
1902
|
function buildDispatchScript(messageLiteral) {
|
|
1672
1903
|
return [
|
|
1673
1904
|
"(function(){",
|
|
@@ -1679,8 +1910,14 @@ function buildDispatchScript(messageLiteral) {
|
|
|
1679
1910
|
"})();"
|
|
1680
1911
|
].join("\n");
|
|
1681
1912
|
}
|
|
1682
|
-
function buildFilesPickedScript(
|
|
1683
|
-
return buildDispatchScript(
|
|
1913
|
+
function buildFilesPickedScript(batch) {
|
|
1914
|
+
return buildDispatchScript(JSON.stringify({
|
|
1915
|
+
type: "sagepilot:files_picked",
|
|
1916
|
+
batch_id: batch.batchId,
|
|
1917
|
+
chat_id: batch.target?.chatId,
|
|
1918
|
+
conversation_id: batch.target?.chatId,
|
|
1919
|
+
files: batch.files
|
|
1920
|
+
}));
|
|
1684
1921
|
}
|
|
1685
1922
|
function buildFilePickerErrorScript(message, code) {
|
|
1686
1923
|
return buildDispatchScript(
|
|
@@ -1727,7 +1964,7 @@ function isInternalWebViewScheme(url) {
|
|
|
1727
1964
|
var hostedChatWebViewProps = {
|
|
1728
1965
|
allowFileAccess: true,
|
|
1729
1966
|
allowFileAccessFromFileURLs: true,
|
|
1730
|
-
...
|
|
1967
|
+
...import_react_native3.Platform.OS === "ios" ? {
|
|
1731
1968
|
automaticallyAdjustContentInsets: false,
|
|
1732
1969
|
contentInsetAdjustmentBehavior: "never",
|
|
1733
1970
|
hideKeyboardAccessoryView: true
|
|
@@ -1742,7 +1979,7 @@ var hostedChatWebViewProps = {
|
|
|
1742
1979
|
sharedCookiesEnabled: true,
|
|
1743
1980
|
thirdPartyCookiesEnabled: true
|
|
1744
1981
|
};
|
|
1745
|
-
var AndroidInsetsView =
|
|
1982
|
+
var AndroidInsetsView = import_react_native3.Platform.OS === "android" ? SagepilotInsetsViewNativeComponent_default : import_react_native3.View;
|
|
1746
1983
|
var emptyAndroidInsets = { top: 0, bottom: 0 };
|
|
1747
1984
|
function SagepilotChatProvider({
|
|
1748
1985
|
children,
|
|
@@ -1761,8 +1998,11 @@ function SagepilotChatProvider({
|
|
|
1761
1998
|
const deliveryAttemptsRef = (0, import_react.useRef)(0);
|
|
1762
1999
|
const pendingPingRef = (0, import_react.useRef)(null);
|
|
1763
2000
|
const pingTimeoutRef = (0, import_react.useRef)(null);
|
|
2001
|
+
const livenessResumeTimerRef = (0, import_react.useRef)(null);
|
|
2002
|
+
const livenessPausedUntilRef = (0, import_react.useRef)(0);
|
|
2003
|
+
const pickerInFlightRef = (0, import_react.useRef)(false);
|
|
1764
2004
|
const livenessRemountCountRef = (0, import_react.useRef)(0);
|
|
1765
|
-
const appStateRef = (0, import_react.useRef)(
|
|
2005
|
+
const appStateRef = (0, import_react.useRef)(import_react_native3.AppState.currentState);
|
|
1766
2006
|
const didReconcileRef = (0, import_react.useRef)(false);
|
|
1767
2007
|
const [androidRepaintTick, setAndroidRepaintTick] = (0, import_react.useState)(0);
|
|
1768
2008
|
const [sourceChooser, setSourceChooser] = (0, import_react.useState)(null);
|
|
@@ -1783,6 +2023,7 @@ function SagepilotChatProvider({
|
|
|
1783
2023
|
if (hasFileStore) {
|
|
1784
2024
|
const manifest = queue.filter((batch) => batch.storageKeys && batch.storageKeys.length === batch.files.length).map((batch) => ({
|
|
1785
2025
|
batchId: batch.batchId,
|
|
2026
|
+
target: batch.target,
|
|
1786
2027
|
files: batch.files.map((file, index) => ({
|
|
1787
2028
|
file_name: file.file_name,
|
|
1788
2029
|
mime_type: file.mime_type,
|
|
@@ -1801,6 +2042,7 @@ function SagepilotChatProvider({
|
|
|
1801
2042
|
if (totalBytes <= PERSIST_MAX_TOTAL_BYTES) {
|
|
1802
2043
|
const manifest = queue.map((batch) => ({
|
|
1803
2044
|
batchId: batch.batchId,
|
|
2045
|
+
target: batch.target,
|
|
1804
2046
|
files: batch.files.map((file) => ({
|
|
1805
2047
|
file_name: file.file_name,
|
|
1806
2048
|
mime_type: file.mime_type,
|
|
@@ -1842,12 +2084,17 @@ function SagepilotChatProvider({
|
|
|
1842
2084
|
}
|
|
1843
2085
|
const batch = pendingBatchesRef.current[0];
|
|
1844
2086
|
if (!batch) return;
|
|
2087
|
+
if (internalSagepilotChat.restoreHostedAttachmentTarget(batch.target)) {
|
|
2088
|
+
widgetReadyRef.current = false;
|
|
2089
|
+
deliveryTimerRef.current = setTimeout(() => pumpDelivery(), DELIVERY_RETRY_MS);
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
1845
2092
|
deliveryAttemptsRef.current += 1;
|
|
1846
2093
|
const attempts = deliveryAttemptsRef.current;
|
|
1847
2094
|
const ref = nativeWebViewRef.current;
|
|
1848
2095
|
const shouldDeliver = ref && (widgetReadyRef.current || attempts >= DELIVERY_LEGACY_FALLBACK_ATTEMPTS);
|
|
1849
2096
|
if (shouldDeliver && ref) {
|
|
1850
|
-
ref.injectJavaScript(buildFilesPickedScript(batch
|
|
2097
|
+
ref.injectJavaScript(buildFilesPickedScript(batch));
|
|
1851
2098
|
}
|
|
1852
2099
|
if (attempts < DELIVERY_MAX_ATTEMPTS) {
|
|
1853
2100
|
deliveryTimerRef.current = setTimeout(() => pumpDelivery(), DELIVERY_RETRY_MS);
|
|
@@ -1878,22 +2125,38 @@ function SagepilotChatProvider({
|
|
|
1878
2125
|
const batchId = nextBatchId();
|
|
1879
2126
|
const hasFileStore = Boolean(internalSagepilotChat.getConfig()?.fileStore);
|
|
1880
2127
|
const storageKeys = hasFileStore ? files.map((_, index) => storageKeyFor(batchId, index)) : void 0;
|
|
1881
|
-
const batch = {
|
|
2128
|
+
const batch = {
|
|
2129
|
+
batchId,
|
|
2130
|
+
files,
|
|
2131
|
+
storageKeys,
|
|
2132
|
+
target: internalSagepilotChat.getHostedAttachmentTarget()
|
|
2133
|
+
};
|
|
1882
2134
|
pendingBatchesRef.current = [...pendingBatchesRef.current, batch];
|
|
1883
2135
|
ensureDelivery();
|
|
1884
2136
|
writeManifest();
|
|
1885
2137
|
void writeBatchBytes(batch);
|
|
1886
2138
|
}, [ensureDelivery, writeManifest, writeBatchBytes]);
|
|
1887
|
-
const recoverNativeWebView = (0, import_react.useCallback)(() => {
|
|
2139
|
+
const recoverNativeWebView = (0, import_react.useCallback)((_reasonOrEvent) => {
|
|
1888
2140
|
widgetReadyRef.current = false;
|
|
1889
2141
|
setNativeWebViewKey((key) => key + 1);
|
|
1890
2142
|
}, []);
|
|
1891
2143
|
const recoverPreloadWebView = (0, import_react.useCallback)(() => {
|
|
1892
2144
|
setPreloadWebViewKey((key) => key + 1);
|
|
1893
2145
|
}, []);
|
|
2146
|
+
const pauseLivenessAfterPicker = (0, import_react.useCallback)((durationMs) => {
|
|
2147
|
+
if (import_react_native3.Platform.OS !== "android") return;
|
|
2148
|
+
livenessPausedUntilRef.current = Math.max(livenessPausedUntilRef.current, Date.now() + durationMs);
|
|
2149
|
+
}, []);
|
|
1894
2150
|
const runNativeFilePicker = (0, import_react.useCallback)((source, multiple) => {
|
|
1895
2151
|
const filePicker = internalSagepilotChat.getConfig()?.filePicker;
|
|
1896
|
-
if (!filePicker)
|
|
2152
|
+
if (!filePicker) {
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
if (pickerInFlightRef.current) {
|
|
2156
|
+
return;
|
|
2157
|
+
}
|
|
2158
|
+
pauseLivenessAfterPicker(6e4);
|
|
2159
|
+
pickerInFlightRef.current = true;
|
|
1897
2160
|
filePicker.pickFiles({ source, multiple }).then((files) => {
|
|
1898
2161
|
if (files.length === 0) {
|
|
1899
2162
|
nativeWebViewRef.current?.injectJavaScript(buildFilePickerCancelledScript());
|
|
@@ -1903,13 +2166,18 @@ function SagepilotChatProvider({
|
|
|
1903
2166
|
}).catch((error) => {
|
|
1904
2167
|
const { message, code } = readFilePickerError(error);
|
|
1905
2168
|
nativeWebViewRef.current?.injectJavaScript(buildFilePickerErrorScript(message, code));
|
|
2169
|
+
}).finally(() => {
|
|
2170
|
+
pickerInFlightRef.current = false;
|
|
2171
|
+
pauseLivenessAfterPicker(LIVENESS_PICKER_GRACE_MS);
|
|
1906
2172
|
});
|
|
1907
|
-
}, [queuePickedFiles]);
|
|
2173
|
+
}, [pauseLivenessAfterPicker, queuePickedFiles]);
|
|
1908
2174
|
const openNativeFilePicker = (0, import_react.useCallback)((multiple) => {
|
|
1909
2175
|
const filePicker = internalSagepilotChat.getConfig()?.filePicker;
|
|
1910
|
-
if (!filePicker || filePicker.sources.length === 0)
|
|
2176
|
+
if (!filePicker || filePicker.sources.length === 0) {
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
1911
2179
|
const [onlySource] = filePicker.sources;
|
|
1912
|
-
if (filePicker.sources.length === 1 && onlySource) {
|
|
2180
|
+
if (filePicker.sources.length === 1 && onlySource && onlySource !== "camera") {
|
|
1913
2181
|
runNativeFilePicker(onlySource, multiple);
|
|
1914
2182
|
return;
|
|
1915
2183
|
}
|
|
@@ -1962,7 +2230,8 @@ function SagepilotChatProvider({
|
|
|
1962
2230
|
restored.push({
|
|
1963
2231
|
batchId: batch.batchId,
|
|
1964
2232
|
files,
|
|
1965
|
-
storageKeys: storageKeys.length === files.length ? storageKeys : void 0
|
|
2233
|
+
storageKeys: storageKeys.length === files.length ? storageKeys : void 0,
|
|
2234
|
+
target: batch.target
|
|
1966
2235
|
});
|
|
1967
2236
|
}
|
|
1968
2237
|
}
|
|
@@ -1983,6 +2252,19 @@ function SagepilotChatProvider({
|
|
|
1983
2252
|
cancelled = true;
|
|
1984
2253
|
};
|
|
1985
2254
|
}, [getPendingCache, startDelivery, writeManifest]);
|
|
2255
|
+
const clearPing = (0, import_react.useCallback)(() => {
|
|
2256
|
+
pendingPingRef.current = null;
|
|
2257
|
+
if (pingTimeoutRef.current) {
|
|
2258
|
+
clearTimeout(pingTimeoutRef.current);
|
|
2259
|
+
pingTimeoutRef.current = null;
|
|
2260
|
+
}
|
|
2261
|
+
}, []);
|
|
2262
|
+
const clearDeferredLivenessProbe = (0, import_react.useCallback)(() => {
|
|
2263
|
+
if (livenessResumeTimerRef.current) {
|
|
2264
|
+
clearTimeout(livenessResumeTimerRef.current);
|
|
2265
|
+
livenessResumeTimerRef.current = null;
|
|
2266
|
+
}
|
|
2267
|
+
}, []);
|
|
1986
2268
|
(0, import_react.useEffect)(() => {
|
|
1987
2269
|
return () => {
|
|
1988
2270
|
if (deliveryTimerRef.current) {
|
|
@@ -1993,19 +2275,23 @@ function SagepilotChatProvider({
|
|
|
1993
2275
|
clearTimeout(pingTimeoutRef.current);
|
|
1994
2276
|
pingTimeoutRef.current = null;
|
|
1995
2277
|
}
|
|
2278
|
+
clearDeferredLivenessProbe();
|
|
1996
2279
|
};
|
|
1997
|
-
}, []);
|
|
1998
|
-
const clearPing = (0, import_react.useCallback)(() => {
|
|
1999
|
-
pendingPingRef.current = null;
|
|
2000
|
-
if (pingTimeoutRef.current) {
|
|
2001
|
-
clearTimeout(pingTimeoutRef.current);
|
|
2002
|
-
pingTimeoutRef.current = null;
|
|
2003
|
-
}
|
|
2004
|
-
}, []);
|
|
2280
|
+
}, [clearDeferredLivenessProbe]);
|
|
2005
2281
|
const runLivenessProbe = (0, import_react.useCallback)(() => {
|
|
2006
|
-
if (
|
|
2282
|
+
if (import_react_native3.Platform.OS !== "android") return;
|
|
2007
2283
|
const ref = nativeWebViewRef.current;
|
|
2008
2284
|
if (!ref || !internalSagepilotChat.isPresented()) return;
|
|
2285
|
+
const pauseRemainingMs = livenessPausedUntilRef.current - Date.now();
|
|
2286
|
+
if (pickerInFlightRef.current || pauseRemainingMs > 0) {
|
|
2287
|
+
const retryDelayMs = pickerInFlightRef.current ? 500 : Math.max(0, pauseRemainingMs);
|
|
2288
|
+
clearDeferredLivenessProbe();
|
|
2289
|
+
livenessResumeTimerRef.current = setTimeout(() => {
|
|
2290
|
+
livenessResumeTimerRef.current = null;
|
|
2291
|
+
runLivenessProbe();
|
|
2292
|
+
}, retryDelayMs);
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2009
2295
|
setAndroidRepaintTick((tick) => tick + 1);
|
|
2010
2296
|
const attempts = (pendingPingRef.current?.attempts ?? 0) + 1;
|
|
2011
2297
|
const nonce = `${Date.now().toString(36)}_${attempts}`;
|
|
@@ -2015,14 +2301,14 @@ function SagepilotChatProvider({
|
|
|
2015
2301
|
pingTimeoutRef.current = setTimeout(() => {
|
|
2016
2302
|
if (attempts >= LIVENESS_MAX_PING_ATTEMPTS) {
|
|
2017
2303
|
clearPing();
|
|
2018
|
-
recoverNativeWebView();
|
|
2304
|
+
recoverNativeWebView("liveness_timeout");
|
|
2019
2305
|
return;
|
|
2020
2306
|
}
|
|
2021
2307
|
runLivenessProbe();
|
|
2022
2308
|
}, LIVENESS_PONG_TIMEOUT_MS);
|
|
2023
|
-
}, [clearPing, recoverNativeWebView]);
|
|
2309
|
+
}, [clearDeferredLivenessProbe, clearPing, recoverNativeWebView]);
|
|
2024
2310
|
(0, import_react.useEffect)(() => {
|
|
2025
|
-
const subscription =
|
|
2311
|
+
const subscription = import_react_native3.AppState.addEventListener("change", (nextState) => {
|
|
2026
2312
|
const prev = appStateRef.current;
|
|
2027
2313
|
appStateRef.current = nextState;
|
|
2028
2314
|
if (nextState === "active" && (prev === "background" || prev === "inactive")) {
|
|
@@ -2050,11 +2336,11 @@ function SagepilotChatProvider({
|
|
|
2050
2336
|
const presentationStyle = state.presentation?.style ?? "sheet";
|
|
2051
2337
|
const isFullScreenModal = presentationStyle === "fullScreen";
|
|
2052
2338
|
const animationType = presentationStyle === "fullScreen" || presentationStyle === "push" ? "slide" : "fade";
|
|
2053
|
-
const ModalContainer =
|
|
2054
|
-
const NativeModalContainer =
|
|
2055
|
-
const ChatContentContainer =
|
|
2056
|
-
const nativeModalContainerProps =
|
|
2057
|
-
const chatContentContainerProps =
|
|
2339
|
+
const ModalContainer = import_react_native3.Platform.OS === "ios" && isFullScreenModal ? import_react_native3.SafeAreaView : import_react_native3.View;
|
|
2340
|
+
const NativeModalContainer = import_react_native3.Platform.OS === "android" ? AndroidInsetsView : ModalContainer;
|
|
2341
|
+
const ChatContentContainer = import_react_native3.Platform.OS === "ios" ? import_react_native3.KeyboardAvoidingView : import_react_native3.View;
|
|
2342
|
+
const nativeModalContainerProps = import_react_native3.Platform.OS === "android" ? { style: styles.container, onInsetsChange: handleAndroidInsetsChange } : { style: styles.container };
|
|
2343
|
+
const chatContentContainerProps = import_react_native3.Platform.OS === "ios" ? {
|
|
2058
2344
|
behavior: "padding",
|
|
2059
2345
|
enabled: true,
|
|
2060
2346
|
keyboardVerticalOffset: 0,
|
|
@@ -2062,7 +2348,7 @@ function SagepilotChatProvider({
|
|
|
2062
2348
|
} : {
|
|
2063
2349
|
style: [
|
|
2064
2350
|
styles.modalContent,
|
|
2065
|
-
|
|
2351
|
+
import_react_native3.Platform.OS === "android" ? {
|
|
2066
2352
|
paddingTop: androidModalInsets.top,
|
|
2067
2353
|
paddingBottom: androidModalInsets.bottom
|
|
2068
2354
|
} : null
|
|
@@ -2094,7 +2380,7 @@ function SagepilotChatProvider({
|
|
|
2094
2380
|
clearPing();
|
|
2095
2381
|
if (livenessRemountCountRef.current < LIVENESS_MAX_REMOUNTS) {
|
|
2096
2382
|
livenessRemountCountRef.current += 1;
|
|
2097
|
-
recoverNativeWebView();
|
|
2383
|
+
recoverNativeWebView("liveness_dead_pong");
|
|
2098
2384
|
}
|
|
2099
2385
|
} else {
|
|
2100
2386
|
livenessRemountCountRef.current = 0;
|
|
@@ -2116,7 +2402,12 @@ function SagepilotChatProvider({
|
|
|
2116
2402
|
if (!script || !nativeWebViewRef.current) return;
|
|
2117
2403
|
nativeWebViewRef.current.injectJavaScript(script);
|
|
2118
2404
|
};
|
|
2405
|
+
const injectNativeBridgeToNativeWebView = () => {
|
|
2406
|
+
if (!nativeWebViewRef.current) return;
|
|
2407
|
+
nativeWebViewRef.current.injectJavaScript(getInjectedWebViewScript());
|
|
2408
|
+
};
|
|
2119
2409
|
const handleNativeWebViewLoadEnd = () => {
|
|
2410
|
+
injectNativeBridgeToNativeWebView();
|
|
2120
2411
|
postIdentityToNativeWebView();
|
|
2121
2412
|
widgetReadyRef.current = false;
|
|
2122
2413
|
startDelivery();
|
|
@@ -2130,11 +2421,11 @@ function SagepilotChatProvider({
|
|
|
2130
2421
|
if (widgetOrigin && readUrlOrigin(url) === widgetOrigin) {
|
|
2131
2422
|
return true;
|
|
2132
2423
|
}
|
|
2133
|
-
|
|
2424
|
+
import_react_native3.Linking.openURL(url).catch(() => void 0);
|
|
2134
2425
|
return false;
|
|
2135
2426
|
}, [state.conversationUrl, state.preloadUrl]);
|
|
2136
2427
|
(0, import_react.useEffect)(() => {
|
|
2137
|
-
if (
|
|
2428
|
+
if (import_react_native3.Platform.OS !== "web" || typeof window === "undefined") return;
|
|
2138
2429
|
const trustedWidgetOrigin = readUrlOrigin(state.conversationUrl);
|
|
2139
2430
|
if (!trustedWidgetOrigin) return;
|
|
2140
2431
|
const handleWindowMessage = (event) => {
|
|
@@ -2145,16 +2436,16 @@ function SagepilotChatProvider({
|
|
|
2145
2436
|
return () => window.removeEventListener("message", handleWindowMessage);
|
|
2146
2437
|
}, [state.conversationUrl]);
|
|
2147
2438
|
(0, import_react.useEffect)(() => {
|
|
2148
|
-
if (
|
|
2439
|
+
if (import_react_native3.Platform.OS !== "web" || !state.isPresented) return;
|
|
2149
2440
|
postIdentityToWebFrame();
|
|
2150
2441
|
}, [state.isPresented, state.conversationUrl, state.configured]);
|
|
2151
2442
|
(0, import_react.useEffect)(() => {
|
|
2152
|
-
if (
|
|
2443
|
+
if (import_react_native3.Platform.OS === "web" || !state.isPresented) return;
|
|
2153
2444
|
postIdentityToNativeWebView();
|
|
2154
2445
|
}, [state.isPresented, state.conversationUrl, state.configured]);
|
|
2155
|
-
if (
|
|
2446
|
+
if (import_react_native3.Platform.OS === "web") {
|
|
2156
2447
|
return (0, import_react.createElement)(
|
|
2157
|
-
|
|
2448
|
+
import_react_native3.View,
|
|
2158
2449
|
{ style: styles.root },
|
|
2159
2450
|
children,
|
|
2160
2451
|
state.configured && !state.isPresented && state.shouldPreload && state.preloadUrl ? (0, import_react.createElement)("iframe", {
|
|
@@ -2163,20 +2454,20 @@ function SagepilotChatProvider({
|
|
|
2163
2454
|
title: "Sagepilot chat preload"
|
|
2164
2455
|
}) : null,
|
|
2165
2456
|
state.configured && state.isPresented && state.conversationUrl ? (0, import_react.createElement)(
|
|
2166
|
-
|
|
2457
|
+
import_react_native3.View,
|
|
2167
2458
|
{ style: styles.webOverlay },
|
|
2168
2459
|
(0, import_react.createElement)(
|
|
2169
|
-
|
|
2460
|
+
import_react_native3.View,
|
|
2170
2461
|
{ style: styles.webPanel },
|
|
2171
2462
|
showCloseButton ? (0, import_react.createElement)(
|
|
2172
|
-
|
|
2463
|
+
import_react_native3.Pressable,
|
|
2173
2464
|
{
|
|
2174
2465
|
accessibilityRole: "button",
|
|
2175
2466
|
accessibilityLabel: closeLabel,
|
|
2176
2467
|
onPress: () => internalSagepilotChat.dismiss(),
|
|
2177
2468
|
style: styles.webCloseButton
|
|
2178
2469
|
},
|
|
2179
|
-
(0, import_react.createElement)(
|
|
2470
|
+
(0, import_react.createElement)(import_react_native3.Text, { style: styles.closeText }, closeLabel)
|
|
2180
2471
|
) : null,
|
|
2181
2472
|
(0, import_react.createElement)("iframe", {
|
|
2182
2473
|
ref: webFrameRef,
|
|
@@ -2190,7 +2481,7 @@ function SagepilotChatProvider({
|
|
|
2190
2481
|
);
|
|
2191
2482
|
}
|
|
2192
2483
|
return (0, import_react.createElement)(
|
|
2193
|
-
|
|
2484
|
+
import_react_native3.View,
|
|
2194
2485
|
{ style: styles.root },
|
|
2195
2486
|
children,
|
|
2196
2487
|
state.configured && !state.isPresented && state.shouldPreload && state.preloadUrl ? (0, import_react.createElement)(import_react_native_webview.WebView, {
|
|
@@ -2209,13 +2500,13 @@ function SagepilotChatProvider({
|
|
|
2209
2500
|
onContentProcessDidTerminate: recoverPreloadWebView
|
|
2210
2501
|
}) : null,
|
|
2211
2502
|
(0, import_react.createElement)(
|
|
2212
|
-
|
|
2503
|
+
import_react_native3.Modal,
|
|
2213
2504
|
{
|
|
2214
2505
|
visible: state.configured && state.isPresented,
|
|
2215
2506
|
animationType,
|
|
2216
2507
|
presentationStyle: isFullScreenModal ? "fullScreen" : "pageSheet",
|
|
2217
|
-
statusBarTranslucent:
|
|
2218
|
-
navigationBarTranslucent:
|
|
2508
|
+
statusBarTranslucent: import_react_native3.Platform.OS === "android",
|
|
2509
|
+
navigationBarTranslucent: import_react_native3.Platform.OS === "android",
|
|
2219
2510
|
onRequestClose: () => internalSagepilotChat.dismiss()
|
|
2220
2511
|
},
|
|
2221
2512
|
(0, import_react.createElement)(
|
|
@@ -2225,17 +2516,17 @@ function SagepilotChatProvider({
|
|
|
2225
2516
|
ChatContentContainer,
|
|
2226
2517
|
chatContentContainerProps,
|
|
2227
2518
|
showCloseButton ? (0, import_react.createElement)(
|
|
2228
|
-
|
|
2519
|
+
import_react_native3.View,
|
|
2229
2520
|
{ style: styles.header },
|
|
2230
2521
|
(0, import_react.createElement)(
|
|
2231
|
-
|
|
2522
|
+
import_react_native3.Pressable,
|
|
2232
2523
|
{
|
|
2233
2524
|
accessibilityRole: "button",
|
|
2234
2525
|
accessibilityLabel: closeLabel,
|
|
2235
2526
|
onPress: () => internalSagepilotChat.dismiss(),
|
|
2236
2527
|
style: styles.closeButton
|
|
2237
2528
|
},
|
|
2238
|
-
(0, import_react.createElement)(
|
|
2529
|
+
(0, import_react.createElement)(import_react_native3.Text, { style: styles.closeText }, closeLabel)
|
|
2239
2530
|
)
|
|
2240
2531
|
) : null,
|
|
2241
2532
|
state.conversationUrl ? (0, import_react.createElement)(import_react_native_webview.WebView, {
|
|
@@ -2246,7 +2537,7 @@ function SagepilotChatProvider({
|
|
|
2246
2537
|
// The imperceptible opacity toggle forces the Android WebView
|
|
2247
2538
|
// surface to re-composite on resume, clearing the blank-but-alive
|
|
2248
2539
|
// surface bug without a reload (see runLivenessProbe).
|
|
2249
|
-
style:
|
|
2540
|
+
style: import_react_native3.Platform.OS === "android" ? [styles.webview, { opacity: 1 - androidRepaintTick % 2 * 1e-3 }] : styles.webview,
|
|
2250
2541
|
startInLoadingState: true,
|
|
2251
2542
|
injectedJavaScriptBeforeContentLoaded: getInjectedWebViewScript(),
|
|
2252
2543
|
onMessage: handleWebViewMessage,
|
|
@@ -2258,10 +2549,10 @@ function SagepilotChatProvider({
|
|
|
2258
2549
|
// iOS equivalent: WKWebView content process terminated.
|
|
2259
2550
|
onContentProcessDidTerminate: recoverNativeWebView,
|
|
2260
2551
|
renderLoading: () => (0, import_react.createElement)(
|
|
2261
|
-
|
|
2552
|
+
import_react_native3.View,
|
|
2262
2553
|
{ style: styles.loading },
|
|
2263
|
-
(0, import_react.createElement)(
|
|
2264
|
-
(0, import_react.createElement)(
|
|
2554
|
+
(0, import_react.createElement)(import_react_native3.ActivityIndicator, null),
|
|
2555
|
+
(0, import_react.createElement)(import_react_native3.Text, { style: styles.loadingText }, loadingLabel)
|
|
2265
2556
|
)
|
|
2266
2557
|
}) : null
|
|
2267
2558
|
)
|
|
@@ -2270,7 +2561,7 @@ function SagepilotChatProvider({
|
|
|
2270
2561
|
// Native attachment-source chooser. Replaces the Android Alert (capped at
|
|
2271
2562
|
// 3 buttons) so camera/library/documents all show on every platform.
|
|
2272
2563
|
(0, import_react.createElement)(
|
|
2273
|
-
|
|
2564
|
+
import_react_native3.Modal,
|
|
2274
2565
|
{
|
|
2275
2566
|
visible: sourceChooser !== null,
|
|
2276
2567
|
transparent: true,
|
|
@@ -2279,39 +2570,50 @@ function SagepilotChatProvider({
|
|
|
2279
2570
|
onRequestClose: () => setSourceChooser(null)
|
|
2280
2571
|
},
|
|
2281
2572
|
(0, import_react.createElement)(
|
|
2282
|
-
|
|
2573
|
+
import_react_native3.Pressable,
|
|
2283
2574
|
{ style: styles.sheetBackdrop, onPress: () => setSourceChooser(null) },
|
|
2284
2575
|
(0, import_react.createElement)(
|
|
2285
|
-
|
|
2576
|
+
import_react_native3.View,
|
|
2286
2577
|
{ style: styles.sheetCard },
|
|
2287
|
-
(0, import_react.createElement)(
|
|
2578
|
+
(0, import_react.createElement)(import_react_native3.View, { style: styles.sheetHandle }),
|
|
2579
|
+
(0, import_react.createElement)(import_react_native3.Text, { style: styles.sheetTitle }, "Add attachment"),
|
|
2288
2580
|
...(internalSagepilotChat.getConfig()?.filePicker?.sources ?? []).map(
|
|
2289
2581
|
(source) => (0, import_react.createElement)(
|
|
2290
|
-
|
|
2582
|
+
import_react_native3.Pressable,
|
|
2291
2583
|
{
|
|
2292
2584
|
key: source,
|
|
2293
2585
|
accessibilityRole: "button",
|
|
2294
|
-
|
|
2586
|
+
android_ripple: { color: "rgba(15, 23, 42, 0.06)" },
|
|
2587
|
+
hitSlop: 4,
|
|
2588
|
+
style: ({ pressed }) => getSheetOptionStyle(pressed),
|
|
2295
2589
|
onPress: () => handleSourceChoice(source)
|
|
2296
2590
|
},
|
|
2297
|
-
(
|
|
2591
|
+
renderFilePickerSourceIcon(source),
|
|
2592
|
+
(0, import_react.createElement)(
|
|
2593
|
+
import_react_native3.View,
|
|
2594
|
+
{ style: styles.sheetOptionText },
|
|
2595
|
+
(0, import_react.createElement)(import_react_native3.Text, { style: styles.sheetButtonText }, FILE_PICKER_SOURCE_LABELS[source]),
|
|
2596
|
+
(0, import_react.createElement)(import_react_native3.Text, { style: styles.sheetButtonDescription }, FILE_PICKER_SOURCE_DESCRIPTIONS[source])
|
|
2597
|
+
)
|
|
2298
2598
|
)
|
|
2299
2599
|
),
|
|
2300
2600
|
(0, import_react.createElement)(
|
|
2301
|
-
|
|
2601
|
+
import_react_native3.Pressable,
|
|
2302
2602
|
{
|
|
2303
2603
|
accessibilityRole: "button",
|
|
2304
|
-
|
|
2604
|
+
android_ripple: { color: "rgba(15, 23, 42, 0.06)" },
|
|
2605
|
+
style: ({ pressed }) => getSheetCancelStyle(pressed),
|
|
2305
2606
|
onPress: () => setSourceChooser(null)
|
|
2306
2607
|
},
|
|
2307
|
-
(0, import_react.createElement)(
|
|
2608
|
+
(0, import_react.createElement)(import_lucide_react_native.X, { color: "#334155", size: 18, strokeWidth: 2.5, style: styles.sheetCancelIcon }),
|
|
2609
|
+
(0, import_react.createElement)(import_react_native3.Text, { style: styles.sheetCancelText }, "Cancel")
|
|
2308
2610
|
)
|
|
2309
2611
|
)
|
|
2310
2612
|
)
|
|
2311
2613
|
)
|
|
2312
2614
|
);
|
|
2313
2615
|
}
|
|
2314
|
-
var styles =
|
|
2616
|
+
var styles = import_react_native3.StyleSheet.create({
|
|
2315
2617
|
root: {
|
|
2316
2618
|
flex: 1
|
|
2317
2619
|
},
|
|
@@ -2397,7 +2699,7 @@ var styles = import_react_native2.StyleSheet.create({
|
|
|
2397
2699
|
borderStyle: "none"
|
|
2398
2700
|
},
|
|
2399
2701
|
loading: {
|
|
2400
|
-
...
|
|
2702
|
+
...import_react_native3.StyleSheet.absoluteFillObject,
|
|
2401
2703
|
alignItems: "center",
|
|
2402
2704
|
justifyContent: "center",
|
|
2403
2705
|
backgroundColor: "#ffffff"
|
|
@@ -2409,43 +2711,109 @@ var styles = import_react_native2.StyleSheet.create({
|
|
|
2409
2711
|
},
|
|
2410
2712
|
sheetBackdrop: {
|
|
2411
2713
|
flex: 1,
|
|
2714
|
+
alignItems: "center",
|
|
2412
2715
|
justifyContent: "flex-end",
|
|
2413
|
-
backgroundColor: "rgba(
|
|
2716
|
+
backgroundColor: "rgba(15, 23, 42, 0.48)"
|
|
2414
2717
|
},
|
|
2415
2718
|
sheetCard: {
|
|
2719
|
+
width: "100%",
|
|
2720
|
+
maxWidth: 540,
|
|
2416
2721
|
backgroundColor: "#ffffff",
|
|
2417
|
-
borderTopLeftRadius:
|
|
2418
|
-
borderTopRightRadius:
|
|
2419
|
-
paddingTop:
|
|
2420
|
-
paddingBottom:
|
|
2421
|
-
paddingHorizontal:
|
|
2722
|
+
borderTopLeftRadius: 26,
|
|
2723
|
+
borderTopRightRadius: 26,
|
|
2724
|
+
paddingTop: 10,
|
|
2725
|
+
paddingBottom: 18,
|
|
2726
|
+
paddingHorizontal: 14,
|
|
2727
|
+
shadowColor: "#0f172a",
|
|
2728
|
+
shadowOpacity: 0.22,
|
|
2729
|
+
shadowRadius: 28,
|
|
2730
|
+
shadowOffset: { width: 0, height: -10 },
|
|
2731
|
+
elevation: 18
|
|
2732
|
+
},
|
|
2733
|
+
sheetHandle: {
|
|
2734
|
+
alignSelf: "center",
|
|
2735
|
+
width: 42,
|
|
2736
|
+
height: 5,
|
|
2737
|
+
borderRadius: 999,
|
|
2738
|
+
backgroundColor: "#d1d5db",
|
|
2739
|
+
marginBottom: 14
|
|
2422
2740
|
},
|
|
2423
2741
|
sheetTitle: {
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2742
|
+
color: "#111827",
|
|
2743
|
+
fontSize: 17,
|
|
2744
|
+
fontWeight: "700",
|
|
2745
|
+
paddingBottom: 12,
|
|
2746
|
+
paddingHorizontal: 4
|
|
2747
|
+
},
|
|
2748
|
+
sheetOption: {
|
|
2749
|
+
minHeight: 70,
|
|
2750
|
+
flexDirection: "row",
|
|
2751
|
+
alignItems: "center",
|
|
2752
|
+
borderRadius: 18,
|
|
2753
|
+
paddingHorizontal: 12,
|
|
2754
|
+
marginBottom: 6,
|
|
2755
|
+
backgroundColor: "#ffffff"
|
|
2756
|
+
},
|
|
2757
|
+
sheetOptionPressed: {
|
|
2758
|
+
backgroundColor: "#f8fafc",
|
|
2759
|
+
transform: [{ scale: 0.985 }]
|
|
2429
2760
|
},
|
|
2430
|
-
|
|
2431
|
-
|
|
2761
|
+
sheetOptionIcon: {
|
|
2762
|
+
width: 46,
|
|
2763
|
+
height: 46,
|
|
2764
|
+
borderRadius: 16,
|
|
2765
|
+
marginRight: 14,
|
|
2432
2766
|
alignItems: "center",
|
|
2433
2767
|
justifyContent: "center",
|
|
2434
|
-
|
|
2768
|
+
position: "relative",
|
|
2769
|
+
overflow: "hidden"
|
|
2770
|
+
},
|
|
2771
|
+
sheetOptionText: {
|
|
2772
|
+
flex: 1,
|
|
2773
|
+
justifyContent: "center"
|
|
2435
2774
|
},
|
|
2436
2775
|
sheetButtonText: {
|
|
2437
|
-
color: "#
|
|
2776
|
+
color: "#0f172a",
|
|
2438
2777
|
fontSize: 16,
|
|
2439
|
-
fontWeight: "
|
|
2778
|
+
fontWeight: "700"
|
|
2779
|
+
},
|
|
2780
|
+
sheetButtonDescription: {
|
|
2781
|
+
color: "#64748b",
|
|
2782
|
+
fontSize: 13,
|
|
2783
|
+
fontWeight: "500",
|
|
2784
|
+
marginTop: 3
|
|
2785
|
+
},
|
|
2786
|
+
cameraOptionIcon: {
|
|
2787
|
+
backgroundColor: "#e0f2fe"
|
|
2788
|
+
},
|
|
2789
|
+
libraryOptionIcon: {
|
|
2790
|
+
backgroundColor: "#ecfdf5"
|
|
2791
|
+
},
|
|
2792
|
+
documentsOptionIcon: {
|
|
2793
|
+
backgroundColor: "#f5f3ff"
|
|
2440
2794
|
},
|
|
2441
2795
|
sheetCancelButton: {
|
|
2442
|
-
|
|
2443
|
-
|
|
2796
|
+
minHeight: 56,
|
|
2797
|
+
flexDirection: "row",
|
|
2798
|
+
alignItems: "center",
|
|
2799
|
+
justifyContent: "center",
|
|
2800
|
+
borderRadius: 18,
|
|
2801
|
+
marginTop: 8,
|
|
2802
|
+
backgroundColor: "#f1f5f9"
|
|
2803
|
+
},
|
|
2804
|
+
sheetCancelButtonPressed: {
|
|
2805
|
+
backgroundColor: "#e2e8f0",
|
|
2806
|
+
transform: [{ scale: 0.985 }]
|
|
2807
|
+
},
|
|
2808
|
+
sheetCancelIcon: {
|
|
2809
|
+
width: 18,
|
|
2810
|
+
height: 18,
|
|
2811
|
+
marginRight: 8
|
|
2444
2812
|
},
|
|
2445
2813
|
sheetCancelText: {
|
|
2446
|
-
color: "#
|
|
2814
|
+
color: "#0f172a",
|
|
2447
2815
|
fontSize: 16,
|
|
2448
|
-
fontWeight: "
|
|
2816
|
+
fontWeight: "700"
|
|
2449
2817
|
}
|
|
2450
2818
|
});
|
|
2451
2819
|
|
|
@@ -2500,6 +2868,9 @@ function useSagepilotChat() {
|
|
|
2500
2868
|
}
|
|
2501
2869
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2502
2870
|
0 && (module.exports = {
|
|
2871
|
+
SAGEPILOT_DEFAULT_IMAGE_MAX_DIMENSION,
|
|
2872
|
+
SAGEPILOT_DEFAULT_IMAGE_MAX_FILE_SIZE_BYTES,
|
|
2873
|
+
SAGEPILOT_DEFAULT_IMAGE_QUALITY,
|
|
2503
2874
|
SagepilotChat,
|
|
2504
2875
|
SagepilotChatError,
|
|
2505
2876
|
SagepilotChatProvider,
|
|
@@ -2508,5 +2879,7 @@ function useSagepilotChat() {
|
|
|
2508
2879
|
createKeychainTokenStorage,
|
|
2509
2880
|
createSagepilotFilePicker,
|
|
2510
2881
|
createSagepilotFileStore,
|
|
2882
|
+
ensureSagepilotAndroidCameraPermission,
|
|
2883
|
+
promptOpenSagepilotCameraSettings,
|
|
2511
2884
|
useSagepilotChat
|
|
2512
2885
|
});
|