@paymanai/payman-ask-sdk 4.0.19 → 4.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +93 -26
- package/dist/index.d.ts +93 -26
- package/dist/index.js +1745 -391
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1737 -393
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +484 -25
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
- package/dist/index.native.js +0 -3681
- package/dist/index.native.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -40,7 +40,10 @@ var remarkBreaks__default = /*#__PURE__*/_interopDefault(remarkBreaks);
|
|
|
40
40
|
|
|
41
41
|
var __defProp = Object.defineProperty;
|
|
42
42
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
43
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, key + "", value);
|
|
43
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
44
|
+
var __defProp2 = Object.defineProperty;
|
|
45
|
+
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
46
|
+
var __publicField2 = (obj, key, value) => __defNormalProp2(obj, key + "", value);
|
|
44
47
|
function generateId() {
|
|
45
48
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
46
49
|
const r = Math.random() * 16 | 0;
|
|
@@ -736,7 +739,8 @@ function buildRequestBody(config, userMessage, sessionId, options) {
|
|
|
736
739
|
sessionAttributes,
|
|
737
740
|
analysisMode: options?.analysisMode,
|
|
738
741
|
locale: resolveLocale(config.locale),
|
|
739
|
-
timezone: resolveTimezone(config.timezone)
|
|
742
|
+
timezone: resolveTimezone(config.timezone),
|
|
743
|
+
...options?.attachments?.length ? { attachments: options.attachments } : {}
|
|
740
744
|
};
|
|
741
745
|
}
|
|
742
746
|
function resolveLocale(configured) {
|
|
@@ -781,6 +785,7 @@ function buildResolveImagesUrl(config) {
|
|
|
781
785
|
const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
|
|
782
786
|
return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
|
|
783
787
|
}
|
|
788
|
+
var NGROK_SKIP_BROWSER_WARNING = "ngrok-skip-browser-warning";
|
|
784
789
|
function buildRequestHeaders(config) {
|
|
785
790
|
const headers = {
|
|
786
791
|
...config.api.headers
|
|
@@ -788,6 +793,9 @@ function buildRequestHeaders(config) {
|
|
|
788
793
|
if (config.api.authToken) {
|
|
789
794
|
headers.Authorization = `Bearer ${config.api.authToken}`;
|
|
790
795
|
}
|
|
796
|
+
if (!headers[NGROK_SKIP_BROWSER_WARNING]) {
|
|
797
|
+
headers[NGROK_SKIP_BROWSER_WARNING] = "true";
|
|
798
|
+
}
|
|
791
799
|
return headers;
|
|
792
800
|
}
|
|
793
801
|
var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
|
|
@@ -1068,10 +1076,85 @@ function createCancelledMessageUpdate(steps, currentMessage) {
|
|
|
1068
1076
|
currentMessage: currentMessage || "Thinking..."
|
|
1069
1077
|
};
|
|
1070
1078
|
}
|
|
1079
|
+
var DEFAULT_SIGNED_URL_ENDPOINT = "/api/files/signed-url";
|
|
1080
|
+
function fileExtension(filename) {
|
|
1081
|
+
const ext = filename.split(".").pop()?.toLowerCase().replace(/^\./, "");
|
|
1082
|
+
return ext && ext.length > 0 ? ext : "bin";
|
|
1083
|
+
}
|
|
1084
|
+
function resolveMimeType(file) {
|
|
1085
|
+
if (file.type && file.type.trim().length > 0) return file.type;
|
|
1086
|
+
const ext = fileExtension(file.name);
|
|
1087
|
+
const byExt = {
|
|
1088
|
+
pdf: "application/pdf",
|
|
1089
|
+
png: "image/png",
|
|
1090
|
+
jpg: "image/jpeg",
|
|
1091
|
+
jpeg: "image/jpeg",
|
|
1092
|
+
gif: "image/gif",
|
|
1093
|
+
webp: "image/webp"
|
|
1094
|
+
};
|
|
1095
|
+
return byExt[ext] ?? "application/octet-stream";
|
|
1096
|
+
}
|
|
1097
|
+
function buildSignedUrlEndpoint(config, ext) {
|
|
1098
|
+
const endpoint = config.api.signedUrlEndpoint || DEFAULT_SIGNED_URL_ENDPOINT;
|
|
1099
|
+
const queryParams = new URLSearchParams({ extn: ext.replace(/^\./, "") });
|
|
1100
|
+
return `${config.api.baseUrl}${endpoint}?${queryParams.toString()}`;
|
|
1101
|
+
}
|
|
1102
|
+
async function requestSignedUrl(config, ext, signal) {
|
|
1103
|
+
const url = buildSignedUrlEndpoint(config, ext);
|
|
1104
|
+
const headers = buildRequestHeaders(config);
|
|
1105
|
+
const response = await fetch(url, {
|
|
1106
|
+
method: "GET",
|
|
1107
|
+
headers,
|
|
1108
|
+
signal
|
|
1109
|
+
});
|
|
1110
|
+
if (!response.ok) {
|
|
1111
|
+
const errorText = await response.text().catch(() => "");
|
|
1112
|
+
throw new Error(
|
|
1113
|
+
errorText ? `Failed to get upload URL (${response.status}): ${errorText}` : `Failed to get upload URL (${response.status})`
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
const data = await response.json();
|
|
1117
|
+
if (!data.key || !data.url) {
|
|
1118
|
+
throw new Error("Signed URL response missing key or url");
|
|
1119
|
+
}
|
|
1120
|
+
return { key: data.key, url: data.url };
|
|
1121
|
+
}
|
|
1122
|
+
async function uploadToSignedUrl(signedUrl, file, mimeType, signal) {
|
|
1123
|
+
const response = await fetch(signedUrl, {
|
|
1124
|
+
method: "PUT",
|
|
1125
|
+
headers: {
|
|
1126
|
+
"Content-Type": mimeType,
|
|
1127
|
+
"x-ms-blob-type": "BlockBlob"
|
|
1128
|
+
},
|
|
1129
|
+
body: file,
|
|
1130
|
+
signal
|
|
1131
|
+
});
|
|
1132
|
+
if (!response.ok) {
|
|
1133
|
+
const errorText = await response.text().catch(() => "");
|
|
1134
|
+
throw new Error(
|
|
1135
|
+
errorText ? `Failed to upload file (${response.status}): ${errorText}` : `Failed to upload file (${response.status})`
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
async function uploadAttachment(config, file, signal) {
|
|
1140
|
+
const ext = fileExtension(file.name);
|
|
1141
|
+
const mimeType = resolveMimeType(file);
|
|
1142
|
+
const { key, url } = await requestSignedUrl(config, ext, signal);
|
|
1143
|
+
await uploadToSignedUrl(url, file, mimeType, signal);
|
|
1144
|
+
return {
|
|
1145
|
+
tempKey: key,
|
|
1146
|
+
filename: file.name,
|
|
1147
|
+
mimeType
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
async function uploadAttachments(config, files, signal) {
|
|
1151
|
+
const uploads = files.map((file) => uploadAttachment(config, file, signal));
|
|
1152
|
+
return Promise.all(uploads);
|
|
1153
|
+
}
|
|
1071
1154
|
var UserActionStaleError = class extends Error {
|
|
1072
1155
|
constructor(userActionId, message = "User action is no longer actionable") {
|
|
1073
1156
|
super(message);
|
|
1074
|
-
|
|
1157
|
+
__publicField2(this, "userActionId");
|
|
1075
1158
|
this.name = "UserActionStaleError";
|
|
1076
1159
|
this.userActionId = userActionId;
|
|
1077
1160
|
}
|
|
@@ -1104,9 +1187,6 @@ async function cancelUserAction(config, userActionId) {
|
|
|
1104
1187
|
async function resendUserAction(config, userActionId) {
|
|
1105
1188
|
return sendUserActionRequest(config, userActionId, "resend");
|
|
1106
1189
|
}
|
|
1107
|
-
async function expireUserAction(config, userActionId) {
|
|
1108
|
-
return sendUserActionRequest(config, userActionId, "expired");
|
|
1109
|
-
}
|
|
1110
1190
|
var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
|
|
1111
1191
|
function upsertPrompt(prompts, req) {
|
|
1112
1192
|
const active = { ...req, status: "pending" };
|
|
@@ -1135,12 +1215,32 @@ function getStoredOrInitialMessages(config) {
|
|
|
1135
1215
|
function getSessionIdFromMessages(messages) {
|
|
1136
1216
|
return messages.find((message) => message.sessionId)?.sessionId;
|
|
1137
1217
|
}
|
|
1218
|
+
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set(["jpg", "jpeg", "png", "gif", "webp", "avif"]);
|
|
1219
|
+
function attachmentKindFromFile(file) {
|
|
1220
|
+
const ext = file.name.split(".").pop()?.toLowerCase() ?? "";
|
|
1221
|
+
if (IMAGE_EXTENSIONS.has(ext) || file.type.startsWith("image/")) return "image";
|
|
1222
|
+
return "file";
|
|
1223
|
+
}
|
|
1224
|
+
function buildMessageAttachments(files) {
|
|
1225
|
+
return files.map((file, index) => {
|
|
1226
|
+
const kind = attachmentKindFromFile(file);
|
|
1227
|
+
return {
|
|
1228
|
+
id: `att-${Date.now()}-${index}`,
|
|
1229
|
+
filename: file.name,
|
|
1230
|
+
mimeType: file.type || "application/octet-stream",
|
|
1231
|
+
// Blob URL for all local files so PDFs/docs stay previewable after send.
|
|
1232
|
+
previewUrl: URL.createObjectURL(file),
|
|
1233
|
+
kind
|
|
1234
|
+
};
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1138
1237
|
function useChatV2(config, callbacks = {}) {
|
|
1139
1238
|
const [messages, setMessages] = react.useState(() => getStoredOrInitialMessages(config));
|
|
1140
1239
|
const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(() => {
|
|
1141
1240
|
if (!config.userId) return false;
|
|
1142
1241
|
return activeStreamStore.get(config.userId)?.isWaiting ?? false;
|
|
1143
1242
|
});
|
|
1243
|
+
const [isUploadingAttachments, setIsUploadingAttachments] = react.useState(false);
|
|
1144
1244
|
const sessionIdRef = react.useRef(
|
|
1145
1245
|
getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
|
|
1146
1246
|
);
|
|
@@ -1215,21 +1315,45 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1215
1315
|
);
|
|
1216
1316
|
const sendMessage = react.useCallback(
|
|
1217
1317
|
async (userMessage, options) => {
|
|
1218
|
-
|
|
1318
|
+
const trimmedMessage = userMessage.trim();
|
|
1319
|
+
const files = options?.files ?? [];
|
|
1320
|
+
const hasPreuploadedAttachments = (options?.attachments?.length ?? 0) > 0;
|
|
1321
|
+
if (!trimmedMessage && files.length === 0 && !hasPreuploadedAttachments) return;
|
|
1322
|
+
let streamAttachments = options?.attachments;
|
|
1323
|
+
if (!streamAttachments && files.length > 0) {
|
|
1324
|
+
setIsUploadingAttachments(true);
|
|
1325
|
+
try {
|
|
1326
|
+
streamAttachments = await uploadAttachments(configRef.current, files);
|
|
1327
|
+
} catch (error) {
|
|
1328
|
+
if (error.name !== "AbortError") {
|
|
1329
|
+
callbacksRef.current.onError?.(error);
|
|
1330
|
+
}
|
|
1331
|
+
throw error;
|
|
1332
|
+
} finally {
|
|
1333
|
+
setIsUploadingAttachments(false);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1219
1336
|
if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
|
|
1220
1337
|
sessionIdRef.current = generateId();
|
|
1221
1338
|
callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
|
|
1222
1339
|
}
|
|
1340
|
+
const messageAttachments = files.length > 0 ? buildMessageAttachments(files) : streamAttachments?.length ? streamAttachments.map((attachment, index) => ({
|
|
1341
|
+
id: `att-${Date.now()}-${index}`,
|
|
1342
|
+
filename: attachment.filename,
|
|
1343
|
+
mimeType: attachment.mimeType,
|
|
1344
|
+
kind: attachment.mimeType.startsWith("image/") ? "image" : "file"
|
|
1345
|
+
})) : void 0;
|
|
1223
1346
|
const userMessageId = `user-${Date.now()}`;
|
|
1224
1347
|
const userMsg = {
|
|
1225
1348
|
id: userMessageId,
|
|
1226
1349
|
sessionId: sessionIdRef.current,
|
|
1227
1350
|
role: "user",
|
|
1228
|
-
content:
|
|
1229
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1351
|
+
content: trimmedMessage,
|
|
1352
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1353
|
+
attachments: messageAttachments
|
|
1230
1354
|
};
|
|
1231
1355
|
setMessages((prev) => [...prev, userMsg]);
|
|
1232
|
-
callbacksRef.current.onMessageSent?.(
|
|
1356
|
+
callbacksRef.current.onMessageSent?.(trimmedMessage);
|
|
1233
1357
|
setIsWaitingForResponse(true);
|
|
1234
1358
|
callbacksRef.current.onStreamStart?.();
|
|
1235
1359
|
const streamingId = `assistant-${Date.now()}`;
|
|
@@ -1256,11 +1380,14 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1256
1380
|
activeStreamStore.start(userId, abortController, initialMessages);
|
|
1257
1381
|
}
|
|
1258
1382
|
const newSessionId = await startStream(
|
|
1259
|
-
|
|
1383
|
+
trimmedMessage,
|
|
1260
1384
|
streamingId,
|
|
1261
1385
|
sessionIdRef.current,
|
|
1262
1386
|
abortController,
|
|
1263
|
-
|
|
1387
|
+
{
|
|
1388
|
+
analysisMode: options?.analysisMode,
|
|
1389
|
+
attachments: streamAttachments
|
|
1390
|
+
}
|
|
1264
1391
|
);
|
|
1265
1392
|
const finalStreamUserId = streamUserIdRef.current ?? userId;
|
|
1266
1393
|
if (finalStreamUserId) {
|
|
@@ -1398,19 +1525,6 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1398
1525
|
},
|
|
1399
1526
|
[setPromptStatus]
|
|
1400
1527
|
);
|
|
1401
|
-
const expireUserAction2 = react.useCallback(
|
|
1402
|
-
async (userActionId) => {
|
|
1403
|
-
setPromptStatus(userActionId, "expired");
|
|
1404
|
-
try {
|
|
1405
|
-
await expireUserAction(configRef.current, userActionId);
|
|
1406
|
-
} catch (error) {
|
|
1407
|
-
if (error instanceof UserActionStaleError) return;
|
|
1408
|
-
callbacksRef.current.onError?.(error);
|
|
1409
|
-
throw error;
|
|
1410
|
-
}
|
|
1411
|
-
},
|
|
1412
|
-
[setPromptStatus]
|
|
1413
|
-
);
|
|
1414
1528
|
const dismissNotification = react.useCallback((id) => {
|
|
1415
1529
|
setUserActionState((prev) => ({
|
|
1416
1530
|
...prev,
|
|
@@ -1451,6 +1565,18 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1451
1565
|
setMessages(config.initialMessages);
|
|
1452
1566
|
sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
|
|
1453
1567
|
}, [config.initialMessages, config.initialSessionId, config.userId]);
|
|
1568
|
+
const hydratedSessionIdRef = react.useRef(void 0);
|
|
1569
|
+
react.useEffect(() => {
|
|
1570
|
+
if (config.userId) return;
|
|
1571
|
+
if (!config.initialMessages?.length) return;
|
|
1572
|
+
const sessionId = config.initialSessionId;
|
|
1573
|
+
if (hydratedSessionIdRef.current === sessionId && messagesRef.current.length > 0) {
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
hydratedSessionIdRef.current = sessionId;
|
|
1577
|
+
setMessages(config.initialMessages);
|
|
1578
|
+
sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? sessionId;
|
|
1579
|
+
}, [config.initialMessages, config.initialSessionId, config.userId]);
|
|
1454
1580
|
react.useEffect(() => {
|
|
1455
1581
|
const prevUserId = prevUserIdRef.current;
|
|
1456
1582
|
prevUserIdRef.current = config.userId;
|
|
@@ -1485,12 +1611,12 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1485
1611
|
getSessionId,
|
|
1486
1612
|
getMessages,
|
|
1487
1613
|
isWaitingForResponse,
|
|
1614
|
+
isUploadingAttachments,
|
|
1488
1615
|
sessionId: sessionIdRef.current,
|
|
1489
1616
|
userActionState,
|
|
1490
1617
|
submitUserAction: submitUserAction2,
|
|
1491
1618
|
cancelUserAction: cancelUserAction2,
|
|
1492
1619
|
resendUserAction: resendUserAction2,
|
|
1493
|
-
expireUserAction: expireUserAction2,
|
|
1494
1620
|
dismissNotification
|
|
1495
1621
|
};
|
|
1496
1622
|
}
|
|
@@ -1709,6 +1835,102 @@ function useVoice(config = {}, callbacks = {}) {
|
|
|
1709
1835
|
reset
|
|
1710
1836
|
};
|
|
1711
1837
|
}
|
|
1838
|
+
function useAttachmentUpload(config) {
|
|
1839
|
+
const configRef = react.useRef(config);
|
|
1840
|
+
configRef.current = config;
|
|
1841
|
+
const [entries, setEntries] = react.useState(/* @__PURE__ */ new Map());
|
|
1842
|
+
const [orderedIds, setOrderedIds] = react.useState([]);
|
|
1843
|
+
const abortControllersRef = react.useRef(/* @__PURE__ */ new Map());
|
|
1844
|
+
const activeIdsRef = react.useRef(/* @__PURE__ */ new Set());
|
|
1845
|
+
const startUpload = react.useCallback((id, file) => {
|
|
1846
|
+
if (activeIdsRef.current.has(id)) return;
|
|
1847
|
+
activeIdsRef.current.add(id);
|
|
1848
|
+
const controller = new AbortController();
|
|
1849
|
+
abortControllersRef.current.set(id, controller);
|
|
1850
|
+
setEntries((prev) => {
|
|
1851
|
+
const next = new Map(prev);
|
|
1852
|
+
next.set(id, { id, status: "uploading" });
|
|
1853
|
+
return next;
|
|
1854
|
+
});
|
|
1855
|
+
void uploadAttachment(configRef.current, file, controller.signal).then((payload) => {
|
|
1856
|
+
if (controller.signal.aborted) return;
|
|
1857
|
+
setEntries((prev) => {
|
|
1858
|
+
const next = new Map(prev);
|
|
1859
|
+
next.set(id, { id, status: "done", payload });
|
|
1860
|
+
return next;
|
|
1861
|
+
});
|
|
1862
|
+
}).catch((error) => {
|
|
1863
|
+
if (error.name === "AbortError") return;
|
|
1864
|
+
setEntries((prev) => {
|
|
1865
|
+
const next = new Map(prev);
|
|
1866
|
+
next.set(id, {
|
|
1867
|
+
id,
|
|
1868
|
+
status: "error",
|
|
1869
|
+
error: error.message || "Upload failed"
|
|
1870
|
+
});
|
|
1871
|
+
return next;
|
|
1872
|
+
});
|
|
1873
|
+
}).finally(() => {
|
|
1874
|
+
abortControllersRef.current.delete(id);
|
|
1875
|
+
});
|
|
1876
|
+
}, []);
|
|
1877
|
+
const syncAttachments = react.useCallback(
|
|
1878
|
+
(attachments) => {
|
|
1879
|
+
const nextIds = attachments.map((attachment) => attachment.id);
|
|
1880
|
+
setOrderedIds(nextIds);
|
|
1881
|
+
const nextIdSet = new Set(nextIds);
|
|
1882
|
+
for (const id of activeIdsRef.current) {
|
|
1883
|
+
if (nextIdSet.has(id)) continue;
|
|
1884
|
+
abortControllersRef.current.get(id)?.abort();
|
|
1885
|
+
abortControllersRef.current.delete(id);
|
|
1886
|
+
activeIdsRef.current.delete(id);
|
|
1887
|
+
}
|
|
1888
|
+
setEntries((prev) => {
|
|
1889
|
+
const next = new Map(prev);
|
|
1890
|
+
for (const id of next.keys()) {
|
|
1891
|
+
if (!nextIdSet.has(id)) next.delete(id);
|
|
1892
|
+
}
|
|
1893
|
+
return next;
|
|
1894
|
+
});
|
|
1895
|
+
for (const attachment of attachments) {
|
|
1896
|
+
startUpload(attachment.id, attachment.file);
|
|
1897
|
+
}
|
|
1898
|
+
},
|
|
1899
|
+
[startUpload]
|
|
1900
|
+
);
|
|
1901
|
+
const clearAll = react.useCallback(() => {
|
|
1902
|
+
for (const controller of abortControllersRef.current.values()) {
|
|
1903
|
+
controller.abort();
|
|
1904
|
+
}
|
|
1905
|
+
abortControllersRef.current.clear();
|
|
1906
|
+
activeIdsRef.current.clear();
|
|
1907
|
+
setOrderedIds([]);
|
|
1908
|
+
setEntries(/* @__PURE__ */ new Map());
|
|
1909
|
+
}, []);
|
|
1910
|
+
const entryList = react.useMemo(
|
|
1911
|
+
() => orderedIds.map((id) => entries.get(id)).filter((entry) => entry != null),
|
|
1912
|
+
[entries, orderedIds]
|
|
1913
|
+
);
|
|
1914
|
+
const isUploading = entryList.some((entry) => entry.status === "uploading");
|
|
1915
|
+
const hasErrors = entryList.some((entry) => entry.status === "error");
|
|
1916
|
+
const allReady = orderedIds.length === 0 || orderedIds.every((id) => entries.get(id)?.status === "done");
|
|
1917
|
+
const payloads = entryList.filter((entry) => entry.status === "done" && entry.payload).map((entry) => entry.payload);
|
|
1918
|
+
const statusById = react.useMemo(
|
|
1919
|
+
() => Object.fromEntries(
|
|
1920
|
+
entryList.map((entry) => [entry.id, entry.status])
|
|
1921
|
+
),
|
|
1922
|
+
[entryList]
|
|
1923
|
+
);
|
|
1924
|
+
return {
|
|
1925
|
+
syncAttachments,
|
|
1926
|
+
clearAll,
|
|
1927
|
+
isUploading,
|
|
1928
|
+
hasErrors,
|
|
1929
|
+
allReady,
|
|
1930
|
+
payloads,
|
|
1931
|
+
statusById
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1712
1934
|
function classifyField(field) {
|
|
1713
1935
|
if (!field) return "text";
|
|
1714
1936
|
if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
|
|
@@ -1825,6 +2047,66 @@ function buildContent(schema, values) {
|
|
|
1825
2047
|
}
|
|
1826
2048
|
return content;
|
|
1827
2049
|
}
|
|
2050
|
+
var ATTACHMENTS_SUFFIX_RE = /\n\n\[Attachments:[^\]]*\]\s*$/;
|
|
2051
|
+
function stripAttachmentsSuffixFromIntent(intent) {
|
|
2052
|
+
return intent.replace(ATTACHMENTS_SUFFIX_RE, "").trimEnd();
|
|
2053
|
+
}
|
|
2054
|
+
function mapFeedback(feedback) {
|
|
2055
|
+
if (!feedback?.feedback) return null;
|
|
2056
|
+
return feedback.feedback === "POSITIVE" ? "up" : "down";
|
|
2057
|
+
}
|
|
2058
|
+
function mapHistoryAttachments(executionId, attachments) {
|
|
2059
|
+
const mapped = (attachments ?? []).map((attachment, index) => ({
|
|
2060
|
+
id: `${executionId}:att:${index}`,
|
|
2061
|
+
filename: attachment.filename,
|
|
2062
|
+
mimeType: attachment.mimeType,
|
|
2063
|
+
url: attachment.url ?? void 0,
|
|
2064
|
+
kind: attachment.mimeType.startsWith("image/") ? "image" : "file"
|
|
2065
|
+
}));
|
|
2066
|
+
return mapped.length > 0 ? mapped : void 0;
|
|
2067
|
+
}
|
|
2068
|
+
function mapExecutionHistoryToChatMessages(message) {
|
|
2069
|
+
const timestamp = message.startTime || message.endTime || (/* @__PURE__ */ new Date()).toISOString();
|
|
2070
|
+
const executionId = message.executionId ?? message.traceId ?? message.id;
|
|
2071
|
+
const attachments = mapHistoryAttachments(message.id, message.attachments);
|
|
2072
|
+
const rows = [
|
|
2073
|
+
{
|
|
2074
|
+
id: `${message.id}:user`,
|
|
2075
|
+
role: "user",
|
|
2076
|
+
content: stripAttachmentsSuffixFromIntent(message.sessionUserIntent),
|
|
2077
|
+
timestamp,
|
|
2078
|
+
attachments
|
|
2079
|
+
}
|
|
2080
|
+
];
|
|
2081
|
+
if (message.agentResponse) {
|
|
2082
|
+
rows.push({
|
|
2083
|
+
id: `${message.id}:assistant`,
|
|
2084
|
+
role: "assistant",
|
|
2085
|
+
content: message.agentResponse,
|
|
2086
|
+
timestamp: message.endTime || timestamp,
|
|
2087
|
+
isError: message.status === "FAILED",
|
|
2088
|
+
executionId,
|
|
2089
|
+
feedback: mapFeedback(message.feedback)
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2092
|
+
return rows;
|
|
2093
|
+
}
|
|
2094
|
+
function mapExecutionHistoryPageToChatMessages(messages) {
|
|
2095
|
+
return messages.flatMap(mapExecutionHistoryToChatMessages);
|
|
2096
|
+
}
|
|
2097
|
+
function attachmentDisplayUrl(attachment) {
|
|
2098
|
+
return attachment.previewUrl ?? attachment.url;
|
|
2099
|
+
}
|
|
2100
|
+
function isImageAttachment(attachment) {
|
|
2101
|
+
const displayUrl = attachmentDisplayUrl(attachment);
|
|
2102
|
+
return attachment.kind === "image" || Boolean(displayUrl) && attachment.mimeType.startsWith("image/");
|
|
2103
|
+
}
|
|
2104
|
+
function isPdfAttachmentMeta(attachment) {
|
|
2105
|
+
return attachment.mimeType === "application/pdf" || /\.pdf$/i.test(attachment.filename);
|
|
2106
|
+
}
|
|
2107
|
+
function isPdfFile(file) {
|
|
2108
|
+
return file.type === "application/pdf" || /\.pdf$/i.test(file.name);
|
|
2109
|
+
}
|
|
1828
2110
|
var PaymanChatContext = react.createContext(void 0);
|
|
1829
2111
|
function usePaymanChat() {
|
|
1830
2112
|
const context = react.useContext(PaymanChatContext);
|
|
@@ -1935,6 +2217,111 @@ function subscribeToCfRay(urlPattern, listener) {
|
|
|
1935
2217
|
};
|
|
1936
2218
|
}
|
|
1937
2219
|
|
|
2220
|
+
// src/utils/attachmentConfig.ts
|
|
2221
|
+
var DEFAULT_IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp"];
|
|
2222
|
+
var DEFAULT_DOCUMENT_EXTENSIONS = ["pdf", "docx", "xlsx", "xls"];
|
|
2223
|
+
function resolveChatAttachmentConfig(config) {
|
|
2224
|
+
const nested = config.attachments;
|
|
2225
|
+
const enabled = nested?.enabled ?? config.showAttachmentButton ?? true;
|
|
2226
|
+
const uploadImage = nested?.uploadImage ?? config.showUploadImageButton ?? true;
|
|
2227
|
+
const attachFile = nested?.attachFile ?? config.showAttachFileButton ?? true;
|
|
2228
|
+
return {
|
|
2229
|
+
showAttachmentButton: enabled && (uploadImage || attachFile),
|
|
2230
|
+
showUploadImageButton: enabled && uploadImage,
|
|
2231
|
+
showAttachFileButton: enabled && attachFile,
|
|
2232
|
+
maxCount: nested?.maxCount ?? nested?.maxImages ?? nested?.maxDocuments,
|
|
2233
|
+
maxFileBytes: nested?.maxFileBytes,
|
|
2234
|
+
maxTotalBytes: nested?.maxTotalBytes,
|
|
2235
|
+
allowedImageExtensions: nested?.imageExtensions ?? DEFAULT_IMAGE_EXTENSIONS,
|
|
2236
|
+
allowedFileExtensions: nested?.documentExtensions ?? DEFAULT_DOCUMENT_EXTENSIONS
|
|
2237
|
+
};
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
// src/utils/formatAttachmentBytes.ts
|
|
2241
|
+
function formatAttachmentBytes(bytes) {
|
|
2242
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
|
|
2243
|
+
const units = ["B", "KB", "MB", "GB"];
|
|
2244
|
+
let value = bytes;
|
|
2245
|
+
let unitIndex = 0;
|
|
2246
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
2247
|
+
value /= 1024;
|
|
2248
|
+
unitIndex += 1;
|
|
2249
|
+
}
|
|
2250
|
+
const rounded = unitIndex === 0 ? String(Math.round(value)) : value >= 10 ? value.toFixed(0) : value.toFixed(1).replace(/\.0$/, "");
|
|
2251
|
+
return `${rounded} ${units[unitIndex]}`;
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
// src/utils/pdfLink.ts
|
|
2255
|
+
function filenameFromContentDisposition(value) {
|
|
2256
|
+
if (!value) return void 0;
|
|
2257
|
+
let decoded = value;
|
|
2258
|
+
try {
|
|
2259
|
+
decoded = decodeURIComponent(value);
|
|
2260
|
+
} catch {
|
|
2261
|
+
decoded = value;
|
|
2262
|
+
}
|
|
2263
|
+
const quoted = decoded.match(/filename\*=(?:UTF-8''([^;]+)|"([^"]+)")/i);
|
|
2264
|
+
if (quoted?.[1] || quoted?.[2]) {
|
|
2265
|
+
return (quoted[1] ?? quoted[2])?.trim();
|
|
2266
|
+
}
|
|
2267
|
+
const unquoted = decoded.match(/filename=([^;]+)/i);
|
|
2268
|
+
return unquoted?.[1]?.replace(/"/g, "").trim();
|
|
2269
|
+
}
|
|
2270
|
+
function filenameFromUrlPath(href) {
|
|
2271
|
+
try {
|
|
2272
|
+
const parts = new URL(href).pathname.split("/").filter(Boolean);
|
|
2273
|
+
const last = parts[parts.length - 1];
|
|
2274
|
+
return last ? decodeURIComponent(last) : void 0;
|
|
2275
|
+
} catch {
|
|
2276
|
+
return void 0;
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
function isPdfUrl(href) {
|
|
2280
|
+
if (!href) return false;
|
|
2281
|
+
const lower = href.toLowerCase();
|
|
2282
|
+
if (lower.includes(".pdf")) return true;
|
|
2283
|
+
try {
|
|
2284
|
+
const url = new URL(href);
|
|
2285
|
+
if (url.pathname.toLowerCase().endsWith(".pdf")) return true;
|
|
2286
|
+
const filename = url.searchParams.get("filename");
|
|
2287
|
+
if (filename?.toLowerCase().includes(".pdf")) return true;
|
|
2288
|
+
for (const key of ["rscd", "response-content-disposition"]) {
|
|
2289
|
+
const fromDisposition = filenameFromContentDisposition(
|
|
2290
|
+
url.searchParams.get(key)
|
|
2291
|
+
);
|
|
2292
|
+
if (fromDisposition?.toLowerCase().includes(".pdf")) return true;
|
|
2293
|
+
}
|
|
2294
|
+
} catch {
|
|
2295
|
+
return lower.endsWith(".pdf");
|
|
2296
|
+
}
|
|
2297
|
+
return false;
|
|
2298
|
+
}
|
|
2299
|
+
function getPdfTitleFromUrl(href, linkText) {
|
|
2300
|
+
const text = linkText?.trim();
|
|
2301
|
+
if (text) return text;
|
|
2302
|
+
try {
|
|
2303
|
+
const url = new URL(href);
|
|
2304
|
+
const filename = url.searchParams.get("filename");
|
|
2305
|
+
if (filename) {
|
|
2306
|
+
return filename.replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
|
|
2307
|
+
}
|
|
2308
|
+
for (const key of ["rscd", "response-content-disposition"]) {
|
|
2309
|
+
const fromDisposition = filenameFromContentDisposition(
|
|
2310
|
+
url.searchParams.get(key)
|
|
2311
|
+
);
|
|
2312
|
+
if (fromDisposition) {
|
|
2313
|
+
return fromDisposition.replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
} catch {
|
|
2317
|
+
}
|
|
2318
|
+
const fromPath = filenameFromUrlPath(href);
|
|
2319
|
+
if (fromPath) {
|
|
2320
|
+
return fromPath.replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
|
|
2321
|
+
}
|
|
2322
|
+
return "Document";
|
|
2323
|
+
}
|
|
2324
|
+
|
|
1938
2325
|
// src/utils/slashCommands.ts
|
|
1939
2326
|
var DEFAULT_SLASH_COMMANDS = [
|
|
1940
2327
|
{
|
|
@@ -3086,6 +3473,108 @@ function ActionTooltipV2({ label, children }) {
|
|
|
3086
3473
|
] }) })
|
|
3087
3474
|
] });
|
|
3088
3475
|
}
|
|
3476
|
+
function FilePreviewShell({
|
|
3477
|
+
filename,
|
|
3478
|
+
typeLabel,
|
|
3479
|
+
onClick,
|
|
3480
|
+
thumbnail,
|
|
3481
|
+
className,
|
|
3482
|
+
"aria-label": ariaLabel
|
|
3483
|
+
}) {
|
|
3484
|
+
const shellClass = cn(
|
|
3485
|
+
"payman-v2-file-preview-shell",
|
|
3486
|
+
"payman-v2-file-preview-shell-sent",
|
|
3487
|
+
onClick && "payman-v2-file-preview-shell-clickable",
|
|
3488
|
+
className
|
|
3489
|
+
);
|
|
3490
|
+
const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3491
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-file-preview-thumb", children: thumbnail ?? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3492
|
+
lucideReact.FileText,
|
|
3493
|
+
{
|
|
3494
|
+
size: 16,
|
|
3495
|
+
strokeWidth: 1.75,
|
|
3496
|
+
className: "payman-v2-file-preview-doc-icon"
|
|
3497
|
+
}
|
|
3498
|
+
) }),
|
|
3499
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-file-preview-info", children: [
|
|
3500
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-file-preview-name", children: filename }),
|
|
3501
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-file-preview-type", children: typeLabel })
|
|
3502
|
+
] })
|
|
3503
|
+
] });
|
|
3504
|
+
if (onClick) {
|
|
3505
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3506
|
+
"button",
|
|
3507
|
+
{
|
|
3508
|
+
type: "button",
|
|
3509
|
+
className: shellClass,
|
|
3510
|
+
onClick,
|
|
3511
|
+
"aria-label": ariaLabel ?? `Open ${filename}`,
|
|
3512
|
+
children: body
|
|
3513
|
+
}
|
|
3514
|
+
);
|
|
3515
|
+
}
|
|
3516
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: shellClass, children: body });
|
|
3517
|
+
}
|
|
3518
|
+
function FilePreviewBlockLayout({
|
|
3519
|
+
children,
|
|
3520
|
+
className,
|
|
3521
|
+
...rest
|
|
3522
|
+
}) {
|
|
3523
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("payman-v2-file-preview-item", className), ...rest, children });
|
|
3524
|
+
}
|
|
3525
|
+
function attachmentTypeLabel(attachment) {
|
|
3526
|
+
if (isImageAttachment(attachment)) {
|
|
3527
|
+
return "Image";
|
|
3528
|
+
}
|
|
3529
|
+
const ext = attachment.filename.split(".").pop()?.toUpperCase();
|
|
3530
|
+
return ext || "Document";
|
|
3531
|
+
}
|
|
3532
|
+
function isPdfAttachment(attachment) {
|
|
3533
|
+
const displayUrl = attachmentDisplayUrl(attachment);
|
|
3534
|
+
return isPdfAttachmentMeta(attachment) || Boolean(displayUrl) && isPdfUrl(displayUrl);
|
|
3535
|
+
}
|
|
3536
|
+
function AttachmentPreviewBlock({
|
|
3537
|
+
attachment,
|
|
3538
|
+
onImageClick
|
|
3539
|
+
}) {
|
|
3540
|
+
const chatContext = react.useContext(PaymanChatContext);
|
|
3541
|
+
const displayUrl = attachmentDisplayUrl(attachment);
|
|
3542
|
+
const typeLabel = attachmentTypeLabel(attachment);
|
|
3543
|
+
const isImage = isImageAttachment(attachment) && displayUrl;
|
|
3544
|
+
const openAttachment = () => {
|
|
3545
|
+
if (!displayUrl) return;
|
|
3546
|
+
if (isImageAttachment(attachment)) {
|
|
3547
|
+
onImageClick?.(displayUrl, attachment.filename);
|
|
3548
|
+
return;
|
|
3549
|
+
}
|
|
3550
|
+
if (isPdfAttachment(attachment)) {
|
|
3551
|
+
chatContext?.openPdfSheet(displayUrl, attachment.filename);
|
|
3552
|
+
return;
|
|
3553
|
+
}
|
|
3554
|
+
window.open(displayUrl, "_blank", "noopener,noreferrer");
|
|
3555
|
+
};
|
|
3556
|
+
if (isImage) {
|
|
3557
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FilePreviewBlockLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3558
|
+
FilePreviewShell,
|
|
3559
|
+
{
|
|
3560
|
+
filename: attachment.filename,
|
|
3561
|
+
typeLabel,
|
|
3562
|
+
onClick: openAttachment,
|
|
3563
|
+
"aria-label": `Preview ${attachment.filename}`,
|
|
3564
|
+
thumbnail: /* @__PURE__ */ jsxRuntime.jsx("img", { src: displayUrl, alt: "", draggable: false })
|
|
3565
|
+
}
|
|
3566
|
+
) });
|
|
3567
|
+
}
|
|
3568
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FilePreviewBlockLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3569
|
+
FilePreviewShell,
|
|
3570
|
+
{
|
|
3571
|
+
filename: attachment.filename,
|
|
3572
|
+
typeLabel,
|
|
3573
|
+
onClick: displayUrl ? openAttachment : void 0,
|
|
3574
|
+
"aria-label": `Open ${attachment.filename}`
|
|
3575
|
+
}
|
|
3576
|
+
) });
|
|
3577
|
+
}
|
|
3089
3578
|
function formatMessageTime(timestamp) {
|
|
3090
3579
|
const value = new Date(timestamp);
|
|
3091
3580
|
if (Number.isNaN(value.getTime())) return "";
|
|
@@ -3098,6 +3587,7 @@ function UserMessageV2({
|
|
|
3098
3587
|
message,
|
|
3099
3588
|
onEdit,
|
|
3100
3589
|
onRetry,
|
|
3590
|
+
onImageClick,
|
|
3101
3591
|
retryDisabled = false,
|
|
3102
3592
|
actions
|
|
3103
3593
|
}) {
|
|
@@ -3180,7 +3670,15 @@ function UserMessageV2({
|
|
|
3180
3670
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3181
3671
|
toastPortal,
|
|
3182
3672
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-user-msg payman-v2-fade-in", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-user-msg-group", children: [
|
|
3183
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-user-msg-
|
|
3673
|
+
message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-file-preview payman-v2-user-msg-attachments", children: message.attachments.map((attachment) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3674
|
+
AttachmentPreviewBlock,
|
|
3675
|
+
{
|
|
3676
|
+
attachment,
|
|
3677
|
+
onImageClick
|
|
3678
|
+
},
|
|
3679
|
+
attachment.id
|
|
3680
|
+
)) }),
|
|
3681
|
+
(message.content.trim().length > 0 || !message.attachments?.length) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-user-msg-bubble", children: parsedCommand ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-user-msg-command", children: [
|
|
3184
3682
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-user-msg-command-chip", children: parsedCommand.command }),
|
|
3185
3683
|
parsedCommand.body.trim() ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-user-msg-text", children: parsedCommand.body }) : null
|
|
3186
3684
|
] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-user-msg-text", children: message.content }) }),
|
|
@@ -3405,9 +3903,44 @@ function MarkdownImageV2({
|
|
|
3405
3903
|
}
|
|
3406
3904
|
) });
|
|
3407
3905
|
}
|
|
3408
|
-
function
|
|
3906
|
+
function PdfBlockV2({ title, href, onOpen, autoOpen = false }) {
|
|
3907
|
+
const autoOpenedHrefRef = react.useRef(null);
|
|
3908
|
+
react.useEffect(() => {
|
|
3909
|
+
if (!autoOpen || autoOpenedHrefRef.current === href) return;
|
|
3910
|
+
autoOpenedHrefRef.current = href;
|
|
3911
|
+
onOpen(href, title, { auto: true });
|
|
3912
|
+
}, [href, autoOpen]);
|
|
3913
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FilePreviewBlockLayout, { "data-payman-file-block": true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3914
|
+
FilePreviewShell,
|
|
3915
|
+
{
|
|
3916
|
+
filename: title,
|
|
3917
|
+
typeLabel: "PDF",
|
|
3918
|
+
onClick: () => onOpen(href, title),
|
|
3919
|
+
"aria-label": `Open PDF: ${title}`
|
|
3920
|
+
}
|
|
3921
|
+
) });
|
|
3922
|
+
}
|
|
3923
|
+
function childrenToText(children) {
|
|
3924
|
+
if (typeof children === "string") return children;
|
|
3925
|
+
if (typeof children === "number") return String(children);
|
|
3926
|
+
if (Array.isArray(children)) return children.map(childrenToText).join("");
|
|
3927
|
+
if (children && typeof children === "object" && "props" in children) {
|
|
3928
|
+
return childrenToText(children.props.children);
|
|
3929
|
+
}
|
|
3930
|
+
return "";
|
|
3931
|
+
}
|
|
3932
|
+
function isFileBlockChild(child) {
|
|
3933
|
+
return react.isValidElement(child) && typeof child.props === "object" && child.props !== null && "data-payman-file-block" in child.props;
|
|
3934
|
+
}
|
|
3935
|
+
function buildComponents(onImageClick, isResolvingRef, onPdfClick, autoOpenPdf) {
|
|
3409
3936
|
return {
|
|
3410
|
-
p: ({ children }) =>
|
|
3937
|
+
p: ({ children }) => {
|
|
3938
|
+
const childArray = react.Children.toArray(children);
|
|
3939
|
+
if (childArray.length === 1 && isFileBlockChild(childArray[0])) {
|
|
3940
|
+
return childArray[0];
|
|
3941
|
+
}
|
|
3942
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { children });
|
|
3943
|
+
},
|
|
3411
3944
|
code: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("code", { children }),
|
|
3412
3945
|
pre: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-markdown-pre", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { children }) }),
|
|
3413
3946
|
ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { children }),
|
|
@@ -3420,7 +3953,15 @@ function buildComponents(onImageClick, isResolvingRef) {
|
|
|
3420
3953
|
em: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("em", { children }),
|
|
3421
3954
|
blockquote: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("blockquote", { children }),
|
|
3422
3955
|
hr: () => /* @__PURE__ */ jsxRuntime.jsx("hr", {}),
|
|
3423
|
-
a: ({ href, children }) =>
|
|
3956
|
+
a: ({ href, children }) => {
|
|
3957
|
+
const url = href ?? "";
|
|
3958
|
+
if (onPdfClick && isPdfUrl(url)) {
|
|
3959
|
+
const linkText = childrenToText(children).trim();
|
|
3960
|
+
const title = getPdfTitleFromUrl(url, linkText);
|
|
3961
|
+
return /* @__PURE__ */ jsxRuntime.jsx(PdfBlockV2, { href: url, title, onOpen: onPdfClick, autoOpen: autoOpenPdf });
|
|
3962
|
+
}
|
|
3963
|
+
return /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children });
|
|
3964
|
+
},
|
|
3424
3965
|
img: ({ src, alt }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3425
3966
|
MarkdownImageV2,
|
|
3426
3967
|
{
|
|
@@ -3441,13 +3982,15 @@ function MarkdownRendererV2({
|
|
|
3441
3982
|
content,
|
|
3442
3983
|
isStreaming,
|
|
3443
3984
|
isResolvingImages,
|
|
3444
|
-
onImageClick
|
|
3985
|
+
onImageClick,
|
|
3986
|
+
onPdfClick,
|
|
3987
|
+
autoOpenPdf
|
|
3445
3988
|
}) {
|
|
3446
3989
|
const isResolvingRef = react.useRef(isResolvingImages);
|
|
3447
3990
|
isResolvingRef.current = isResolvingImages;
|
|
3448
3991
|
const components = react.useMemo(
|
|
3449
|
-
() => buildComponents(onImageClick, isResolvingRef),
|
|
3450
|
-
[onImageClick]
|
|
3992
|
+
() => buildComponents(onImageClick, isResolvingRef, onPdfClick, autoOpenPdf),
|
|
3993
|
+
[onImageClick, onPdfClick, autoOpenPdf]
|
|
3451
3994
|
);
|
|
3452
3995
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3453
3996
|
"div",
|
|
@@ -3816,6 +4359,17 @@ function stripIncompleteImageToken(text) {
|
|
|
3816
4359
|
if (/^!\[[^\]]*\]\([^)]*\)/.test(after)) return text;
|
|
3817
4360
|
return text.slice(0, lastBang);
|
|
3818
4361
|
}
|
|
4362
|
+
function stripIncompleteLinkToken(text) {
|
|
4363
|
+
const lastBracket = text.lastIndexOf("[");
|
|
4364
|
+
if (lastBracket === -1) return text;
|
|
4365
|
+
if (lastBracket > 0 && text[lastBracket - 1] === "!") return text;
|
|
4366
|
+
const after = text.slice(lastBracket);
|
|
4367
|
+
if (/^\[[^\]]*\]\([^)]*\)/.test(after)) return text;
|
|
4368
|
+
return text.slice(0, lastBracket);
|
|
4369
|
+
}
|
|
4370
|
+
function stripIncompleteMarkdownTokens(text) {
|
|
4371
|
+
return stripIncompleteLinkToken(stripIncompleteImageToken(text));
|
|
4372
|
+
}
|
|
3819
4373
|
function getFeedbackState(message) {
|
|
3820
4374
|
const feedback = message.feedback;
|
|
3821
4375
|
if (feedback === "up" || feedback === "down") return feedback;
|
|
@@ -3839,6 +4393,12 @@ function AssistantMessageV2({
|
|
|
3839
4393
|
() => getFeedbackState(message)
|
|
3840
4394
|
);
|
|
3841
4395
|
const [reasonModalOpen, setReasonModalOpen] = react.useState(false);
|
|
4396
|
+
const chatContext = react.useContext(PaymanChatContext);
|
|
4397
|
+
const chatContextRef = react.useRef(chatContext);
|
|
4398
|
+
chatContextRef.current = chatContext;
|
|
4399
|
+
const handlePdfClick = react.useCallback((href, title, options) => {
|
|
4400
|
+
chatContextRef.current?.openPdfSheet(href, title, options);
|
|
4401
|
+
}, []);
|
|
3842
4402
|
const canSubmitFeedback = !!onSubmitFeedback && !!message.executionId;
|
|
3843
4403
|
const [toast, setToast] = react.useState(null);
|
|
3844
4404
|
const copyResetTimerRef = react.useRef(null);
|
|
@@ -3863,7 +4423,7 @@ function AssistantMessageV2({
|
|
|
3863
4423
|
const raw = message.isStreaming ? message.streamingContent || message.content : message.content;
|
|
3864
4424
|
if (!raw) return "";
|
|
3865
4425
|
const normalized = raw.replace(/\\n/g, "\n");
|
|
3866
|
-
return message.isStreaming ?
|
|
4426
|
+
return message.isStreaming ? stripIncompleteMarkdownTokens(normalized) : normalized;
|
|
3867
4427
|
})();
|
|
3868
4428
|
const isThinkingStreaming = !!message.isStreaming && !rawResponseContent && !message.isError;
|
|
3869
4429
|
const responseTypingEnabled = hasEverStreamed.current && Boolean(rawResponseContent) && !message.isError;
|
|
@@ -4033,10 +4593,12 @@ function AssistantMessageV2({
|
|
|
4033
4593
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-assistant-msg-content-area", children: displayContent ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4034
4594
|
MarkdownRendererV2,
|
|
4035
4595
|
{
|
|
4036
|
-
content: displayContent,
|
|
4596
|
+
content: message.isStreaming && !isCancelled || isResponseTyping ? stripIncompleteMarkdownTokens(displayContent) : displayContent,
|
|
4037
4597
|
isStreaming: message.isStreaming && !isCancelled || isResponseTyping,
|
|
4038
4598
|
isResolvingImages: message.isResolvingImages,
|
|
4039
|
-
onImageClick
|
|
4599
|
+
onImageClick,
|
|
4600
|
+
onPdfClick: handlePdfClick,
|
|
4601
|
+
autoOpenPdf: hasEverStreamed.current
|
|
4040
4602
|
}
|
|
4041
4603
|
) : !isThinkingStreaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-assistant-msg-placeholder", children: "..." }) : null }),
|
|
4042
4604
|
isCancelled && message.isStreaming && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-assistant-msg-paused", children: [
|
|
@@ -4253,7 +4815,6 @@ function VerificationInline({
|
|
|
4253
4815
|
const [code, setCode] = react.useState("");
|
|
4254
4816
|
const [errored, setErrored] = react.useState(false);
|
|
4255
4817
|
const [resendSec, setResendSec] = react.useState(0);
|
|
4256
|
-
const [hiddenAfterResend, setHiddenAfterResend] = react.useState(false);
|
|
4257
4818
|
const lastSubmittedRef = react.useRef(null);
|
|
4258
4819
|
const resendTimerRef = react.useRef(void 0);
|
|
4259
4820
|
const status = prompt.status;
|
|
@@ -4267,9 +4828,6 @@ function VerificationInline({
|
|
|
4267
4828
|
lastSubmittedRef.current = null;
|
|
4268
4829
|
}
|
|
4269
4830
|
}, [prompt.subAction, prompt.userActionId]);
|
|
4270
|
-
react.useEffect(() => {
|
|
4271
|
-
setHiddenAfterResend(false);
|
|
4272
|
-
}, [prompt.expirySeconds, prompt.message, prompt.subAction, prompt.userActionId]);
|
|
4273
4831
|
react.useEffect(() => {
|
|
4274
4832
|
if (code.length < codeLen) lastSubmittedRef.current = null;
|
|
4275
4833
|
}, [code, codeLen]);
|
|
@@ -4320,13 +4878,11 @@ function VerificationInline({
|
|
|
4320
4878
|
if (locked || resendSec > 0) return;
|
|
4321
4879
|
setErrored(false);
|
|
4322
4880
|
setCode("");
|
|
4323
|
-
setHiddenAfterResend(true);
|
|
4324
4881
|
lastSubmittedRef.current = null;
|
|
4325
4882
|
try {
|
|
4326
4883
|
await onResend(prompt.userActionId);
|
|
4327
4884
|
startResendCooldown();
|
|
4328
4885
|
} catch {
|
|
4329
|
-
setHiddenAfterResend(false);
|
|
4330
4886
|
}
|
|
4331
4887
|
}, [locked, onResend, prompt.userActionId, resendSec, startResendCooldown]);
|
|
4332
4888
|
const handleCancel = react.useCallback(() => {
|
|
@@ -4334,7 +4890,6 @@ function VerificationInline({
|
|
|
4334
4890
|
void onCancel(prompt.userActionId);
|
|
4335
4891
|
}, [busy, onCancel, prompt.userActionId]);
|
|
4336
4892
|
const description = prompt.message?.trim() || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
|
|
4337
|
-
if (hiddenAfterResend) return null;
|
|
4338
4893
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Verification required", children: [
|
|
4339
4894
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-head", children: [
|
|
4340
4895
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
|
|
@@ -4430,9 +4985,6 @@ function SchemaFormInline({
|
|
|
4430
4985
|
const busy = status === "submitting";
|
|
4431
4986
|
const stale = status === "stale";
|
|
4432
4987
|
const locked = busy || stale || expired;
|
|
4433
|
-
const isUserConfirmation = prompt.subAction === "UserConfirmation";
|
|
4434
|
-
const messageFormat = prompt.metadata?.["payman/messageFormat"];
|
|
4435
|
-
const renderMarkdown = messageFormat === "markdown";
|
|
4436
4988
|
const setValue = (key, value) => {
|
|
4437
4989
|
setValues((prev) => ({ ...prev, [key]: value }));
|
|
4438
4990
|
setErrors((prev) => {
|
|
@@ -4458,8 +5010,8 @@ function SchemaFormInline({
|
|
|
4458
5010
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
|
|
4459
5011
|
typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
|
|
4460
5012
|
] }),
|
|
4461
|
-
prompt.message?.trim() &&
|
|
4462
|
-
stale ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ?
|
|
5013
|
+
prompt.message?.trim() && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: prompt.message }),
|
|
5014
|
+
stale ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: "This action has no inputs to fill." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
|
|
4463
5015
|
const widget = classifyField(field);
|
|
4464
5016
|
const label = field.title || key;
|
|
4465
5017
|
const required = isRequired(schema, key);
|
|
@@ -4532,7 +5084,7 @@ function SchemaFormInline({
|
|
|
4532
5084
|
className: "payman-v2-ua-btn payman-v2-ua-btn-primary",
|
|
4533
5085
|
disabled: locked,
|
|
4534
5086
|
onClick: handleSubmit,
|
|
4535
|
-
children: busy ? "Submitting\u2026" :
|
|
5087
|
+
children: busy ? "Submitting\u2026" : "Submit"
|
|
4536
5088
|
}
|
|
4537
5089
|
),
|
|
4538
5090
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4588,25 +5140,10 @@ function useExpiryCountdown(prompt) {
|
|
|
4588
5140
|
const expired = initial !== void 0 && secondsLeft === 0;
|
|
4589
5141
|
return [secondsLeft, expired];
|
|
4590
5142
|
}
|
|
4591
|
-
function UserActionInline({
|
|
4592
|
-
prompt,
|
|
4593
|
-
onSubmit,
|
|
4594
|
-
onCancel,
|
|
4595
|
-
onResend,
|
|
4596
|
-
onExpired
|
|
4597
|
-
}) {
|
|
5143
|
+
function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
|
|
4598
5144
|
const [secondsLeft, expired] = useExpiryCountdown(prompt);
|
|
4599
|
-
react.useEffect(() => {
|
|
4600
|
-
if (expired && prompt.kind !== "notification") onExpired?.();
|
|
4601
|
-
}, [expired, onExpired, prompt.kind]);
|
|
4602
5145
|
let body;
|
|
4603
|
-
if (
|
|
4604
|
-
const note = {
|
|
4605
|
-
id: `${prompt.userActionId}-expired`,
|
|
4606
|
-
message: prompt.kind === "verification" ? "Verification Request Expired" : "User Form Expired"
|
|
4607
|
-
};
|
|
4608
|
-
body = /* @__PURE__ */ jsxRuntime.jsx(NotificationInline, { notification: note });
|
|
4609
|
-
} else if (prompt.kind === "verification") {
|
|
5146
|
+
if (prompt.kind === "verification") {
|
|
4610
5147
|
body = /* @__PURE__ */ jsxRuntime.jsx(
|
|
4611
5148
|
VerificationInline,
|
|
4612
5149
|
{
|
|
@@ -4646,23 +5183,10 @@ function UserActionInline({
|
|
|
4646
5183
|
}
|
|
4647
5184
|
var SCROLL_THRESHOLD2 = 100;
|
|
4648
5185
|
var USER_SCROLL_UP_EPSILON = 4;
|
|
4649
|
-
var PROMPT_KEY_SEPARATOR = "";
|
|
4650
|
-
function getPromptSlotKey(prompt) {
|
|
4651
|
-
return prompt.toolCallId || prompt.userActionId;
|
|
4652
|
-
}
|
|
4653
|
-
function getPromptViewKey(prompt) {
|
|
4654
|
-
return [
|
|
4655
|
-
getPromptSlotKey(prompt),
|
|
4656
|
-
prompt.userActionId,
|
|
4657
|
-
prompt.subAction ?? "",
|
|
4658
|
-
prompt.expirySeconds ?? "",
|
|
4659
|
-
prompt.message ?? ""
|
|
4660
|
-
].join(PROMPT_KEY_SEPARATOR);
|
|
4661
|
-
}
|
|
4662
5186
|
var MessageListV2 = react.forwardRef(
|
|
4663
5187
|
function MessageListV22({
|
|
4664
5188
|
messages,
|
|
4665
|
-
isStreaming = false,
|
|
5189
|
+
isStreaming: _isStreaming = false,
|
|
4666
5190
|
onEditUserMessage,
|
|
4667
5191
|
onRetryUserMessage,
|
|
4668
5192
|
onImageClick,
|
|
@@ -4674,10 +5198,10 @@ var MessageListV2 = react.forwardRef(
|
|
|
4674
5198
|
onSubmitUserAction,
|
|
4675
5199
|
onCancelUserAction,
|
|
4676
5200
|
onResendUserAction,
|
|
4677
|
-
onExpireUserAction,
|
|
4678
5201
|
onDismissNotification,
|
|
4679
5202
|
onSubmitFeedback,
|
|
4680
|
-
typingSpeed = 4
|
|
5203
|
+
typingSpeed = 4,
|
|
5204
|
+
sidePanelOpen = false
|
|
4681
5205
|
}, ref) {
|
|
4682
5206
|
const noop = react.useCallback(async () => {
|
|
4683
5207
|
}, []);
|
|
@@ -4685,11 +5209,11 @@ var MessageListV2 = react.forwardRef(
|
|
|
4685
5209
|
const scrollInnerRef = react.useRef(null);
|
|
4686
5210
|
const isNearBottomRef = react.useRef(true);
|
|
4687
5211
|
const [showScrollBtn, setShowScrollBtn] = react.useState(false);
|
|
4688
|
-
const [expiredPromptViewState, setExpiredPromptViewState] = react.useState({});
|
|
4689
|
-
const expiredUserActionIdsRef = react.useRef(/* @__PURE__ */ new Set());
|
|
4690
5212
|
const prevCountRef = react.useRef(messages.length);
|
|
4691
5213
|
const pauseStickUntilUserMessageRef = react.useRef(false);
|
|
4692
5214
|
const followingBottomRef = react.useRef(true);
|
|
5215
|
+
const sidePanelOpenRef = react.useRef(sidePanelOpen);
|
|
5216
|
+
sidePanelOpenRef.current = sidePanelOpen;
|
|
4693
5217
|
const lastPinAtRef = react.useRef(0);
|
|
4694
5218
|
const prevScrollTopRef = react.useRef(0);
|
|
4695
5219
|
const getDistanceFromBottom = react.useCallback(() => {
|
|
@@ -4697,92 +5221,6 @@ var MessageListV2 = react.forwardRef(
|
|
|
4697
5221
|
if (!el) return 0;
|
|
4698
5222
|
return el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
4699
5223
|
}, []);
|
|
4700
|
-
const messageActivityFingerprint = react.useMemo(() => {
|
|
4701
|
-
const last = messages[messages.length - 1];
|
|
4702
|
-
const promptFingerprint = (userActionPrompts ?? []).map((prompt) => `${getPromptViewKey(prompt)}:${prompt.status}`).join(PROMPT_KEY_SEPARATOR);
|
|
4703
|
-
const notificationFingerprint = (notifications ?? []).map((notification) => notification.id).join(PROMPT_KEY_SEPARATOR);
|
|
4704
|
-
if (!last) {
|
|
4705
|
-
return [
|
|
4706
|
-
0,
|
|
4707
|
-
isStreaming ? "streaming" : "idle",
|
|
4708
|
-
promptFingerprint,
|
|
4709
|
-
notificationFingerprint
|
|
4710
|
-
].join(PROMPT_KEY_SEPARATOR);
|
|
4711
|
-
}
|
|
4712
|
-
return [
|
|
4713
|
-
messages.length,
|
|
4714
|
-
isStreaming ? "streaming" : "idle",
|
|
4715
|
-
last.id,
|
|
4716
|
-
last.role,
|
|
4717
|
-
last.content ?? "",
|
|
4718
|
-
last.isStreaming ? "streaming" : "done",
|
|
4719
|
-
last.streamProgress ?? "",
|
|
4720
|
-
last.steps?.length ?? 0,
|
|
4721
|
-
last.errorDetails ?? "",
|
|
4722
|
-
promptFingerprint,
|
|
4723
|
-
notificationFingerprint
|
|
4724
|
-
].join(PROMPT_KEY_SEPARATOR);
|
|
4725
|
-
}, [isStreaming, messages, notifications, userActionPrompts]);
|
|
4726
|
-
const handleUserActionExpired = react.useCallback(
|
|
4727
|
-
(promptKey, userActionId) => {
|
|
4728
|
-
setExpiredPromptViewState((prev) => {
|
|
4729
|
-
if (prev[promptKey]) return prev;
|
|
4730
|
-
return {
|
|
4731
|
-
...prev,
|
|
4732
|
-
[promptKey]: { baseline: messageActivityFingerprint, hidden: false }
|
|
4733
|
-
};
|
|
4734
|
-
});
|
|
4735
|
-
if (!expiredUserActionIdsRef.current.has(userActionId)) {
|
|
4736
|
-
expiredUserActionIdsRef.current.add(userActionId);
|
|
4737
|
-
void onExpireUserAction?.(userActionId)?.catch(() => {
|
|
4738
|
-
});
|
|
4739
|
-
}
|
|
4740
|
-
},
|
|
4741
|
-
[messageActivityFingerprint, onExpireUserAction]
|
|
4742
|
-
);
|
|
4743
|
-
react.useEffect(() => {
|
|
4744
|
-
setExpiredPromptViewState((prev) => {
|
|
4745
|
-
let changed = false;
|
|
4746
|
-
const next = {};
|
|
4747
|
-
for (const [key, state] of Object.entries(prev)) {
|
|
4748
|
-
if (!state.hidden && state.baseline !== messageActivityFingerprint) {
|
|
4749
|
-
next[key] = { ...state, hidden: true };
|
|
4750
|
-
changed = true;
|
|
4751
|
-
} else {
|
|
4752
|
-
next[key] = state;
|
|
4753
|
-
}
|
|
4754
|
-
}
|
|
4755
|
-
return changed ? next : prev;
|
|
4756
|
-
});
|
|
4757
|
-
}, [messageActivityFingerprint]);
|
|
4758
|
-
react.useEffect(() => {
|
|
4759
|
-
const livePromptKeys = new Set((userActionPrompts ?? []).map(getPromptViewKey));
|
|
4760
|
-
const liveUserActionIds = new Set((userActionPrompts ?? []).map((p) => p.userActionId));
|
|
4761
|
-
for (const userActionId of expiredUserActionIdsRef.current) {
|
|
4762
|
-
if (!liveUserActionIds.has(userActionId)) {
|
|
4763
|
-
expiredUserActionIdsRef.current.delete(userActionId);
|
|
4764
|
-
}
|
|
4765
|
-
}
|
|
4766
|
-
setExpiredPromptViewState((prev) => {
|
|
4767
|
-
let changed = false;
|
|
4768
|
-
const next = {};
|
|
4769
|
-
for (const [key, state] of Object.entries(prev)) {
|
|
4770
|
-
if (livePromptKeys.has(key)) {
|
|
4771
|
-
next[key] = state;
|
|
4772
|
-
} else {
|
|
4773
|
-
changed = true;
|
|
4774
|
-
}
|
|
4775
|
-
}
|
|
4776
|
-
return changed ? next : prev;
|
|
4777
|
-
});
|
|
4778
|
-
}, [userActionPrompts]);
|
|
4779
|
-
const visibleUserActionPrompts = react.useMemo(
|
|
4780
|
-
() => userActionPrompts?.filter((prompt) => {
|
|
4781
|
-
const promptKey = getPromptViewKey(prompt);
|
|
4782
|
-
return !expiredPromptViewState[promptKey]?.hidden;
|
|
4783
|
-
}),
|
|
4784
|
-
[expiredPromptViewState, userActionPrompts]
|
|
4785
|
-
);
|
|
4786
5224
|
const pinToBottom = react.useCallback((behavior = "instant") => {
|
|
4787
5225
|
const el = scrollRef.current;
|
|
4788
5226
|
if (!el) return;
|
|
@@ -4823,17 +5261,25 @@ var MessageListV2 = react.forwardRef(
|
|
|
4823
5261
|
const nearBottom = distance <= SCROLL_THRESHOLD2;
|
|
4824
5262
|
isNearBottomRef.current = nearBottom;
|
|
4825
5263
|
setShowScrollBtn(!nearBottom);
|
|
4826
|
-
const sincePin = performance.now() - lastPinAtRef.current;
|
|
4827
|
-
if (sincePin < 250) return;
|
|
4828
5264
|
const scrolledUp = currentScrollTop < prevScrollTop - USER_SCROLL_UP_EPSILON;
|
|
4829
5265
|
if (scrolledUp) {
|
|
4830
5266
|
followingBottomRef.current = false;
|
|
4831
5267
|
pauseStickUntilUserMessageRef.current = true;
|
|
4832
|
-
|
|
5268
|
+
return;
|
|
5269
|
+
}
|
|
5270
|
+
const sincePin = performance.now() - lastPinAtRef.current;
|
|
5271
|
+
if (sincePin < 250) return;
|
|
5272
|
+
if (nearBottom) {
|
|
4833
5273
|
followingBottomRef.current = true;
|
|
4834
5274
|
pauseStickUntilUserMessageRef.current = false;
|
|
4835
5275
|
}
|
|
4836
5276
|
}, [getDistanceFromBottom]);
|
|
5277
|
+
const handleWheel = react.useCallback((e) => {
|
|
5278
|
+
if (e.deltaY < 0) {
|
|
5279
|
+
followingBottomRef.current = false;
|
|
5280
|
+
pauseStickUntilUserMessageRef.current = true;
|
|
5281
|
+
}
|
|
5282
|
+
}, []);
|
|
4837
5283
|
react.useEffect(() => {
|
|
4838
5284
|
const prevCount = prevCountRef.current;
|
|
4839
5285
|
prevCountRef.current = messages.length;
|
|
@@ -4843,7 +5289,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
4843
5289
|
pauseStickUntilUserMessageRef.current = false;
|
|
4844
5290
|
followingBottomRef.current = true;
|
|
4845
5291
|
requestAnimationFrame(() => scrollToBottom());
|
|
4846
|
-
} else if (!pauseStickUntilUserMessageRef.current && followingBottomRef.current) {
|
|
5292
|
+
} else if (!sidePanelOpenRef.current && !pauseStickUntilUserMessageRef.current && followingBottomRef.current) {
|
|
4847
5293
|
requestAnimationFrame(() => scrollToBottom("instant"));
|
|
4848
5294
|
}
|
|
4849
5295
|
}
|
|
@@ -4852,27 +5298,16 @@ var MessageListV2 = react.forwardRef(
|
|
|
4852
5298
|
const inner = scrollInnerRef.current;
|
|
4853
5299
|
if (!inner) return;
|
|
4854
5300
|
const pinIfFollowing = () => {
|
|
5301
|
+
if (sidePanelOpenRef.current) return;
|
|
4855
5302
|
if (pauseStickUntilUserMessageRef.current) return;
|
|
4856
5303
|
if (!followingBottomRef.current) return;
|
|
5304
|
+
if (getDistanceFromBottom() > SCROLL_THRESHOLD2) return;
|
|
4857
5305
|
pinToBottom("instant");
|
|
4858
5306
|
};
|
|
4859
|
-
const ro = new ResizeObserver(
|
|
4860
|
-
pinIfFollowing();
|
|
4861
|
-
});
|
|
5307
|
+
const ro = new ResizeObserver(pinIfFollowing);
|
|
4862
5308
|
ro.observe(inner);
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
});
|
|
4866
|
-
mo.observe(inner, {
|
|
4867
|
-
childList: true,
|
|
4868
|
-
subtree: true,
|
|
4869
|
-
characterData: true
|
|
4870
|
-
});
|
|
4871
|
-
return () => {
|
|
4872
|
-
ro.disconnect();
|
|
4873
|
-
mo.disconnect();
|
|
4874
|
-
};
|
|
4875
|
-
}, [pinToBottom]);
|
|
5309
|
+
return () => ro.disconnect();
|
|
5310
|
+
}, [pinToBottom, getDistanceFromBottom]);
|
|
4876
5311
|
react.useEffect(() => {
|
|
4877
5312
|
if (messages.length > 0) {
|
|
4878
5313
|
setTimeout(() => scrollToBottom("instant"), 50);
|
|
@@ -4884,6 +5319,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
4884
5319
|
{
|
|
4885
5320
|
ref: scrollRef,
|
|
4886
5321
|
onScroll: handleScroll,
|
|
5322
|
+
onWheel: handleWheel,
|
|
4887
5323
|
className: "payman-v2-message-scroll payman-v2-scrollbar",
|
|
4888
5324
|
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4889
5325
|
"div",
|
|
@@ -4897,6 +5333,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
4897
5333
|
message,
|
|
4898
5334
|
onEdit: onEditUserMessage,
|
|
4899
5335
|
onRetry: onRetryUserMessage,
|
|
5336
|
+
onImageClick,
|
|
4900
5337
|
retryDisabled,
|
|
4901
5338
|
actions: messageActions?.userMessageActions
|
|
4902
5339
|
}
|
|
@@ -4919,20 +5356,16 @@ var MessageListV2 = react.forwardRef(
|
|
|
4919
5356
|
},
|
|
4920
5357
|
note.id
|
|
4921
5358
|
)),
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
},
|
|
4933
|
-
promptKey
|
|
4934
|
-
);
|
|
4935
|
-
})
|
|
5359
|
+
userActionPrompts?.map((prompt) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5360
|
+
UserActionInline,
|
|
5361
|
+
{
|
|
5362
|
+
prompt,
|
|
5363
|
+
onSubmit: onSubmitUserAction ?? noop,
|
|
5364
|
+
onCancel: onCancelUserAction ?? noop,
|
|
5365
|
+
onResend: onResendUserAction ?? noop
|
|
5366
|
+
},
|
|
5367
|
+
prompt.toolCallId || prompt.userActionId
|
|
5368
|
+
))
|
|
4936
5369
|
]
|
|
4937
5370
|
}
|
|
4938
5371
|
)
|
|
@@ -4962,29 +5395,146 @@ var MessageListV2 = react.forwardRef(
|
|
|
4962
5395
|
] });
|
|
4963
5396
|
}
|
|
4964
5397
|
);
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
5398
|
+
function FilePreviewModal({ src, name, onClose }) {
|
|
5399
|
+
const [isMounted, setIsMounted] = react.useState(false);
|
|
5400
|
+
const [isLoaded, setIsLoaded] = react.useState(false);
|
|
5401
|
+
react.useEffect(() => {
|
|
5402
|
+
setIsMounted(true);
|
|
5403
|
+
return () => setIsMounted(false);
|
|
5404
|
+
}, []);
|
|
5405
|
+
react.useEffect(() => {
|
|
5406
|
+
setIsLoaded(false);
|
|
5407
|
+
}, [src]);
|
|
5408
|
+
const handleKeyDown = react.useCallback(
|
|
5409
|
+
(e) => {
|
|
5410
|
+
if (e.key === "Escape") onClose();
|
|
5411
|
+
},
|
|
5412
|
+
[onClose]
|
|
5413
|
+
);
|
|
5414
|
+
react.useEffect(() => {
|
|
5415
|
+
if (!src || typeof document === "undefined") return;
|
|
5416
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
5417
|
+
const prev = document.body.style.overflow;
|
|
5418
|
+
document.body.style.overflow = "hidden";
|
|
5419
|
+
return () => {
|
|
5420
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
5421
|
+
document.body.style.overflow = prev;
|
|
5422
|
+
};
|
|
5423
|
+
}, [src, handleKeyDown]);
|
|
5424
|
+
if (!isMounted || typeof document === "undefined") return null;
|
|
5425
|
+
return reactDom.createPortal(
|
|
5426
|
+
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: src ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5427
|
+
framerMotion.motion.div,
|
|
5428
|
+
{
|
|
5429
|
+
className: "payman-v2-file-preview-overlay",
|
|
5430
|
+
initial: { opacity: 0 },
|
|
5431
|
+
animate: { opacity: 1 },
|
|
5432
|
+
exit: { opacity: 0 },
|
|
5433
|
+
transition: { duration: 0.18 },
|
|
5434
|
+
onClick: onClose,
|
|
5435
|
+
role: "dialog",
|
|
5436
|
+
"aria-modal": "true",
|
|
5437
|
+
"aria-label": `Preview: ${name}`,
|
|
5438
|
+
children: [
|
|
5439
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5440
|
+
"button",
|
|
5441
|
+
{
|
|
5442
|
+
type: "button",
|
|
5443
|
+
className: "payman-v2-file-preview-close",
|
|
5444
|
+
"aria-label": "Close preview",
|
|
5445
|
+
onClick: (e) => {
|
|
5446
|
+
e.stopPropagation();
|
|
5447
|
+
onClose();
|
|
5448
|
+
},
|
|
5449
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 18, strokeWidth: 2 })
|
|
5450
|
+
}
|
|
5451
|
+
),
|
|
5452
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5453
|
+
framerMotion.motion.div,
|
|
5454
|
+
{
|
|
5455
|
+
className: "payman-v2-file-preview-inner",
|
|
5456
|
+
initial: { scale: 0.93, opacity: 0 },
|
|
5457
|
+
animate: { scale: isLoaded ? 1 : 0.93, opacity: isLoaded ? 1 : 0 },
|
|
5458
|
+
exit: { scale: 0.93, opacity: 0 },
|
|
5459
|
+
transition: { duration: 0.2 },
|
|
5460
|
+
onClick: (e) => e.stopPropagation(),
|
|
5461
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5462
|
+
"img",
|
|
5463
|
+
{
|
|
5464
|
+
src,
|
|
5465
|
+
alt: name,
|
|
5466
|
+
className: "payman-v2-file-preview-img",
|
|
5467
|
+
draggable: false,
|
|
5468
|
+
onLoad: () => setIsLoaded(true)
|
|
5469
|
+
}
|
|
5470
|
+
)
|
|
5471
|
+
}
|
|
5472
|
+
)
|
|
5473
|
+
]
|
|
5474
|
+
},
|
|
5475
|
+
"file-preview"
|
|
5476
|
+
) : null }),
|
|
5477
|
+
document.body
|
|
5478
|
+
);
|
|
5479
|
+
}
|
|
5480
|
+
function normalizeExtension(ext) {
|
|
5481
|
+
return ext.replace(/^\./, "").toLowerCase();
|
|
5482
|
+
}
|
|
5483
|
+
function extOf(filename) {
|
|
5484
|
+
return filename.split(".").pop()?.toLowerCase() ?? "";
|
|
5485
|
+
}
|
|
5486
|
+
function isAllowedImage(file, allowedExtensions) {
|
|
5487
|
+
const allowed = new Set(allowedExtensions.map(normalizeExtension));
|
|
5488
|
+
const ext = extOf(file.name);
|
|
5489
|
+
if (allowed.has(ext)) return true;
|
|
5490
|
+
if (!file.type.startsWith("image/")) return false;
|
|
5491
|
+
const mimeSubtype = file.type.slice("image/".length).toLowerCase();
|
|
5492
|
+
return allowed.has(mimeSubtype) || mimeSubtype === "jpeg" && allowed.has("jpg");
|
|
5493
|
+
}
|
|
5494
|
+
function isAllowedDocument(file, allowedExtensions) {
|
|
5495
|
+
const allowed = new Set(allowedExtensions.map(normalizeExtension));
|
|
5496
|
+
return allowed.has(extOf(file.name));
|
|
5497
|
+
}
|
|
5498
|
+
function fileTypeLabel(file, kind) {
|
|
5499
|
+
if (kind === "image") return "Image";
|
|
5500
|
+
const ext = extOf(file.name);
|
|
5501
|
+
return ext ? ext.toUpperCase() : "Document";
|
|
5502
|
+
}
|
|
5503
|
+
var ChatInputV2 = react.forwardRef(
|
|
5504
|
+
function ChatInputV22({
|
|
5505
|
+
onSend,
|
|
5506
|
+
disabled = false,
|
|
5507
|
+
isStreaming = false,
|
|
5508
|
+
isUploadingAttachments = false,
|
|
5509
|
+
attachmentsReady = true,
|
|
5510
|
+
hasAttachmentUploadErrors = false,
|
|
5511
|
+
attachmentUploadStatusById = {},
|
|
5512
|
+
uploadedAttachmentPayloads = [],
|
|
5513
|
+
placeholder = "Reply...",
|
|
5514
|
+
enableVoice = false,
|
|
5515
|
+
voiceAvailable = false,
|
|
5516
|
+
isRecording = false,
|
|
5517
|
+
onVoicePress,
|
|
5518
|
+
transcribedText = "",
|
|
5519
|
+
showResetSession = false,
|
|
5520
|
+
onResetSession,
|
|
5521
|
+
showAttachmentButton = true,
|
|
5522
|
+
showUploadImageButton = true,
|
|
5523
|
+
showAttachFileButton = true,
|
|
5524
|
+
onUploadImageClick,
|
|
5525
|
+
onAttachFileClick,
|
|
5526
|
+
allowedImageExtensions = ["png", "jpg", "jpeg", "gif", "webp"],
|
|
5527
|
+
allowedFileExtensions = ["pdf", "docx", "xlsx", "xls"],
|
|
5528
|
+
maxCount,
|
|
5529
|
+
maxFileBytes,
|
|
5530
|
+
maxTotalBytes,
|
|
5531
|
+
onFilesChange,
|
|
5532
|
+
onAttachmentsChange,
|
|
5533
|
+
editingMessageId = null,
|
|
5534
|
+
onClearEditing,
|
|
5535
|
+
analysisMode,
|
|
5536
|
+
onAnalysisModeChange,
|
|
5537
|
+
slashCommands = []
|
|
4988
5538
|
}, ref) {
|
|
4989
5539
|
const [value, setValue] = react.useState("");
|
|
4990
5540
|
const [isFocused, setIsFocused] = react.useState(false);
|
|
@@ -4993,14 +5543,133 @@ var ChatInputV2 = react.forwardRef(
|
|
|
4993
5543
|
const [selectedCommandIndex, setSelectedCommandIndex] = react.useState(0);
|
|
4994
5544
|
const [inlineHint, setInlineHint] = react.useState(null);
|
|
4995
5545
|
const [commandMenuDismissed, setCommandMenuDismissed] = react.useState(false);
|
|
5546
|
+
const [attachedFiles, setAttachedFiles] = react.useState([]);
|
|
5547
|
+
const [previewFile, setPreviewFile] = react.useState(null);
|
|
5548
|
+
const [isSending, setIsSending] = react.useState(false);
|
|
4996
5549
|
const textareaRef = react.useRef(null);
|
|
4997
5550
|
const actionsRef = react.useRef(null);
|
|
5551
|
+
const imageInputRef = react.useRef(null);
|
|
5552
|
+
const fileInputRef = react.useRef(null);
|
|
4998
5553
|
const preRecordTextRef = react.useRef("");
|
|
4999
5554
|
const voiceTooltipTimerRef = react.useRef(
|
|
5000
5555
|
null
|
|
5001
5556
|
);
|
|
5002
5557
|
const voiceDraftSyncActiveRef = react.useRef(false);
|
|
5003
|
-
const
|
|
5558
|
+
const chatContext = react.useContext(PaymanChatContext);
|
|
5559
|
+
react.useEffect(() => {
|
|
5560
|
+
return () => {
|
|
5561
|
+
attachedFiles.forEach((f) => URL.revokeObjectURL(f.objectUrl));
|
|
5562
|
+
};
|
|
5563
|
+
}, []);
|
|
5564
|
+
const notifyAttachmentList = react.useCallback(
|
|
5565
|
+
(files) => {
|
|
5566
|
+
onFilesChange?.(files.map((f) => f.file));
|
|
5567
|
+
onAttachmentsChange?.(files.map((f) => ({ id: f.id, file: f.file })));
|
|
5568
|
+
},
|
|
5569
|
+
[onAttachmentsChange, onFilesChange]
|
|
5570
|
+
);
|
|
5571
|
+
const clearAttachmentsFromInput = react.useCallback(() => {
|
|
5572
|
+
setAttachedFiles((prev) => {
|
|
5573
|
+
prev.forEach((f) => URL.revokeObjectURL(f.objectUrl));
|
|
5574
|
+
return [];
|
|
5575
|
+
});
|
|
5576
|
+
setPreviewFile(null);
|
|
5577
|
+
onFilesChange?.([]);
|
|
5578
|
+
onAttachmentsChange?.([]);
|
|
5579
|
+
}, [onAttachmentsChange, onFilesChange]);
|
|
5580
|
+
const addFiles = react.useCallback(
|
|
5581
|
+
(incoming, source) => {
|
|
5582
|
+
const isImage = source === "image";
|
|
5583
|
+
const allowedExtensions = isImage ? allowedImageExtensions : allowedFileExtensions;
|
|
5584
|
+
const isAllowed = isImage ? isAllowedImage : isAllowedDocument;
|
|
5585
|
+
const kindLabel = isImage ? "image" : "document";
|
|
5586
|
+
setAttachedFiles((prev) => {
|
|
5587
|
+
const accepted = [];
|
|
5588
|
+
let rejectedType = false;
|
|
5589
|
+
let hitCountLimit = false;
|
|
5590
|
+
let hitFileSizeLimit = false;
|
|
5591
|
+
let hitTotalSizeLimit = false;
|
|
5592
|
+
let oversizedName = "";
|
|
5593
|
+
const currentTotalBytes = prev.reduce(
|
|
5594
|
+
(sum, item) => sum + item.file.size,
|
|
5595
|
+
0
|
|
5596
|
+
);
|
|
5597
|
+
let addedBytes = 0;
|
|
5598
|
+
for (const file of incoming) {
|
|
5599
|
+
if (!isAllowed(file, allowedExtensions)) {
|
|
5600
|
+
rejectedType = true;
|
|
5601
|
+
continue;
|
|
5602
|
+
}
|
|
5603
|
+
if (maxFileBytes != null && file.size > maxFileBytes) {
|
|
5604
|
+
hitFileSizeLimit = true;
|
|
5605
|
+
oversizedName = file.name;
|
|
5606
|
+
continue;
|
|
5607
|
+
}
|
|
5608
|
+
if (maxTotalBytes != null && currentTotalBytes + addedBytes + file.size > maxTotalBytes) {
|
|
5609
|
+
hitTotalSizeLimit = true;
|
|
5610
|
+
break;
|
|
5611
|
+
}
|
|
5612
|
+
if (maxCount != null && prev.length + accepted.length >= maxCount) {
|
|
5613
|
+
hitCountLimit = true;
|
|
5614
|
+
break;
|
|
5615
|
+
}
|
|
5616
|
+
accepted.push({
|
|
5617
|
+
id: `${Date.now()}-${Math.random()}`,
|
|
5618
|
+
file,
|
|
5619
|
+
kind: source,
|
|
5620
|
+
objectUrl: URL.createObjectURL(file)
|
|
5621
|
+
});
|
|
5622
|
+
addedBytes += file.size;
|
|
5623
|
+
}
|
|
5624
|
+
if (rejectedType) {
|
|
5625
|
+
setInlineHint(
|
|
5626
|
+
`Unsupported ${kindLabel} type. Allowed: ${allowedExtensions.map(normalizeExtension).join(", ")}.`
|
|
5627
|
+
);
|
|
5628
|
+
} else if (hitFileSizeLimit) {
|
|
5629
|
+
setInlineHint(
|
|
5630
|
+
maxFileBytes != null ? `"${oversizedName}" exceeds the ${formatAttachmentBytes(maxFileBytes)} per-file limit.` : "File exceeds the maximum allowed size."
|
|
5631
|
+
);
|
|
5632
|
+
} else if (hitTotalSizeLimit) {
|
|
5633
|
+
setInlineHint(
|
|
5634
|
+
maxTotalBytes != null ? `Total attachment size would exceed ${formatAttachmentBytes(maxTotalBytes)}.` : "Total attachment size exceeds the limit."
|
|
5635
|
+
);
|
|
5636
|
+
} else if (hitCountLimit) {
|
|
5637
|
+
setInlineHint(
|
|
5638
|
+
maxCount === 1 ? "Only 1 attachment is allowed per message." : `Maximum ${maxCount} attachments allowed per message.`
|
|
5639
|
+
);
|
|
5640
|
+
}
|
|
5641
|
+
if (accepted.length === 0) return prev;
|
|
5642
|
+
const updated = [...prev, ...accepted];
|
|
5643
|
+
notifyAttachmentList(updated);
|
|
5644
|
+
return updated;
|
|
5645
|
+
});
|
|
5646
|
+
},
|
|
5647
|
+
[
|
|
5648
|
+
allowedFileExtensions,
|
|
5649
|
+
allowedImageExtensions,
|
|
5650
|
+
maxCount,
|
|
5651
|
+
maxFileBytes,
|
|
5652
|
+
maxTotalBytes,
|
|
5653
|
+
notifyAttachmentList
|
|
5654
|
+
]
|
|
5655
|
+
);
|
|
5656
|
+
const removeFile = react.useCallback(
|
|
5657
|
+
(id) => {
|
|
5658
|
+
setAttachedFiles((prev) => {
|
|
5659
|
+
const target = prev.find((f) => f.id === id);
|
|
5660
|
+
if (target) URL.revokeObjectURL(target.objectUrl);
|
|
5661
|
+
const updated = prev.filter((f) => f.id !== id);
|
|
5662
|
+
notifyAttachmentList(updated);
|
|
5663
|
+
return updated;
|
|
5664
|
+
});
|
|
5665
|
+
setPreviewFile((p) => p?.id === id ? null : p);
|
|
5666
|
+
},
|
|
5667
|
+
[notifyAttachmentList]
|
|
5668
|
+
);
|
|
5669
|
+
const imageAccept = allowedImageExtensions.map((e) => `.${e.replace(/^\./, "")}`).join(",");
|
|
5670
|
+
const fileAccept = allowedFileExtensions.map((e) => `.${e.replace(/^\./, "")}`).join(",");
|
|
5671
|
+
const isInputBusy = isSending || isUploadingAttachments;
|
|
5672
|
+
const isInputLocked = disabled || isRecording || isInputBusy;
|
|
5004
5673
|
const hasAttachmentOptions = showUploadImageButton || showAttachFileButton;
|
|
5005
5674
|
const showAttachmentMenuButton = showAttachmentButton && hasAttachmentOptions;
|
|
5006
5675
|
const showVoiceButton = enableVoice && onVoicePress != null;
|
|
@@ -5040,9 +5709,10 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5040
5709
|
const end = message.length;
|
|
5041
5710
|
textarea.setSelectionRange(end, end);
|
|
5042
5711
|
});
|
|
5043
|
-
}
|
|
5712
|
+
},
|
|
5713
|
+
clearAttachments: clearAttachmentsFromInput
|
|
5044
5714
|
}),
|
|
5045
|
-
[disabled]
|
|
5715
|
+
[disabled, clearAttachmentsFromInput]
|
|
5046
5716
|
);
|
|
5047
5717
|
react.useEffect(() => {
|
|
5048
5718
|
if (!showActions) return;
|
|
@@ -5076,8 +5746,23 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5076
5746
|
const separator = base && !base.endsWith(" ") && transcribedText ? " " : "";
|
|
5077
5747
|
setValue(`${base}${separator}${transcribedText}`);
|
|
5078
5748
|
}, [isRecording, transcribedText]);
|
|
5079
|
-
|
|
5080
|
-
if (
|
|
5749
|
+
function uploadStatusLabel(status, file, kind) {
|
|
5750
|
+
if (status === "uploading") return "Uploading\u2026";
|
|
5751
|
+
if (status === "error") return "Upload failed";
|
|
5752
|
+
return fileTypeLabel(file, kind);
|
|
5753
|
+
}
|
|
5754
|
+
const handleSend = react.useCallback(async () => {
|
|
5755
|
+
const hasText = value.trim().length > 0;
|
|
5756
|
+
const hasFiles = attachedFiles.length > 0;
|
|
5757
|
+
if (!hasText && !hasFiles || disabled || isSending || isUploadingAttachments) return;
|
|
5758
|
+
if (hasFiles && !attachmentsReady) {
|
|
5759
|
+
setInlineHint("Waiting for attachments to finish uploading.");
|
|
5760
|
+
return;
|
|
5761
|
+
}
|
|
5762
|
+
if (hasFiles && hasAttachmentUploadErrors) {
|
|
5763
|
+
setInlineHint("Remove or re-add attachments that failed to upload.");
|
|
5764
|
+
return;
|
|
5765
|
+
}
|
|
5081
5766
|
const commandHint = getSlashCommandValidationHint(value);
|
|
5082
5767
|
if (commandHint) {
|
|
5083
5768
|
setInlineHint(commandHint);
|
|
@@ -5087,15 +5772,38 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5087
5772
|
preRecordTextRef.current = "";
|
|
5088
5773
|
setInlineHint(null);
|
|
5089
5774
|
onClearEditing?.();
|
|
5090
|
-
|
|
5775
|
+
const textToSend = value.trim();
|
|
5776
|
+
const filesToSend = attachedFiles.map((f) => f.file);
|
|
5777
|
+
const attachmentsToSend = [...uploadedAttachmentPayloads];
|
|
5091
5778
|
setValue("");
|
|
5779
|
+
clearAttachmentsFromInput();
|
|
5092
5780
|
requestAnimationFrame(() => {
|
|
5093
5781
|
if (textareaRef.current) {
|
|
5094
5782
|
textareaRef.current.style.height = "auto";
|
|
5095
5783
|
textareaRef.current.focus();
|
|
5096
5784
|
}
|
|
5097
5785
|
});
|
|
5098
|
-
|
|
5786
|
+
setIsSending(true);
|
|
5787
|
+
try {
|
|
5788
|
+
await onSend(textToSend, filesToSend, attachmentsToSend);
|
|
5789
|
+
} catch {
|
|
5790
|
+
setInlineHint("Failed to send message. Please try again.");
|
|
5791
|
+
} finally {
|
|
5792
|
+
setIsSending(false);
|
|
5793
|
+
}
|
|
5794
|
+
}, [
|
|
5795
|
+
value,
|
|
5796
|
+
attachedFiles,
|
|
5797
|
+
uploadedAttachmentPayloads,
|
|
5798
|
+
disabled,
|
|
5799
|
+
isSending,
|
|
5800
|
+
isUploadingAttachments,
|
|
5801
|
+
attachmentsReady,
|
|
5802
|
+
hasAttachmentUploadErrors,
|
|
5803
|
+
onClearEditing,
|
|
5804
|
+
onSend,
|
|
5805
|
+
clearAttachmentsFromInput
|
|
5806
|
+
]);
|
|
5099
5807
|
const selectCommand = react.useCallback(
|
|
5100
5808
|
(command) => {
|
|
5101
5809
|
const insertText = command.insertText ?? `${command.name} `;
|
|
@@ -5140,18 +5848,30 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5140
5848
|
}
|
|
5141
5849
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
5142
5850
|
e.preventDefault();
|
|
5143
|
-
if (isStreaming) return;
|
|
5144
|
-
handleSend();
|
|
5851
|
+
if (isStreaming || isSending || isUploadingAttachments) return;
|
|
5852
|
+
void handleSend();
|
|
5145
5853
|
}
|
|
5146
5854
|
};
|
|
5147
5855
|
const handleUploadImageClick = () => {
|
|
5856
|
+
imageInputRef.current?.click();
|
|
5148
5857
|
onUploadImageClick?.();
|
|
5149
5858
|
setShowActions(false);
|
|
5150
5859
|
};
|
|
5151
5860
|
const handleAttachFileClick = () => {
|
|
5861
|
+
fileInputRef.current?.click();
|
|
5152
5862
|
onAttachFileClick?.();
|
|
5153
5863
|
setShowActions(false);
|
|
5154
5864
|
};
|
|
5865
|
+
const handleImageFilesSelected = (e) => {
|
|
5866
|
+
const files = Array.from(e.target.files ?? []);
|
|
5867
|
+
if (files.length) addFiles(files, "image");
|
|
5868
|
+
e.target.value = "";
|
|
5869
|
+
};
|
|
5870
|
+
const handleDocFilesSelected = (e) => {
|
|
5871
|
+
const files = Array.from(e.target.files ?? []);
|
|
5872
|
+
if (files.length) addFiles(files, "file");
|
|
5873
|
+
e.target.value = "";
|
|
5874
|
+
};
|
|
5155
5875
|
const hideVoiceTooltip = react.useCallback(() => {
|
|
5156
5876
|
if (voiceTooltipTimerRef.current) {
|
|
5157
5877
|
clearTimeout(voiceTooltipTimerRef.current);
|
|
@@ -5179,9 +5899,39 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5179
5899
|
}
|
|
5180
5900
|
onVoicePress();
|
|
5181
5901
|
};
|
|
5182
|
-
const canSend = value.trim().length > 0 && !disabled;
|
|
5183
|
-
const sendDisabled = !canSend || isStreaming;
|
|
5902
|
+
const canSend = (value.trim().length > 0 || attachedFiles.length > 0) && !disabled;
|
|
5903
|
+
const sendDisabled = !canSend || isStreaming || isUploadingAttachments || !attachmentsReady || hasAttachmentUploadErrors || isSending;
|
|
5184
5904
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-input-container", children: [
|
|
5905
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5906
|
+
"input",
|
|
5907
|
+
{
|
|
5908
|
+
ref: imageInputRef,
|
|
5909
|
+
type: "file",
|
|
5910
|
+
accept: imageAccept,
|
|
5911
|
+
multiple: maxCount == null || maxCount > 1,
|
|
5912
|
+
style: { display: "none" },
|
|
5913
|
+
onChange: handleImageFilesSelected
|
|
5914
|
+
}
|
|
5915
|
+
),
|
|
5916
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5917
|
+
"input",
|
|
5918
|
+
{
|
|
5919
|
+
ref: fileInputRef,
|
|
5920
|
+
type: "file",
|
|
5921
|
+
accept: fileAccept,
|
|
5922
|
+
multiple: maxCount == null || maxCount > 1,
|
|
5923
|
+
style: { display: "none" },
|
|
5924
|
+
onChange: handleDocFilesSelected
|
|
5925
|
+
}
|
|
5926
|
+
),
|
|
5927
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5928
|
+
FilePreviewModal,
|
|
5929
|
+
{
|
|
5930
|
+
src: previewFile?.kind === "image" ? previewFile.objectUrl : null,
|
|
5931
|
+
name: previewFile?.file.name ?? "",
|
|
5932
|
+
onClose: () => setPreviewFile(null)
|
|
5933
|
+
}
|
|
5934
|
+
),
|
|
5185
5935
|
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showCommandSuggestions && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5186
5936
|
framerMotion.motion.div,
|
|
5187
5937
|
{
|
|
@@ -5220,6 +5970,115 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5220
5970
|
),
|
|
5221
5971
|
children: [
|
|
5222
5972
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-input-body", children: [
|
|
5973
|
+
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: attachedFiles.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5974
|
+
framerMotion.motion.div,
|
|
5975
|
+
{
|
|
5976
|
+
initial: { opacity: 0, height: 0 },
|
|
5977
|
+
animate: { opacity: 1, height: "auto" },
|
|
5978
|
+
exit: { opacity: 0, height: 0 },
|
|
5979
|
+
transition: { duration: 0.15, ease: "easeOut" },
|
|
5980
|
+
className: "payman-v2-file-preview",
|
|
5981
|
+
children: attachedFiles.map((af) => {
|
|
5982
|
+
const uploadStatus = attachmentUploadStatusById[af.id];
|
|
5983
|
+
const isUploadingFile = uploadStatus === "uploading";
|
|
5984
|
+
const hasUploadError = uploadStatus === "error";
|
|
5985
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-file-preview-item", children: [
|
|
5986
|
+
af.kind === "image" ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5987
|
+
"button",
|
|
5988
|
+
{
|
|
5989
|
+
type: "button",
|
|
5990
|
+
className: cn(
|
|
5991
|
+
"payman-v2-file-preview-shell payman-v2-file-preview-shell-clickable",
|
|
5992
|
+
hasUploadError && "payman-v2-file-preview-shell-error"
|
|
5993
|
+
),
|
|
5994
|
+
onClick: () => setPreviewFile(af),
|
|
5995
|
+
disabled: isUploadingFile,
|
|
5996
|
+
"aria-label": `Preview ${af.file.name}`,
|
|
5997
|
+
children: [
|
|
5998
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-file-preview-thumb", children: [
|
|
5999
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6000
|
+
"img",
|
|
6001
|
+
{
|
|
6002
|
+
src: af.objectUrl,
|
|
6003
|
+
alt: "",
|
|
6004
|
+
draggable: false
|
|
6005
|
+
}
|
|
6006
|
+
),
|
|
6007
|
+
isUploadingFile && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-file-preview-uploading", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 14, className: "payman-v2-spin" }) })
|
|
6008
|
+
] }),
|
|
6009
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-file-preview-info", children: [
|
|
6010
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-file-preview-name", children: af.file.name }),
|
|
6011
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-file-preview-type", children: uploadStatusLabel(uploadStatus, af.file, af.kind) })
|
|
6012
|
+
] })
|
|
6013
|
+
]
|
|
6014
|
+
}
|
|
6015
|
+
) : isPdfFile(af.file) ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6016
|
+
"button",
|
|
6017
|
+
{
|
|
6018
|
+
type: "button",
|
|
6019
|
+
className: cn(
|
|
6020
|
+
"payman-v2-file-preview-shell payman-v2-file-preview-shell-clickable",
|
|
6021
|
+
hasUploadError && "payman-v2-file-preview-shell-error"
|
|
6022
|
+
),
|
|
6023
|
+
onClick: () => chatContext?.openPdfSheet(
|
|
6024
|
+
af.objectUrl,
|
|
6025
|
+
af.file.name
|
|
6026
|
+
),
|
|
6027
|
+
disabled: isUploadingFile,
|
|
6028
|
+
"aria-label": `Preview ${af.file.name}`,
|
|
6029
|
+
children: [
|
|
6030
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-file-preview-thumb", children: isUploadingFile ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 16, className: "payman-v2-spin payman-v2-file-preview-doc-icon" }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
6031
|
+
lucideReact.FileText,
|
|
6032
|
+
{
|
|
6033
|
+
size: 16,
|
|
6034
|
+
strokeWidth: 1.75,
|
|
6035
|
+
className: "payman-v2-file-preview-doc-icon"
|
|
6036
|
+
}
|
|
6037
|
+
) }),
|
|
6038
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-file-preview-info", children: [
|
|
6039
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-file-preview-name", children: af.file.name }),
|
|
6040
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-file-preview-type", children: uploadStatusLabel(uploadStatus, af.file, af.kind) })
|
|
6041
|
+
] })
|
|
6042
|
+
]
|
|
6043
|
+
}
|
|
6044
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6045
|
+
"div",
|
|
6046
|
+
{
|
|
6047
|
+
className: cn(
|
|
6048
|
+
"payman-v2-file-preview-shell",
|
|
6049
|
+
hasUploadError && "payman-v2-file-preview-shell-error"
|
|
6050
|
+
),
|
|
6051
|
+
children: [
|
|
6052
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-file-preview-thumb", children: isUploadingFile ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 16, className: "payman-v2-spin payman-v2-file-preview-doc-icon" }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
6053
|
+
lucideReact.FileText,
|
|
6054
|
+
{
|
|
6055
|
+
size: 16,
|
|
6056
|
+
strokeWidth: 1.75,
|
|
6057
|
+
className: "payman-v2-file-preview-doc-icon"
|
|
6058
|
+
}
|
|
6059
|
+
) }),
|
|
6060
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-file-preview-info", children: [
|
|
6061
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-file-preview-name", children: af.file.name }),
|
|
6062
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-file-preview-type", children: uploadStatusLabel(uploadStatus, af.file, af.kind) })
|
|
6063
|
+
] })
|
|
6064
|
+
]
|
|
6065
|
+
}
|
|
6066
|
+
),
|
|
6067
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6068
|
+
"button",
|
|
6069
|
+
{
|
|
6070
|
+
type: "button",
|
|
6071
|
+
className: "payman-v2-file-preview-remove",
|
|
6072
|
+
onClick: () => removeFile(af.id),
|
|
6073
|
+
"aria-label": `Remove ${af.file.name}`,
|
|
6074
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 12, strokeWidth: 2.5 })
|
|
6075
|
+
}
|
|
6076
|
+
)
|
|
6077
|
+
] }, af.id);
|
|
6078
|
+
})
|
|
6079
|
+
},
|
|
6080
|
+
"file-preview"
|
|
6081
|
+
) }),
|
|
5223
6082
|
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: editingMessageId && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5224
6083
|
framerMotion.motion.div,
|
|
5225
6084
|
{
|
|
@@ -5451,13 +6310,13 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5451
6310
|
"button",
|
|
5452
6311
|
{
|
|
5453
6312
|
type: "button",
|
|
5454
|
-
onClick: handleSend,
|
|
6313
|
+
onClick: () => void handleSend(),
|
|
5455
6314
|
disabled: sendDisabled,
|
|
5456
6315
|
className: cn(
|
|
5457
6316
|
"payman-v2-input-send-btn",
|
|
5458
6317
|
sendDisabled && "payman-v2-input-send-btn-disabled"
|
|
5459
6318
|
),
|
|
5460
|
-
"aria-label": "Send message",
|
|
6319
|
+
"aria-label": isUploadingAttachments || isSending ? "Uploading attachments" : "Send message",
|
|
5461
6320
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5462
6321
|
lucideReact.ArrowUp,
|
|
5463
6322
|
{
|
|
@@ -5992,6 +6851,389 @@ function TimelineBars({
|
|
|
5992
6851
|
)
|
|
5993
6852
|
] });
|
|
5994
6853
|
}
|
|
6854
|
+
|
|
6855
|
+
// src/utils/pdfPreview.ts
|
|
6856
|
+
var PdfPreviewError = class extends Error {
|
|
6857
|
+
constructor(kind, title, userMessage) {
|
|
6858
|
+
super(userMessage);
|
|
6859
|
+
__publicField(this, "kind");
|
|
6860
|
+
__publicField(this, "title");
|
|
6861
|
+
__publicField(this, "userMessage");
|
|
6862
|
+
this.name = "PdfPreviewError";
|
|
6863
|
+
this.kind = kind;
|
|
6864
|
+
this.title = title;
|
|
6865
|
+
this.userMessage = userMessage;
|
|
6866
|
+
}
|
|
6867
|
+
};
|
|
6868
|
+
function classifyErrorBody(body, status) {
|
|
6869
|
+
const text = body.replace(/\s+/g, " ").trim();
|
|
6870
|
+
if (/signed expiry time|must be after signed start time|expired|expir/i.test(text)) {
|
|
6871
|
+
return new PdfPreviewError(
|
|
6872
|
+
"expired",
|
|
6873
|
+
"Link expired",
|
|
6874
|
+
"This download link has expired. Ask the assistant to generate a new copy of the document."
|
|
6875
|
+
);
|
|
6876
|
+
}
|
|
6877
|
+
if (/AuthenticationFailed|authorization header|formed correctly including the signature/i.test(
|
|
6878
|
+
text
|
|
6879
|
+
)) {
|
|
6880
|
+
return new PdfPreviewError(
|
|
6881
|
+
"forbidden",
|
|
6882
|
+
"Link no longer valid",
|
|
6883
|
+
"This preview link is no longer valid. Request a fresh download link and try again."
|
|
6884
|
+
);
|
|
6885
|
+
}
|
|
6886
|
+
if (status === 404 || /BlobNotFound|ResourceNotFound|The specified blob does not exist/i.test(text)) {
|
|
6887
|
+
return new PdfPreviewError(
|
|
6888
|
+
"not_found",
|
|
6889
|
+
"Document not found",
|
|
6890
|
+
"The file is no longer available. It may have been removed or the link is incorrect."
|
|
6891
|
+
);
|
|
6892
|
+
}
|
|
6893
|
+
if (status === 403) {
|
|
6894
|
+
return new PdfPreviewError(
|
|
6895
|
+
"forbidden",
|
|
6896
|
+
"Can't open document",
|
|
6897
|
+
"You may not have access to this file, or the link has expired."
|
|
6898
|
+
);
|
|
6899
|
+
}
|
|
6900
|
+
if (status === 401) {
|
|
6901
|
+
return new PdfPreviewError(
|
|
6902
|
+
"forbidden",
|
|
6903
|
+
"Access denied",
|
|
6904
|
+
"This preview link could not be verified. Request a new download link."
|
|
6905
|
+
);
|
|
6906
|
+
}
|
|
6907
|
+
return new PdfPreviewError(
|
|
6908
|
+
"unknown",
|
|
6909
|
+
"Can't preview document",
|
|
6910
|
+
"Something went wrong while opening this file. Try downloading it or request a new link."
|
|
6911
|
+
);
|
|
6912
|
+
}
|
|
6913
|
+
function normalizePdfPreviewError(error) {
|
|
6914
|
+
if (error instanceof PdfPreviewError) return error;
|
|
6915
|
+
if (error instanceof TypeError) {
|
|
6916
|
+
return new PdfPreviewError(
|
|
6917
|
+
"network",
|
|
6918
|
+
"Connection problem",
|
|
6919
|
+
"Could not load the document. Check your connection and try again."
|
|
6920
|
+
);
|
|
6921
|
+
}
|
|
6922
|
+
return new PdfPreviewError(
|
|
6923
|
+
"unknown",
|
|
6924
|
+
"Can't preview document",
|
|
6925
|
+
"Something went wrong while opening this file. Try downloading it or request a new link."
|
|
6926
|
+
);
|
|
6927
|
+
}
|
|
6928
|
+
async function assertPdfBlob(blob, status) {
|
|
6929
|
+
if (!blob.size) {
|
|
6930
|
+
return Promise.reject(
|
|
6931
|
+
new PdfPreviewError(
|
|
6932
|
+
"empty",
|
|
6933
|
+
"Document unavailable",
|
|
6934
|
+
"The file response was empty. Request a new download link and try again."
|
|
6935
|
+
)
|
|
6936
|
+
);
|
|
6937
|
+
}
|
|
6938
|
+
const head = await blob.slice(0, Math.min(blob.size, 1024)).text();
|
|
6939
|
+
if (head.startsWith("%PDF")) {
|
|
6940
|
+
return blob;
|
|
6941
|
+
}
|
|
6942
|
+
throw classifyErrorBody(head, status);
|
|
6943
|
+
}
|
|
6944
|
+
async function fetchPdfBlob(src) {
|
|
6945
|
+
let response;
|
|
6946
|
+
try {
|
|
6947
|
+
response = await fetch(src, {
|
|
6948
|
+
method: "GET",
|
|
6949
|
+
headers: { Accept: "application/pdf,*/*" }
|
|
6950
|
+
});
|
|
6951
|
+
} catch {
|
|
6952
|
+
throw new PdfPreviewError(
|
|
6953
|
+
"network",
|
|
6954
|
+
"Connection problem",
|
|
6955
|
+
"Could not load the document. Check your connection and try again."
|
|
6956
|
+
);
|
|
6957
|
+
}
|
|
6958
|
+
const blob = await response.blob();
|
|
6959
|
+
if (!response.ok) {
|
|
6960
|
+
const body = await blob.text().catch(() => "");
|
|
6961
|
+
throw classifyErrorBody(body, response.status);
|
|
6962
|
+
}
|
|
6963
|
+
return assertPdfBlob(blob, response.status);
|
|
6964
|
+
}
|
|
6965
|
+
var MIN_WIDTH = 320;
|
|
6966
|
+
var MAX_WIDTH_RATIO = 0.6;
|
|
6967
|
+
var DEFAULT_WIDTH = 520;
|
|
6968
|
+
var SPRING = { type: "spring", stiffness: 340, damping: 34, mass: 0.85 };
|
|
6969
|
+
var SHEET_EXIT = { duration: 0.22, ease: [0.4, 0, 1, 1] };
|
|
6970
|
+
function pdfDownloadName(title) {
|
|
6971
|
+
const base = title.trim() || "document";
|
|
6972
|
+
return base.toLowerCase().endsWith(".pdf") ? base : `${base}.pdf`;
|
|
6973
|
+
}
|
|
6974
|
+
function clampSplitWidth(w) {
|
|
6975
|
+
const maxW = Math.floor(window.innerWidth * MAX_WIDTH_RATIO);
|
|
6976
|
+
return Math.max(MIN_WIDTH, Math.min(maxW, w));
|
|
6977
|
+
}
|
|
6978
|
+
function PdfSheetV2({
|
|
6979
|
+
src,
|
|
6980
|
+
title,
|
|
6981
|
+
onClose,
|
|
6982
|
+
mode = "split"
|
|
6983
|
+
}) {
|
|
6984
|
+
return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: src && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6985
|
+
PdfSheetPanel,
|
|
6986
|
+
{
|
|
6987
|
+
src,
|
|
6988
|
+
title,
|
|
6989
|
+
onClose,
|
|
6990
|
+
mode
|
|
6991
|
+
},
|
|
6992
|
+
"pdf-panel"
|
|
6993
|
+
) });
|
|
6994
|
+
}
|
|
6995
|
+
function PdfSheetPanel({
|
|
6996
|
+
src,
|
|
6997
|
+
title,
|
|
6998
|
+
onClose,
|
|
6999
|
+
mode
|
|
7000
|
+
}) {
|
|
7001
|
+
const isSheet = mode === "sheet";
|
|
7002
|
+
const [isLoaded, setIsLoaded] = react.useState(false);
|
|
7003
|
+
const [isDownloading, setIsDownloading] = react.useState(false);
|
|
7004
|
+
const [previewUrl, setPreviewUrl] = react.useState(null);
|
|
7005
|
+
const [previewError, setPreviewError] = react.useState(null);
|
|
7006
|
+
const [loadAttempt, setLoadAttempt] = react.useState(0);
|
|
7007
|
+
const blobRef = react.useRef(null);
|
|
7008
|
+
const objectUrlRef = react.useRef(null);
|
|
7009
|
+
const [panelWidth, setPanelWidth] = react.useState(DEFAULT_WIDTH);
|
|
7010
|
+
const [splitOpened, setSplitOpened] = react.useState(false);
|
|
7011
|
+
const [isDragging, setIsDragging] = react.useState(false);
|
|
7012
|
+
react.useEffect(() => {
|
|
7013
|
+
setIsLoaded(false);
|
|
7014
|
+
setPreviewUrl(null);
|
|
7015
|
+
setPreviewError(null);
|
|
7016
|
+
setPanelWidth(DEFAULT_WIDTH);
|
|
7017
|
+
setSplitOpened(false);
|
|
7018
|
+
blobRef.current = null;
|
|
7019
|
+
if (objectUrlRef.current) {
|
|
7020
|
+
URL.revokeObjectURL(objectUrlRef.current);
|
|
7021
|
+
objectUrlRef.current = null;
|
|
7022
|
+
}
|
|
7023
|
+
let cancelled = false;
|
|
7024
|
+
const loadPreview = async () => {
|
|
7025
|
+
try {
|
|
7026
|
+
const blob = await fetchPdfBlob(src);
|
|
7027
|
+
if (cancelled) return;
|
|
7028
|
+
blobRef.current = blob;
|
|
7029
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
7030
|
+
objectUrlRef.current = objectUrl;
|
|
7031
|
+
setPreviewUrl(objectUrl);
|
|
7032
|
+
} catch (error) {
|
|
7033
|
+
if (!cancelled) {
|
|
7034
|
+
setPreviewError(normalizePdfPreviewError(error));
|
|
7035
|
+
}
|
|
7036
|
+
}
|
|
7037
|
+
};
|
|
7038
|
+
void loadPreview();
|
|
7039
|
+
return () => {
|
|
7040
|
+
cancelled = true;
|
|
7041
|
+
if (objectUrlRef.current) {
|
|
7042
|
+
URL.revokeObjectURL(objectUrlRef.current);
|
|
7043
|
+
objectUrlRef.current = null;
|
|
7044
|
+
}
|
|
7045
|
+
blobRef.current = null;
|
|
7046
|
+
};
|
|
7047
|
+
}, [src, loadAttempt]);
|
|
7048
|
+
react.useEffect(() => {
|
|
7049
|
+
if (isSheet) return;
|
|
7050
|
+
const onWindowResize = () => setPanelWidth((size) => clampSplitWidth(size));
|
|
7051
|
+
window.addEventListener("resize", onWindowResize);
|
|
7052
|
+
return () => window.removeEventListener("resize", onWindowResize);
|
|
7053
|
+
}, [isSheet]);
|
|
7054
|
+
const handleKeyDown = react.useCallback(
|
|
7055
|
+
(e) => {
|
|
7056
|
+
if (e.key === "Escape") onClose();
|
|
7057
|
+
},
|
|
7058
|
+
[onClose]
|
|
7059
|
+
);
|
|
7060
|
+
react.useEffect(() => {
|
|
7061
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
7062
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
7063
|
+
}, [handleKeyDown]);
|
|
7064
|
+
const canDownload = isLoaded && previewUrl != null && !previewError;
|
|
7065
|
+
const handleDownload = async () => {
|
|
7066
|
+
const filename = pdfDownloadName(title);
|
|
7067
|
+
setIsDownloading(true);
|
|
7068
|
+
try {
|
|
7069
|
+
const blob = blobRef.current ?? await fetchPdfBlob(src);
|
|
7070
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
7071
|
+
const a = document.createElement("a");
|
|
7072
|
+
a.href = objectUrl;
|
|
7073
|
+
a.download = filename;
|
|
7074
|
+
a.target = "_blank";
|
|
7075
|
+
a.rel = "noopener noreferrer";
|
|
7076
|
+
document.body.appendChild(a);
|
|
7077
|
+
a.click();
|
|
7078
|
+
a.remove();
|
|
7079
|
+
URL.revokeObjectURL(objectUrl);
|
|
7080
|
+
} catch (error) {
|
|
7081
|
+
setPreviewError(normalizePdfPreviewError(error));
|
|
7082
|
+
} finally {
|
|
7083
|
+
setIsDownloading(false);
|
|
7084
|
+
}
|
|
7085
|
+
};
|
|
7086
|
+
const handleRetry = () => {
|
|
7087
|
+
setPreviewError(null);
|
|
7088
|
+
setIsLoaded(false);
|
|
7089
|
+
setPreviewUrl(null);
|
|
7090
|
+
setLoadAttempt((attempt) => attempt + 1);
|
|
7091
|
+
};
|
|
7092
|
+
const handleOpenInNewTab = () => {
|
|
7093
|
+
window.open(src, "_blank", "noopener,noreferrer");
|
|
7094
|
+
};
|
|
7095
|
+
const onResizeMouseDown = (e) => {
|
|
7096
|
+
if (e.button !== 0) return;
|
|
7097
|
+
e.preventDefault();
|
|
7098
|
+
const startX = e.clientX;
|
|
7099
|
+
const startW = panelWidth;
|
|
7100
|
+
setIsDragging(true);
|
|
7101
|
+
const onMove = (ev) => {
|
|
7102
|
+
setPanelWidth(clampSplitWidth(startW + (startX - ev.clientX)));
|
|
7103
|
+
};
|
|
7104
|
+
const onUp = () => {
|
|
7105
|
+
setIsDragging(false);
|
|
7106
|
+
document.removeEventListener("mousemove", onMove);
|
|
7107
|
+
document.removeEventListener("mouseup", onUp);
|
|
7108
|
+
};
|
|
7109
|
+
document.addEventListener("mousemove", onMove);
|
|
7110
|
+
document.addEventListener("mouseup", onUp);
|
|
7111
|
+
};
|
|
7112
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7113
|
+
framerMotion.motion.div,
|
|
7114
|
+
{
|
|
7115
|
+
className: cn(
|
|
7116
|
+
"payman-v2-root payman-v2-pdf-sheet-panel",
|
|
7117
|
+
isSheet && "payman-v2-pdf-sheet-panel--sheet"
|
|
7118
|
+
),
|
|
7119
|
+
style: { minWidth: 0 },
|
|
7120
|
+
initial: isSheet ? { x: "100%", opacity: 0 } : { width: 0, opacity: 0 },
|
|
7121
|
+
animate: isSheet ? { x: 0, opacity: 1 } : { width: panelWidth, opacity: 1 },
|
|
7122
|
+
exit: isSheet ? { x: "100%", opacity: 0, transition: { x: SHEET_EXIT, opacity: SHEET_EXIT } } : { width: 0, opacity: 0, transition: { width: SHEET_EXIT, opacity: SHEET_EXIT } },
|
|
7123
|
+
transition: isSheet ? { x: SPRING, opacity: SPRING } : {
|
|
7124
|
+
width: splitOpened ? { duration: 0 } : SPRING,
|
|
7125
|
+
opacity: SPRING
|
|
7126
|
+
},
|
|
7127
|
+
onAnimationComplete: () => {
|
|
7128
|
+
if (!isSheet && !splitOpened) setSplitOpened(true);
|
|
7129
|
+
},
|
|
7130
|
+
children: [
|
|
7131
|
+
!isSheet && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7132
|
+
"div",
|
|
7133
|
+
{
|
|
7134
|
+
className: "payman-v2-pdf-sheet-resize-handle",
|
|
7135
|
+
onMouseDown: onResizeMouseDown,
|
|
7136
|
+
"aria-hidden": "true",
|
|
7137
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-pdf-sheet-resize-grip" })
|
|
7138
|
+
}
|
|
7139
|
+
),
|
|
7140
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-header", children: [
|
|
7141
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-header-left", children: [
|
|
7142
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-pdf-sheet-file-icon", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { size: 14, strokeWidth: 1.75 }) }),
|
|
7143
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-pdf-sheet-title", title, children: title || "Document" })
|
|
7144
|
+
] }),
|
|
7145
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-header-actions", children: [
|
|
7146
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7147
|
+
"button",
|
|
7148
|
+
{
|
|
7149
|
+
type: "button",
|
|
7150
|
+
className: "payman-v2-pdf-sheet-download-btn",
|
|
7151
|
+
"aria-label": "Download PDF",
|
|
7152
|
+
disabled: !canDownload || isDownloading,
|
|
7153
|
+
onClick: () => void handleDownload(),
|
|
7154
|
+
children: [
|
|
7155
|
+
isDownloading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 13, strokeWidth: 2, style: { animation: "payman-v2-spin 0.65s linear infinite" } }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { size: 13, strokeWidth: 2 }),
|
|
7156
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: isDownloading ? "Downloading\u2026" : "Download" })
|
|
7157
|
+
]
|
|
7158
|
+
}
|
|
7159
|
+
),
|
|
7160
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7161
|
+
"button",
|
|
7162
|
+
{
|
|
7163
|
+
type: "button",
|
|
7164
|
+
className: "payman-v2-pdf-sheet-close-btn",
|
|
7165
|
+
"aria-label": "Close preview",
|
|
7166
|
+
onClick: (e) => {
|
|
7167
|
+
e.stopPropagation();
|
|
7168
|
+
onClose();
|
|
7169
|
+
},
|
|
7170
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 15, strokeWidth: 2.25 })
|
|
7171
|
+
}
|
|
7172
|
+
)
|
|
7173
|
+
] })
|
|
7174
|
+
] }),
|
|
7175
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-pdf-sheet-body", children: previewError ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-error", role: "alert", children: [
|
|
7176
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-pdf-sheet-error-icon", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { size: 22, strokeWidth: 1.75 }) }),
|
|
7177
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-pdf-sheet-error-title", children: previewError.title }),
|
|
7178
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-pdf-sheet-error-message", children: previewError.userMessage }),
|
|
7179
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-error-actions", children: [
|
|
7180
|
+
previewError.kind === "network" && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7181
|
+
"button",
|
|
7182
|
+
{
|
|
7183
|
+
type: "button",
|
|
7184
|
+
className: "payman-v2-pdf-sheet-error-btn",
|
|
7185
|
+
onClick: handleRetry,
|
|
7186
|
+
children: [
|
|
7187
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: 14, strokeWidth: 2 }),
|
|
7188
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Try again" })
|
|
7189
|
+
]
|
|
7190
|
+
}
|
|
7191
|
+
),
|
|
7192
|
+
(previewError.kind === "network" || previewError.kind === "unknown") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7193
|
+
"button",
|
|
7194
|
+
{
|
|
7195
|
+
type: "button",
|
|
7196
|
+
className: "payman-v2-pdf-sheet-error-btn payman-v2-pdf-sheet-error-btn-secondary",
|
|
7197
|
+
onClick: handleOpenInNewTab,
|
|
7198
|
+
children: [
|
|
7199
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 14, strokeWidth: 2 }),
|
|
7200
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Open link" })
|
|
7201
|
+
]
|
|
7202
|
+
}
|
|
7203
|
+
)
|
|
7204
|
+
] })
|
|
7205
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7206
|
+
(!isLoaded || !previewUrl) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-loading", children: [
|
|
7207
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7208
|
+
lucideReact.Loader2,
|
|
7209
|
+
{
|
|
7210
|
+
size: 20,
|
|
7211
|
+
strokeWidth: 2,
|
|
7212
|
+
style: { animation: "payman-v2-spin 0.65s linear infinite", color: "var(--payman-v2-text-3)" }
|
|
7213
|
+
}
|
|
7214
|
+
),
|
|
7215
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-pdf-sheet-loading-text", children: previewUrl ? "Preparing preview\u2026" : "Opening document\u2026" })
|
|
7216
|
+
] }),
|
|
7217
|
+
previewUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7218
|
+
"iframe",
|
|
7219
|
+
{
|
|
7220
|
+
src: previewUrl,
|
|
7221
|
+
title: title || "PDF Preview",
|
|
7222
|
+
className: "payman-v2-pdf-sheet-iframe",
|
|
7223
|
+
style: {
|
|
7224
|
+
opacity: isLoaded ? 1 : 0,
|
|
7225
|
+
transition: "opacity 0.3s ease",
|
|
7226
|
+
pointerEvents: isDragging ? "none" : "auto"
|
|
7227
|
+
},
|
|
7228
|
+
onLoad: () => setIsLoaded(true)
|
|
7229
|
+
},
|
|
7230
|
+
previewUrl
|
|
7231
|
+
)
|
|
7232
|
+
] }) })
|
|
7233
|
+
]
|
|
7234
|
+
}
|
|
7235
|
+
);
|
|
7236
|
+
}
|
|
5995
7237
|
var DEFAULT_USER_ACTION_STATE = {
|
|
5996
7238
|
prompts: [],
|
|
5997
7239
|
notifications: []
|
|
@@ -6086,7 +7328,8 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6086
7328
|
onLoadMoreMessages,
|
|
6087
7329
|
isLoadingMoreMessages = false,
|
|
6088
7330
|
hasMoreMessages = false,
|
|
6089
|
-
chat
|
|
7331
|
+
chat,
|
|
7332
|
+
attachmentUpload
|
|
6090
7333
|
}, ref) {
|
|
6091
7334
|
const [inputValue, setInputValue] = react.useState("");
|
|
6092
7335
|
const prevInputValueRef = react.useRef(inputValue);
|
|
@@ -6140,7 +7383,6 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6140
7383
|
const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
|
|
6141
7384
|
const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
|
|
6142
7385
|
const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
|
|
6143
|
-
const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
|
|
6144
7386
|
const dismissNotification = chat.dismissNotification ?? NOOP;
|
|
6145
7387
|
const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
|
|
6146
7388
|
const {
|
|
@@ -6166,6 +7408,19 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6166
7408
|
}
|
|
6167
7409
|
}
|
|
6168
7410
|
);
|
|
7411
|
+
const [pdfSheet, setPdfSheet] = react.useState(null);
|
|
7412
|
+
const autoOpenedPdfHrefsRef = react.useRef(/* @__PURE__ */ new Set());
|
|
7413
|
+
const pdfPreviewMode = config.pdfPreviewMode ?? "split";
|
|
7414
|
+
const openPdfSheet = react.useCallback((href, title, options) => {
|
|
7415
|
+
if (options?.auto) {
|
|
7416
|
+
if (autoOpenedPdfHrefsRef.current.has(href)) return;
|
|
7417
|
+
autoOpenedPdfHrefsRef.current.add(href);
|
|
7418
|
+
}
|
|
7419
|
+
setPdfSheet({ href, title });
|
|
7420
|
+
}, []);
|
|
7421
|
+
const closePdfSheet = react.useCallback(() => {
|
|
7422
|
+
setPdfSheet(null);
|
|
7423
|
+
}, []);
|
|
6169
7424
|
const contextValue = react.useMemo(
|
|
6170
7425
|
() => ({
|
|
6171
7426
|
resetSession,
|
|
@@ -6174,7 +7429,8 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6174
7429
|
cancelStream,
|
|
6175
7430
|
getSessionId,
|
|
6176
7431
|
getMessages,
|
|
6177
|
-
isWaitingForResponse
|
|
7432
|
+
isWaitingForResponse,
|
|
7433
|
+
openPdfSheet
|
|
6178
7434
|
}),
|
|
6179
7435
|
[
|
|
6180
7436
|
resetSession,
|
|
@@ -6183,7 +7439,8 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6183
7439
|
cancelStream,
|
|
6184
7440
|
getSessionId,
|
|
6185
7441
|
getMessages,
|
|
6186
|
-
isWaitingForResponse
|
|
7442
|
+
isWaitingForResponse,
|
|
7443
|
+
openPdfSheet
|
|
6187
7444
|
]
|
|
6188
7445
|
);
|
|
6189
7446
|
const {
|
|
@@ -6239,7 +7496,10 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6239
7496
|
setInputValue("");
|
|
6240
7497
|
setLightboxSrc(null);
|
|
6241
7498
|
setLightboxAlt("");
|
|
7499
|
+
setPdfSheet(null);
|
|
7500
|
+
autoOpenedPdfHrefsRef.current.clear();
|
|
6242
7501
|
chatInputV2Ref.current?.setDraft("");
|
|
7502
|
+
chatInputV2Ref.current?.clearAttachments();
|
|
6243
7503
|
clearTranscript();
|
|
6244
7504
|
if (isRecording) {
|
|
6245
7505
|
stopRecording();
|
|
@@ -6290,14 +7550,20 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6290
7550
|
emptyStateComponent,
|
|
6291
7551
|
showResetSession = false,
|
|
6292
7552
|
enableDeepModeToggle = true,
|
|
6293
|
-
showAttachmentButton = true,
|
|
6294
|
-
showUploadImageButton = true,
|
|
6295
|
-
showAttachFileButton = true,
|
|
6296
7553
|
messageActions: messageActionsConfig,
|
|
6297
7554
|
enableSlashCommands = true,
|
|
6298
7555
|
slashCommands: slashCommandsConfig,
|
|
6299
7556
|
commandPermissions
|
|
6300
7557
|
} = config;
|
|
7558
|
+
const attachmentSettings = react.useMemo(
|
|
7559
|
+
() => resolveChatAttachmentConfig(config),
|
|
7560
|
+
[
|
|
7561
|
+
config.attachments,
|
|
7562
|
+
config.showAttachmentButton,
|
|
7563
|
+
config.showUploadImageButton,
|
|
7564
|
+
config.showAttachFileButton
|
|
7565
|
+
]
|
|
7566
|
+
);
|
|
6301
7567
|
const messageActions = react.useMemo(
|
|
6302
7568
|
() => ({
|
|
6303
7569
|
userMessageActions: {
|
|
@@ -6385,11 +7651,22 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6385
7651
|
};
|
|
6386
7652
|
const userActionPrompts = isUserActionSupported ? userActionState.prompts : void 0;
|
|
6387
7653
|
const notifications = userActionState.notifications;
|
|
6388
|
-
const
|
|
7654
|
+
const handleAttachmentsChange = react.useCallback(
|
|
7655
|
+
(attachments) => {
|
|
7656
|
+
attachmentUpload.syncAttachments(attachments);
|
|
7657
|
+
},
|
|
7658
|
+
[attachmentUpload]
|
|
7659
|
+
);
|
|
7660
|
+
const handleV2Send = (text, files = [], attachments = []) => {
|
|
6389
7661
|
if (isRecording) stopRecording();
|
|
6390
|
-
if (text.trim() && !disableInput && isSessionParamsConfigured) {
|
|
7662
|
+
if ((text.trim() || files.length > 0) && !disableInput && isSessionParamsConfigured) {
|
|
7663
|
+
if (files.length > 0 && attachments.length === 0) return;
|
|
6391
7664
|
setEditingMessageId(null);
|
|
6392
|
-
void sendMessage(text.trim(), {
|
|
7665
|
+
void sendMessage(text.trim(), {
|
|
7666
|
+
analysisMode: effectiveAnalysisMode,
|
|
7667
|
+
attachments,
|
|
7668
|
+
files
|
|
7669
|
+
});
|
|
6393
7670
|
}
|
|
6394
7671
|
};
|
|
6395
7672
|
const handleVoicePress = react.useCallback(async () => {
|
|
@@ -6437,156 +7714,214 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6437
7714
|
style,
|
|
6438
7715
|
children: [
|
|
6439
7716
|
children,
|
|
6440
|
-
/* @__PURE__ */ jsxRuntime.
|
|
6441
|
-
|
|
7717
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7718
|
+
"div",
|
|
6442
7719
|
{
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6450
|
-
MessageList,
|
|
6451
|
-
{
|
|
6452
|
-
messages,
|
|
6453
|
-
isLoading: false,
|
|
6454
|
-
emptyStateText,
|
|
6455
|
-
showEmptyStateIcon,
|
|
6456
|
-
emptyStateComponent,
|
|
6457
|
-
layout,
|
|
6458
|
-
showTimestamps,
|
|
6459
|
-
stage: config.stage || "DEVELOPMENT",
|
|
6460
|
-
animated,
|
|
6461
|
-
showAgentName,
|
|
6462
|
-
agentName,
|
|
6463
|
-
showAvatars,
|
|
6464
|
-
showUserAvatar,
|
|
6465
|
-
showAssistantAvatar,
|
|
6466
|
-
showExecutionSteps,
|
|
6467
|
-
showStreamingDot,
|
|
6468
|
-
streamingStepsText,
|
|
6469
|
-
completedStepsText,
|
|
6470
|
-
onExecutionTraceClick,
|
|
6471
|
-
onLoadMoreMessages,
|
|
6472
|
-
isLoadingMoreMessages,
|
|
6473
|
-
hasMoreMessages
|
|
6474
|
-
}
|
|
6475
|
-
),
|
|
6476
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7720
|
+
className: cn(
|
|
7721
|
+
"payman-v2-split-layout",
|
|
7722
|
+
pdfPreviewMode === "sheet" && "payman-v2-split-layout--sheet"
|
|
7723
|
+
),
|
|
7724
|
+
children: [
|
|
7725
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-chat-column", children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6477
7726
|
framerMotion.motion.div,
|
|
6478
7727
|
{
|
|
6479
|
-
initial: { opacity:
|
|
6480
|
-
|
|
6481
|
-
transition: {
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
7728
|
+
initial: { opacity: 1 },
|
|
7729
|
+
exit: { opacity: 0 },
|
|
7730
|
+
transition: { duration: 0.3 },
|
|
7731
|
+
className: "payman-v2-chat-layout",
|
|
7732
|
+
style: { justifyContent: "center", alignItems: "center" },
|
|
7733
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", flex: 1, width: "100%" }, children: [
|
|
7734
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7735
|
+
MessageList,
|
|
7736
|
+
{
|
|
7737
|
+
messages,
|
|
7738
|
+
isLoading: false,
|
|
7739
|
+
emptyStateText,
|
|
7740
|
+
showEmptyStateIcon,
|
|
7741
|
+
emptyStateComponent,
|
|
7742
|
+
layout,
|
|
7743
|
+
showTimestamps,
|
|
7744
|
+
stage: config.stage || "DEVELOPMENT",
|
|
7745
|
+
animated,
|
|
7746
|
+
showAgentName,
|
|
7747
|
+
agentName,
|
|
7748
|
+
showAvatars,
|
|
7749
|
+
showUserAvatar,
|
|
7750
|
+
showAssistantAvatar,
|
|
7751
|
+
showExecutionSteps,
|
|
7752
|
+
showStreamingDot,
|
|
7753
|
+
streamingStepsText,
|
|
7754
|
+
completedStepsText,
|
|
7755
|
+
onExecutionTraceClick,
|
|
7756
|
+
onLoadMoreMessages,
|
|
7757
|
+
isLoadingMoreMessages,
|
|
7758
|
+
hasMoreMessages
|
|
7759
|
+
}
|
|
7760
|
+
),
|
|
7761
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7762
|
+
framerMotion.motion.div,
|
|
7763
|
+
{
|
|
7764
|
+
initial: { opacity: 0, y: 12 },
|
|
7765
|
+
animate: { opacity: 1, y: 0 },
|
|
7766
|
+
transition: { delay: 0.2, duration: 0.4, ease: [0.25, 0.46, 0.45, 0.94] },
|
|
7767
|
+
style: { width: "100%" },
|
|
7768
|
+
children: hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7769
|
+
ChatInputV2,
|
|
7770
|
+
{
|
|
7771
|
+
ref: chatInputV2Ref,
|
|
7772
|
+
onSend: handleV2Send,
|
|
7773
|
+
onCancel: cancelStream,
|
|
7774
|
+
disabled: isV2InputDisabled,
|
|
7775
|
+
isStreaming: isWaitingForResponse,
|
|
7776
|
+
isUploadingAttachments: attachmentUpload.isUploading,
|
|
7777
|
+
attachmentsReady: attachmentUpload.allReady,
|
|
7778
|
+
hasAttachmentUploadErrors: attachmentUpload.hasErrors,
|
|
7779
|
+
attachmentUploadStatusById: attachmentUpload.statusById,
|
|
7780
|
+
uploadedAttachmentPayloads: attachmentUpload.payloads,
|
|
7781
|
+
onAttachmentsChange: handleAttachmentsChange,
|
|
7782
|
+
placeholder: isRecording ? "Listening..." : placeholder,
|
|
7783
|
+
enableVoice: config.enableVoice === true,
|
|
7784
|
+
transcribedText: config.enableVoice === true ? transcribedText : "",
|
|
7785
|
+
voiceAvailable: config.enableVoice === true && voiceAvailable,
|
|
7786
|
+
isRecording,
|
|
7787
|
+
onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
|
|
7788
|
+
onCancelRecording: handleCancelRecording,
|
|
7789
|
+
onConfirmRecording: handleConfirmRecording,
|
|
7790
|
+
showResetSession,
|
|
7791
|
+
onResetSession: requestResetSession,
|
|
7792
|
+
showAttachmentButton: attachmentSettings.showAttachmentButton,
|
|
7793
|
+
showUploadImageButton: attachmentSettings.showUploadImageButton,
|
|
7794
|
+
showAttachFileButton: attachmentSettings.showAttachFileButton,
|
|
7795
|
+
allowedImageExtensions: attachmentSettings.allowedImageExtensions,
|
|
7796
|
+
allowedFileExtensions: attachmentSettings.allowedFileExtensions,
|
|
7797
|
+
maxCount: attachmentSettings.maxCount,
|
|
7798
|
+
maxFileBytes: attachmentSettings.maxFileBytes,
|
|
7799
|
+
maxTotalBytes: attachmentSettings.maxTotalBytes,
|
|
7800
|
+
onUploadImageClick,
|
|
7801
|
+
onAttachFileClick,
|
|
7802
|
+
editingMessageId,
|
|
7803
|
+
onClearEditing: handleClearEditing,
|
|
7804
|
+
analysisMode: enableDeepModeToggle ? analysisMode : void 0,
|
|
7805
|
+
onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
|
|
7806
|
+
slashCommands
|
|
7807
|
+
}
|
|
7808
|
+
)
|
|
7809
|
+
}
|
|
7810
|
+
)
|
|
7811
|
+
] })
|
|
7812
|
+
},
|
|
7813
|
+
"v2-empty"
|
|
7814
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7815
|
+
framerMotion.motion.div,
|
|
6528
7816
|
{
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
7817
|
+
initial: hasEverSentMessage ? { opacity: 0 } : false,
|
|
7818
|
+
animate: { opacity: 1 },
|
|
7819
|
+
transition: { duration: 0.3 },
|
|
7820
|
+
className: "payman-v2-chat-layout",
|
|
7821
|
+
children: [
|
|
7822
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7823
|
+
MessageListV2,
|
|
7824
|
+
{
|
|
7825
|
+
ref: messageListV2Ref,
|
|
7826
|
+
messages,
|
|
7827
|
+
isStreaming: isWaitingForResponse,
|
|
7828
|
+
sidePanelOpen: !!pdfSheet,
|
|
7829
|
+
onEditUserMessage: handleEditMessageDraft,
|
|
7830
|
+
onRetryUserMessage: handleRetryUserMessage,
|
|
7831
|
+
onImageClick: handleImageClick,
|
|
7832
|
+
onExecutionTraceClick,
|
|
7833
|
+
messageActions,
|
|
7834
|
+
retryDisabled: isWaitingForResponse,
|
|
7835
|
+
typingSpeed: config.typingSpeed ?? 4,
|
|
7836
|
+
userActionPrompts,
|
|
7837
|
+
notifications,
|
|
7838
|
+
onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
|
|
7839
|
+
onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
|
|
7840
|
+
onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
|
|
7841
|
+
onDismissNotification: dismissNotification,
|
|
7842
|
+
onSubmitFeedback: handleSubmitFeedback
|
|
7843
|
+
}
|
|
7844
|
+
),
|
|
7845
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7846
|
+
StreamingIndicatorV2,
|
|
7847
|
+
{
|
|
7848
|
+
isStreaming: isWaitingForResponse,
|
|
7849
|
+
loadingAnimation: config.loadingAnimation
|
|
7850
|
+
}
|
|
7851
|
+
),
|
|
7852
|
+
hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7853
|
+
ChatInputV2,
|
|
7854
|
+
{
|
|
7855
|
+
ref: chatInputV2Ref,
|
|
7856
|
+
onSend: handleV2Send,
|
|
7857
|
+
onCancel: cancelStream,
|
|
7858
|
+
disabled: isV2InputDisabled,
|
|
7859
|
+
isStreaming: isWaitingForResponse,
|
|
7860
|
+
isUploadingAttachments: attachmentUpload.isUploading,
|
|
7861
|
+
attachmentsReady: attachmentUpload.allReady,
|
|
7862
|
+
hasAttachmentUploadErrors: attachmentUpload.hasErrors,
|
|
7863
|
+
attachmentUploadStatusById: attachmentUpload.statusById,
|
|
7864
|
+
uploadedAttachmentPayloads: attachmentUpload.payloads,
|
|
7865
|
+
onAttachmentsChange: handleAttachmentsChange,
|
|
7866
|
+
placeholder: isRecording ? "Listening..." : placeholder,
|
|
7867
|
+
enableVoice: config.enableVoice === true,
|
|
7868
|
+
transcribedText: config.enableVoice === true ? transcribedText : "",
|
|
7869
|
+
voiceAvailable: config.enableVoice === true && voiceAvailable,
|
|
7870
|
+
isRecording,
|
|
7871
|
+
onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
|
|
7872
|
+
onCancelRecording: handleCancelRecording,
|
|
7873
|
+
onConfirmRecording: handleConfirmRecording,
|
|
7874
|
+
showResetSession,
|
|
7875
|
+
onResetSession: requestResetSession,
|
|
7876
|
+
showAttachmentButton: attachmentSettings.showAttachmentButton,
|
|
7877
|
+
showUploadImageButton: attachmentSettings.showUploadImageButton,
|
|
7878
|
+
showAttachFileButton: attachmentSettings.showAttachFileButton,
|
|
7879
|
+
allowedImageExtensions: attachmentSettings.allowedImageExtensions,
|
|
7880
|
+
allowedFileExtensions: attachmentSettings.allowedFileExtensions,
|
|
7881
|
+
maxCount: attachmentSettings.maxCount,
|
|
7882
|
+
maxFileBytes: attachmentSettings.maxFileBytes,
|
|
7883
|
+
maxTotalBytes: attachmentSettings.maxTotalBytes,
|
|
7884
|
+
onUploadImageClick,
|
|
7885
|
+
onAttachFileClick,
|
|
7886
|
+
editingMessageId,
|
|
7887
|
+
onClearEditing: handleClearEditing,
|
|
7888
|
+
analysisMode: enableDeepModeToggle ? analysisMode : void 0,
|
|
7889
|
+
onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
|
|
7890
|
+
slashCommands
|
|
7891
|
+
}
|
|
7892
|
+
)
|
|
7893
|
+
]
|
|
7894
|
+
},
|
|
7895
|
+
"v2-chat"
|
|
7896
|
+
) }) }),
|
|
7897
|
+
pdfPreviewMode === "split" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7898
|
+
PdfSheetV2,
|
|
6551
7899
|
{
|
|
6552
|
-
|
|
6553
|
-
|
|
7900
|
+
src: pdfSheet?.href ?? null,
|
|
7901
|
+
title: pdfSheet?.title ?? "",
|
|
7902
|
+
onClose: closePdfSheet,
|
|
7903
|
+
mode: "split"
|
|
6554
7904
|
}
|
|
6555
7905
|
),
|
|
6556
|
-
|
|
6557
|
-
|
|
7906
|
+
pdfPreviewMode === "sheet" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7907
|
+
"div",
|
|
6558
7908
|
{
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
onCancelRecording: handleCancelRecording,
|
|
6571
|
-
onConfirmRecording: handleConfirmRecording,
|
|
6572
|
-
showResetSession,
|
|
6573
|
-
onResetSession: requestResetSession,
|
|
6574
|
-
showAttachmentButton,
|
|
6575
|
-
showUploadImageButton,
|
|
6576
|
-
showAttachFileButton,
|
|
6577
|
-
onUploadImageClick,
|
|
6578
|
-
onAttachFileClick,
|
|
6579
|
-
editingMessageId,
|
|
6580
|
-
onClearEditing: handleClearEditing,
|
|
6581
|
-
analysisMode: enableDeepModeToggle ? analysisMode : void 0,
|
|
6582
|
-
onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
|
|
6583
|
-
slashCommands
|
|
7909
|
+
className: "payman-v2-pdf-sheet-top-anchor",
|
|
7910
|
+
"aria-hidden": !pdfSheet,
|
|
7911
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7912
|
+
PdfSheetV2,
|
|
7913
|
+
{
|
|
7914
|
+
src: pdfSheet?.href ?? null,
|
|
7915
|
+
title: pdfSheet?.title ?? "",
|
|
7916
|
+
onClose: closePdfSheet,
|
|
7917
|
+
mode: "sheet"
|
|
7918
|
+
}
|
|
7919
|
+
)
|
|
6584
7920
|
}
|
|
6585
7921
|
)
|
|
6586
7922
|
]
|
|
6587
|
-
}
|
|
6588
|
-
|
|
6589
|
-
) }),
|
|
7923
|
+
}
|
|
7924
|
+
),
|
|
6590
7925
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6591
7926
|
ImageLightboxV2,
|
|
6592
7927
|
{
|
|
@@ -6621,20 +7956,39 @@ var PaymanChat = react.forwardRef(
|
|
|
6621
7956
|
function PaymanChat2(props, ref) {
|
|
6622
7957
|
const mergedCallbacks = useSentryChatCallbacks(props.callbacks, props.config);
|
|
6623
7958
|
const chat = useChatV2(props.config, mergedCallbacks);
|
|
6624
|
-
|
|
7959
|
+
const attachmentUpload = useAttachmentUpload(props.config);
|
|
7960
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7961
|
+
PaymanChatInner,
|
|
7962
|
+
{
|
|
7963
|
+
...props,
|
|
7964
|
+
chat,
|
|
7965
|
+
attachmentUpload,
|
|
7966
|
+
ref
|
|
7967
|
+
}
|
|
7968
|
+
);
|
|
6625
7969
|
}
|
|
6626
7970
|
);
|
|
6627
7971
|
|
|
6628
7972
|
exports.PaymanChat = PaymanChat;
|
|
6629
7973
|
exports.PaymanChatContext = PaymanChatContext;
|
|
7974
|
+
exports.PdfSheetV2 = PdfSheetV2;
|
|
6630
7975
|
exports.UserActionStaleError = UserActionStaleError;
|
|
7976
|
+
exports.buildSignedUrlEndpoint = buildSignedUrlEndpoint;
|
|
6631
7977
|
exports.cancelUserAction = cancelUserAction;
|
|
6632
7978
|
exports.captureSentryError = captureSentryError;
|
|
6633
7979
|
exports.cn = cn;
|
|
6634
|
-
exports.
|
|
7980
|
+
exports.formatAttachmentBytes = formatAttachmentBytes;
|
|
6635
7981
|
exports.formatDate = formatDate;
|
|
7982
|
+
exports.getPdfTitleFromUrl = getPdfTitleFromUrl;
|
|
7983
|
+
exports.isPdfUrl = isPdfUrl;
|
|
7984
|
+
exports.mapExecutionHistoryPageToChatMessages = mapExecutionHistoryPageToChatMessages;
|
|
7985
|
+
exports.mapExecutionHistoryToChatMessages = mapExecutionHistoryToChatMessages;
|
|
6636
7986
|
exports.resendUserAction = resendUserAction;
|
|
7987
|
+
exports.stripAttachmentsSuffixFromIntent = stripAttachmentsSuffixFromIntent;
|
|
6637
7988
|
exports.submitUserAction = submitUserAction;
|
|
7989
|
+
exports.uploadAttachment = uploadAttachment;
|
|
7990
|
+
exports.uploadAttachments = uploadAttachments;
|
|
7991
|
+
exports.useAttachmentUpload = useAttachmentUpload;
|
|
6638
7992
|
exports.useChatV2 = useChatV2;
|
|
6639
7993
|
exports.usePaymanChat = usePaymanChat;
|
|
6640
7994
|
exports.useVoice = useVoice;
|