@paymanai/payman-ask-sdk 4.0.20 → 4.0.22

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.js CHANGED
@@ -40,10 +40,7 @@ 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, 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);
43
+ var __publicField = (obj, key, value) => __defNormalProp(obj, key + "", value);
47
44
  function generateId() {
48
45
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
49
46
  const r = Math.random() * 16 | 0;
@@ -739,8 +736,7 @@ function buildRequestBody(config, userMessage, sessionId, options) {
739
736
  sessionAttributes,
740
737
  analysisMode: options?.analysisMode,
741
738
  locale: resolveLocale(config.locale),
742
- timezone: resolveTimezone(config.timezone),
743
- ...options?.attachments?.length ? { attachments: options.attachments } : {}
739
+ timezone: resolveTimezone(config.timezone)
744
740
  };
745
741
  }
746
742
  function resolveLocale(configured) {
@@ -785,7 +781,6 @@ function buildResolveImagesUrl(config) {
785
781
  const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
786
782
  return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
787
783
  }
788
- var NGROK_SKIP_BROWSER_WARNING = "ngrok-skip-browser-warning";
789
784
  function buildRequestHeaders(config) {
790
785
  const headers = {
791
786
  ...config.api.headers
@@ -793,9 +788,6 @@ function buildRequestHeaders(config) {
793
788
  if (config.api.authToken) {
794
789
  headers.Authorization = `Bearer ${config.api.authToken}`;
795
790
  }
796
- if (!headers[NGROK_SKIP_BROWSER_WARNING]) {
797
- headers[NGROK_SKIP_BROWSER_WARNING] = "true";
798
- }
799
791
  return headers;
800
792
  }
801
793
  var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
@@ -1076,85 +1068,10 @@ function createCancelledMessageUpdate(steps, currentMessage) {
1076
1068
  currentMessage: currentMessage || "Thinking..."
1077
1069
  };
1078
1070
  }
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
- }
1154
1071
  var UserActionStaleError = class extends Error {
1155
1072
  constructor(userActionId, message = "User action is no longer actionable") {
1156
1073
  super(message);
1157
- __publicField2(this, "userActionId");
1074
+ __publicField(this, "userActionId");
1158
1075
  this.name = "UserActionStaleError";
1159
1076
  this.userActionId = userActionId;
1160
1077
  }
@@ -1187,6 +1104,9 @@ async function cancelUserAction(config, userActionId) {
1187
1104
  async function resendUserAction(config, userActionId) {
1188
1105
  return sendUserActionRequest(config, userActionId, "resend");
1189
1106
  }
1107
+ async function expireUserAction(config, userActionId) {
1108
+ return sendUserActionRequest(config, userActionId, "expired");
1109
+ }
1190
1110
  var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
1191
1111
  function upsertPrompt(prompts, req) {
1192
1112
  const active = { ...req, status: "pending" };
@@ -1215,32 +1135,12 @@ function getStoredOrInitialMessages(config) {
1215
1135
  function getSessionIdFromMessages(messages) {
1216
1136
  return messages.find((message) => message.sessionId)?.sessionId;
1217
1137
  }
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
- }
1237
1138
  function useChatV2(config, callbacks = {}) {
1238
1139
  const [messages, setMessages] = react.useState(() => getStoredOrInitialMessages(config));
1239
1140
  const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(() => {
1240
1141
  if (!config.userId) return false;
1241
1142
  return activeStreamStore.get(config.userId)?.isWaiting ?? false;
1242
1143
  });
1243
- const [isUploadingAttachments, setIsUploadingAttachments] = react.useState(false);
1244
1144
  const sessionIdRef = react.useRef(
1245
1145
  getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
1246
1146
  );
@@ -1315,45 +1215,21 @@ function useChatV2(config, callbacks = {}) {
1315
1215
  );
1316
1216
  const sendMessage = react.useCallback(
1317
1217
  async (userMessage, options) => {
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
- }
1218
+ if (!userMessage.trim()) return;
1336
1219
  if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
1337
1220
  sessionIdRef.current = generateId();
1338
1221
  callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
1339
1222
  }
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;
1346
1223
  const userMessageId = `user-${Date.now()}`;
1347
1224
  const userMsg = {
1348
1225
  id: userMessageId,
1349
1226
  sessionId: sessionIdRef.current,
1350
1227
  role: "user",
1351
- content: trimmedMessage,
1352
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1353
- attachments: messageAttachments
1228
+ content: userMessage,
1229
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1354
1230
  };
1355
1231
  setMessages((prev) => [...prev, userMsg]);
1356
- callbacksRef.current.onMessageSent?.(trimmedMessage);
1232
+ callbacksRef.current.onMessageSent?.(userMessage);
1357
1233
  setIsWaitingForResponse(true);
1358
1234
  callbacksRef.current.onStreamStart?.();
1359
1235
  const streamingId = `assistant-${Date.now()}`;
@@ -1380,14 +1256,11 @@ function useChatV2(config, callbacks = {}) {
1380
1256
  activeStreamStore.start(userId, abortController, initialMessages);
1381
1257
  }
1382
1258
  const newSessionId = await startStream(
1383
- trimmedMessage,
1259
+ userMessage,
1384
1260
  streamingId,
1385
1261
  sessionIdRef.current,
1386
1262
  abortController,
1387
- {
1388
- analysisMode: options?.analysisMode,
1389
- attachments: streamAttachments
1390
- }
1263
+ options
1391
1264
  );
1392
1265
  const finalStreamUserId = streamUserIdRef.current ?? userId;
1393
1266
  if (finalStreamUserId) {
@@ -1525,6 +1398,19 @@ function useChatV2(config, callbacks = {}) {
1525
1398
  },
1526
1399
  [setPromptStatus]
1527
1400
  );
1401
+ const expireUserAction2 = react.useCallback(
1402
+ async (userActionId) => {
1403
+ setPromptStatus(userActionId, "expired");
1404
+ try {
1405
+ await expireUserAction(configRef.current, userActionId);
1406
+ } catch (error) {
1407
+ if (error instanceof UserActionStaleError) return;
1408
+ callbacksRef.current.onError?.(error);
1409
+ throw error;
1410
+ }
1411
+ },
1412
+ [setPromptStatus]
1413
+ );
1528
1414
  const dismissNotification = react.useCallback((id) => {
1529
1415
  setUserActionState((prev) => ({
1530
1416
  ...prev,
@@ -1565,18 +1451,6 @@ function useChatV2(config, callbacks = {}) {
1565
1451
  setMessages(config.initialMessages);
1566
1452
  sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
1567
1453
  }, [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]);
1580
1454
  react.useEffect(() => {
1581
1455
  const prevUserId = prevUserIdRef.current;
1582
1456
  prevUserIdRef.current = config.userId;
@@ -1611,12 +1485,12 @@ function useChatV2(config, callbacks = {}) {
1611
1485
  getSessionId,
1612
1486
  getMessages,
1613
1487
  isWaitingForResponse,
1614
- isUploadingAttachments,
1615
1488
  sessionId: sessionIdRef.current,
1616
1489
  userActionState,
1617
1490
  submitUserAction: submitUserAction2,
1618
1491
  cancelUserAction: cancelUserAction2,
1619
1492
  resendUserAction: resendUserAction2,
1493
+ expireUserAction: expireUserAction2,
1620
1494
  dismissNotification
1621
1495
  };
1622
1496
  }
@@ -1835,102 +1709,6 @@ function useVoice(config = {}, callbacks = {}) {
1835
1709
  reset
1836
1710
  };
1837
1711
  }
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
- }
1934
1712
  function classifyField(field) {
1935
1713
  if (!field) return "text";
1936
1714
  if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
@@ -2047,66 +1825,6 @@ function buildContent(schema, values) {
2047
1825
  }
2048
1826
  return content;
2049
1827
  }
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
- }
2110
1828
  var PaymanChatContext = react.createContext(void 0);
2111
1829
  function usePaymanChat() {
2112
1830
  const context = react.useContext(PaymanChatContext);
@@ -2217,111 +1935,6 @@ function subscribeToCfRay(urlPattern, listener) {
2217
1935
  };
2218
1936
  }
2219
1937
 
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
-
2325
1938
  // src/utils/slashCommands.ts
2326
1939
  var DEFAULT_SLASH_COMMANDS = [
2327
1940
  {
@@ -3473,108 +3086,6 @@ function ActionTooltipV2({ label, children }) {
3473
3086
  ] }) })
3474
3087
  ] });
3475
3088
  }
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
- }
3578
3089
  function formatMessageTime(timestamp) {
3579
3090
  const value = new Date(timestamp);
3580
3091
  if (Number.isNaN(value.getTime())) return "";
@@ -3587,7 +3098,6 @@ function UserMessageV2({
3587
3098
  message,
3588
3099
  onEdit,
3589
3100
  onRetry,
3590
- onImageClick,
3591
3101
  retryDisabled = false,
3592
3102
  actions
3593
3103
  }) {
@@ -3670,15 +3180,7 @@ function UserMessageV2({
3670
3180
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3671
3181
  toastPortal,
3672
3182
  /* @__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: [
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: [
3183
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-user-msg-bubble", children: parsedCommand ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-user-msg-command", children: [
3682
3184
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-user-msg-command-chip", children: parsedCommand.command }),
3683
3185
  parsedCommand.body.trim() ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-user-msg-text", children: parsedCommand.body }) : null
3684
3186
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-user-msg-text", children: message.content }) }),
@@ -3903,44 +3405,9 @@ function MarkdownImageV2({
3903
3405
  }
3904
3406
  ) });
3905
3407
  }
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) {
3408
+ function buildComponents(onImageClick, isResolvingRef) {
3936
3409
  return {
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
- },
3410
+ p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { children }),
3944
3411
  code: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("code", { children }),
3945
3412
  pre: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-markdown-pre", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { children }) }),
3946
3413
  ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { children }),
@@ -3953,15 +3420,7 @@ function buildComponents(onImageClick, isResolvingRef, onPdfClick, autoOpenPdf)
3953
3420
  em: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("em", { children }),
3954
3421
  blockquote: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("blockquote", { children }),
3955
3422
  hr: () => /* @__PURE__ */ jsxRuntime.jsx("hr", {}),
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
- },
3423
+ a: ({ href, children }) => /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children }),
3965
3424
  img: ({ src, alt }) => /* @__PURE__ */ jsxRuntime.jsx(
3966
3425
  MarkdownImageV2,
3967
3426
  {
@@ -3982,15 +3441,13 @@ function MarkdownRendererV2({
3982
3441
  content,
3983
3442
  isStreaming,
3984
3443
  isResolvingImages,
3985
- onImageClick,
3986
- onPdfClick,
3987
- autoOpenPdf
3444
+ onImageClick
3988
3445
  }) {
3989
3446
  const isResolvingRef = react.useRef(isResolvingImages);
3990
3447
  isResolvingRef.current = isResolvingImages;
3991
3448
  const components = react.useMemo(
3992
- () => buildComponents(onImageClick, isResolvingRef, onPdfClick, autoOpenPdf),
3993
- [onImageClick, onPdfClick, autoOpenPdf]
3449
+ () => buildComponents(onImageClick, isResolvingRef),
3450
+ [onImageClick]
3994
3451
  );
3995
3452
  return /* @__PURE__ */ jsxRuntime.jsx(
3996
3453
  "div",
@@ -4359,17 +3816,6 @@ function stripIncompleteImageToken(text) {
4359
3816
  if (/^!\[[^\]]*\]\([^)]*\)/.test(after)) return text;
4360
3817
  return text.slice(0, lastBang);
4361
3818
  }
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
- }
4373
3819
  function getFeedbackState(message) {
4374
3820
  const feedback = message.feedback;
4375
3821
  if (feedback === "up" || feedback === "down") return feedback;
@@ -4393,12 +3839,6 @@ function AssistantMessageV2({
4393
3839
  () => getFeedbackState(message)
4394
3840
  );
4395
3841
  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
- }, []);
4402
3842
  const canSubmitFeedback = !!onSubmitFeedback && !!message.executionId;
4403
3843
  const [toast, setToast] = react.useState(null);
4404
3844
  const copyResetTimerRef = react.useRef(null);
@@ -4423,7 +3863,7 @@ function AssistantMessageV2({
4423
3863
  const raw = message.isStreaming ? message.streamingContent || message.content : message.content;
4424
3864
  if (!raw) return "";
4425
3865
  const normalized = raw.replace(/\\n/g, "\n");
4426
- return message.isStreaming ? stripIncompleteMarkdownTokens(normalized) : normalized;
3866
+ return message.isStreaming ? stripIncompleteImageToken(normalized) : normalized;
4427
3867
  })();
4428
3868
  const isThinkingStreaming = !!message.isStreaming && !rawResponseContent && !message.isError;
4429
3869
  const responseTypingEnabled = hasEverStreamed.current && Boolean(rawResponseContent) && !message.isError;
@@ -4593,12 +4033,10 @@ function AssistantMessageV2({
4593
4033
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-assistant-msg-content-area", children: displayContent ? /* @__PURE__ */ jsxRuntime.jsx(
4594
4034
  MarkdownRendererV2,
4595
4035
  {
4596
- content: message.isStreaming && !isCancelled || isResponseTyping ? stripIncompleteMarkdownTokens(displayContent) : displayContent,
4036
+ content: displayContent,
4597
4037
  isStreaming: message.isStreaming && !isCancelled || isResponseTyping,
4598
4038
  isResolvingImages: message.isResolvingImages,
4599
- onImageClick,
4600
- onPdfClick: handlePdfClick,
4601
- autoOpenPdf: hasEverStreamed.current
4039
+ onImageClick
4602
4040
  }
4603
4041
  ) : !isThinkingStreaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-assistant-msg-placeholder", children: "..." }) : null }),
4604
4042
  isCancelled && message.isStreaming && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-assistant-msg-paused", children: [
@@ -4815,6 +4253,7 @@ function VerificationInline({
4815
4253
  const [code, setCode] = react.useState("");
4816
4254
  const [errored, setErrored] = react.useState(false);
4817
4255
  const [resendSec, setResendSec] = react.useState(0);
4256
+ const [hiddenAfterResend, setHiddenAfterResend] = react.useState(false);
4818
4257
  const lastSubmittedRef = react.useRef(null);
4819
4258
  const resendTimerRef = react.useRef(void 0);
4820
4259
  const status = prompt.status;
@@ -4828,6 +4267,9 @@ function VerificationInline({
4828
4267
  lastSubmittedRef.current = null;
4829
4268
  }
4830
4269
  }, [prompt.subAction, prompt.userActionId]);
4270
+ react.useEffect(() => {
4271
+ setHiddenAfterResend(false);
4272
+ }, [prompt.expirySeconds, prompt.message, prompt.subAction, prompt.userActionId]);
4831
4273
  react.useEffect(() => {
4832
4274
  if (code.length < codeLen) lastSubmittedRef.current = null;
4833
4275
  }, [code, codeLen]);
@@ -4878,11 +4320,13 @@ function VerificationInline({
4878
4320
  if (locked || resendSec > 0) return;
4879
4321
  setErrored(false);
4880
4322
  setCode("");
4323
+ setHiddenAfterResend(true);
4881
4324
  lastSubmittedRef.current = null;
4882
4325
  try {
4883
4326
  await onResend(prompt.userActionId);
4884
4327
  startResendCooldown();
4885
4328
  } catch {
4329
+ setHiddenAfterResend(false);
4886
4330
  }
4887
4331
  }, [locked, onResend, prompt.userActionId, resendSec, startResendCooldown]);
4888
4332
  const handleCancel = react.useCallback(() => {
@@ -4890,6 +4334,7 @@ function VerificationInline({
4890
4334
  void onCancel(prompt.userActionId);
4891
4335
  }, [busy, onCancel, prompt.userActionId]);
4892
4336
  const description = prompt.message?.trim() || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
4337
+ if (hiddenAfterResend) return null;
4893
4338
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Verification required", children: [
4894
4339
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-head", children: [
4895
4340
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
@@ -4985,6 +4430,9 @@ function SchemaFormInline({
4985
4430
  const busy = status === "submitting";
4986
4431
  const stale = status === "stale";
4987
4432
  const locked = busy || stale || expired;
4433
+ const isUserConfirmation = prompt.subAction === "UserConfirmation";
4434
+ const messageFormat = prompt.metadata?.["payman/messageFormat"];
4435
+ const renderMarkdown = messageFormat === "markdown";
4988
4436
  const setValue = (key, value) => {
4989
4437
  setValues((prev) => ({ ...prev, [key]: value }));
4990
4438
  setErrors((prev) => {
@@ -5010,8 +4458,8 @@ function SchemaFormInline({
5010
4458
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
5011
4459
  typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
5012
4460
  ] }),
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]) => {
4461
+ prompt.message?.trim() && (renderMarkdown ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-markdown", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRendererV2, { content: prompt.message }) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: prompt.message })),
4462
+ stale ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
5015
4463
  const widget = classifyField(field);
5016
4464
  const label = field.title || key;
5017
4465
  const required = isRequired(schema, key);
@@ -5084,7 +4532,7 @@ function SchemaFormInline({
5084
4532
  className: "payman-v2-ua-btn payman-v2-ua-btn-primary",
5085
4533
  disabled: locked,
5086
4534
  onClick: handleSubmit,
5087
- children: busy ? "Submitting\u2026" : "Submit"
4535
+ children: busy ? "Submitting\u2026" : isUserConfirmation ? "Confirm" : "Submit"
5088
4536
  }
5089
4537
  ),
5090
4538
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5124,26 +4572,51 @@ function useExpiryCountdown(prompt) {
5124
4572
  setSecondsLeft(void 0);
5125
4573
  return;
5126
4574
  }
5127
- setSecondsLeft(initial);
5128
- const id = window.setInterval(() => {
5129
- setSecondsLeft((s) => {
5130
- if (s === void 0) return s;
5131
- if (s <= 1) {
5132
- window.clearInterval(id);
5133
- return 0;
5134
- }
5135
- return s - 1;
5136
- });
5137
- }, 1e3);
5138
- return () => window.clearInterval(id);
4575
+ const deadlineMs = Date.now() + initial * 1e3;
4576
+ let intervalId;
4577
+ const remaining = () => Math.max(0, Math.ceil((deadlineMs - Date.now()) / 1e3));
4578
+ const sync = () => {
4579
+ const next = remaining();
4580
+ setSecondsLeft(next);
4581
+ if (next === 0 && intervalId !== void 0) {
4582
+ window.clearInterval(intervalId);
4583
+ intervalId = void 0;
4584
+ }
4585
+ };
4586
+ sync();
4587
+ intervalId = window.setInterval(sync, 1e3);
4588
+ document.addEventListener("visibilitychange", sync);
4589
+ window.addEventListener("focus", sync);
4590
+ window.addEventListener("pageshow", sync);
4591
+ return () => {
4592
+ if (intervalId !== void 0) window.clearInterval(intervalId);
4593
+ document.removeEventListener("visibilitychange", sync);
4594
+ window.removeEventListener("focus", sync);
4595
+ window.removeEventListener("pageshow", sync);
4596
+ };
5139
4597
  }, [prompt.userActionId, prompt.subAction, initial]);
5140
4598
  const expired = initial !== void 0 && secondsLeft === 0;
5141
4599
  return [secondsLeft, expired];
5142
4600
  }
5143
- function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
4601
+ function UserActionInline({
4602
+ prompt,
4603
+ onSubmit,
4604
+ onCancel,
4605
+ onResend,
4606
+ onExpired
4607
+ }) {
5144
4608
  const [secondsLeft, expired] = useExpiryCountdown(prompt);
4609
+ react.useEffect(() => {
4610
+ if (expired && prompt.kind !== "notification") onExpired?.();
4611
+ }, [expired, onExpired, prompt.kind]);
5145
4612
  let body;
5146
- if (prompt.kind === "verification") {
4613
+ if (expired && prompt.kind !== "notification") {
4614
+ const note = {
4615
+ id: `${prompt.userActionId}-expired`,
4616
+ message: prompt.kind === "verification" ? "Verification Request Expired" : "User Form Expired"
4617
+ };
4618
+ body = /* @__PURE__ */ jsxRuntime.jsx(NotificationInline, { notification: note });
4619
+ } else if (prompt.kind === "verification") {
5147
4620
  body = /* @__PURE__ */ jsxRuntime.jsx(
5148
4621
  VerificationInline,
5149
4622
  {
@@ -5183,10 +4656,23 @@ function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
5183
4656
  }
5184
4657
  var SCROLL_THRESHOLD2 = 100;
5185
4658
  var USER_SCROLL_UP_EPSILON = 4;
4659
+ var PROMPT_KEY_SEPARATOR = "";
4660
+ function getPromptSlotKey(prompt) {
4661
+ return prompt.toolCallId || prompt.userActionId;
4662
+ }
4663
+ function getPromptViewKey(prompt) {
4664
+ return [
4665
+ getPromptSlotKey(prompt),
4666
+ prompt.userActionId,
4667
+ prompt.subAction ?? "",
4668
+ prompt.expirySeconds ?? "",
4669
+ prompt.message ?? ""
4670
+ ].join(PROMPT_KEY_SEPARATOR);
4671
+ }
5186
4672
  var MessageListV2 = react.forwardRef(
5187
4673
  function MessageListV22({
5188
4674
  messages,
5189
- isStreaming: _isStreaming = false,
4675
+ isStreaming = false,
5190
4676
  onEditUserMessage,
5191
4677
  onRetryUserMessage,
5192
4678
  onImageClick,
@@ -5198,10 +4684,10 @@ var MessageListV2 = react.forwardRef(
5198
4684
  onSubmitUserAction,
5199
4685
  onCancelUserAction,
5200
4686
  onResendUserAction,
4687
+ onExpireUserAction,
5201
4688
  onDismissNotification,
5202
4689
  onSubmitFeedback,
5203
- typingSpeed = 4,
5204
- sidePanelOpen = false
4690
+ typingSpeed = 4
5205
4691
  }, ref) {
5206
4692
  const noop = react.useCallback(async () => {
5207
4693
  }, []);
@@ -5209,11 +4695,11 @@ var MessageListV2 = react.forwardRef(
5209
4695
  const scrollInnerRef = react.useRef(null);
5210
4696
  const isNearBottomRef = react.useRef(true);
5211
4697
  const [showScrollBtn, setShowScrollBtn] = react.useState(false);
4698
+ const [expiredPromptViewState, setExpiredPromptViewState] = react.useState({});
4699
+ const expiredUserActionIdsRef = react.useRef(/* @__PURE__ */ new Set());
5212
4700
  const prevCountRef = react.useRef(messages.length);
5213
4701
  const pauseStickUntilUserMessageRef = react.useRef(false);
5214
4702
  const followingBottomRef = react.useRef(true);
5215
- const sidePanelOpenRef = react.useRef(sidePanelOpen);
5216
- sidePanelOpenRef.current = sidePanelOpen;
5217
4703
  const lastPinAtRef = react.useRef(0);
5218
4704
  const prevScrollTopRef = react.useRef(0);
5219
4705
  const getDistanceFromBottom = react.useCallback(() => {
@@ -5221,6 +4707,92 @@ var MessageListV2 = react.forwardRef(
5221
4707
  if (!el) return 0;
5222
4708
  return el.scrollHeight - el.scrollTop - el.clientHeight;
5223
4709
  }, []);
4710
+ const messageActivityFingerprint = react.useMemo(() => {
4711
+ const last = messages[messages.length - 1];
4712
+ const promptFingerprint = (userActionPrompts ?? []).map((prompt) => `${getPromptViewKey(prompt)}:${prompt.status}`).join(PROMPT_KEY_SEPARATOR);
4713
+ const notificationFingerprint = (notifications ?? []).map((notification) => notification.id).join(PROMPT_KEY_SEPARATOR);
4714
+ if (!last) {
4715
+ return [
4716
+ 0,
4717
+ isStreaming ? "streaming" : "idle",
4718
+ promptFingerprint,
4719
+ notificationFingerprint
4720
+ ].join(PROMPT_KEY_SEPARATOR);
4721
+ }
4722
+ return [
4723
+ messages.length,
4724
+ isStreaming ? "streaming" : "idle",
4725
+ last.id,
4726
+ last.role,
4727
+ last.content ?? "",
4728
+ last.isStreaming ? "streaming" : "done",
4729
+ last.streamProgress ?? "",
4730
+ last.steps?.length ?? 0,
4731
+ last.errorDetails ?? "",
4732
+ promptFingerprint,
4733
+ notificationFingerprint
4734
+ ].join(PROMPT_KEY_SEPARATOR);
4735
+ }, [isStreaming, messages, notifications, userActionPrompts]);
4736
+ const handleUserActionExpired = react.useCallback(
4737
+ (promptKey, userActionId) => {
4738
+ setExpiredPromptViewState((prev) => {
4739
+ if (prev[promptKey]) return prev;
4740
+ return {
4741
+ ...prev,
4742
+ [promptKey]: { baseline: messageActivityFingerprint, hidden: false }
4743
+ };
4744
+ });
4745
+ if (!expiredUserActionIdsRef.current.has(userActionId)) {
4746
+ expiredUserActionIdsRef.current.add(userActionId);
4747
+ void onExpireUserAction?.(userActionId)?.catch(() => {
4748
+ });
4749
+ }
4750
+ },
4751
+ [messageActivityFingerprint, onExpireUserAction]
4752
+ );
4753
+ react.useEffect(() => {
4754
+ setExpiredPromptViewState((prev) => {
4755
+ let changed = false;
4756
+ const next = {};
4757
+ for (const [key, state] of Object.entries(prev)) {
4758
+ if (!state.hidden && state.baseline !== messageActivityFingerprint) {
4759
+ next[key] = { ...state, hidden: true };
4760
+ changed = true;
4761
+ } else {
4762
+ next[key] = state;
4763
+ }
4764
+ }
4765
+ return changed ? next : prev;
4766
+ });
4767
+ }, [messageActivityFingerprint]);
4768
+ react.useEffect(() => {
4769
+ const livePromptKeys = new Set((userActionPrompts ?? []).map(getPromptViewKey));
4770
+ const liveUserActionIds = new Set((userActionPrompts ?? []).map((p) => p.userActionId));
4771
+ for (const userActionId of expiredUserActionIdsRef.current) {
4772
+ if (!liveUserActionIds.has(userActionId)) {
4773
+ expiredUserActionIdsRef.current.delete(userActionId);
4774
+ }
4775
+ }
4776
+ setExpiredPromptViewState((prev) => {
4777
+ let changed = false;
4778
+ const next = {};
4779
+ for (const [key, state] of Object.entries(prev)) {
4780
+ if (livePromptKeys.has(key)) {
4781
+ next[key] = state;
4782
+ } else {
4783
+ changed = true;
4784
+ }
4785
+ }
4786
+ return changed ? next : prev;
4787
+ });
4788
+ }, [userActionPrompts]);
4789
+ const visibleUserActionPrompts = react.useMemo(
4790
+ () => userActionPrompts?.filter((prompt) => {
4791
+ const promptKey = getPromptViewKey(prompt);
4792
+ return !expiredPromptViewState[promptKey]?.hidden;
4793
+ }),
4794
+ [expiredPromptViewState, userActionPrompts]
4795
+ );
5224
4796
  const pinToBottom = react.useCallback((behavior = "instant") => {
5225
4797
  const el = scrollRef.current;
5226
4798
  if (!el) return;
@@ -5261,25 +4833,17 @@ var MessageListV2 = react.forwardRef(
5261
4833
  const nearBottom = distance <= SCROLL_THRESHOLD2;
5262
4834
  isNearBottomRef.current = nearBottom;
5263
4835
  setShowScrollBtn(!nearBottom);
4836
+ const sincePin = performance.now() - lastPinAtRef.current;
4837
+ if (sincePin < 250) return;
5264
4838
  const scrolledUp = currentScrollTop < prevScrollTop - USER_SCROLL_UP_EPSILON;
5265
4839
  if (scrolledUp) {
5266
4840
  followingBottomRef.current = false;
5267
4841
  pauseStickUntilUserMessageRef.current = true;
5268
- return;
5269
- }
5270
- const sincePin = performance.now() - lastPinAtRef.current;
5271
- if (sincePin < 250) return;
5272
- if (nearBottom) {
4842
+ } else if (nearBottom) {
5273
4843
  followingBottomRef.current = true;
5274
4844
  pauseStickUntilUserMessageRef.current = false;
5275
4845
  }
5276
4846
  }, [getDistanceFromBottom]);
5277
- const handleWheel = react.useCallback((e) => {
5278
- if (e.deltaY < 0) {
5279
- followingBottomRef.current = false;
5280
- pauseStickUntilUserMessageRef.current = true;
5281
- }
5282
- }, []);
5283
4847
  react.useEffect(() => {
5284
4848
  const prevCount = prevCountRef.current;
5285
4849
  prevCountRef.current = messages.length;
@@ -5289,7 +4853,7 @@ var MessageListV2 = react.forwardRef(
5289
4853
  pauseStickUntilUserMessageRef.current = false;
5290
4854
  followingBottomRef.current = true;
5291
4855
  requestAnimationFrame(() => scrollToBottom());
5292
- } else if (!sidePanelOpenRef.current && !pauseStickUntilUserMessageRef.current && followingBottomRef.current) {
4856
+ } else if (!pauseStickUntilUserMessageRef.current && followingBottomRef.current) {
5293
4857
  requestAnimationFrame(() => scrollToBottom("instant"));
5294
4858
  }
5295
4859
  }
@@ -5298,16 +4862,27 @@ var MessageListV2 = react.forwardRef(
5298
4862
  const inner = scrollInnerRef.current;
5299
4863
  if (!inner) return;
5300
4864
  const pinIfFollowing = () => {
5301
- if (sidePanelOpenRef.current) return;
5302
4865
  if (pauseStickUntilUserMessageRef.current) return;
5303
4866
  if (!followingBottomRef.current) return;
5304
- if (getDistanceFromBottom() > SCROLL_THRESHOLD2) return;
5305
4867
  pinToBottom("instant");
5306
4868
  };
5307
- const ro = new ResizeObserver(pinIfFollowing);
4869
+ const ro = new ResizeObserver(() => {
4870
+ pinIfFollowing();
4871
+ });
5308
4872
  ro.observe(inner);
5309
- return () => ro.disconnect();
5310
- }, [pinToBottom, getDistanceFromBottom]);
4873
+ const mo = new MutationObserver(() => {
4874
+ pinIfFollowing();
4875
+ });
4876
+ mo.observe(inner, {
4877
+ childList: true,
4878
+ subtree: true,
4879
+ characterData: true
4880
+ });
4881
+ return () => {
4882
+ ro.disconnect();
4883
+ mo.disconnect();
4884
+ };
4885
+ }, [pinToBottom]);
5311
4886
  react.useEffect(() => {
5312
4887
  if (messages.length > 0) {
5313
4888
  setTimeout(() => scrollToBottom("instant"), 50);
@@ -5319,7 +4894,6 @@ var MessageListV2 = react.forwardRef(
5319
4894
  {
5320
4895
  ref: scrollRef,
5321
4896
  onScroll: handleScroll,
5322
- onWheel: handleWheel,
5323
4897
  className: "payman-v2-message-scroll payman-v2-scrollbar",
5324
4898
  children: /* @__PURE__ */ jsxRuntime.jsxs(
5325
4899
  "div",
@@ -5333,7 +4907,6 @@ var MessageListV2 = react.forwardRef(
5333
4907
  message,
5334
4908
  onEdit: onEditUserMessage,
5335
4909
  onRetry: onRetryUserMessage,
5336
- onImageClick,
5337
4910
  retryDisabled,
5338
4911
  actions: messageActions?.userMessageActions
5339
4912
  }
@@ -5356,16 +4929,20 @@ var MessageListV2 = react.forwardRef(
5356
4929
  },
5357
4930
  note.id
5358
4931
  )),
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
- ))
4932
+ visibleUserActionPrompts?.map((prompt) => {
4933
+ const promptKey = getPromptViewKey(prompt);
4934
+ return /* @__PURE__ */ jsxRuntime.jsx(
4935
+ UserActionInline,
4936
+ {
4937
+ prompt,
4938
+ onSubmit: onSubmitUserAction ?? noop,
4939
+ onCancel: onCancelUserAction ?? noop,
4940
+ onResend: onResendUserAction ?? noop,
4941
+ onExpired: () => handleUserActionExpired(promptKey, prompt.userActionId)
4942
+ },
4943
+ promptKey
4944
+ );
4945
+ })
5369
4946
  ]
5370
4947
  }
5371
4948
  )
@@ -5395,121 +4972,11 @@ var MessageListV2 = react.forwardRef(
5395
4972
  ] });
5396
4973
  }
5397
4974
  );
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
4975
  var ChatInputV2 = react.forwardRef(
5504
4976
  function ChatInputV22({
5505
4977
  onSend,
5506
4978
  disabled = false,
5507
4979
  isStreaming = false,
5508
- isUploadingAttachments = false,
5509
- attachmentsReady = true,
5510
- hasAttachmentUploadErrors = false,
5511
- attachmentUploadStatusById = {},
5512
- uploadedAttachmentPayloads = [],
5513
4980
  placeholder = "Reply...",
5514
4981
  enableVoice = false,
5515
4982
  voiceAvailable = false,
@@ -5523,13 +4990,6 @@ var ChatInputV2 = react.forwardRef(
5523
4990
  showAttachFileButton = true,
5524
4991
  onUploadImageClick,
5525
4992
  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
4993
  editingMessageId = null,
5534
4994
  onClearEditing,
5535
4995
  analysisMode,
@@ -5543,133 +5003,14 @@ var ChatInputV2 = react.forwardRef(
5543
5003
  const [selectedCommandIndex, setSelectedCommandIndex] = react.useState(0);
5544
5004
  const [inlineHint, setInlineHint] = react.useState(null);
5545
5005
  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
5006
  const textareaRef = react.useRef(null);
5550
5007
  const actionsRef = react.useRef(null);
5551
- const imageInputRef = react.useRef(null);
5552
- const fileInputRef = react.useRef(null);
5553
5008
  const preRecordTextRef = react.useRef("");
5554
5009
  const voiceTooltipTimerRef = react.useRef(
5555
5010
  null
5556
5011
  );
5557
5012
  const voiceDraftSyncActiveRef = react.useRef(false);
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;
5013
+ const isInputLocked = disabled || isRecording;
5673
5014
  const hasAttachmentOptions = showUploadImageButton || showAttachFileButton;
5674
5015
  const showAttachmentMenuButton = showAttachmentButton && hasAttachmentOptions;
5675
5016
  const showVoiceButton = enableVoice && onVoicePress != null;
@@ -5709,10 +5050,9 @@ var ChatInputV2 = react.forwardRef(
5709
5050
  const end = message.length;
5710
5051
  textarea.setSelectionRange(end, end);
5711
5052
  });
5712
- },
5713
- clearAttachments: clearAttachmentsFromInput
5053
+ }
5714
5054
  }),
5715
- [disabled, clearAttachmentsFromInput]
5055
+ [disabled]
5716
5056
  );
5717
5057
  react.useEffect(() => {
5718
5058
  if (!showActions) return;
@@ -5746,23 +5086,8 @@ var ChatInputV2 = react.forwardRef(
5746
5086
  const separator = base && !base.endsWith(" ") && transcribedText ? " " : "";
5747
5087
  setValue(`${base}${separator}${transcribedText}`);
5748
5088
  }, [isRecording, transcribedText]);
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
- }
5089
+ const handleSend = react.useCallback(() => {
5090
+ if (!value.trim() || disabled) return;
5766
5091
  const commandHint = getSlashCommandValidationHint(value);
5767
5092
  if (commandHint) {
5768
5093
  setInlineHint(commandHint);
@@ -5772,38 +5097,15 @@ var ChatInputV2 = react.forwardRef(
5772
5097
  preRecordTextRef.current = "";
5773
5098
  setInlineHint(null);
5774
5099
  onClearEditing?.();
5775
- const textToSend = value.trim();
5776
- const filesToSend = attachedFiles.map((f) => f.file);
5777
- const attachmentsToSend = [...uploadedAttachmentPayloads];
5100
+ onSend(value.trim());
5778
5101
  setValue("");
5779
- clearAttachmentsFromInput();
5780
5102
  requestAnimationFrame(() => {
5781
5103
  if (textareaRef.current) {
5782
5104
  textareaRef.current.style.height = "auto";
5783
5105
  textareaRef.current.focus();
5784
5106
  }
5785
5107
  });
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
- ]);
5108
+ }, [value, disabled, onClearEditing, onSend]);
5807
5109
  const selectCommand = react.useCallback(
5808
5110
  (command) => {
5809
5111
  const insertText = command.insertText ?? `${command.name} `;
@@ -5848,30 +5150,18 @@ var ChatInputV2 = react.forwardRef(
5848
5150
  }
5849
5151
  if (e.key === "Enter" && !e.shiftKey) {
5850
5152
  e.preventDefault();
5851
- if (isStreaming || isSending || isUploadingAttachments) return;
5852
- void handleSend();
5153
+ if (isStreaming) return;
5154
+ handleSend();
5853
5155
  }
5854
5156
  };
5855
5157
  const handleUploadImageClick = () => {
5856
- imageInputRef.current?.click();
5857
5158
  onUploadImageClick?.();
5858
5159
  setShowActions(false);
5859
5160
  };
5860
5161
  const handleAttachFileClick = () => {
5861
- fileInputRef.current?.click();
5862
5162
  onAttachFileClick?.();
5863
5163
  setShowActions(false);
5864
5164
  };
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
- };
5875
5165
  const hideVoiceTooltip = react.useCallback(() => {
5876
5166
  if (voiceTooltipTimerRef.current) {
5877
5167
  clearTimeout(voiceTooltipTimerRef.current);
@@ -5899,39 +5189,9 @@ var ChatInputV2 = react.forwardRef(
5899
5189
  }
5900
5190
  onVoicePress();
5901
5191
  };
5902
- const canSend = (value.trim().length > 0 || attachedFiles.length > 0) && !disabled;
5903
- const sendDisabled = !canSend || isStreaming || isUploadingAttachments || !attachmentsReady || hasAttachmentUploadErrors || isSending;
5192
+ const canSend = value.trim().length > 0 && !disabled;
5193
+ const sendDisabled = !canSend || isStreaming;
5904
5194
  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
- ),
5935
5195
  /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showCommandSuggestions && /* @__PURE__ */ jsxRuntime.jsx(
5936
5196
  framerMotion.motion.div,
5937
5197
  {
@@ -5970,115 +5230,6 @@ var ChatInputV2 = react.forwardRef(
5970
5230
  ),
5971
5231
  children: [
5972
5232
  /* @__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
- ) }),
6082
5233
  /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: editingMessageId && /* @__PURE__ */ jsxRuntime.jsxs(
6083
5234
  framerMotion.motion.div,
6084
5235
  {
@@ -6310,13 +5461,13 @@ var ChatInputV2 = react.forwardRef(
6310
5461
  "button",
6311
5462
  {
6312
5463
  type: "button",
6313
- onClick: () => void handleSend(),
5464
+ onClick: handleSend,
6314
5465
  disabled: sendDisabled,
6315
5466
  className: cn(
6316
5467
  "payman-v2-input-send-btn",
6317
5468
  sendDisabled && "payman-v2-input-send-btn-disabled"
6318
5469
  ),
6319
- "aria-label": isUploadingAttachments || isSending ? "Uploading attachments" : "Send message",
5470
+ "aria-label": "Send message",
6320
5471
  children: /* @__PURE__ */ jsxRuntime.jsx(
6321
5472
  lucideReact.ArrowUp,
6322
5473
  {
@@ -6851,389 +6002,6 @@ function TimelineBars({
6851
6002
  )
6852
6003
  ] });
6853
6004
  }
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
- }
7237
6005
  var DEFAULT_USER_ACTION_STATE = {
7238
6006
  prompts: [],
7239
6007
  notifications: []
@@ -7328,8 +6096,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7328
6096
  onLoadMoreMessages,
7329
6097
  isLoadingMoreMessages = false,
7330
6098
  hasMoreMessages = false,
7331
- chat,
7332
- attachmentUpload
6099
+ chat
7333
6100
  }, ref) {
7334
6101
  const [inputValue, setInputValue] = react.useState("");
7335
6102
  const prevInputValueRef = react.useRef(inputValue);
@@ -7383,6 +6150,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7383
6150
  const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
7384
6151
  const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
7385
6152
  const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
6153
+ const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
7386
6154
  const dismissNotification = chat.dismissNotification ?? NOOP;
7387
6155
  const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
7388
6156
  const {
@@ -7408,19 +6176,6 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7408
6176
  }
7409
6177
  }
7410
6178
  );
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
- }, []);
7424
6179
  const contextValue = react.useMemo(
7425
6180
  () => ({
7426
6181
  resetSession,
@@ -7429,8 +6184,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7429
6184
  cancelStream,
7430
6185
  getSessionId,
7431
6186
  getMessages,
7432
- isWaitingForResponse,
7433
- openPdfSheet
6187
+ isWaitingForResponse
7434
6188
  }),
7435
6189
  [
7436
6190
  resetSession,
@@ -7439,8 +6193,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7439
6193
  cancelStream,
7440
6194
  getSessionId,
7441
6195
  getMessages,
7442
- isWaitingForResponse,
7443
- openPdfSheet
6196
+ isWaitingForResponse
7444
6197
  ]
7445
6198
  );
7446
6199
  const {
@@ -7496,10 +6249,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7496
6249
  setInputValue("");
7497
6250
  setLightboxSrc(null);
7498
6251
  setLightboxAlt("");
7499
- setPdfSheet(null);
7500
- autoOpenedPdfHrefsRef.current.clear();
7501
6252
  chatInputV2Ref.current?.setDraft("");
7502
- chatInputV2Ref.current?.clearAttachments();
7503
6253
  clearTranscript();
7504
6254
  if (isRecording) {
7505
6255
  stopRecording();
@@ -7550,20 +6300,14 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7550
6300
  emptyStateComponent,
7551
6301
  showResetSession = false,
7552
6302
  enableDeepModeToggle = true,
6303
+ showAttachmentButton = true,
6304
+ showUploadImageButton = true,
6305
+ showAttachFileButton = true,
7553
6306
  messageActions: messageActionsConfig,
7554
6307
  enableSlashCommands = true,
7555
6308
  slashCommands: slashCommandsConfig,
7556
6309
  commandPermissions
7557
6310
  } = config;
7558
- const attachmentSettings = react.useMemo(
7559
- () => resolveChatAttachmentConfig(config),
7560
- [
7561
- config.attachments,
7562
- config.showAttachmentButton,
7563
- config.showUploadImageButton,
7564
- config.showAttachFileButton
7565
- ]
7566
- );
7567
6311
  const messageActions = react.useMemo(
7568
6312
  () => ({
7569
6313
  userMessageActions: {
@@ -7651,22 +6395,11 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7651
6395
  };
7652
6396
  const userActionPrompts = isUserActionSupported ? userActionState.prompts : void 0;
7653
6397
  const notifications = userActionState.notifications;
7654
- const handleAttachmentsChange = react.useCallback(
7655
- (attachments) => {
7656
- attachmentUpload.syncAttachments(attachments);
7657
- },
7658
- [attachmentUpload]
7659
- );
7660
- const handleV2Send = (text, files = [], attachments = []) => {
6398
+ const handleV2Send = (text) => {
7661
6399
  if (isRecording) stopRecording();
7662
- if ((text.trim() || files.length > 0) && !disableInput && isSessionParamsConfigured) {
7663
- if (files.length > 0 && attachments.length === 0) return;
6400
+ if (text.trim() && !disableInput && isSessionParamsConfigured) {
7664
6401
  setEditingMessageId(null);
7665
- void sendMessage(text.trim(), {
7666
- analysisMode: effectiveAnalysisMode,
7667
- attachments,
7668
- files
7669
- });
6402
+ void sendMessage(text.trim(), { analysisMode: effectiveAnalysisMode });
7670
6403
  }
7671
6404
  };
7672
6405
  const handleVoicePress = react.useCallback(async () => {
@@ -7714,214 +6447,156 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7714
6447
  style,
7715
6448
  children: [
7716
6449
  children,
7717
- /* @__PURE__ */ jsxRuntime.jsxs(
7718
- "div",
6450
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsxRuntime.jsx(
6451
+ framerMotion.motion.div,
7719
6452
  {
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(
7726
- framerMotion.motion.div,
7727
- {
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,
7816
- {
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,
6453
+ initial: { opacity: 1 },
6454
+ exit: { opacity: 0 },
6455
+ transition: { duration: 0.3 },
6456
+ className: "payman-v2-chat-layout",
6457
+ style: { justifyContent: "center", alignItems: "center" },
6458
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", flex: 1, width: "100%" }, children: [
6459
+ /* @__PURE__ */ jsxRuntime.jsx(
6460
+ MessageList,
7899
6461
  {
7900
- src: pdfSheet?.href ?? null,
7901
- title: pdfSheet?.title ?? "",
7902
- onClose: closePdfSheet,
7903
- mode: "split"
6462
+ messages,
6463
+ isLoading: false,
6464
+ emptyStateText,
6465
+ showEmptyStateIcon,
6466
+ emptyStateComponent,
6467
+ layout,
6468
+ showTimestamps,
6469
+ stage: config.stage || "DEVELOPMENT",
6470
+ animated,
6471
+ showAgentName,
6472
+ agentName,
6473
+ showAvatars,
6474
+ showUserAvatar,
6475
+ showAssistantAvatar,
6476
+ showExecutionSteps,
6477
+ showStreamingDot,
6478
+ streamingStepsText,
6479
+ completedStepsText,
6480
+ onExecutionTraceClick,
6481
+ onLoadMoreMessages,
6482
+ isLoadingMoreMessages,
6483
+ hasMoreMessages
7904
6484
  }
7905
6485
  ),
7906
- pdfPreviewMode === "sheet" && /* @__PURE__ */ jsxRuntime.jsx(
7907
- "div",
6486
+ /* @__PURE__ */ jsxRuntime.jsx(
6487
+ framerMotion.motion.div,
7908
6488
  {
7909
- className: "payman-v2-pdf-sheet-top-anchor",
7910
- "aria-hidden": !pdfSheet,
7911
- children: /* @__PURE__ */ jsxRuntime.jsx(
7912
- PdfSheetV2,
6489
+ initial: { opacity: 0, y: 12 },
6490
+ animate: { opacity: 1, y: 0 },
6491
+ transition: { delay: 0.2, duration: 0.4, ease: [0.25, 0.46, 0.45, 0.94] },
6492
+ style: { width: "100%" },
6493
+ children: hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
6494
+ ChatInputV2,
7913
6495
  {
7914
- src: pdfSheet?.href ?? null,
7915
- title: pdfSheet?.title ?? "",
7916
- onClose: closePdfSheet,
7917
- mode: "sheet"
6496
+ ref: chatInputV2Ref,
6497
+ onSend: handleV2Send,
6498
+ onCancel: cancelStream,
6499
+ disabled: isV2InputDisabled,
6500
+ isStreaming: isWaitingForResponse,
6501
+ placeholder: isRecording ? "Listening..." : placeholder,
6502
+ enableVoice: config.enableVoice === true,
6503
+ transcribedText: config.enableVoice === true ? transcribedText : "",
6504
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
6505
+ isRecording,
6506
+ onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
6507
+ onCancelRecording: handleCancelRecording,
6508
+ onConfirmRecording: handleConfirmRecording,
6509
+ showResetSession,
6510
+ onResetSession: requestResetSession,
6511
+ showAttachmentButton,
6512
+ showUploadImageButton,
6513
+ showAttachFileButton,
6514
+ onUploadImageClick,
6515
+ onAttachFileClick,
6516
+ editingMessageId,
6517
+ onClearEditing: handleClearEditing,
6518
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
6519
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
6520
+ slashCommands
7918
6521
  }
7919
6522
  )
7920
6523
  }
7921
6524
  )
6525
+ ] })
6526
+ },
6527
+ "v2-empty"
6528
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
6529
+ framerMotion.motion.div,
6530
+ {
6531
+ initial: hasEverSentMessage ? { opacity: 0 } : false,
6532
+ animate: { opacity: 1 },
6533
+ transition: { duration: 0.3 },
6534
+ className: "payman-v2-chat-layout",
6535
+ children: [
6536
+ /* @__PURE__ */ jsxRuntime.jsx(
6537
+ MessageListV2,
6538
+ {
6539
+ ref: messageListV2Ref,
6540
+ messages,
6541
+ isStreaming: isWaitingForResponse,
6542
+ onEditUserMessage: handleEditMessageDraft,
6543
+ onRetryUserMessage: handleRetryUserMessage,
6544
+ onImageClick: handleImageClick,
6545
+ onExecutionTraceClick,
6546
+ messageActions,
6547
+ retryDisabled: isWaitingForResponse,
6548
+ typingSpeed: config.typingSpeed ?? 4,
6549
+ userActionPrompts,
6550
+ notifications,
6551
+ onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6552
+ onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6553
+ onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6554
+ onExpireUserAction: isUserActionSupported ? expireUserAction2 : void 0,
6555
+ onDismissNotification: dismissNotification,
6556
+ onSubmitFeedback: handleSubmitFeedback
6557
+ }
6558
+ ),
6559
+ /* @__PURE__ */ jsxRuntime.jsx(
6560
+ StreamingIndicatorV2,
6561
+ {
6562
+ isStreaming: isWaitingForResponse,
6563
+ loadingAnimation: config.loadingAnimation
6564
+ }
6565
+ ),
6566
+ hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
6567
+ ChatInputV2,
6568
+ {
6569
+ ref: chatInputV2Ref,
6570
+ onSend: handleV2Send,
6571
+ onCancel: cancelStream,
6572
+ disabled: isV2InputDisabled,
6573
+ isStreaming: isWaitingForResponse,
6574
+ placeholder: isRecording ? "Listening..." : placeholder,
6575
+ enableVoice: config.enableVoice === true,
6576
+ transcribedText: config.enableVoice === true ? transcribedText : "",
6577
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
6578
+ isRecording,
6579
+ onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
6580
+ onCancelRecording: handleCancelRecording,
6581
+ onConfirmRecording: handleConfirmRecording,
6582
+ showResetSession,
6583
+ onResetSession: requestResetSession,
6584
+ showAttachmentButton,
6585
+ showUploadImageButton,
6586
+ showAttachFileButton,
6587
+ onUploadImageClick,
6588
+ onAttachFileClick,
6589
+ editingMessageId,
6590
+ onClearEditing: handleClearEditing,
6591
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
6592
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
6593
+ slashCommands
6594
+ }
6595
+ )
7922
6596
  ]
7923
- }
7924
- ),
6597
+ },
6598
+ "v2-chat"
6599
+ ) }),
7925
6600
  /* @__PURE__ */ jsxRuntime.jsx(
7926
6601
  ImageLightboxV2,
7927
6602
  {
@@ -7956,39 +6631,19 @@ var PaymanChat = react.forwardRef(
7956
6631
  function PaymanChat2(props, ref) {
7957
6632
  const mergedCallbacks = useSentryChatCallbacks(props.callbacks, props.config);
7958
6633
  const chat = useChatV2(props.config, mergedCallbacks);
7959
- const attachmentUpload = useAttachmentUpload(props.config);
7960
- return /* @__PURE__ */ jsxRuntime.jsx(
7961
- PaymanChatInner,
7962
- {
7963
- ...props,
7964
- chat,
7965
- attachmentUpload,
7966
- ref
7967
- }
7968
- );
6634
+ return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatInner, { ...props, chat, ref });
7969
6635
  }
7970
6636
  );
7971
6637
 
7972
6638
  exports.PaymanChat = PaymanChat;
7973
6639
  exports.PaymanChatContext = PaymanChatContext;
7974
- exports.PdfSheetV2 = PdfSheetV2;
7975
6640
  exports.UserActionStaleError = UserActionStaleError;
7976
- exports.buildSignedUrlEndpoint = buildSignedUrlEndpoint;
7977
6641
  exports.cancelUserAction = cancelUserAction;
7978
6642
  exports.captureSentryError = captureSentryError;
7979
6643
  exports.cn = cn;
7980
- exports.formatAttachmentBytes = formatAttachmentBytes;
7981
6644
  exports.formatDate = formatDate;
7982
- exports.getPdfTitleFromUrl = getPdfTitleFromUrl;
7983
- exports.isPdfUrl = isPdfUrl;
7984
- exports.mapExecutionHistoryPageToChatMessages = mapExecutionHistoryPageToChatMessages;
7985
- exports.mapExecutionHistoryToChatMessages = mapExecutionHistoryToChatMessages;
7986
6645
  exports.resendUserAction = resendUserAction;
7987
- exports.stripAttachmentsSuffixFromIntent = stripAttachmentsSuffixFromIntent;
7988
6646
  exports.submitUserAction = submitUserAction;
7989
- exports.uploadAttachment = uploadAttachment;
7990
- exports.uploadAttachments = uploadAttachments;
7991
- exports.useAttachmentUpload = useAttachmentUpload;
7992
6647
  exports.useChatV2 = useChatV2;
7993
6648
  exports.usePaymanChat = usePaymanChat;
7994
6649
  exports.useVoice = useVoice;