@paymanai/payman-ask-sdk 4.0.18 → 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 -25
- package/dist/index.d.ts +93 -25
- package/dist/index.js +1757 -381
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1749 -384
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +484 -25
- package/dist/styles.css.map +1 -1
- package/package.json +2 -2
- package/dist/index.native.js +0 -3675
- 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/;
|
|
@@ -873,17 +881,6 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
873
881
|
signal: abortController.signal,
|
|
874
882
|
onEvent: (event) => {
|
|
875
883
|
if (abortController.signal.aborted) return;
|
|
876
|
-
try {
|
|
877
|
-
const et = event?.eventType;
|
|
878
|
-
if (et === "RUN_IN_PROGRESS" || et === "INTENT_PROGRESS" || et === "THINKING_DELTA") {
|
|
879
|
-
const len = (event.partialText ?? event.text ?? "").length;
|
|
880
|
-
console.log(`[stream] ${et} (+${len} chars)`);
|
|
881
|
-
} else {
|
|
882
|
-
console.log(`[stream] ${et ?? "?"}:`, JSON.stringify(event));
|
|
883
|
-
}
|
|
884
|
-
} catch {
|
|
885
|
-
console.log("[stream] (unserializable event)", event?.eventType);
|
|
886
|
-
}
|
|
887
884
|
processStreamEventV2(event, state);
|
|
888
885
|
if (state.lastUserAction) {
|
|
889
886
|
callbacksRef.current.onUserActionRequired?.(state.lastUserAction);
|
|
@@ -1079,10 +1076,85 @@ function createCancelledMessageUpdate(steps, currentMessage) {
|
|
|
1079
1076
|
currentMessage: currentMessage || "Thinking..."
|
|
1080
1077
|
};
|
|
1081
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
|
+
}
|
|
1082
1154
|
var UserActionStaleError = class extends Error {
|
|
1083
1155
|
constructor(userActionId, message = "User action is no longer actionable") {
|
|
1084
1156
|
super(message);
|
|
1085
|
-
|
|
1157
|
+
__publicField2(this, "userActionId");
|
|
1086
1158
|
this.name = "UserActionStaleError";
|
|
1087
1159
|
this.userActionId = userActionId;
|
|
1088
1160
|
}
|
|
@@ -1143,12 +1215,32 @@ function getStoredOrInitialMessages(config) {
|
|
|
1143
1215
|
function getSessionIdFromMessages(messages) {
|
|
1144
1216
|
return messages.find((message) => message.sessionId)?.sessionId;
|
|
1145
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
|
+
}
|
|
1146
1237
|
function useChatV2(config, callbacks = {}) {
|
|
1147
1238
|
const [messages, setMessages] = react.useState(() => getStoredOrInitialMessages(config));
|
|
1148
1239
|
const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(() => {
|
|
1149
1240
|
if (!config.userId) return false;
|
|
1150
1241
|
return activeStreamStore.get(config.userId)?.isWaiting ?? false;
|
|
1151
1242
|
});
|
|
1243
|
+
const [isUploadingAttachments, setIsUploadingAttachments] = react.useState(false);
|
|
1152
1244
|
const sessionIdRef = react.useRef(
|
|
1153
1245
|
getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
|
|
1154
1246
|
);
|
|
@@ -1223,21 +1315,45 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1223
1315
|
);
|
|
1224
1316
|
const sendMessage = react.useCallback(
|
|
1225
1317
|
async (userMessage, options) => {
|
|
1226
|
-
|
|
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
|
+
}
|
|
1227
1336
|
if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
|
|
1228
1337
|
sessionIdRef.current = generateId();
|
|
1229
1338
|
callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
|
|
1230
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;
|
|
1231
1346
|
const userMessageId = `user-${Date.now()}`;
|
|
1232
1347
|
const userMsg = {
|
|
1233
1348
|
id: userMessageId,
|
|
1234
1349
|
sessionId: sessionIdRef.current,
|
|
1235
1350
|
role: "user",
|
|
1236
|
-
content:
|
|
1237
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1351
|
+
content: trimmedMessage,
|
|
1352
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1353
|
+
attachments: messageAttachments
|
|
1238
1354
|
};
|
|
1239
1355
|
setMessages((prev) => [...prev, userMsg]);
|
|
1240
|
-
callbacksRef.current.onMessageSent?.(
|
|
1356
|
+
callbacksRef.current.onMessageSent?.(trimmedMessage);
|
|
1241
1357
|
setIsWaitingForResponse(true);
|
|
1242
1358
|
callbacksRef.current.onStreamStart?.();
|
|
1243
1359
|
const streamingId = `assistant-${Date.now()}`;
|
|
@@ -1264,11 +1380,14 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1264
1380
|
activeStreamStore.start(userId, abortController, initialMessages);
|
|
1265
1381
|
}
|
|
1266
1382
|
const newSessionId = await startStream(
|
|
1267
|
-
|
|
1383
|
+
trimmedMessage,
|
|
1268
1384
|
streamingId,
|
|
1269
1385
|
sessionIdRef.current,
|
|
1270
1386
|
abortController,
|
|
1271
|
-
|
|
1387
|
+
{
|
|
1388
|
+
analysisMode: options?.analysisMode,
|
|
1389
|
+
attachments: streamAttachments
|
|
1390
|
+
}
|
|
1272
1391
|
);
|
|
1273
1392
|
const finalStreamUserId = streamUserIdRef.current ?? userId;
|
|
1274
1393
|
if (finalStreamUserId) {
|
|
@@ -1446,6 +1565,18 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1446
1565
|
setMessages(config.initialMessages);
|
|
1447
1566
|
sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
|
|
1448
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]);
|
|
1449
1580
|
react.useEffect(() => {
|
|
1450
1581
|
const prevUserId = prevUserIdRef.current;
|
|
1451
1582
|
prevUserIdRef.current = config.userId;
|
|
@@ -1480,6 +1611,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1480
1611
|
getSessionId,
|
|
1481
1612
|
getMessages,
|
|
1482
1613
|
isWaitingForResponse,
|
|
1614
|
+
isUploadingAttachments,
|
|
1483
1615
|
sessionId: sessionIdRef.current,
|
|
1484
1616
|
userActionState,
|
|
1485
1617
|
submitUserAction: submitUserAction2,
|
|
@@ -1703,6 +1835,102 @@ function useVoice(config = {}, callbacks = {}) {
|
|
|
1703
1835
|
reset
|
|
1704
1836
|
};
|
|
1705
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
|
+
}
|
|
1706
1934
|
function classifyField(field) {
|
|
1707
1935
|
if (!field) return "text";
|
|
1708
1936
|
if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
|
|
@@ -1819,6 +2047,66 @@ function buildContent(schema, values) {
|
|
|
1819
2047
|
}
|
|
1820
2048
|
return content;
|
|
1821
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
|
+
}
|
|
1822
2110
|
var PaymanChatContext = react.createContext(void 0);
|
|
1823
2111
|
function usePaymanChat() {
|
|
1824
2112
|
const context = react.useContext(PaymanChatContext);
|
|
@@ -1929,6 +2217,111 @@ function subscribeToCfRay(urlPattern, listener) {
|
|
|
1929
2217
|
};
|
|
1930
2218
|
}
|
|
1931
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
|
+
|
|
1932
2325
|
// src/utils/slashCommands.ts
|
|
1933
2326
|
var DEFAULT_SLASH_COMMANDS = [
|
|
1934
2327
|
{
|
|
@@ -3080,6 +3473,108 @@ function ActionTooltipV2({ label, children }) {
|
|
|
3080
3473
|
] }) })
|
|
3081
3474
|
] });
|
|
3082
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
|
+
}
|
|
3083
3578
|
function formatMessageTime(timestamp) {
|
|
3084
3579
|
const value = new Date(timestamp);
|
|
3085
3580
|
if (Number.isNaN(value.getTime())) return "";
|
|
@@ -3092,6 +3587,7 @@ function UserMessageV2({
|
|
|
3092
3587
|
message,
|
|
3093
3588
|
onEdit,
|
|
3094
3589
|
onRetry,
|
|
3590
|
+
onImageClick,
|
|
3095
3591
|
retryDisabled = false,
|
|
3096
3592
|
actions
|
|
3097
3593
|
}) {
|
|
@@ -3174,7 +3670,15 @@ function UserMessageV2({
|
|
|
3174
3670
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3175
3671
|
toastPortal,
|
|
3176
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: [
|
|
3177
|
-
/* @__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: [
|
|
3178
3682
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-user-msg-command-chip", children: parsedCommand.command }),
|
|
3179
3683
|
parsedCommand.body.trim() ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-user-msg-text", children: parsedCommand.body }) : null
|
|
3180
3684
|
] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-user-msg-text", children: message.content }) }),
|
|
@@ -3399,9 +3903,44 @@ function MarkdownImageV2({
|
|
|
3399
3903
|
}
|
|
3400
3904
|
) });
|
|
3401
3905
|
}
|
|
3402
|
-
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) {
|
|
3403
3936
|
return {
|
|
3404
|
-
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
|
+
},
|
|
3405
3944
|
code: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("code", { children }),
|
|
3406
3945
|
pre: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-markdown-pre", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { children }) }),
|
|
3407
3946
|
ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { children }),
|
|
@@ -3414,7 +3953,15 @@ function buildComponents(onImageClick, isResolvingRef) {
|
|
|
3414
3953
|
em: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("em", { children }),
|
|
3415
3954
|
blockquote: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("blockquote", { children }),
|
|
3416
3955
|
hr: () => /* @__PURE__ */ jsxRuntime.jsx("hr", {}),
|
|
3417
|
-
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
|
+
},
|
|
3418
3965
|
img: ({ src, alt }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3419
3966
|
MarkdownImageV2,
|
|
3420
3967
|
{
|
|
@@ -3435,13 +3982,15 @@ function MarkdownRendererV2({
|
|
|
3435
3982
|
content,
|
|
3436
3983
|
isStreaming,
|
|
3437
3984
|
isResolvingImages,
|
|
3438
|
-
onImageClick
|
|
3985
|
+
onImageClick,
|
|
3986
|
+
onPdfClick,
|
|
3987
|
+
autoOpenPdf
|
|
3439
3988
|
}) {
|
|
3440
3989
|
const isResolvingRef = react.useRef(isResolvingImages);
|
|
3441
3990
|
isResolvingRef.current = isResolvingImages;
|
|
3442
3991
|
const components = react.useMemo(
|
|
3443
|
-
() => buildComponents(onImageClick, isResolvingRef),
|
|
3444
|
-
[onImageClick]
|
|
3992
|
+
() => buildComponents(onImageClick, isResolvingRef, onPdfClick, autoOpenPdf),
|
|
3993
|
+
[onImageClick, onPdfClick, autoOpenPdf]
|
|
3445
3994
|
);
|
|
3446
3995
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3447
3996
|
"div",
|
|
@@ -3810,6 +4359,17 @@ function stripIncompleteImageToken(text) {
|
|
|
3810
4359
|
if (/^!\[[^\]]*\]\([^)]*\)/.test(after)) return text;
|
|
3811
4360
|
return text.slice(0, lastBang);
|
|
3812
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
|
+
}
|
|
3813
4373
|
function getFeedbackState(message) {
|
|
3814
4374
|
const feedback = message.feedback;
|
|
3815
4375
|
if (feedback === "up" || feedback === "down") return feedback;
|
|
@@ -3833,6 +4393,12 @@ function AssistantMessageV2({
|
|
|
3833
4393
|
() => getFeedbackState(message)
|
|
3834
4394
|
);
|
|
3835
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
|
+
}, []);
|
|
3836
4402
|
const canSubmitFeedback = !!onSubmitFeedback && !!message.executionId;
|
|
3837
4403
|
const [toast, setToast] = react.useState(null);
|
|
3838
4404
|
const copyResetTimerRef = react.useRef(null);
|
|
@@ -3857,7 +4423,7 @@ function AssistantMessageV2({
|
|
|
3857
4423
|
const raw = message.isStreaming ? message.streamingContent || message.content : message.content;
|
|
3858
4424
|
if (!raw) return "";
|
|
3859
4425
|
const normalized = raw.replace(/\\n/g, "\n");
|
|
3860
|
-
return message.isStreaming ?
|
|
4426
|
+
return message.isStreaming ? stripIncompleteMarkdownTokens(normalized) : normalized;
|
|
3861
4427
|
})();
|
|
3862
4428
|
const isThinkingStreaming = !!message.isStreaming && !rawResponseContent && !message.isError;
|
|
3863
4429
|
const responseTypingEnabled = hasEverStreamed.current && Boolean(rawResponseContent) && !message.isError;
|
|
@@ -4027,10 +4593,12 @@ function AssistantMessageV2({
|
|
|
4027
4593
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-assistant-msg-content-area", children: displayContent ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4028
4594
|
MarkdownRendererV2,
|
|
4029
4595
|
{
|
|
4030
|
-
content: displayContent,
|
|
4596
|
+
content: message.isStreaming && !isCancelled || isResponseTyping ? stripIncompleteMarkdownTokens(displayContent) : displayContent,
|
|
4031
4597
|
isStreaming: message.isStreaming && !isCancelled || isResponseTyping,
|
|
4032
4598
|
isResolvingImages: message.isResolvingImages,
|
|
4033
|
-
onImageClick
|
|
4599
|
+
onImageClick,
|
|
4600
|
+
onPdfClick: handlePdfClick,
|
|
4601
|
+
autoOpenPdf: hasEverStreamed.current
|
|
4034
4602
|
}
|
|
4035
4603
|
) : !isThinkingStreaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-assistant-msg-placeholder", children: "..." }) : null }),
|
|
4036
4604
|
isCancelled && message.isStreaming && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-assistant-msg-paused", children: [
|
|
@@ -4247,7 +4815,6 @@ function VerificationInline({
|
|
|
4247
4815
|
const [code, setCode] = react.useState("");
|
|
4248
4816
|
const [errored, setErrored] = react.useState(false);
|
|
4249
4817
|
const [resendSec, setResendSec] = react.useState(0);
|
|
4250
|
-
const [hiddenAfterResend, setHiddenAfterResend] = react.useState(false);
|
|
4251
4818
|
const lastSubmittedRef = react.useRef(null);
|
|
4252
4819
|
const resendTimerRef = react.useRef(void 0);
|
|
4253
4820
|
const status = prompt.status;
|
|
@@ -4261,9 +4828,6 @@ function VerificationInline({
|
|
|
4261
4828
|
lastSubmittedRef.current = null;
|
|
4262
4829
|
}
|
|
4263
4830
|
}, [prompt.subAction, prompt.userActionId]);
|
|
4264
|
-
react.useEffect(() => {
|
|
4265
|
-
setHiddenAfterResend(false);
|
|
4266
|
-
}, [prompt.expirySeconds, prompt.message, prompt.subAction, prompt.userActionId]);
|
|
4267
4831
|
react.useEffect(() => {
|
|
4268
4832
|
if (code.length < codeLen) lastSubmittedRef.current = null;
|
|
4269
4833
|
}, [code, codeLen]);
|
|
@@ -4314,13 +4878,11 @@ function VerificationInline({
|
|
|
4314
4878
|
if (locked || resendSec > 0) return;
|
|
4315
4879
|
setErrored(false);
|
|
4316
4880
|
setCode("");
|
|
4317
|
-
setHiddenAfterResend(true);
|
|
4318
4881
|
lastSubmittedRef.current = null;
|
|
4319
4882
|
try {
|
|
4320
4883
|
await onResend(prompt.userActionId);
|
|
4321
4884
|
startResendCooldown();
|
|
4322
4885
|
} catch {
|
|
4323
|
-
setHiddenAfterResend(false);
|
|
4324
4886
|
}
|
|
4325
4887
|
}, [locked, onResend, prompt.userActionId, resendSec, startResendCooldown]);
|
|
4326
4888
|
const handleCancel = react.useCallback(() => {
|
|
@@ -4328,7 +4890,6 @@ function VerificationInline({
|
|
|
4328
4890
|
void onCancel(prompt.userActionId);
|
|
4329
4891
|
}, [busy, onCancel, prompt.userActionId]);
|
|
4330
4892
|
const description = prompt.message?.trim() || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
|
|
4331
|
-
if (hiddenAfterResend) return null;
|
|
4332
4893
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Verification required", children: [
|
|
4333
4894
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-head", children: [
|
|
4334
4895
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
|
|
@@ -4424,9 +4985,6 @@ function SchemaFormInline({
|
|
|
4424
4985
|
const busy = status === "submitting";
|
|
4425
4986
|
const stale = status === "stale";
|
|
4426
4987
|
const locked = busy || stale || expired;
|
|
4427
|
-
const isUserConfirmation = prompt.subAction === "UserConfirmation";
|
|
4428
|
-
const messageFormat = prompt.metadata?.["payman/messageFormat"];
|
|
4429
|
-
const renderMarkdown = messageFormat === "markdown";
|
|
4430
4988
|
const setValue = (key, value) => {
|
|
4431
4989
|
setValues((prev) => ({ ...prev, [key]: value }));
|
|
4432
4990
|
setErrors((prev) => {
|
|
@@ -4452,8 +5010,8 @@ function SchemaFormInline({
|
|
|
4452
5010
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
|
|
4453
5011
|
typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
|
|
4454
5012
|
] }),
|
|
4455
|
-
prompt.message?.trim() &&
|
|
4456
|
-
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]) => {
|
|
4457
5015
|
const widget = classifyField(field);
|
|
4458
5016
|
const label = field.title || key;
|
|
4459
5017
|
const required = isRequired(schema, key);
|
|
@@ -4526,7 +5084,7 @@ function SchemaFormInline({
|
|
|
4526
5084
|
className: "payman-v2-ua-btn payman-v2-ua-btn-primary",
|
|
4527
5085
|
disabled: locked,
|
|
4528
5086
|
onClick: handleSubmit,
|
|
4529
|
-
children: busy ? "Submitting\u2026" :
|
|
5087
|
+
children: busy ? "Submitting\u2026" : "Submit"
|
|
4530
5088
|
}
|
|
4531
5089
|
),
|
|
4532
5090
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4582,25 +5140,10 @@ function useExpiryCountdown(prompt) {
|
|
|
4582
5140
|
const expired = initial !== void 0 && secondsLeft === 0;
|
|
4583
5141
|
return [secondsLeft, expired];
|
|
4584
5142
|
}
|
|
4585
|
-
function UserActionInline({
|
|
4586
|
-
prompt,
|
|
4587
|
-
onSubmit,
|
|
4588
|
-
onCancel,
|
|
4589
|
-
onResend,
|
|
4590
|
-
onExpired
|
|
4591
|
-
}) {
|
|
5143
|
+
function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
|
|
4592
5144
|
const [secondsLeft, expired] = useExpiryCountdown(prompt);
|
|
4593
|
-
react.useEffect(() => {
|
|
4594
|
-
if (expired && prompt.kind !== "notification") onExpired?.();
|
|
4595
|
-
}, [expired, onExpired, prompt.kind]);
|
|
4596
5145
|
let body;
|
|
4597
|
-
if (
|
|
4598
|
-
const note = {
|
|
4599
|
-
id: `${prompt.userActionId}-expired`,
|
|
4600
|
-
message: prompt.kind === "verification" ? "Verification Request Expired" : "User Form Expired"
|
|
4601
|
-
};
|
|
4602
|
-
body = /* @__PURE__ */ jsxRuntime.jsx(NotificationInline, { notification: note });
|
|
4603
|
-
} else if (prompt.kind === "verification") {
|
|
5146
|
+
if (prompt.kind === "verification") {
|
|
4604
5147
|
body = /* @__PURE__ */ jsxRuntime.jsx(
|
|
4605
5148
|
VerificationInline,
|
|
4606
5149
|
{
|
|
@@ -4640,23 +5183,10 @@ function UserActionInline({
|
|
|
4640
5183
|
}
|
|
4641
5184
|
var SCROLL_THRESHOLD2 = 100;
|
|
4642
5185
|
var USER_SCROLL_UP_EPSILON = 4;
|
|
4643
|
-
var PROMPT_KEY_SEPARATOR = "";
|
|
4644
|
-
function getPromptSlotKey(prompt) {
|
|
4645
|
-
return prompt.toolCallId || prompt.userActionId;
|
|
4646
|
-
}
|
|
4647
|
-
function getPromptViewKey(prompt) {
|
|
4648
|
-
return [
|
|
4649
|
-
getPromptSlotKey(prompt),
|
|
4650
|
-
prompt.userActionId,
|
|
4651
|
-
prompt.subAction ?? "",
|
|
4652
|
-
prompt.expirySeconds ?? "",
|
|
4653
|
-
prompt.message ?? ""
|
|
4654
|
-
].join(PROMPT_KEY_SEPARATOR);
|
|
4655
|
-
}
|
|
4656
5186
|
var MessageListV2 = react.forwardRef(
|
|
4657
5187
|
function MessageListV22({
|
|
4658
5188
|
messages,
|
|
4659
|
-
isStreaming = false,
|
|
5189
|
+
isStreaming: _isStreaming = false,
|
|
4660
5190
|
onEditUserMessage,
|
|
4661
5191
|
onRetryUserMessage,
|
|
4662
5192
|
onImageClick,
|
|
@@ -4670,7 +5200,8 @@ var MessageListV2 = react.forwardRef(
|
|
|
4670
5200
|
onResendUserAction,
|
|
4671
5201
|
onDismissNotification,
|
|
4672
5202
|
onSubmitFeedback,
|
|
4673
|
-
typingSpeed = 4
|
|
5203
|
+
typingSpeed = 4,
|
|
5204
|
+
sidePanelOpen = false
|
|
4674
5205
|
}, ref) {
|
|
4675
5206
|
const noop = react.useCallback(async () => {
|
|
4676
5207
|
}, []);
|
|
@@ -4678,10 +5209,11 @@ var MessageListV2 = react.forwardRef(
|
|
|
4678
5209
|
const scrollInnerRef = react.useRef(null);
|
|
4679
5210
|
const isNearBottomRef = react.useRef(true);
|
|
4680
5211
|
const [showScrollBtn, setShowScrollBtn] = react.useState(false);
|
|
4681
|
-
const [expiredPromptViewState, setExpiredPromptViewState] = react.useState({});
|
|
4682
5212
|
const prevCountRef = react.useRef(messages.length);
|
|
4683
5213
|
const pauseStickUntilUserMessageRef = react.useRef(false);
|
|
4684
5214
|
const followingBottomRef = react.useRef(true);
|
|
5215
|
+
const sidePanelOpenRef = react.useRef(sidePanelOpen);
|
|
5216
|
+
sidePanelOpenRef.current = sidePanelOpen;
|
|
4685
5217
|
const lastPinAtRef = react.useRef(0);
|
|
4686
5218
|
const prevScrollTopRef = react.useRef(0);
|
|
4687
5219
|
const getDistanceFromBottom = react.useCallback(() => {
|
|
@@ -4689,81 +5221,6 @@ var MessageListV2 = react.forwardRef(
|
|
|
4689
5221
|
if (!el) return 0;
|
|
4690
5222
|
return el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
4691
5223
|
}, []);
|
|
4692
|
-
const messageActivityFingerprint = react.useMemo(() => {
|
|
4693
|
-
const last = messages[messages.length - 1];
|
|
4694
|
-
const promptFingerprint = (userActionPrompts ?? []).map((prompt) => `${getPromptViewKey(prompt)}:${prompt.status}`).join(PROMPT_KEY_SEPARATOR);
|
|
4695
|
-
const notificationFingerprint = (notifications ?? []).map((notification) => notification.id).join(PROMPT_KEY_SEPARATOR);
|
|
4696
|
-
if (!last) {
|
|
4697
|
-
return [
|
|
4698
|
-
0,
|
|
4699
|
-
isStreaming ? "streaming" : "idle",
|
|
4700
|
-
promptFingerprint,
|
|
4701
|
-
notificationFingerprint
|
|
4702
|
-
].join(PROMPT_KEY_SEPARATOR);
|
|
4703
|
-
}
|
|
4704
|
-
return [
|
|
4705
|
-
messages.length,
|
|
4706
|
-
isStreaming ? "streaming" : "idle",
|
|
4707
|
-
last.id,
|
|
4708
|
-
last.role,
|
|
4709
|
-
last.content ?? "",
|
|
4710
|
-
last.isStreaming ? "streaming" : "done",
|
|
4711
|
-
last.streamProgress ?? "",
|
|
4712
|
-
last.steps?.length ?? 0,
|
|
4713
|
-
last.errorDetails ?? "",
|
|
4714
|
-
promptFingerprint,
|
|
4715
|
-
notificationFingerprint
|
|
4716
|
-
].join(PROMPT_KEY_SEPARATOR);
|
|
4717
|
-
}, [isStreaming, messages, notifications, userActionPrompts]);
|
|
4718
|
-
const handleUserActionExpired = react.useCallback(
|
|
4719
|
-
(promptKey) => {
|
|
4720
|
-
setExpiredPromptViewState((prev) => {
|
|
4721
|
-
if (prev[promptKey]) return prev;
|
|
4722
|
-
return {
|
|
4723
|
-
...prev,
|
|
4724
|
-
[promptKey]: { baseline: messageActivityFingerprint, hidden: false }
|
|
4725
|
-
};
|
|
4726
|
-
});
|
|
4727
|
-
},
|
|
4728
|
-
[messageActivityFingerprint]
|
|
4729
|
-
);
|
|
4730
|
-
react.useEffect(() => {
|
|
4731
|
-
setExpiredPromptViewState((prev) => {
|
|
4732
|
-
let changed = false;
|
|
4733
|
-
const next = {};
|
|
4734
|
-
for (const [key, state] of Object.entries(prev)) {
|
|
4735
|
-
if (!state.hidden && state.baseline !== messageActivityFingerprint) {
|
|
4736
|
-
next[key] = { ...state, hidden: true };
|
|
4737
|
-
changed = true;
|
|
4738
|
-
} else {
|
|
4739
|
-
next[key] = state;
|
|
4740
|
-
}
|
|
4741
|
-
}
|
|
4742
|
-
return changed ? next : prev;
|
|
4743
|
-
});
|
|
4744
|
-
}, [messageActivityFingerprint]);
|
|
4745
|
-
react.useEffect(() => {
|
|
4746
|
-
const livePromptKeys = new Set((userActionPrompts ?? []).map(getPromptViewKey));
|
|
4747
|
-
setExpiredPromptViewState((prev) => {
|
|
4748
|
-
let changed = false;
|
|
4749
|
-
const next = {};
|
|
4750
|
-
for (const [key, state] of Object.entries(prev)) {
|
|
4751
|
-
if (livePromptKeys.has(key)) {
|
|
4752
|
-
next[key] = state;
|
|
4753
|
-
} else {
|
|
4754
|
-
changed = true;
|
|
4755
|
-
}
|
|
4756
|
-
}
|
|
4757
|
-
return changed ? next : prev;
|
|
4758
|
-
});
|
|
4759
|
-
}, [userActionPrompts]);
|
|
4760
|
-
const visibleUserActionPrompts = react.useMemo(
|
|
4761
|
-
() => userActionPrompts?.filter((prompt) => {
|
|
4762
|
-
const promptKey = getPromptViewKey(prompt);
|
|
4763
|
-
return !expiredPromptViewState[promptKey]?.hidden;
|
|
4764
|
-
}),
|
|
4765
|
-
[expiredPromptViewState, userActionPrompts]
|
|
4766
|
-
);
|
|
4767
5224
|
const pinToBottom = react.useCallback((behavior = "instant") => {
|
|
4768
5225
|
const el = scrollRef.current;
|
|
4769
5226
|
if (!el) return;
|
|
@@ -4804,17 +5261,25 @@ var MessageListV2 = react.forwardRef(
|
|
|
4804
5261
|
const nearBottom = distance <= SCROLL_THRESHOLD2;
|
|
4805
5262
|
isNearBottomRef.current = nearBottom;
|
|
4806
5263
|
setShowScrollBtn(!nearBottom);
|
|
4807
|
-
const sincePin = performance.now() - lastPinAtRef.current;
|
|
4808
|
-
if (sincePin < 250) return;
|
|
4809
5264
|
const scrolledUp = currentScrollTop < prevScrollTop - USER_SCROLL_UP_EPSILON;
|
|
4810
5265
|
if (scrolledUp) {
|
|
4811
5266
|
followingBottomRef.current = false;
|
|
4812
5267
|
pauseStickUntilUserMessageRef.current = true;
|
|
4813
|
-
|
|
5268
|
+
return;
|
|
5269
|
+
}
|
|
5270
|
+
const sincePin = performance.now() - lastPinAtRef.current;
|
|
5271
|
+
if (sincePin < 250) return;
|
|
5272
|
+
if (nearBottom) {
|
|
4814
5273
|
followingBottomRef.current = true;
|
|
4815
5274
|
pauseStickUntilUserMessageRef.current = false;
|
|
4816
5275
|
}
|
|
4817
5276
|
}, [getDistanceFromBottom]);
|
|
5277
|
+
const handleWheel = react.useCallback((e) => {
|
|
5278
|
+
if (e.deltaY < 0) {
|
|
5279
|
+
followingBottomRef.current = false;
|
|
5280
|
+
pauseStickUntilUserMessageRef.current = true;
|
|
5281
|
+
}
|
|
5282
|
+
}, []);
|
|
4818
5283
|
react.useEffect(() => {
|
|
4819
5284
|
const prevCount = prevCountRef.current;
|
|
4820
5285
|
prevCountRef.current = messages.length;
|
|
@@ -4824,7 +5289,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
4824
5289
|
pauseStickUntilUserMessageRef.current = false;
|
|
4825
5290
|
followingBottomRef.current = true;
|
|
4826
5291
|
requestAnimationFrame(() => scrollToBottom());
|
|
4827
|
-
} else if (!pauseStickUntilUserMessageRef.current && followingBottomRef.current) {
|
|
5292
|
+
} else if (!sidePanelOpenRef.current && !pauseStickUntilUserMessageRef.current && followingBottomRef.current) {
|
|
4828
5293
|
requestAnimationFrame(() => scrollToBottom("instant"));
|
|
4829
5294
|
}
|
|
4830
5295
|
}
|
|
@@ -4833,27 +5298,16 @@ var MessageListV2 = react.forwardRef(
|
|
|
4833
5298
|
const inner = scrollInnerRef.current;
|
|
4834
5299
|
if (!inner) return;
|
|
4835
5300
|
const pinIfFollowing = () => {
|
|
5301
|
+
if (sidePanelOpenRef.current) return;
|
|
4836
5302
|
if (pauseStickUntilUserMessageRef.current) return;
|
|
4837
5303
|
if (!followingBottomRef.current) return;
|
|
5304
|
+
if (getDistanceFromBottom() > SCROLL_THRESHOLD2) return;
|
|
4838
5305
|
pinToBottom("instant");
|
|
4839
5306
|
};
|
|
4840
|
-
const ro = new ResizeObserver(
|
|
4841
|
-
pinIfFollowing();
|
|
4842
|
-
});
|
|
5307
|
+
const ro = new ResizeObserver(pinIfFollowing);
|
|
4843
5308
|
ro.observe(inner);
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
});
|
|
4847
|
-
mo.observe(inner, {
|
|
4848
|
-
childList: true,
|
|
4849
|
-
subtree: true,
|
|
4850
|
-
characterData: true
|
|
4851
|
-
});
|
|
4852
|
-
return () => {
|
|
4853
|
-
ro.disconnect();
|
|
4854
|
-
mo.disconnect();
|
|
4855
|
-
};
|
|
4856
|
-
}, [pinToBottom]);
|
|
5309
|
+
return () => ro.disconnect();
|
|
5310
|
+
}, [pinToBottom, getDistanceFromBottom]);
|
|
4857
5311
|
react.useEffect(() => {
|
|
4858
5312
|
if (messages.length > 0) {
|
|
4859
5313
|
setTimeout(() => scrollToBottom("instant"), 50);
|
|
@@ -4865,6 +5319,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
4865
5319
|
{
|
|
4866
5320
|
ref: scrollRef,
|
|
4867
5321
|
onScroll: handleScroll,
|
|
5322
|
+
onWheel: handleWheel,
|
|
4868
5323
|
className: "payman-v2-message-scroll payman-v2-scrollbar",
|
|
4869
5324
|
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4870
5325
|
"div",
|
|
@@ -4878,6 +5333,7 @@ var MessageListV2 = react.forwardRef(
|
|
|
4878
5333
|
message,
|
|
4879
5334
|
onEdit: onEditUserMessage,
|
|
4880
5335
|
onRetry: onRetryUserMessage,
|
|
5336
|
+
onImageClick,
|
|
4881
5337
|
retryDisabled,
|
|
4882
5338
|
actions: messageActions?.userMessageActions
|
|
4883
5339
|
}
|
|
@@ -4900,20 +5356,16 @@ var MessageListV2 = react.forwardRef(
|
|
|
4900
5356
|
},
|
|
4901
5357
|
note.id
|
|
4902
5358
|
)),
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
},
|
|
4914
|
-
promptKey
|
|
4915
|
-
);
|
|
4916
|
-
})
|
|
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
|
+
))
|
|
4917
5369
|
]
|
|
4918
5370
|
}
|
|
4919
5371
|
)
|
|
@@ -4943,45 +5395,281 @@ var MessageListV2 = react.forwardRef(
|
|
|
4943
5395
|
] });
|
|
4944
5396
|
}
|
|
4945
5397
|
);
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
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 = []
|
|
5538
|
+
}, ref) {
|
|
5539
|
+
const [value, setValue] = react.useState("");
|
|
5540
|
+
const [isFocused, setIsFocused] = react.useState(false);
|
|
5541
|
+
const [showActions, setShowActions] = react.useState(false);
|
|
5542
|
+
const [showVoiceTooltip, setShowVoiceTooltip] = react.useState(false);
|
|
5543
|
+
const [selectedCommandIndex, setSelectedCommandIndex] = react.useState(0);
|
|
5544
|
+
const [inlineHint, setInlineHint] = react.useState(null);
|
|
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);
|
|
5549
|
+
const textareaRef = react.useRef(null);
|
|
5550
|
+
const actionsRef = react.useRef(null);
|
|
5551
|
+
const imageInputRef = react.useRef(null);
|
|
5552
|
+
const fileInputRef = react.useRef(null);
|
|
5553
|
+
const preRecordTextRef = react.useRef("");
|
|
5554
|
+
const voiceTooltipTimerRef = react.useRef(
|
|
4981
5555
|
null
|
|
4982
5556
|
);
|
|
4983
5557
|
const voiceDraftSyncActiveRef = react.useRef(false);
|
|
4984
|
-
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;
|
|
4985
5673
|
const hasAttachmentOptions = showUploadImageButton || showAttachFileButton;
|
|
4986
5674
|
const showAttachmentMenuButton = showAttachmentButton && hasAttachmentOptions;
|
|
4987
5675
|
const showVoiceButton = enableVoice && onVoicePress != null;
|
|
@@ -5021,9 +5709,10 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5021
5709
|
const end = message.length;
|
|
5022
5710
|
textarea.setSelectionRange(end, end);
|
|
5023
5711
|
});
|
|
5024
|
-
}
|
|
5712
|
+
},
|
|
5713
|
+
clearAttachments: clearAttachmentsFromInput
|
|
5025
5714
|
}),
|
|
5026
|
-
[disabled]
|
|
5715
|
+
[disabled, clearAttachmentsFromInput]
|
|
5027
5716
|
);
|
|
5028
5717
|
react.useEffect(() => {
|
|
5029
5718
|
if (!showActions) return;
|
|
@@ -5057,8 +5746,23 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5057
5746
|
const separator = base && !base.endsWith(" ") && transcribedText ? " " : "";
|
|
5058
5747
|
setValue(`${base}${separator}${transcribedText}`);
|
|
5059
5748
|
}, [isRecording, transcribedText]);
|
|
5060
|
-
|
|
5061
|
-
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
|
+
}
|
|
5062
5766
|
const commandHint = getSlashCommandValidationHint(value);
|
|
5063
5767
|
if (commandHint) {
|
|
5064
5768
|
setInlineHint(commandHint);
|
|
@@ -5068,15 +5772,38 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5068
5772
|
preRecordTextRef.current = "";
|
|
5069
5773
|
setInlineHint(null);
|
|
5070
5774
|
onClearEditing?.();
|
|
5071
|
-
|
|
5775
|
+
const textToSend = value.trim();
|
|
5776
|
+
const filesToSend = attachedFiles.map((f) => f.file);
|
|
5777
|
+
const attachmentsToSend = [...uploadedAttachmentPayloads];
|
|
5072
5778
|
setValue("");
|
|
5779
|
+
clearAttachmentsFromInput();
|
|
5073
5780
|
requestAnimationFrame(() => {
|
|
5074
5781
|
if (textareaRef.current) {
|
|
5075
5782
|
textareaRef.current.style.height = "auto";
|
|
5076
5783
|
textareaRef.current.focus();
|
|
5077
5784
|
}
|
|
5078
5785
|
});
|
|
5079
|
-
|
|
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
|
+
]);
|
|
5080
5807
|
const selectCommand = react.useCallback(
|
|
5081
5808
|
(command) => {
|
|
5082
5809
|
const insertText = command.insertText ?? `${command.name} `;
|
|
@@ -5121,18 +5848,30 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5121
5848
|
}
|
|
5122
5849
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
5123
5850
|
e.preventDefault();
|
|
5124
|
-
if (isStreaming) return;
|
|
5125
|
-
handleSend();
|
|
5851
|
+
if (isStreaming || isSending || isUploadingAttachments) return;
|
|
5852
|
+
void handleSend();
|
|
5126
5853
|
}
|
|
5127
5854
|
};
|
|
5128
5855
|
const handleUploadImageClick = () => {
|
|
5856
|
+
imageInputRef.current?.click();
|
|
5129
5857
|
onUploadImageClick?.();
|
|
5130
5858
|
setShowActions(false);
|
|
5131
5859
|
};
|
|
5132
5860
|
const handleAttachFileClick = () => {
|
|
5861
|
+
fileInputRef.current?.click();
|
|
5133
5862
|
onAttachFileClick?.();
|
|
5134
5863
|
setShowActions(false);
|
|
5135
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
|
+
};
|
|
5136
5875
|
const hideVoiceTooltip = react.useCallback(() => {
|
|
5137
5876
|
if (voiceTooltipTimerRef.current) {
|
|
5138
5877
|
clearTimeout(voiceTooltipTimerRef.current);
|
|
@@ -5160,9 +5899,39 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5160
5899
|
}
|
|
5161
5900
|
onVoicePress();
|
|
5162
5901
|
};
|
|
5163
|
-
const canSend = value.trim().length > 0 && !disabled;
|
|
5164
|
-
const sendDisabled = !canSend || isStreaming;
|
|
5902
|
+
const canSend = (value.trim().length > 0 || attachedFiles.length > 0) && !disabled;
|
|
5903
|
+
const sendDisabled = !canSend || isStreaming || isUploadingAttachments || !attachmentsReady || hasAttachmentUploadErrors || isSending;
|
|
5165
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
|
+
),
|
|
5166
5935
|
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showCommandSuggestions && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5167
5936
|
framerMotion.motion.div,
|
|
5168
5937
|
{
|
|
@@ -5201,6 +5970,115 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5201
5970
|
),
|
|
5202
5971
|
children: [
|
|
5203
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
|
+
) }),
|
|
5204
6082
|
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: editingMessageId && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5205
6083
|
framerMotion.motion.div,
|
|
5206
6084
|
{
|
|
@@ -5432,13 +6310,13 @@ var ChatInputV2 = react.forwardRef(
|
|
|
5432
6310
|
"button",
|
|
5433
6311
|
{
|
|
5434
6312
|
type: "button",
|
|
5435
|
-
onClick: handleSend,
|
|
6313
|
+
onClick: () => void handleSend(),
|
|
5436
6314
|
disabled: sendDisabled,
|
|
5437
6315
|
className: cn(
|
|
5438
6316
|
"payman-v2-input-send-btn",
|
|
5439
6317
|
sendDisabled && "payman-v2-input-send-btn-disabled"
|
|
5440
6318
|
),
|
|
5441
|
-
"aria-label": "Send message",
|
|
6319
|
+
"aria-label": isUploadingAttachments || isSending ? "Uploading attachments" : "Send message",
|
|
5442
6320
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5443
6321
|
lucideReact.ArrowUp,
|
|
5444
6322
|
{
|
|
@@ -5973,6 +6851,389 @@ function TimelineBars({
|
|
|
5973
6851
|
)
|
|
5974
6852
|
] });
|
|
5975
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
|
+
}
|
|
5976
7237
|
var DEFAULT_USER_ACTION_STATE = {
|
|
5977
7238
|
prompts: [],
|
|
5978
7239
|
notifications: []
|
|
@@ -6067,7 +7328,8 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6067
7328
|
onLoadMoreMessages,
|
|
6068
7329
|
isLoadingMoreMessages = false,
|
|
6069
7330
|
hasMoreMessages = false,
|
|
6070
|
-
chat
|
|
7331
|
+
chat,
|
|
7332
|
+
attachmentUpload
|
|
6071
7333
|
}, ref) {
|
|
6072
7334
|
const [inputValue, setInputValue] = react.useState("");
|
|
6073
7335
|
const prevInputValueRef = react.useRef(inputValue);
|
|
@@ -6146,6 +7408,19 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6146
7408
|
}
|
|
6147
7409
|
}
|
|
6148
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
|
+
}, []);
|
|
6149
7424
|
const contextValue = react.useMemo(
|
|
6150
7425
|
() => ({
|
|
6151
7426
|
resetSession,
|
|
@@ -6154,7 +7429,8 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6154
7429
|
cancelStream,
|
|
6155
7430
|
getSessionId,
|
|
6156
7431
|
getMessages,
|
|
6157
|
-
isWaitingForResponse
|
|
7432
|
+
isWaitingForResponse,
|
|
7433
|
+
openPdfSheet
|
|
6158
7434
|
}),
|
|
6159
7435
|
[
|
|
6160
7436
|
resetSession,
|
|
@@ -6163,7 +7439,8 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6163
7439
|
cancelStream,
|
|
6164
7440
|
getSessionId,
|
|
6165
7441
|
getMessages,
|
|
6166
|
-
isWaitingForResponse
|
|
7442
|
+
isWaitingForResponse,
|
|
7443
|
+
openPdfSheet
|
|
6167
7444
|
]
|
|
6168
7445
|
);
|
|
6169
7446
|
const {
|
|
@@ -6219,7 +7496,10 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6219
7496
|
setInputValue("");
|
|
6220
7497
|
setLightboxSrc(null);
|
|
6221
7498
|
setLightboxAlt("");
|
|
7499
|
+
setPdfSheet(null);
|
|
7500
|
+
autoOpenedPdfHrefsRef.current.clear();
|
|
6222
7501
|
chatInputV2Ref.current?.setDraft("");
|
|
7502
|
+
chatInputV2Ref.current?.clearAttachments();
|
|
6223
7503
|
clearTranscript();
|
|
6224
7504
|
if (isRecording) {
|
|
6225
7505
|
stopRecording();
|
|
@@ -6270,14 +7550,20 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6270
7550
|
emptyStateComponent,
|
|
6271
7551
|
showResetSession = false,
|
|
6272
7552
|
enableDeepModeToggle = true,
|
|
6273
|
-
showAttachmentButton = true,
|
|
6274
|
-
showUploadImageButton = true,
|
|
6275
|
-
showAttachFileButton = true,
|
|
6276
7553
|
messageActions: messageActionsConfig,
|
|
6277
7554
|
enableSlashCommands = true,
|
|
6278
7555
|
slashCommands: slashCommandsConfig,
|
|
6279
7556
|
commandPermissions
|
|
6280
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
|
+
);
|
|
6281
7567
|
const messageActions = react.useMemo(
|
|
6282
7568
|
() => ({
|
|
6283
7569
|
userMessageActions: {
|
|
@@ -6365,11 +7651,22 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6365
7651
|
};
|
|
6366
7652
|
const userActionPrompts = isUserActionSupported ? userActionState.prompts : void 0;
|
|
6367
7653
|
const notifications = userActionState.notifications;
|
|
6368
|
-
const
|
|
7654
|
+
const handleAttachmentsChange = react.useCallback(
|
|
7655
|
+
(attachments) => {
|
|
7656
|
+
attachmentUpload.syncAttachments(attachments);
|
|
7657
|
+
},
|
|
7658
|
+
[attachmentUpload]
|
|
7659
|
+
);
|
|
7660
|
+
const handleV2Send = (text, files = [], attachments = []) => {
|
|
6369
7661
|
if (isRecording) stopRecording();
|
|
6370
|
-
if (text.trim() && !disableInput && isSessionParamsConfigured) {
|
|
7662
|
+
if ((text.trim() || files.length > 0) && !disableInput && isSessionParamsConfigured) {
|
|
7663
|
+
if (files.length > 0 && attachments.length === 0) return;
|
|
6371
7664
|
setEditingMessageId(null);
|
|
6372
|
-
void sendMessage(text.trim(), {
|
|
7665
|
+
void sendMessage(text.trim(), {
|
|
7666
|
+
analysisMode: effectiveAnalysisMode,
|
|
7667
|
+
attachments,
|
|
7668
|
+
files
|
|
7669
|
+
});
|
|
6373
7670
|
}
|
|
6374
7671
|
};
|
|
6375
7672
|
const handleVoicePress = react.useCallback(async () => {
|
|
@@ -6417,155 +7714,214 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
|
|
|
6417
7714
|
style,
|
|
6418
7715
|
children: [
|
|
6419
7716
|
children,
|
|
6420
|
-
/* @__PURE__ */ jsxRuntime.
|
|
6421
|
-
|
|
7717
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7718
|
+
"div",
|
|
6422
7719
|
{
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6430
|
-
MessageList,
|
|
6431
|
-
{
|
|
6432
|
-
messages,
|
|
6433
|
-
isLoading: false,
|
|
6434
|
-
emptyStateText,
|
|
6435
|
-
showEmptyStateIcon,
|
|
6436
|
-
emptyStateComponent,
|
|
6437
|
-
layout,
|
|
6438
|
-
showTimestamps,
|
|
6439
|
-
stage: config.stage || "DEVELOPMENT",
|
|
6440
|
-
animated,
|
|
6441
|
-
showAgentName,
|
|
6442
|
-
agentName,
|
|
6443
|
-
showAvatars,
|
|
6444
|
-
showUserAvatar,
|
|
6445
|
-
showAssistantAvatar,
|
|
6446
|
-
showExecutionSteps,
|
|
6447
|
-
showStreamingDot,
|
|
6448
|
-
streamingStepsText,
|
|
6449
|
-
completedStepsText,
|
|
6450
|
-
onExecutionTraceClick,
|
|
6451
|
-
onLoadMoreMessages,
|
|
6452
|
-
isLoadingMoreMessages,
|
|
6453
|
-
hasMoreMessages
|
|
6454
|
-
}
|
|
6455
|
-
),
|
|
6456
|
-
/* @__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(
|
|
6457
7726
|
framerMotion.motion.div,
|
|
6458
7727
|
{
|
|
6459
|
-
initial: { opacity:
|
|
6460
|
-
|
|
6461
|
-
transition: {
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
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
|
-
|
|
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,
|
|
6508
7816
|
{
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
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,
|
|
6530
7899
|
{
|
|
6531
|
-
|
|
6532
|
-
|
|
7900
|
+
src: pdfSheet?.href ?? null,
|
|
7901
|
+
title: pdfSheet?.title ?? "",
|
|
7902
|
+
onClose: closePdfSheet,
|
|
7903
|
+
mode: "split"
|
|
6533
7904
|
}
|
|
6534
7905
|
),
|
|
6535
|
-
|
|
6536
|
-
|
|
7906
|
+
pdfPreviewMode === "sheet" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7907
|
+
"div",
|
|
6537
7908
|
{
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
onCancelRecording: handleCancelRecording,
|
|
6550
|
-
onConfirmRecording: handleConfirmRecording,
|
|
6551
|
-
showResetSession,
|
|
6552
|
-
onResetSession: requestResetSession,
|
|
6553
|
-
showAttachmentButton,
|
|
6554
|
-
showUploadImageButton,
|
|
6555
|
-
showAttachFileButton,
|
|
6556
|
-
onUploadImageClick,
|
|
6557
|
-
onAttachFileClick,
|
|
6558
|
-
editingMessageId,
|
|
6559
|
-
onClearEditing: handleClearEditing,
|
|
6560
|
-
analysisMode: enableDeepModeToggle ? analysisMode : void 0,
|
|
6561
|
-
onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
|
|
6562
|
-
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
|
+
)
|
|
6563
7920
|
}
|
|
6564
7921
|
)
|
|
6565
7922
|
]
|
|
6566
|
-
}
|
|
6567
|
-
|
|
6568
|
-
) }),
|
|
7923
|
+
}
|
|
7924
|
+
),
|
|
6569
7925
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6570
7926
|
ImageLightboxV2,
|
|
6571
7927
|
{
|
|
@@ -6600,19 +7956,39 @@ var PaymanChat = react.forwardRef(
|
|
|
6600
7956
|
function PaymanChat2(props, ref) {
|
|
6601
7957
|
const mergedCallbacks = useSentryChatCallbacks(props.callbacks, props.config);
|
|
6602
7958
|
const chat = useChatV2(props.config, mergedCallbacks);
|
|
6603
|
-
|
|
7959
|
+
const attachmentUpload = useAttachmentUpload(props.config);
|
|
7960
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7961
|
+
PaymanChatInner,
|
|
7962
|
+
{
|
|
7963
|
+
...props,
|
|
7964
|
+
chat,
|
|
7965
|
+
attachmentUpload,
|
|
7966
|
+
ref
|
|
7967
|
+
}
|
|
7968
|
+
);
|
|
6604
7969
|
}
|
|
6605
7970
|
);
|
|
6606
7971
|
|
|
6607
7972
|
exports.PaymanChat = PaymanChat;
|
|
6608
7973
|
exports.PaymanChatContext = PaymanChatContext;
|
|
7974
|
+
exports.PdfSheetV2 = PdfSheetV2;
|
|
6609
7975
|
exports.UserActionStaleError = UserActionStaleError;
|
|
7976
|
+
exports.buildSignedUrlEndpoint = buildSignedUrlEndpoint;
|
|
6610
7977
|
exports.cancelUserAction = cancelUserAction;
|
|
6611
7978
|
exports.captureSentryError = captureSentryError;
|
|
6612
7979
|
exports.cn = cn;
|
|
7980
|
+
exports.formatAttachmentBytes = formatAttachmentBytes;
|
|
6613
7981
|
exports.formatDate = formatDate;
|
|
7982
|
+
exports.getPdfTitleFromUrl = getPdfTitleFromUrl;
|
|
7983
|
+
exports.isPdfUrl = isPdfUrl;
|
|
7984
|
+
exports.mapExecutionHistoryPageToChatMessages = mapExecutionHistoryPageToChatMessages;
|
|
7985
|
+
exports.mapExecutionHistoryToChatMessages = mapExecutionHistoryToChatMessages;
|
|
6614
7986
|
exports.resendUserAction = resendUserAction;
|
|
7987
|
+
exports.stripAttachmentsSuffixFromIntent = stripAttachmentsSuffixFromIntent;
|
|
6615
7988
|
exports.submitUserAction = submitUserAction;
|
|
7989
|
+
exports.uploadAttachment = uploadAttachment;
|
|
7990
|
+
exports.uploadAttachments = uploadAttachments;
|
|
7991
|
+
exports.useAttachmentUpload = useAttachmentUpload;
|
|
6616
7992
|
exports.useChatV2 = useChatV2;
|
|
6617
7993
|
exports.usePaymanChat = usePaymanChat;
|
|
6618
7994
|
exports.useVoice = useVoice;
|