@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.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import { createContext, forwardRef, useCallback, useRef, useState, useImperativeHandle, useEffect, useContext, useMemo, useLayoutEffect, Children, isValidElement } from 'react';
1
+ import { createContext, forwardRef, useCallback, useRef, useState, useMemo, useEffect, useImperativeHandle, useLayoutEffect, useContext } from 'react';
2
2
  import { AnimatePresence, motion } from 'framer-motion';
3
3
  import { clsx } from 'clsx';
4
4
  import { twMerge } from 'tailwind-merge';
5
5
  import * as Sentry from '@sentry/react';
6
- import { ArrowDown, Loader2, FileText, X, Pencil, RotateCcw, Telescope, Zap, Plus, ImagePlus, Paperclip, Mic, ArrowUp, Check, AlertCircle, Copy, WifiOff, ThumbsUp, ThumbsDown, Binoculars, Info, Download, ChevronDown, RefreshCw, ExternalLink, User, Clock, Sparkles, ImageOff, Eye, ChevronRight, ShieldCheck } from 'lucide-react';
6
+ import { ArrowDown, Pencil, X, RotateCcw, Telescope, Zap, Plus, ImagePlus, Paperclip, Mic, ArrowUp, Check, AlertCircle, Copy, WifiOff, ThumbsUp, ThumbsDown, Binoculars, Info, Download, Loader2, ChevronDown, User, Clock, Sparkles, ImageOff, Eye, ChevronRight, ShieldCheck } from 'lucide-react';
7
7
  import ReactMarkdown from 'react-markdown';
8
8
  import remarkGfm from 'remark-gfm';
9
9
  import { createPortal } from 'react-dom';
@@ -13,10 +13,7 @@ import { DotLottieReact } from '@lottiefiles/dotlottie-react';
13
13
 
14
14
  var __defProp = Object.defineProperty;
15
15
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
16
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
17
- var __defProp2 = Object.defineProperty;
18
- var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
19
- var __publicField2 = (obj, key, value) => __defNormalProp2(obj, key + "", value);
16
+ var __publicField = (obj, key, value) => __defNormalProp(obj, key + "", value);
20
17
  function generateId() {
21
18
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
22
19
  const r = Math.random() * 16 | 0;
@@ -712,8 +709,7 @@ function buildRequestBody(config, userMessage, sessionId, options) {
712
709
  sessionAttributes,
713
710
  analysisMode: options?.analysisMode,
714
711
  locale: resolveLocale(config.locale),
715
- timezone: resolveTimezone(config.timezone),
716
- ...options?.attachments?.length ? { attachments: options.attachments } : {}
712
+ timezone: resolveTimezone(config.timezone)
717
713
  };
718
714
  }
719
715
  function resolveLocale(configured) {
@@ -758,7 +754,6 @@ function buildResolveImagesUrl(config) {
758
754
  const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
759
755
  return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
760
756
  }
761
- var NGROK_SKIP_BROWSER_WARNING = "ngrok-skip-browser-warning";
762
757
  function buildRequestHeaders(config) {
763
758
  const headers = {
764
759
  ...config.api.headers
@@ -766,9 +761,6 @@ function buildRequestHeaders(config) {
766
761
  if (config.api.authToken) {
767
762
  headers.Authorization = `Bearer ${config.api.authToken}`;
768
763
  }
769
- if (!headers[NGROK_SKIP_BROWSER_WARNING]) {
770
- headers[NGROK_SKIP_BROWSER_WARNING] = "true";
771
- }
772
764
  return headers;
773
765
  }
774
766
  var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
@@ -1049,85 +1041,10 @@ function createCancelledMessageUpdate(steps, currentMessage) {
1049
1041
  currentMessage: currentMessage || "Thinking..."
1050
1042
  };
1051
1043
  }
1052
- var DEFAULT_SIGNED_URL_ENDPOINT = "/api/files/signed-url";
1053
- function fileExtension(filename) {
1054
- const ext = filename.split(".").pop()?.toLowerCase().replace(/^\./, "");
1055
- return ext && ext.length > 0 ? ext : "bin";
1056
- }
1057
- function resolveMimeType(file) {
1058
- if (file.type && file.type.trim().length > 0) return file.type;
1059
- const ext = fileExtension(file.name);
1060
- const byExt = {
1061
- pdf: "application/pdf",
1062
- png: "image/png",
1063
- jpg: "image/jpeg",
1064
- jpeg: "image/jpeg",
1065
- gif: "image/gif",
1066
- webp: "image/webp"
1067
- };
1068
- return byExt[ext] ?? "application/octet-stream";
1069
- }
1070
- function buildSignedUrlEndpoint(config, ext) {
1071
- const endpoint = config.api.signedUrlEndpoint || DEFAULT_SIGNED_URL_ENDPOINT;
1072
- const queryParams = new URLSearchParams({ extn: ext.replace(/^\./, "") });
1073
- return `${config.api.baseUrl}${endpoint}?${queryParams.toString()}`;
1074
- }
1075
- async function requestSignedUrl(config, ext, signal) {
1076
- const url = buildSignedUrlEndpoint(config, ext);
1077
- const headers = buildRequestHeaders(config);
1078
- const response = await fetch(url, {
1079
- method: "GET",
1080
- headers,
1081
- signal
1082
- });
1083
- if (!response.ok) {
1084
- const errorText = await response.text().catch(() => "");
1085
- throw new Error(
1086
- errorText ? `Failed to get upload URL (${response.status}): ${errorText}` : `Failed to get upload URL (${response.status})`
1087
- );
1088
- }
1089
- const data = await response.json();
1090
- if (!data.key || !data.url) {
1091
- throw new Error("Signed URL response missing key or url");
1092
- }
1093
- return { key: data.key, url: data.url };
1094
- }
1095
- async function uploadToSignedUrl(signedUrl, file, mimeType, signal) {
1096
- const response = await fetch(signedUrl, {
1097
- method: "PUT",
1098
- headers: {
1099
- "Content-Type": mimeType,
1100
- "x-ms-blob-type": "BlockBlob"
1101
- },
1102
- body: file,
1103
- signal
1104
- });
1105
- if (!response.ok) {
1106
- const errorText = await response.text().catch(() => "");
1107
- throw new Error(
1108
- errorText ? `Failed to upload file (${response.status}): ${errorText}` : `Failed to upload file (${response.status})`
1109
- );
1110
- }
1111
- }
1112
- async function uploadAttachment(config, file, signal) {
1113
- const ext = fileExtension(file.name);
1114
- const mimeType = resolveMimeType(file);
1115
- const { key, url } = await requestSignedUrl(config, ext, signal);
1116
- await uploadToSignedUrl(url, file, mimeType, signal);
1117
- return {
1118
- tempKey: key,
1119
- filename: file.name,
1120
- mimeType
1121
- };
1122
- }
1123
- async function uploadAttachments(config, files, signal) {
1124
- const uploads = files.map((file) => uploadAttachment(config, file, signal));
1125
- return Promise.all(uploads);
1126
- }
1127
1044
  var UserActionStaleError = class extends Error {
1128
1045
  constructor(userActionId, message = "User action is no longer actionable") {
1129
1046
  super(message);
1130
- __publicField2(this, "userActionId");
1047
+ __publicField(this, "userActionId");
1131
1048
  this.name = "UserActionStaleError";
1132
1049
  this.userActionId = userActionId;
1133
1050
  }
@@ -1160,6 +1077,9 @@ async function cancelUserAction(config, userActionId) {
1160
1077
  async function resendUserAction(config, userActionId) {
1161
1078
  return sendUserActionRequest(config, userActionId, "resend");
1162
1079
  }
1080
+ async function expireUserAction(config, userActionId) {
1081
+ return sendUserActionRequest(config, userActionId, "expired");
1082
+ }
1163
1083
  var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
1164
1084
  function upsertPrompt(prompts, req) {
1165
1085
  const active = { ...req, status: "pending" };
@@ -1188,32 +1108,12 @@ function getStoredOrInitialMessages(config) {
1188
1108
  function getSessionIdFromMessages(messages) {
1189
1109
  return messages.find((message) => message.sessionId)?.sessionId;
1190
1110
  }
1191
- var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set(["jpg", "jpeg", "png", "gif", "webp", "avif"]);
1192
- function attachmentKindFromFile(file) {
1193
- const ext = file.name.split(".").pop()?.toLowerCase() ?? "";
1194
- if (IMAGE_EXTENSIONS.has(ext) || file.type.startsWith("image/")) return "image";
1195
- return "file";
1196
- }
1197
- function buildMessageAttachments(files) {
1198
- return files.map((file, index) => {
1199
- const kind = attachmentKindFromFile(file);
1200
- return {
1201
- id: `att-${Date.now()}-${index}`,
1202
- filename: file.name,
1203
- mimeType: file.type || "application/octet-stream",
1204
- // Blob URL for all local files so PDFs/docs stay previewable after send.
1205
- previewUrl: URL.createObjectURL(file),
1206
- kind
1207
- };
1208
- });
1209
- }
1210
1111
  function useChatV2(config, callbacks = {}) {
1211
1112
  const [messages, setMessages] = useState(() => getStoredOrInitialMessages(config));
1212
1113
  const [isWaitingForResponse, setIsWaitingForResponse] = useState(() => {
1213
1114
  if (!config.userId) return false;
1214
1115
  return activeStreamStore.get(config.userId)?.isWaiting ?? false;
1215
1116
  });
1216
- const [isUploadingAttachments, setIsUploadingAttachments] = useState(false);
1217
1117
  const sessionIdRef = useRef(
1218
1118
  getSessionIdFromMessages(getStoredOrInitialMessages(config)) ?? config.initialSessionId ?? void 0
1219
1119
  );
@@ -1288,45 +1188,21 @@ function useChatV2(config, callbacks = {}) {
1288
1188
  );
1289
1189
  const sendMessage = useCallback(
1290
1190
  async (userMessage, options) => {
1291
- const trimmedMessage = userMessage.trim();
1292
- const files = options?.files ?? [];
1293
- const hasPreuploadedAttachments = (options?.attachments?.length ?? 0) > 0;
1294
- if (!trimmedMessage && files.length === 0 && !hasPreuploadedAttachments) return;
1295
- let streamAttachments = options?.attachments;
1296
- if (!streamAttachments && files.length > 0) {
1297
- setIsUploadingAttachments(true);
1298
- try {
1299
- streamAttachments = await uploadAttachments(configRef.current, files);
1300
- } catch (error) {
1301
- if (error.name !== "AbortError") {
1302
- callbacksRef.current.onError?.(error);
1303
- }
1304
- throw error;
1305
- } finally {
1306
- setIsUploadingAttachments(false);
1307
- }
1308
- }
1191
+ if (!userMessage.trim()) return;
1309
1192
  if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
1310
1193
  sessionIdRef.current = generateId();
1311
1194
  callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
1312
1195
  }
1313
- const messageAttachments = files.length > 0 ? buildMessageAttachments(files) : streamAttachments?.length ? streamAttachments.map((attachment, index) => ({
1314
- id: `att-${Date.now()}-${index}`,
1315
- filename: attachment.filename,
1316
- mimeType: attachment.mimeType,
1317
- kind: attachment.mimeType.startsWith("image/") ? "image" : "file"
1318
- })) : void 0;
1319
1196
  const userMessageId = `user-${Date.now()}`;
1320
1197
  const userMsg = {
1321
1198
  id: userMessageId,
1322
1199
  sessionId: sessionIdRef.current,
1323
1200
  role: "user",
1324
- content: trimmedMessage,
1325
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1326
- attachments: messageAttachments
1201
+ content: userMessage,
1202
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1327
1203
  };
1328
1204
  setMessages((prev) => [...prev, userMsg]);
1329
- callbacksRef.current.onMessageSent?.(trimmedMessage);
1205
+ callbacksRef.current.onMessageSent?.(userMessage);
1330
1206
  setIsWaitingForResponse(true);
1331
1207
  callbacksRef.current.onStreamStart?.();
1332
1208
  const streamingId = `assistant-${Date.now()}`;
@@ -1353,14 +1229,11 @@ function useChatV2(config, callbacks = {}) {
1353
1229
  activeStreamStore.start(userId, abortController, initialMessages);
1354
1230
  }
1355
1231
  const newSessionId = await startStream(
1356
- trimmedMessage,
1232
+ userMessage,
1357
1233
  streamingId,
1358
1234
  sessionIdRef.current,
1359
1235
  abortController,
1360
- {
1361
- analysisMode: options?.analysisMode,
1362
- attachments: streamAttachments
1363
- }
1236
+ options
1364
1237
  );
1365
1238
  const finalStreamUserId = streamUserIdRef.current ?? userId;
1366
1239
  if (finalStreamUserId) {
@@ -1498,6 +1371,19 @@ function useChatV2(config, callbacks = {}) {
1498
1371
  },
1499
1372
  [setPromptStatus]
1500
1373
  );
1374
+ const expireUserAction2 = useCallback(
1375
+ async (userActionId) => {
1376
+ setPromptStatus(userActionId, "expired");
1377
+ try {
1378
+ await expireUserAction(configRef.current, userActionId);
1379
+ } catch (error) {
1380
+ if (error instanceof UserActionStaleError) return;
1381
+ callbacksRef.current.onError?.(error);
1382
+ throw error;
1383
+ }
1384
+ },
1385
+ [setPromptStatus]
1386
+ );
1501
1387
  const dismissNotification = useCallback((id) => {
1502
1388
  setUserActionState((prev) => ({
1503
1389
  ...prev,
@@ -1538,18 +1424,6 @@ function useChatV2(config, callbacks = {}) {
1538
1424
  setMessages(config.initialMessages);
1539
1425
  sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? config.initialSessionId;
1540
1426
  }, [config.initialMessages, config.initialSessionId, config.userId]);
1541
- const hydratedSessionIdRef = useRef(void 0);
1542
- useEffect(() => {
1543
- if (config.userId) return;
1544
- if (!config.initialMessages?.length) return;
1545
- const sessionId = config.initialSessionId;
1546
- if (hydratedSessionIdRef.current === sessionId && messagesRef.current.length > 0) {
1547
- return;
1548
- }
1549
- hydratedSessionIdRef.current = sessionId;
1550
- setMessages(config.initialMessages);
1551
- sessionIdRef.current = getSessionIdFromMessages(config.initialMessages) ?? sessionId;
1552
- }, [config.initialMessages, config.initialSessionId, config.userId]);
1553
1427
  useEffect(() => {
1554
1428
  const prevUserId = prevUserIdRef.current;
1555
1429
  prevUserIdRef.current = config.userId;
@@ -1584,12 +1458,12 @@ function useChatV2(config, callbacks = {}) {
1584
1458
  getSessionId,
1585
1459
  getMessages,
1586
1460
  isWaitingForResponse,
1587
- isUploadingAttachments,
1588
1461
  sessionId: sessionIdRef.current,
1589
1462
  userActionState,
1590
1463
  submitUserAction: submitUserAction2,
1591
1464
  cancelUserAction: cancelUserAction2,
1592
1465
  resendUserAction: resendUserAction2,
1466
+ expireUserAction: expireUserAction2,
1593
1467
  dismissNotification
1594
1468
  };
1595
1469
  }
@@ -1808,102 +1682,6 @@ function useVoice(config = {}, callbacks = {}) {
1808
1682
  reset
1809
1683
  };
1810
1684
  }
1811
- function useAttachmentUpload(config) {
1812
- const configRef = useRef(config);
1813
- configRef.current = config;
1814
- const [entries, setEntries] = useState(/* @__PURE__ */ new Map());
1815
- const [orderedIds, setOrderedIds] = useState([]);
1816
- const abortControllersRef = useRef(/* @__PURE__ */ new Map());
1817
- const activeIdsRef = useRef(/* @__PURE__ */ new Set());
1818
- const startUpload = useCallback((id, file) => {
1819
- if (activeIdsRef.current.has(id)) return;
1820
- activeIdsRef.current.add(id);
1821
- const controller = new AbortController();
1822
- abortControllersRef.current.set(id, controller);
1823
- setEntries((prev) => {
1824
- const next = new Map(prev);
1825
- next.set(id, { id, status: "uploading" });
1826
- return next;
1827
- });
1828
- void uploadAttachment(configRef.current, file, controller.signal).then((payload) => {
1829
- if (controller.signal.aborted) return;
1830
- setEntries((prev) => {
1831
- const next = new Map(prev);
1832
- next.set(id, { id, status: "done", payload });
1833
- return next;
1834
- });
1835
- }).catch((error) => {
1836
- if (error.name === "AbortError") return;
1837
- setEntries((prev) => {
1838
- const next = new Map(prev);
1839
- next.set(id, {
1840
- id,
1841
- status: "error",
1842
- error: error.message || "Upload failed"
1843
- });
1844
- return next;
1845
- });
1846
- }).finally(() => {
1847
- abortControllersRef.current.delete(id);
1848
- });
1849
- }, []);
1850
- const syncAttachments = useCallback(
1851
- (attachments) => {
1852
- const nextIds = attachments.map((attachment) => attachment.id);
1853
- setOrderedIds(nextIds);
1854
- const nextIdSet = new Set(nextIds);
1855
- for (const id of activeIdsRef.current) {
1856
- if (nextIdSet.has(id)) continue;
1857
- abortControllersRef.current.get(id)?.abort();
1858
- abortControllersRef.current.delete(id);
1859
- activeIdsRef.current.delete(id);
1860
- }
1861
- setEntries((prev) => {
1862
- const next = new Map(prev);
1863
- for (const id of next.keys()) {
1864
- if (!nextIdSet.has(id)) next.delete(id);
1865
- }
1866
- return next;
1867
- });
1868
- for (const attachment of attachments) {
1869
- startUpload(attachment.id, attachment.file);
1870
- }
1871
- },
1872
- [startUpload]
1873
- );
1874
- const clearAll = useCallback(() => {
1875
- for (const controller of abortControllersRef.current.values()) {
1876
- controller.abort();
1877
- }
1878
- abortControllersRef.current.clear();
1879
- activeIdsRef.current.clear();
1880
- setOrderedIds([]);
1881
- setEntries(/* @__PURE__ */ new Map());
1882
- }, []);
1883
- const entryList = useMemo(
1884
- () => orderedIds.map((id) => entries.get(id)).filter((entry) => entry != null),
1885
- [entries, orderedIds]
1886
- );
1887
- const isUploading = entryList.some((entry) => entry.status === "uploading");
1888
- const hasErrors = entryList.some((entry) => entry.status === "error");
1889
- const allReady = orderedIds.length === 0 || orderedIds.every((id) => entries.get(id)?.status === "done");
1890
- const payloads = entryList.filter((entry) => entry.status === "done" && entry.payload).map((entry) => entry.payload);
1891
- const statusById = useMemo(
1892
- () => Object.fromEntries(
1893
- entryList.map((entry) => [entry.id, entry.status])
1894
- ),
1895
- [entryList]
1896
- );
1897
- return {
1898
- syncAttachments,
1899
- clearAll,
1900
- isUploading,
1901
- hasErrors,
1902
- allReady,
1903
- payloads,
1904
- statusById
1905
- };
1906
- }
1907
1685
  function classifyField(field) {
1908
1686
  if (!field) return "text";
1909
1687
  if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
@@ -2020,66 +1798,6 @@ function buildContent(schema, values) {
2020
1798
  }
2021
1799
  return content;
2022
1800
  }
2023
- var ATTACHMENTS_SUFFIX_RE = /\n\n\[Attachments:[^\]]*\]\s*$/;
2024
- function stripAttachmentsSuffixFromIntent(intent) {
2025
- return intent.replace(ATTACHMENTS_SUFFIX_RE, "").trimEnd();
2026
- }
2027
- function mapFeedback(feedback) {
2028
- if (!feedback?.feedback) return null;
2029
- return feedback.feedback === "POSITIVE" ? "up" : "down";
2030
- }
2031
- function mapHistoryAttachments(executionId, attachments) {
2032
- const mapped = (attachments ?? []).map((attachment, index) => ({
2033
- id: `${executionId}:att:${index}`,
2034
- filename: attachment.filename,
2035
- mimeType: attachment.mimeType,
2036
- url: attachment.url ?? void 0,
2037
- kind: attachment.mimeType.startsWith("image/") ? "image" : "file"
2038
- }));
2039
- return mapped.length > 0 ? mapped : void 0;
2040
- }
2041
- function mapExecutionHistoryToChatMessages(message) {
2042
- const timestamp = message.startTime || message.endTime || (/* @__PURE__ */ new Date()).toISOString();
2043
- const executionId = message.executionId ?? message.traceId ?? message.id;
2044
- const attachments = mapHistoryAttachments(message.id, message.attachments);
2045
- const rows = [
2046
- {
2047
- id: `${message.id}:user`,
2048
- role: "user",
2049
- content: stripAttachmentsSuffixFromIntent(message.sessionUserIntent),
2050
- timestamp,
2051
- attachments
2052
- }
2053
- ];
2054
- if (message.agentResponse) {
2055
- rows.push({
2056
- id: `${message.id}:assistant`,
2057
- role: "assistant",
2058
- content: message.agentResponse,
2059
- timestamp: message.endTime || timestamp,
2060
- isError: message.status === "FAILED",
2061
- executionId,
2062
- feedback: mapFeedback(message.feedback)
2063
- });
2064
- }
2065
- return rows;
2066
- }
2067
- function mapExecutionHistoryPageToChatMessages(messages) {
2068
- return messages.flatMap(mapExecutionHistoryToChatMessages);
2069
- }
2070
- function attachmentDisplayUrl(attachment) {
2071
- return attachment.previewUrl ?? attachment.url;
2072
- }
2073
- function isImageAttachment(attachment) {
2074
- const displayUrl = attachmentDisplayUrl(attachment);
2075
- return attachment.kind === "image" || Boolean(displayUrl) && attachment.mimeType.startsWith("image/");
2076
- }
2077
- function isPdfAttachmentMeta(attachment) {
2078
- return attachment.mimeType === "application/pdf" || /\.pdf$/i.test(attachment.filename);
2079
- }
2080
- function isPdfFile(file) {
2081
- return file.type === "application/pdf" || /\.pdf$/i.test(file.name);
2082
- }
2083
1801
  var PaymanChatContext = createContext(void 0);
2084
1802
  function usePaymanChat() {
2085
1803
  const context = useContext(PaymanChatContext);
@@ -2190,111 +1908,6 @@ function subscribeToCfRay(urlPattern, listener) {
2190
1908
  };
2191
1909
  }
2192
1910
 
2193
- // src/utils/attachmentConfig.ts
2194
- var DEFAULT_IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp"];
2195
- var DEFAULT_DOCUMENT_EXTENSIONS = ["pdf", "docx", "xlsx", "xls"];
2196
- function resolveChatAttachmentConfig(config) {
2197
- const nested = config.attachments;
2198
- const enabled = nested?.enabled ?? config.showAttachmentButton ?? true;
2199
- const uploadImage = nested?.uploadImage ?? config.showUploadImageButton ?? true;
2200
- const attachFile = nested?.attachFile ?? config.showAttachFileButton ?? true;
2201
- return {
2202
- showAttachmentButton: enabled && (uploadImage || attachFile),
2203
- showUploadImageButton: enabled && uploadImage,
2204
- showAttachFileButton: enabled && attachFile,
2205
- maxCount: nested?.maxCount ?? nested?.maxImages ?? nested?.maxDocuments,
2206
- maxFileBytes: nested?.maxFileBytes,
2207
- maxTotalBytes: nested?.maxTotalBytes,
2208
- allowedImageExtensions: nested?.imageExtensions ?? DEFAULT_IMAGE_EXTENSIONS,
2209
- allowedFileExtensions: nested?.documentExtensions ?? DEFAULT_DOCUMENT_EXTENSIONS
2210
- };
2211
- }
2212
-
2213
- // src/utils/formatAttachmentBytes.ts
2214
- function formatAttachmentBytes(bytes) {
2215
- if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
2216
- const units = ["B", "KB", "MB", "GB"];
2217
- let value = bytes;
2218
- let unitIndex = 0;
2219
- while (value >= 1024 && unitIndex < units.length - 1) {
2220
- value /= 1024;
2221
- unitIndex += 1;
2222
- }
2223
- const rounded = unitIndex === 0 ? String(Math.round(value)) : value >= 10 ? value.toFixed(0) : value.toFixed(1).replace(/\.0$/, "");
2224
- return `${rounded} ${units[unitIndex]}`;
2225
- }
2226
-
2227
- // src/utils/pdfLink.ts
2228
- function filenameFromContentDisposition(value) {
2229
- if (!value) return void 0;
2230
- let decoded = value;
2231
- try {
2232
- decoded = decodeURIComponent(value);
2233
- } catch {
2234
- decoded = value;
2235
- }
2236
- const quoted = decoded.match(/filename\*=(?:UTF-8''([^;]+)|"([^"]+)")/i);
2237
- if (quoted?.[1] || quoted?.[2]) {
2238
- return (quoted[1] ?? quoted[2])?.trim();
2239
- }
2240
- const unquoted = decoded.match(/filename=([^;]+)/i);
2241
- return unquoted?.[1]?.replace(/"/g, "").trim();
2242
- }
2243
- function filenameFromUrlPath(href) {
2244
- try {
2245
- const parts = new URL(href).pathname.split("/").filter(Boolean);
2246
- const last = parts[parts.length - 1];
2247
- return last ? decodeURIComponent(last) : void 0;
2248
- } catch {
2249
- return void 0;
2250
- }
2251
- }
2252
- function isPdfUrl(href) {
2253
- if (!href) return false;
2254
- const lower = href.toLowerCase();
2255
- if (lower.includes(".pdf")) return true;
2256
- try {
2257
- const url = new URL(href);
2258
- if (url.pathname.toLowerCase().endsWith(".pdf")) return true;
2259
- const filename = url.searchParams.get("filename");
2260
- if (filename?.toLowerCase().includes(".pdf")) return true;
2261
- for (const key of ["rscd", "response-content-disposition"]) {
2262
- const fromDisposition = filenameFromContentDisposition(
2263
- url.searchParams.get(key)
2264
- );
2265
- if (fromDisposition?.toLowerCase().includes(".pdf")) return true;
2266
- }
2267
- } catch {
2268
- return lower.endsWith(".pdf");
2269
- }
2270
- return false;
2271
- }
2272
- function getPdfTitleFromUrl(href, linkText) {
2273
- const text = linkText?.trim();
2274
- if (text) return text;
2275
- try {
2276
- const url = new URL(href);
2277
- const filename = url.searchParams.get("filename");
2278
- if (filename) {
2279
- return filename.replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
2280
- }
2281
- for (const key of ["rscd", "response-content-disposition"]) {
2282
- const fromDisposition = filenameFromContentDisposition(
2283
- url.searchParams.get(key)
2284
- );
2285
- if (fromDisposition) {
2286
- return fromDisposition.replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
2287
- }
2288
- }
2289
- } catch {
2290
- }
2291
- const fromPath = filenameFromUrlPath(href);
2292
- if (fromPath) {
2293
- return fromPath.replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
2294
- }
2295
- return "Document";
2296
- }
2297
-
2298
1911
  // src/utils/slashCommands.ts
2299
1912
  var DEFAULT_SLASH_COMMANDS = [
2300
1913
  {
@@ -3446,108 +3059,6 @@ function ActionTooltipV2({ label, children }) {
3446
3059
  ] }) })
3447
3060
  ] });
3448
3061
  }
3449
- function FilePreviewShell({
3450
- filename,
3451
- typeLabel,
3452
- onClick,
3453
- thumbnail,
3454
- className,
3455
- "aria-label": ariaLabel
3456
- }) {
3457
- const shellClass = cn(
3458
- "payman-v2-file-preview-shell",
3459
- "payman-v2-file-preview-shell-sent",
3460
- onClick && "payman-v2-file-preview-shell-clickable",
3461
- className
3462
- );
3463
- const body = /* @__PURE__ */ jsxs(Fragment, { children: [
3464
- /* @__PURE__ */ jsx("div", { className: "payman-v2-file-preview-thumb", children: thumbnail ?? /* @__PURE__ */ jsx(
3465
- FileText,
3466
- {
3467
- size: 16,
3468
- strokeWidth: 1.75,
3469
- className: "payman-v2-file-preview-doc-icon"
3470
- }
3471
- ) }),
3472
- /* @__PURE__ */ jsxs("div", { className: "payman-v2-file-preview-info", children: [
3473
- /* @__PURE__ */ jsx("span", { className: "payman-v2-file-preview-name", children: filename }),
3474
- /* @__PURE__ */ jsx("span", { className: "payman-v2-file-preview-type", children: typeLabel })
3475
- ] })
3476
- ] });
3477
- if (onClick) {
3478
- return /* @__PURE__ */ jsx(
3479
- "button",
3480
- {
3481
- type: "button",
3482
- className: shellClass,
3483
- onClick,
3484
- "aria-label": ariaLabel ?? `Open ${filename}`,
3485
- children: body
3486
- }
3487
- );
3488
- }
3489
- return /* @__PURE__ */ jsx("div", { className: shellClass, children: body });
3490
- }
3491
- function FilePreviewBlockLayout({
3492
- children,
3493
- className,
3494
- ...rest
3495
- }) {
3496
- return /* @__PURE__ */ jsx("div", { className: cn("payman-v2-file-preview-item", className), ...rest, children });
3497
- }
3498
- function attachmentTypeLabel(attachment) {
3499
- if (isImageAttachment(attachment)) {
3500
- return "Image";
3501
- }
3502
- const ext = attachment.filename.split(".").pop()?.toUpperCase();
3503
- return ext || "Document";
3504
- }
3505
- function isPdfAttachment(attachment) {
3506
- const displayUrl = attachmentDisplayUrl(attachment);
3507
- return isPdfAttachmentMeta(attachment) || Boolean(displayUrl) && isPdfUrl(displayUrl);
3508
- }
3509
- function AttachmentPreviewBlock({
3510
- attachment,
3511
- onImageClick
3512
- }) {
3513
- const chatContext = useContext(PaymanChatContext);
3514
- const displayUrl = attachmentDisplayUrl(attachment);
3515
- const typeLabel = attachmentTypeLabel(attachment);
3516
- const isImage = isImageAttachment(attachment) && displayUrl;
3517
- const openAttachment = () => {
3518
- if (!displayUrl) return;
3519
- if (isImageAttachment(attachment)) {
3520
- onImageClick?.(displayUrl, attachment.filename);
3521
- return;
3522
- }
3523
- if (isPdfAttachment(attachment)) {
3524
- chatContext?.openPdfSheet(displayUrl, attachment.filename);
3525
- return;
3526
- }
3527
- window.open(displayUrl, "_blank", "noopener,noreferrer");
3528
- };
3529
- if (isImage) {
3530
- return /* @__PURE__ */ jsx(FilePreviewBlockLayout, { children: /* @__PURE__ */ jsx(
3531
- FilePreviewShell,
3532
- {
3533
- filename: attachment.filename,
3534
- typeLabel,
3535
- onClick: openAttachment,
3536
- "aria-label": `Preview ${attachment.filename}`,
3537
- thumbnail: /* @__PURE__ */ jsx("img", { src: displayUrl, alt: "", draggable: false })
3538
- }
3539
- ) });
3540
- }
3541
- return /* @__PURE__ */ jsx(FilePreviewBlockLayout, { children: /* @__PURE__ */ jsx(
3542
- FilePreviewShell,
3543
- {
3544
- filename: attachment.filename,
3545
- typeLabel,
3546
- onClick: displayUrl ? openAttachment : void 0,
3547
- "aria-label": `Open ${attachment.filename}`
3548
- }
3549
- ) });
3550
- }
3551
3062
  function formatMessageTime(timestamp) {
3552
3063
  const value = new Date(timestamp);
3553
3064
  if (Number.isNaN(value.getTime())) return "";
@@ -3560,7 +3071,6 @@ function UserMessageV2({
3560
3071
  message,
3561
3072
  onEdit,
3562
3073
  onRetry,
3563
- onImageClick,
3564
3074
  retryDisabled = false,
3565
3075
  actions
3566
3076
  }) {
@@ -3643,15 +3153,7 @@ function UserMessageV2({
3643
3153
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3644
3154
  toastPortal,
3645
3155
  /* @__PURE__ */ jsx("div", { className: "payman-v2-user-msg payman-v2-fade-in", children: /* @__PURE__ */ jsxs("div", { className: "payman-v2-user-msg-group", children: [
3646
- message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: "payman-v2-file-preview payman-v2-user-msg-attachments", children: message.attachments.map((attachment) => /* @__PURE__ */ jsx(
3647
- AttachmentPreviewBlock,
3648
- {
3649
- attachment,
3650
- onImageClick
3651
- },
3652
- attachment.id
3653
- )) }),
3654
- (message.content.trim().length > 0 || !message.attachments?.length) && /* @__PURE__ */ jsx("div", { className: "payman-v2-user-msg-bubble", children: parsedCommand ? /* @__PURE__ */ jsxs("div", { className: "payman-v2-user-msg-command", children: [
3156
+ /* @__PURE__ */ jsx("div", { className: "payman-v2-user-msg-bubble", children: parsedCommand ? /* @__PURE__ */ jsxs("div", { className: "payman-v2-user-msg-command", children: [
3655
3157
  /* @__PURE__ */ jsx("span", { className: "payman-v2-user-msg-command-chip", children: parsedCommand.command }),
3656
3158
  parsedCommand.body.trim() ? /* @__PURE__ */ jsx("p", { className: "payman-v2-user-msg-text", children: parsedCommand.body }) : null
3657
3159
  ] }) : /* @__PURE__ */ jsx("p", { className: "payman-v2-user-msg-text", children: message.content }) }),
@@ -3876,44 +3378,9 @@ function MarkdownImageV2({
3876
3378
  }
3877
3379
  ) });
3878
3380
  }
3879
- function PdfBlockV2({ title, href, onOpen, autoOpen = false }) {
3880
- const autoOpenedHrefRef = useRef(null);
3881
- useEffect(() => {
3882
- if (!autoOpen || autoOpenedHrefRef.current === href) return;
3883
- autoOpenedHrefRef.current = href;
3884
- onOpen(href, title, { auto: true });
3885
- }, [href, autoOpen]);
3886
- return /* @__PURE__ */ jsx(FilePreviewBlockLayout, { "data-payman-file-block": true, children: /* @__PURE__ */ jsx(
3887
- FilePreviewShell,
3888
- {
3889
- filename: title,
3890
- typeLabel: "PDF",
3891
- onClick: () => onOpen(href, title),
3892
- "aria-label": `Open PDF: ${title}`
3893
- }
3894
- ) });
3895
- }
3896
- function childrenToText(children) {
3897
- if (typeof children === "string") return children;
3898
- if (typeof children === "number") return String(children);
3899
- if (Array.isArray(children)) return children.map(childrenToText).join("");
3900
- if (children && typeof children === "object" && "props" in children) {
3901
- return childrenToText(children.props.children);
3902
- }
3903
- return "";
3904
- }
3905
- function isFileBlockChild(child) {
3906
- return isValidElement(child) && typeof child.props === "object" && child.props !== null && "data-payman-file-block" in child.props;
3907
- }
3908
- function buildComponents(onImageClick, isResolvingRef, onPdfClick, autoOpenPdf) {
3381
+ function buildComponents(onImageClick, isResolvingRef) {
3909
3382
  return {
3910
- p: ({ children }) => {
3911
- const childArray = Children.toArray(children);
3912
- if (childArray.length === 1 && isFileBlockChild(childArray[0])) {
3913
- return childArray[0];
3914
- }
3915
- return /* @__PURE__ */ jsx("p", { children });
3916
- },
3383
+ p: ({ children }) => /* @__PURE__ */ jsx("p", { children }),
3917
3384
  code: ({ children }) => /* @__PURE__ */ jsx("code", { children }),
3918
3385
  pre: ({ children }) => /* @__PURE__ */ jsx("div", { className: "payman-v2-markdown-pre", children: /* @__PURE__ */ jsx("pre", { children }) }),
3919
3386
  ul: ({ children }) => /* @__PURE__ */ jsx("ul", { children }),
@@ -3926,15 +3393,7 @@ function buildComponents(onImageClick, isResolvingRef, onPdfClick, autoOpenPdf)
3926
3393
  em: ({ children }) => /* @__PURE__ */ jsx("em", { children }),
3927
3394
  blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", { children }),
3928
3395
  hr: () => /* @__PURE__ */ jsx("hr", {}),
3929
- a: ({ href, children }) => {
3930
- const url = href ?? "";
3931
- if (onPdfClick && isPdfUrl(url)) {
3932
- const linkText = childrenToText(children).trim();
3933
- const title = getPdfTitleFromUrl(url, linkText);
3934
- return /* @__PURE__ */ jsx(PdfBlockV2, { href: url, title, onOpen: onPdfClick, autoOpen: autoOpenPdf });
3935
- }
3936
- return /* @__PURE__ */ jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children });
3937
- },
3396
+ a: ({ href, children }) => /* @__PURE__ */ jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children }),
3938
3397
  img: ({ src, alt }) => /* @__PURE__ */ jsx(
3939
3398
  MarkdownImageV2,
3940
3399
  {
@@ -3955,15 +3414,13 @@ function MarkdownRendererV2({
3955
3414
  content,
3956
3415
  isStreaming,
3957
3416
  isResolvingImages,
3958
- onImageClick,
3959
- onPdfClick,
3960
- autoOpenPdf
3417
+ onImageClick
3961
3418
  }) {
3962
3419
  const isResolvingRef = useRef(isResolvingImages);
3963
3420
  isResolvingRef.current = isResolvingImages;
3964
3421
  const components = useMemo(
3965
- () => buildComponents(onImageClick, isResolvingRef, onPdfClick, autoOpenPdf),
3966
- [onImageClick, onPdfClick, autoOpenPdf]
3422
+ () => buildComponents(onImageClick, isResolvingRef),
3423
+ [onImageClick]
3967
3424
  );
3968
3425
  return /* @__PURE__ */ jsx(
3969
3426
  "div",
@@ -4332,17 +3789,6 @@ function stripIncompleteImageToken(text) {
4332
3789
  if (/^!\[[^\]]*\]\([^)]*\)/.test(after)) return text;
4333
3790
  return text.slice(0, lastBang);
4334
3791
  }
4335
- function stripIncompleteLinkToken(text) {
4336
- const lastBracket = text.lastIndexOf("[");
4337
- if (lastBracket === -1) return text;
4338
- if (lastBracket > 0 && text[lastBracket - 1] === "!") return text;
4339
- const after = text.slice(lastBracket);
4340
- if (/^\[[^\]]*\]\([^)]*\)/.test(after)) return text;
4341
- return text.slice(0, lastBracket);
4342
- }
4343
- function stripIncompleteMarkdownTokens(text) {
4344
- return stripIncompleteLinkToken(stripIncompleteImageToken(text));
4345
- }
4346
3792
  function getFeedbackState(message) {
4347
3793
  const feedback = message.feedback;
4348
3794
  if (feedback === "up" || feedback === "down") return feedback;
@@ -4366,12 +3812,6 @@ function AssistantMessageV2({
4366
3812
  () => getFeedbackState(message)
4367
3813
  );
4368
3814
  const [reasonModalOpen, setReasonModalOpen] = useState(false);
4369
- const chatContext = useContext(PaymanChatContext);
4370
- const chatContextRef = useRef(chatContext);
4371
- chatContextRef.current = chatContext;
4372
- const handlePdfClick = useCallback((href, title, options) => {
4373
- chatContextRef.current?.openPdfSheet(href, title, options);
4374
- }, []);
4375
3815
  const canSubmitFeedback = !!onSubmitFeedback && !!message.executionId;
4376
3816
  const [toast, setToast] = useState(null);
4377
3817
  const copyResetTimerRef = useRef(null);
@@ -4396,7 +3836,7 @@ function AssistantMessageV2({
4396
3836
  const raw = message.isStreaming ? message.streamingContent || message.content : message.content;
4397
3837
  if (!raw) return "";
4398
3838
  const normalized = raw.replace(/\\n/g, "\n");
4399
- return message.isStreaming ? stripIncompleteMarkdownTokens(normalized) : normalized;
3839
+ return message.isStreaming ? stripIncompleteImageToken(normalized) : normalized;
4400
3840
  })();
4401
3841
  const isThinkingStreaming = !!message.isStreaming && !rawResponseContent && !message.isError;
4402
3842
  const responseTypingEnabled = hasEverStreamed.current && Boolean(rawResponseContent) && !message.isError;
@@ -4566,12 +4006,10 @@ function AssistantMessageV2({
4566
4006
  /* @__PURE__ */ jsx("div", { className: "payman-v2-assistant-msg-content-area", children: displayContent ? /* @__PURE__ */ jsx(
4567
4007
  MarkdownRendererV2,
4568
4008
  {
4569
- content: message.isStreaming && !isCancelled || isResponseTyping ? stripIncompleteMarkdownTokens(displayContent) : displayContent,
4009
+ content: displayContent,
4570
4010
  isStreaming: message.isStreaming && !isCancelled || isResponseTyping,
4571
4011
  isResolvingImages: message.isResolvingImages,
4572
- onImageClick,
4573
- onPdfClick: handlePdfClick,
4574
- autoOpenPdf: hasEverStreamed.current
4012
+ onImageClick
4575
4013
  }
4576
4014
  ) : !isThinkingStreaming ? /* @__PURE__ */ jsx("span", { className: "payman-v2-assistant-msg-placeholder", children: "..." }) : null }),
4577
4015
  isCancelled && message.isStreaming && /* @__PURE__ */ jsxs("div", { className: "payman-v2-assistant-msg-paused", children: [
@@ -4788,6 +4226,7 @@ function VerificationInline({
4788
4226
  const [code, setCode] = useState("");
4789
4227
  const [errored, setErrored] = useState(false);
4790
4228
  const [resendSec, setResendSec] = useState(0);
4229
+ const [hiddenAfterResend, setHiddenAfterResend] = useState(false);
4791
4230
  const lastSubmittedRef = useRef(null);
4792
4231
  const resendTimerRef = useRef(void 0);
4793
4232
  const status = prompt.status;
@@ -4801,6 +4240,9 @@ function VerificationInline({
4801
4240
  lastSubmittedRef.current = null;
4802
4241
  }
4803
4242
  }, [prompt.subAction, prompt.userActionId]);
4243
+ useEffect(() => {
4244
+ setHiddenAfterResend(false);
4245
+ }, [prompt.expirySeconds, prompt.message, prompt.subAction, prompt.userActionId]);
4804
4246
  useEffect(() => {
4805
4247
  if (code.length < codeLen) lastSubmittedRef.current = null;
4806
4248
  }, [code, codeLen]);
@@ -4851,11 +4293,13 @@ function VerificationInline({
4851
4293
  if (locked || resendSec > 0) return;
4852
4294
  setErrored(false);
4853
4295
  setCode("");
4296
+ setHiddenAfterResend(true);
4854
4297
  lastSubmittedRef.current = null;
4855
4298
  try {
4856
4299
  await onResend(prompt.userActionId);
4857
4300
  startResendCooldown();
4858
4301
  } catch {
4302
+ setHiddenAfterResend(false);
4859
4303
  }
4860
4304
  }, [locked, onResend, prompt.userActionId, resendSec, startResendCooldown]);
4861
4305
  const handleCancel = useCallback(() => {
@@ -4863,6 +4307,7 @@ function VerificationInline({
4863
4307
  void onCancel(prompt.userActionId);
4864
4308
  }, [busy, onCancel, prompt.userActionId]);
4865
4309
  const description = prompt.message?.trim() || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
4310
+ if (hiddenAfterResend) return null;
4866
4311
  return /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Verification required", children: [
4867
4312
  /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-head", children: [
4868
4313
  /* @__PURE__ */ jsx(ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
@@ -4958,6 +4403,9 @@ function SchemaFormInline({
4958
4403
  const busy = status === "submitting";
4959
4404
  const stale = status === "stale";
4960
4405
  const locked = busy || stale || expired;
4406
+ const isUserConfirmation = prompt.subAction === "UserConfirmation";
4407
+ const messageFormat = prompt.metadata?.["payman/messageFormat"];
4408
+ const renderMarkdown = messageFormat === "markdown";
4961
4409
  const setValue = (key, value) => {
4962
4410
  setValues((prev) => ({ ...prev, [key]: value }));
4963
4411
  setErrors((prev) => {
@@ -4983,8 +4431,8 @@ function SchemaFormInline({
4983
4431
  /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
4984
4432
  typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
4985
4433
  ] }),
4986
- prompt.message?.trim() && /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: prompt.message }),
4987
- stale ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: "This action has no inputs to fill." }) : /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
4434
+ prompt.message?.trim() && (renderMarkdown ? /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-markdown", children: /* @__PURE__ */ jsx(MarkdownRendererV2, { content: prompt.message }) }) : /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: prompt.message })),
4435
+ stale ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? null : /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
4988
4436
  const widget = classifyField(field);
4989
4437
  const label = field.title || key;
4990
4438
  const required = isRequired(schema, key);
@@ -5057,7 +4505,7 @@ function SchemaFormInline({
5057
4505
  className: "payman-v2-ua-btn payman-v2-ua-btn-primary",
5058
4506
  disabled: locked,
5059
4507
  onClick: handleSubmit,
5060
- children: busy ? "Submitting\u2026" : "Submit"
4508
+ children: busy ? "Submitting\u2026" : isUserConfirmation ? "Confirm" : "Submit"
5061
4509
  }
5062
4510
  ),
5063
4511
  /* @__PURE__ */ jsx(
@@ -5097,26 +4545,51 @@ function useExpiryCountdown(prompt) {
5097
4545
  setSecondsLeft(void 0);
5098
4546
  return;
5099
4547
  }
5100
- setSecondsLeft(initial);
5101
- const id = window.setInterval(() => {
5102
- setSecondsLeft((s) => {
5103
- if (s === void 0) return s;
5104
- if (s <= 1) {
5105
- window.clearInterval(id);
5106
- return 0;
5107
- }
5108
- return s - 1;
5109
- });
5110
- }, 1e3);
5111
- return () => window.clearInterval(id);
4548
+ const deadlineMs = Date.now() + initial * 1e3;
4549
+ let intervalId;
4550
+ const remaining = () => Math.max(0, Math.ceil((deadlineMs - Date.now()) / 1e3));
4551
+ const sync = () => {
4552
+ const next = remaining();
4553
+ setSecondsLeft(next);
4554
+ if (next === 0 && intervalId !== void 0) {
4555
+ window.clearInterval(intervalId);
4556
+ intervalId = void 0;
4557
+ }
4558
+ };
4559
+ sync();
4560
+ intervalId = window.setInterval(sync, 1e3);
4561
+ document.addEventListener("visibilitychange", sync);
4562
+ window.addEventListener("focus", sync);
4563
+ window.addEventListener("pageshow", sync);
4564
+ return () => {
4565
+ if (intervalId !== void 0) window.clearInterval(intervalId);
4566
+ document.removeEventListener("visibilitychange", sync);
4567
+ window.removeEventListener("focus", sync);
4568
+ window.removeEventListener("pageshow", sync);
4569
+ };
5112
4570
  }, [prompt.userActionId, prompt.subAction, initial]);
5113
4571
  const expired = initial !== void 0 && secondsLeft === 0;
5114
4572
  return [secondsLeft, expired];
5115
4573
  }
5116
- function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
4574
+ function UserActionInline({
4575
+ prompt,
4576
+ onSubmit,
4577
+ onCancel,
4578
+ onResend,
4579
+ onExpired
4580
+ }) {
5117
4581
  const [secondsLeft, expired] = useExpiryCountdown(prompt);
4582
+ useEffect(() => {
4583
+ if (expired && prompt.kind !== "notification") onExpired?.();
4584
+ }, [expired, onExpired, prompt.kind]);
5118
4585
  let body;
5119
- if (prompt.kind === "verification") {
4586
+ if (expired && prompt.kind !== "notification") {
4587
+ const note = {
4588
+ id: `${prompt.userActionId}-expired`,
4589
+ message: prompt.kind === "verification" ? "Verification Request Expired" : "User Form Expired"
4590
+ };
4591
+ body = /* @__PURE__ */ jsx(NotificationInline, { notification: note });
4592
+ } else if (prompt.kind === "verification") {
5120
4593
  body = /* @__PURE__ */ jsx(
5121
4594
  VerificationInline,
5122
4595
  {
@@ -5156,10 +4629,23 @@ function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
5156
4629
  }
5157
4630
  var SCROLL_THRESHOLD2 = 100;
5158
4631
  var USER_SCROLL_UP_EPSILON = 4;
4632
+ var PROMPT_KEY_SEPARATOR = "";
4633
+ function getPromptSlotKey(prompt) {
4634
+ return prompt.toolCallId || prompt.userActionId;
4635
+ }
4636
+ function getPromptViewKey(prompt) {
4637
+ return [
4638
+ getPromptSlotKey(prompt),
4639
+ prompt.userActionId,
4640
+ prompt.subAction ?? "",
4641
+ prompt.expirySeconds ?? "",
4642
+ prompt.message ?? ""
4643
+ ].join(PROMPT_KEY_SEPARATOR);
4644
+ }
5159
4645
  var MessageListV2 = forwardRef(
5160
4646
  function MessageListV22({
5161
4647
  messages,
5162
- isStreaming: _isStreaming = false,
4648
+ isStreaming = false,
5163
4649
  onEditUserMessage,
5164
4650
  onRetryUserMessage,
5165
4651
  onImageClick,
@@ -5171,10 +4657,10 @@ var MessageListV2 = forwardRef(
5171
4657
  onSubmitUserAction,
5172
4658
  onCancelUserAction,
5173
4659
  onResendUserAction,
4660
+ onExpireUserAction,
5174
4661
  onDismissNotification,
5175
4662
  onSubmitFeedback,
5176
- typingSpeed = 4,
5177
- sidePanelOpen = false
4663
+ typingSpeed = 4
5178
4664
  }, ref) {
5179
4665
  const noop = useCallback(async () => {
5180
4666
  }, []);
@@ -5182,11 +4668,11 @@ var MessageListV2 = forwardRef(
5182
4668
  const scrollInnerRef = useRef(null);
5183
4669
  const isNearBottomRef = useRef(true);
5184
4670
  const [showScrollBtn, setShowScrollBtn] = useState(false);
4671
+ const [expiredPromptViewState, setExpiredPromptViewState] = useState({});
4672
+ const expiredUserActionIdsRef = useRef(/* @__PURE__ */ new Set());
5185
4673
  const prevCountRef = useRef(messages.length);
5186
4674
  const pauseStickUntilUserMessageRef = useRef(false);
5187
4675
  const followingBottomRef = useRef(true);
5188
- const sidePanelOpenRef = useRef(sidePanelOpen);
5189
- sidePanelOpenRef.current = sidePanelOpen;
5190
4676
  const lastPinAtRef = useRef(0);
5191
4677
  const prevScrollTopRef = useRef(0);
5192
4678
  const getDistanceFromBottom = useCallback(() => {
@@ -5194,6 +4680,92 @@ var MessageListV2 = forwardRef(
5194
4680
  if (!el) return 0;
5195
4681
  return el.scrollHeight - el.scrollTop - el.clientHeight;
5196
4682
  }, []);
4683
+ const messageActivityFingerprint = useMemo(() => {
4684
+ const last = messages[messages.length - 1];
4685
+ const promptFingerprint = (userActionPrompts ?? []).map((prompt) => `${getPromptViewKey(prompt)}:${prompt.status}`).join(PROMPT_KEY_SEPARATOR);
4686
+ const notificationFingerprint = (notifications ?? []).map((notification) => notification.id).join(PROMPT_KEY_SEPARATOR);
4687
+ if (!last) {
4688
+ return [
4689
+ 0,
4690
+ isStreaming ? "streaming" : "idle",
4691
+ promptFingerprint,
4692
+ notificationFingerprint
4693
+ ].join(PROMPT_KEY_SEPARATOR);
4694
+ }
4695
+ return [
4696
+ messages.length,
4697
+ isStreaming ? "streaming" : "idle",
4698
+ last.id,
4699
+ last.role,
4700
+ last.content ?? "",
4701
+ last.isStreaming ? "streaming" : "done",
4702
+ last.streamProgress ?? "",
4703
+ last.steps?.length ?? 0,
4704
+ last.errorDetails ?? "",
4705
+ promptFingerprint,
4706
+ notificationFingerprint
4707
+ ].join(PROMPT_KEY_SEPARATOR);
4708
+ }, [isStreaming, messages, notifications, userActionPrompts]);
4709
+ const handleUserActionExpired = useCallback(
4710
+ (promptKey, userActionId) => {
4711
+ setExpiredPromptViewState((prev) => {
4712
+ if (prev[promptKey]) return prev;
4713
+ return {
4714
+ ...prev,
4715
+ [promptKey]: { baseline: messageActivityFingerprint, hidden: false }
4716
+ };
4717
+ });
4718
+ if (!expiredUserActionIdsRef.current.has(userActionId)) {
4719
+ expiredUserActionIdsRef.current.add(userActionId);
4720
+ void onExpireUserAction?.(userActionId)?.catch(() => {
4721
+ });
4722
+ }
4723
+ },
4724
+ [messageActivityFingerprint, onExpireUserAction]
4725
+ );
4726
+ useEffect(() => {
4727
+ setExpiredPromptViewState((prev) => {
4728
+ let changed = false;
4729
+ const next = {};
4730
+ for (const [key, state] of Object.entries(prev)) {
4731
+ if (!state.hidden && state.baseline !== messageActivityFingerprint) {
4732
+ next[key] = { ...state, hidden: true };
4733
+ changed = true;
4734
+ } else {
4735
+ next[key] = state;
4736
+ }
4737
+ }
4738
+ return changed ? next : prev;
4739
+ });
4740
+ }, [messageActivityFingerprint]);
4741
+ useEffect(() => {
4742
+ const livePromptKeys = new Set((userActionPrompts ?? []).map(getPromptViewKey));
4743
+ const liveUserActionIds = new Set((userActionPrompts ?? []).map((p) => p.userActionId));
4744
+ for (const userActionId of expiredUserActionIdsRef.current) {
4745
+ if (!liveUserActionIds.has(userActionId)) {
4746
+ expiredUserActionIdsRef.current.delete(userActionId);
4747
+ }
4748
+ }
4749
+ setExpiredPromptViewState((prev) => {
4750
+ let changed = false;
4751
+ const next = {};
4752
+ for (const [key, state] of Object.entries(prev)) {
4753
+ if (livePromptKeys.has(key)) {
4754
+ next[key] = state;
4755
+ } else {
4756
+ changed = true;
4757
+ }
4758
+ }
4759
+ return changed ? next : prev;
4760
+ });
4761
+ }, [userActionPrompts]);
4762
+ const visibleUserActionPrompts = useMemo(
4763
+ () => userActionPrompts?.filter((prompt) => {
4764
+ const promptKey = getPromptViewKey(prompt);
4765
+ return !expiredPromptViewState[promptKey]?.hidden;
4766
+ }),
4767
+ [expiredPromptViewState, userActionPrompts]
4768
+ );
5197
4769
  const pinToBottom = useCallback((behavior = "instant") => {
5198
4770
  const el = scrollRef.current;
5199
4771
  if (!el) return;
@@ -5234,25 +4806,17 @@ var MessageListV2 = forwardRef(
5234
4806
  const nearBottom = distance <= SCROLL_THRESHOLD2;
5235
4807
  isNearBottomRef.current = nearBottom;
5236
4808
  setShowScrollBtn(!nearBottom);
4809
+ const sincePin = performance.now() - lastPinAtRef.current;
4810
+ if (sincePin < 250) return;
5237
4811
  const scrolledUp = currentScrollTop < prevScrollTop - USER_SCROLL_UP_EPSILON;
5238
4812
  if (scrolledUp) {
5239
4813
  followingBottomRef.current = false;
5240
4814
  pauseStickUntilUserMessageRef.current = true;
5241
- return;
5242
- }
5243
- const sincePin = performance.now() - lastPinAtRef.current;
5244
- if (sincePin < 250) return;
5245
- if (nearBottom) {
4815
+ } else if (nearBottom) {
5246
4816
  followingBottomRef.current = true;
5247
4817
  pauseStickUntilUserMessageRef.current = false;
5248
4818
  }
5249
4819
  }, [getDistanceFromBottom]);
5250
- const handleWheel = useCallback((e) => {
5251
- if (e.deltaY < 0) {
5252
- followingBottomRef.current = false;
5253
- pauseStickUntilUserMessageRef.current = true;
5254
- }
5255
- }, []);
5256
4820
  useEffect(() => {
5257
4821
  const prevCount = prevCountRef.current;
5258
4822
  prevCountRef.current = messages.length;
@@ -5262,7 +4826,7 @@ var MessageListV2 = forwardRef(
5262
4826
  pauseStickUntilUserMessageRef.current = false;
5263
4827
  followingBottomRef.current = true;
5264
4828
  requestAnimationFrame(() => scrollToBottom());
5265
- } else if (!sidePanelOpenRef.current && !pauseStickUntilUserMessageRef.current && followingBottomRef.current) {
4829
+ } else if (!pauseStickUntilUserMessageRef.current && followingBottomRef.current) {
5266
4830
  requestAnimationFrame(() => scrollToBottom("instant"));
5267
4831
  }
5268
4832
  }
@@ -5271,16 +4835,27 @@ var MessageListV2 = forwardRef(
5271
4835
  const inner = scrollInnerRef.current;
5272
4836
  if (!inner) return;
5273
4837
  const pinIfFollowing = () => {
5274
- if (sidePanelOpenRef.current) return;
5275
4838
  if (pauseStickUntilUserMessageRef.current) return;
5276
4839
  if (!followingBottomRef.current) return;
5277
- if (getDistanceFromBottom() > SCROLL_THRESHOLD2) return;
5278
4840
  pinToBottom("instant");
5279
4841
  };
5280
- const ro = new ResizeObserver(pinIfFollowing);
4842
+ const ro = new ResizeObserver(() => {
4843
+ pinIfFollowing();
4844
+ });
5281
4845
  ro.observe(inner);
5282
- return () => ro.disconnect();
5283
- }, [pinToBottom, getDistanceFromBottom]);
4846
+ const mo = new MutationObserver(() => {
4847
+ pinIfFollowing();
4848
+ });
4849
+ mo.observe(inner, {
4850
+ childList: true,
4851
+ subtree: true,
4852
+ characterData: true
4853
+ });
4854
+ return () => {
4855
+ ro.disconnect();
4856
+ mo.disconnect();
4857
+ };
4858
+ }, [pinToBottom]);
5284
4859
  useEffect(() => {
5285
4860
  if (messages.length > 0) {
5286
4861
  setTimeout(() => scrollToBottom("instant"), 50);
@@ -5292,7 +4867,6 @@ var MessageListV2 = forwardRef(
5292
4867
  {
5293
4868
  ref: scrollRef,
5294
4869
  onScroll: handleScroll,
5295
- onWheel: handleWheel,
5296
4870
  className: "payman-v2-message-scroll payman-v2-scrollbar",
5297
4871
  children: /* @__PURE__ */ jsxs(
5298
4872
  "div",
@@ -5306,7 +4880,6 @@ var MessageListV2 = forwardRef(
5306
4880
  message,
5307
4881
  onEdit: onEditUserMessage,
5308
4882
  onRetry: onRetryUserMessage,
5309
- onImageClick,
5310
4883
  retryDisabled,
5311
4884
  actions: messageActions?.userMessageActions
5312
4885
  }
@@ -5329,16 +4902,20 @@ var MessageListV2 = forwardRef(
5329
4902
  },
5330
4903
  note.id
5331
4904
  )),
5332
- userActionPrompts?.map((prompt) => /* @__PURE__ */ jsx(
5333
- UserActionInline,
5334
- {
5335
- prompt,
5336
- onSubmit: onSubmitUserAction ?? noop,
5337
- onCancel: onCancelUserAction ?? noop,
5338
- onResend: onResendUserAction ?? noop
5339
- },
5340
- prompt.toolCallId || prompt.userActionId
5341
- ))
4905
+ visibleUserActionPrompts?.map((prompt) => {
4906
+ const promptKey = getPromptViewKey(prompt);
4907
+ return /* @__PURE__ */ jsx(
4908
+ UserActionInline,
4909
+ {
4910
+ prompt,
4911
+ onSubmit: onSubmitUserAction ?? noop,
4912
+ onCancel: onCancelUserAction ?? noop,
4913
+ onResend: onResendUserAction ?? noop,
4914
+ onExpired: () => handleUserActionExpired(promptKey, prompt.userActionId)
4915
+ },
4916
+ promptKey
4917
+ );
4918
+ })
5342
4919
  ]
5343
4920
  }
5344
4921
  )
@@ -5368,121 +4945,11 @@ var MessageListV2 = forwardRef(
5368
4945
  ] });
5369
4946
  }
5370
4947
  );
5371
- function FilePreviewModal({ src, name, onClose }) {
5372
- const [isMounted, setIsMounted] = useState(false);
5373
- const [isLoaded, setIsLoaded] = useState(false);
5374
- useEffect(() => {
5375
- setIsMounted(true);
5376
- return () => setIsMounted(false);
5377
- }, []);
5378
- useEffect(() => {
5379
- setIsLoaded(false);
5380
- }, [src]);
5381
- const handleKeyDown = useCallback(
5382
- (e) => {
5383
- if (e.key === "Escape") onClose();
5384
- },
5385
- [onClose]
5386
- );
5387
- useEffect(() => {
5388
- if (!src || typeof document === "undefined") return;
5389
- document.addEventListener("keydown", handleKeyDown);
5390
- const prev = document.body.style.overflow;
5391
- document.body.style.overflow = "hidden";
5392
- return () => {
5393
- document.removeEventListener("keydown", handleKeyDown);
5394
- document.body.style.overflow = prev;
5395
- };
5396
- }, [src, handleKeyDown]);
5397
- if (!isMounted || typeof document === "undefined") return null;
5398
- return createPortal(
5399
- /* @__PURE__ */ jsx(AnimatePresence, { children: src ? /* @__PURE__ */ jsxs(
5400
- motion.div,
5401
- {
5402
- className: "payman-v2-file-preview-overlay",
5403
- initial: { opacity: 0 },
5404
- animate: { opacity: 1 },
5405
- exit: { opacity: 0 },
5406
- transition: { duration: 0.18 },
5407
- onClick: onClose,
5408
- role: "dialog",
5409
- "aria-modal": "true",
5410
- "aria-label": `Preview: ${name}`,
5411
- children: [
5412
- /* @__PURE__ */ jsx(
5413
- "button",
5414
- {
5415
- type: "button",
5416
- className: "payman-v2-file-preview-close",
5417
- "aria-label": "Close preview",
5418
- onClick: (e) => {
5419
- e.stopPropagation();
5420
- onClose();
5421
- },
5422
- children: /* @__PURE__ */ jsx(X, { size: 18, strokeWidth: 2 })
5423
- }
5424
- ),
5425
- /* @__PURE__ */ jsx(
5426
- motion.div,
5427
- {
5428
- className: "payman-v2-file-preview-inner",
5429
- initial: { scale: 0.93, opacity: 0 },
5430
- animate: { scale: isLoaded ? 1 : 0.93, opacity: isLoaded ? 1 : 0 },
5431
- exit: { scale: 0.93, opacity: 0 },
5432
- transition: { duration: 0.2 },
5433
- onClick: (e) => e.stopPropagation(),
5434
- children: /* @__PURE__ */ jsx(
5435
- "img",
5436
- {
5437
- src,
5438
- alt: name,
5439
- className: "payman-v2-file-preview-img",
5440
- draggable: false,
5441
- onLoad: () => setIsLoaded(true)
5442
- }
5443
- )
5444
- }
5445
- )
5446
- ]
5447
- },
5448
- "file-preview"
5449
- ) : null }),
5450
- document.body
5451
- );
5452
- }
5453
- function normalizeExtension(ext) {
5454
- return ext.replace(/^\./, "").toLowerCase();
5455
- }
5456
- function extOf(filename) {
5457
- return filename.split(".").pop()?.toLowerCase() ?? "";
5458
- }
5459
- function isAllowedImage(file, allowedExtensions) {
5460
- const allowed = new Set(allowedExtensions.map(normalizeExtension));
5461
- const ext = extOf(file.name);
5462
- if (allowed.has(ext)) return true;
5463
- if (!file.type.startsWith("image/")) return false;
5464
- const mimeSubtype = file.type.slice("image/".length).toLowerCase();
5465
- return allowed.has(mimeSubtype) || mimeSubtype === "jpeg" && allowed.has("jpg");
5466
- }
5467
- function isAllowedDocument(file, allowedExtensions) {
5468
- const allowed = new Set(allowedExtensions.map(normalizeExtension));
5469
- return allowed.has(extOf(file.name));
5470
- }
5471
- function fileTypeLabel(file, kind) {
5472
- if (kind === "image") return "Image";
5473
- const ext = extOf(file.name);
5474
- return ext ? ext.toUpperCase() : "Document";
5475
- }
5476
4948
  var ChatInputV2 = forwardRef(
5477
4949
  function ChatInputV22({
5478
4950
  onSend,
5479
4951
  disabled = false,
5480
4952
  isStreaming = false,
5481
- isUploadingAttachments = false,
5482
- attachmentsReady = true,
5483
- hasAttachmentUploadErrors = false,
5484
- attachmentUploadStatusById = {},
5485
- uploadedAttachmentPayloads = [],
5486
4953
  placeholder = "Reply...",
5487
4954
  enableVoice = false,
5488
4955
  voiceAvailable = false,
@@ -5496,13 +4963,6 @@ var ChatInputV2 = forwardRef(
5496
4963
  showAttachFileButton = true,
5497
4964
  onUploadImageClick,
5498
4965
  onAttachFileClick,
5499
- allowedImageExtensions = ["png", "jpg", "jpeg", "gif", "webp"],
5500
- allowedFileExtensions = ["pdf", "docx", "xlsx", "xls"],
5501
- maxCount,
5502
- maxFileBytes,
5503
- maxTotalBytes,
5504
- onFilesChange,
5505
- onAttachmentsChange,
5506
4966
  editingMessageId = null,
5507
4967
  onClearEditing,
5508
4968
  analysisMode,
@@ -5516,133 +4976,14 @@ var ChatInputV2 = forwardRef(
5516
4976
  const [selectedCommandIndex, setSelectedCommandIndex] = useState(0);
5517
4977
  const [inlineHint, setInlineHint] = useState(null);
5518
4978
  const [commandMenuDismissed, setCommandMenuDismissed] = useState(false);
5519
- const [attachedFiles, setAttachedFiles] = useState([]);
5520
- const [previewFile, setPreviewFile] = useState(null);
5521
- const [isSending, setIsSending] = useState(false);
5522
4979
  const textareaRef = useRef(null);
5523
4980
  const actionsRef = useRef(null);
5524
- const imageInputRef = useRef(null);
5525
- const fileInputRef = useRef(null);
5526
4981
  const preRecordTextRef = useRef("");
5527
4982
  const voiceTooltipTimerRef = useRef(
5528
4983
  null
5529
4984
  );
5530
4985
  const voiceDraftSyncActiveRef = useRef(false);
5531
- const chatContext = useContext(PaymanChatContext);
5532
- useEffect(() => {
5533
- return () => {
5534
- attachedFiles.forEach((f) => URL.revokeObjectURL(f.objectUrl));
5535
- };
5536
- }, []);
5537
- const notifyAttachmentList = useCallback(
5538
- (files) => {
5539
- onFilesChange?.(files.map((f) => f.file));
5540
- onAttachmentsChange?.(files.map((f) => ({ id: f.id, file: f.file })));
5541
- },
5542
- [onAttachmentsChange, onFilesChange]
5543
- );
5544
- const clearAttachmentsFromInput = useCallback(() => {
5545
- setAttachedFiles((prev) => {
5546
- prev.forEach((f) => URL.revokeObjectURL(f.objectUrl));
5547
- return [];
5548
- });
5549
- setPreviewFile(null);
5550
- onFilesChange?.([]);
5551
- onAttachmentsChange?.([]);
5552
- }, [onAttachmentsChange, onFilesChange]);
5553
- const addFiles = useCallback(
5554
- (incoming, source) => {
5555
- const isImage = source === "image";
5556
- const allowedExtensions = isImage ? allowedImageExtensions : allowedFileExtensions;
5557
- const isAllowed = isImage ? isAllowedImage : isAllowedDocument;
5558
- const kindLabel = isImage ? "image" : "document";
5559
- setAttachedFiles((prev) => {
5560
- const accepted = [];
5561
- let rejectedType = false;
5562
- let hitCountLimit = false;
5563
- let hitFileSizeLimit = false;
5564
- let hitTotalSizeLimit = false;
5565
- let oversizedName = "";
5566
- const currentTotalBytes = prev.reduce(
5567
- (sum, item) => sum + item.file.size,
5568
- 0
5569
- );
5570
- let addedBytes = 0;
5571
- for (const file of incoming) {
5572
- if (!isAllowed(file, allowedExtensions)) {
5573
- rejectedType = true;
5574
- continue;
5575
- }
5576
- if (maxFileBytes != null && file.size > maxFileBytes) {
5577
- hitFileSizeLimit = true;
5578
- oversizedName = file.name;
5579
- continue;
5580
- }
5581
- if (maxTotalBytes != null && currentTotalBytes + addedBytes + file.size > maxTotalBytes) {
5582
- hitTotalSizeLimit = true;
5583
- break;
5584
- }
5585
- if (maxCount != null && prev.length + accepted.length >= maxCount) {
5586
- hitCountLimit = true;
5587
- break;
5588
- }
5589
- accepted.push({
5590
- id: `${Date.now()}-${Math.random()}`,
5591
- file,
5592
- kind: source,
5593
- objectUrl: URL.createObjectURL(file)
5594
- });
5595
- addedBytes += file.size;
5596
- }
5597
- if (rejectedType) {
5598
- setInlineHint(
5599
- `Unsupported ${kindLabel} type. Allowed: ${allowedExtensions.map(normalizeExtension).join(", ")}.`
5600
- );
5601
- } else if (hitFileSizeLimit) {
5602
- setInlineHint(
5603
- maxFileBytes != null ? `"${oversizedName}" exceeds the ${formatAttachmentBytes(maxFileBytes)} per-file limit.` : "File exceeds the maximum allowed size."
5604
- );
5605
- } else if (hitTotalSizeLimit) {
5606
- setInlineHint(
5607
- maxTotalBytes != null ? `Total attachment size would exceed ${formatAttachmentBytes(maxTotalBytes)}.` : "Total attachment size exceeds the limit."
5608
- );
5609
- } else if (hitCountLimit) {
5610
- setInlineHint(
5611
- maxCount === 1 ? "Only 1 attachment is allowed per message." : `Maximum ${maxCount} attachments allowed per message.`
5612
- );
5613
- }
5614
- if (accepted.length === 0) return prev;
5615
- const updated = [...prev, ...accepted];
5616
- notifyAttachmentList(updated);
5617
- return updated;
5618
- });
5619
- },
5620
- [
5621
- allowedFileExtensions,
5622
- allowedImageExtensions,
5623
- maxCount,
5624
- maxFileBytes,
5625
- maxTotalBytes,
5626
- notifyAttachmentList
5627
- ]
5628
- );
5629
- const removeFile = useCallback(
5630
- (id) => {
5631
- setAttachedFiles((prev) => {
5632
- const target = prev.find((f) => f.id === id);
5633
- if (target) URL.revokeObjectURL(target.objectUrl);
5634
- const updated = prev.filter((f) => f.id !== id);
5635
- notifyAttachmentList(updated);
5636
- return updated;
5637
- });
5638
- setPreviewFile((p) => p?.id === id ? null : p);
5639
- },
5640
- [notifyAttachmentList]
5641
- );
5642
- const imageAccept = allowedImageExtensions.map((e) => `.${e.replace(/^\./, "")}`).join(",");
5643
- const fileAccept = allowedFileExtensions.map((e) => `.${e.replace(/^\./, "")}`).join(",");
5644
- const isInputBusy = isSending || isUploadingAttachments;
5645
- const isInputLocked = disabled || isRecording || isInputBusy;
4986
+ const isInputLocked = disabled || isRecording;
5646
4987
  const hasAttachmentOptions = showUploadImageButton || showAttachFileButton;
5647
4988
  const showAttachmentMenuButton = showAttachmentButton && hasAttachmentOptions;
5648
4989
  const showVoiceButton = enableVoice && onVoicePress != null;
@@ -5682,10 +5023,9 @@ var ChatInputV2 = forwardRef(
5682
5023
  const end = message.length;
5683
5024
  textarea.setSelectionRange(end, end);
5684
5025
  });
5685
- },
5686
- clearAttachments: clearAttachmentsFromInput
5026
+ }
5687
5027
  }),
5688
- [disabled, clearAttachmentsFromInput]
5028
+ [disabled]
5689
5029
  );
5690
5030
  useEffect(() => {
5691
5031
  if (!showActions) return;
@@ -5719,23 +5059,8 @@ var ChatInputV2 = forwardRef(
5719
5059
  const separator = base && !base.endsWith(" ") && transcribedText ? " " : "";
5720
5060
  setValue(`${base}${separator}${transcribedText}`);
5721
5061
  }, [isRecording, transcribedText]);
5722
- function uploadStatusLabel(status, file, kind) {
5723
- if (status === "uploading") return "Uploading\u2026";
5724
- if (status === "error") return "Upload failed";
5725
- return fileTypeLabel(file, kind);
5726
- }
5727
- const handleSend = useCallback(async () => {
5728
- const hasText = value.trim().length > 0;
5729
- const hasFiles = attachedFiles.length > 0;
5730
- if (!hasText && !hasFiles || disabled || isSending || isUploadingAttachments) return;
5731
- if (hasFiles && !attachmentsReady) {
5732
- setInlineHint("Waiting for attachments to finish uploading.");
5733
- return;
5734
- }
5735
- if (hasFiles && hasAttachmentUploadErrors) {
5736
- setInlineHint("Remove or re-add attachments that failed to upload.");
5737
- return;
5738
- }
5062
+ const handleSend = useCallback(() => {
5063
+ if (!value.trim() || disabled) return;
5739
5064
  const commandHint = getSlashCommandValidationHint(value);
5740
5065
  if (commandHint) {
5741
5066
  setInlineHint(commandHint);
@@ -5745,38 +5070,15 @@ var ChatInputV2 = forwardRef(
5745
5070
  preRecordTextRef.current = "";
5746
5071
  setInlineHint(null);
5747
5072
  onClearEditing?.();
5748
- const textToSend = value.trim();
5749
- const filesToSend = attachedFiles.map((f) => f.file);
5750
- const attachmentsToSend = [...uploadedAttachmentPayloads];
5073
+ onSend(value.trim());
5751
5074
  setValue("");
5752
- clearAttachmentsFromInput();
5753
5075
  requestAnimationFrame(() => {
5754
5076
  if (textareaRef.current) {
5755
5077
  textareaRef.current.style.height = "auto";
5756
5078
  textareaRef.current.focus();
5757
5079
  }
5758
5080
  });
5759
- setIsSending(true);
5760
- try {
5761
- await onSend(textToSend, filesToSend, attachmentsToSend);
5762
- } catch {
5763
- setInlineHint("Failed to send message. Please try again.");
5764
- } finally {
5765
- setIsSending(false);
5766
- }
5767
- }, [
5768
- value,
5769
- attachedFiles,
5770
- uploadedAttachmentPayloads,
5771
- disabled,
5772
- isSending,
5773
- isUploadingAttachments,
5774
- attachmentsReady,
5775
- hasAttachmentUploadErrors,
5776
- onClearEditing,
5777
- onSend,
5778
- clearAttachmentsFromInput
5779
- ]);
5081
+ }, [value, disabled, onClearEditing, onSend]);
5780
5082
  const selectCommand = useCallback(
5781
5083
  (command) => {
5782
5084
  const insertText = command.insertText ?? `${command.name} `;
@@ -5821,30 +5123,18 @@ var ChatInputV2 = forwardRef(
5821
5123
  }
5822
5124
  if (e.key === "Enter" && !e.shiftKey) {
5823
5125
  e.preventDefault();
5824
- if (isStreaming || isSending || isUploadingAttachments) return;
5825
- void handleSend();
5126
+ if (isStreaming) return;
5127
+ handleSend();
5826
5128
  }
5827
5129
  };
5828
5130
  const handleUploadImageClick = () => {
5829
- imageInputRef.current?.click();
5830
5131
  onUploadImageClick?.();
5831
5132
  setShowActions(false);
5832
5133
  };
5833
5134
  const handleAttachFileClick = () => {
5834
- fileInputRef.current?.click();
5835
5135
  onAttachFileClick?.();
5836
5136
  setShowActions(false);
5837
5137
  };
5838
- const handleImageFilesSelected = (e) => {
5839
- const files = Array.from(e.target.files ?? []);
5840
- if (files.length) addFiles(files, "image");
5841
- e.target.value = "";
5842
- };
5843
- const handleDocFilesSelected = (e) => {
5844
- const files = Array.from(e.target.files ?? []);
5845
- if (files.length) addFiles(files, "file");
5846
- e.target.value = "";
5847
- };
5848
5138
  const hideVoiceTooltip = useCallback(() => {
5849
5139
  if (voiceTooltipTimerRef.current) {
5850
5140
  clearTimeout(voiceTooltipTimerRef.current);
@@ -5872,39 +5162,9 @@ var ChatInputV2 = forwardRef(
5872
5162
  }
5873
5163
  onVoicePress();
5874
5164
  };
5875
- const canSend = (value.trim().length > 0 || attachedFiles.length > 0) && !disabled;
5876
- const sendDisabled = !canSend || isStreaming || isUploadingAttachments || !attachmentsReady || hasAttachmentUploadErrors || isSending;
5165
+ const canSend = value.trim().length > 0 && !disabled;
5166
+ const sendDisabled = !canSend || isStreaming;
5877
5167
  return /* @__PURE__ */ jsxs("div", { className: "payman-v2-input-container", children: [
5878
- /* @__PURE__ */ jsx(
5879
- "input",
5880
- {
5881
- ref: imageInputRef,
5882
- type: "file",
5883
- accept: imageAccept,
5884
- multiple: maxCount == null || maxCount > 1,
5885
- style: { display: "none" },
5886
- onChange: handleImageFilesSelected
5887
- }
5888
- ),
5889
- /* @__PURE__ */ jsx(
5890
- "input",
5891
- {
5892
- ref: fileInputRef,
5893
- type: "file",
5894
- accept: fileAccept,
5895
- multiple: maxCount == null || maxCount > 1,
5896
- style: { display: "none" },
5897
- onChange: handleDocFilesSelected
5898
- }
5899
- ),
5900
- /* @__PURE__ */ jsx(
5901
- FilePreviewModal,
5902
- {
5903
- src: previewFile?.kind === "image" ? previewFile.objectUrl : null,
5904
- name: previewFile?.file.name ?? "",
5905
- onClose: () => setPreviewFile(null)
5906
- }
5907
- ),
5908
5168
  /* @__PURE__ */ jsx(AnimatePresence, { children: showCommandSuggestions && /* @__PURE__ */ jsx(
5909
5169
  motion.div,
5910
5170
  {
@@ -5943,115 +5203,6 @@ var ChatInputV2 = forwardRef(
5943
5203
  ),
5944
5204
  children: [
5945
5205
  /* @__PURE__ */ jsxs("div", { className: "payman-v2-input-body", children: [
5946
- /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: attachedFiles.length > 0 && /* @__PURE__ */ jsx(
5947
- motion.div,
5948
- {
5949
- initial: { opacity: 0, height: 0 },
5950
- animate: { opacity: 1, height: "auto" },
5951
- exit: { opacity: 0, height: 0 },
5952
- transition: { duration: 0.15, ease: "easeOut" },
5953
- className: "payman-v2-file-preview",
5954
- children: attachedFiles.map((af) => {
5955
- const uploadStatus = attachmentUploadStatusById[af.id];
5956
- const isUploadingFile = uploadStatus === "uploading";
5957
- const hasUploadError = uploadStatus === "error";
5958
- return /* @__PURE__ */ jsxs("div", { className: "payman-v2-file-preview-item", children: [
5959
- af.kind === "image" ? /* @__PURE__ */ jsxs(
5960
- "button",
5961
- {
5962
- type: "button",
5963
- className: cn(
5964
- "payman-v2-file-preview-shell payman-v2-file-preview-shell-clickable",
5965
- hasUploadError && "payman-v2-file-preview-shell-error"
5966
- ),
5967
- onClick: () => setPreviewFile(af),
5968
- disabled: isUploadingFile,
5969
- "aria-label": `Preview ${af.file.name}`,
5970
- children: [
5971
- /* @__PURE__ */ jsxs("div", { className: "payman-v2-file-preview-thumb", children: [
5972
- /* @__PURE__ */ jsx(
5973
- "img",
5974
- {
5975
- src: af.objectUrl,
5976
- alt: "",
5977
- draggable: false
5978
- }
5979
- ),
5980
- isUploadingFile && /* @__PURE__ */ jsx("div", { className: "payman-v2-file-preview-uploading", children: /* @__PURE__ */ jsx(Loader2, { size: 14, className: "payman-v2-spin" }) })
5981
- ] }),
5982
- /* @__PURE__ */ jsxs("div", { className: "payman-v2-file-preview-info", children: [
5983
- /* @__PURE__ */ jsx("p", { className: "payman-v2-file-preview-name", children: af.file.name }),
5984
- /* @__PURE__ */ jsx("p", { className: "payman-v2-file-preview-type", children: uploadStatusLabel(uploadStatus, af.file, af.kind) })
5985
- ] })
5986
- ]
5987
- }
5988
- ) : isPdfFile(af.file) ? /* @__PURE__ */ jsxs(
5989
- "button",
5990
- {
5991
- type: "button",
5992
- className: cn(
5993
- "payman-v2-file-preview-shell payman-v2-file-preview-shell-clickable",
5994
- hasUploadError && "payman-v2-file-preview-shell-error"
5995
- ),
5996
- onClick: () => chatContext?.openPdfSheet(
5997
- af.objectUrl,
5998
- af.file.name
5999
- ),
6000
- disabled: isUploadingFile,
6001
- "aria-label": `Preview ${af.file.name}`,
6002
- children: [
6003
- /* @__PURE__ */ jsx("div", { className: "payman-v2-file-preview-thumb", children: isUploadingFile ? /* @__PURE__ */ jsx(Loader2, { size: 16, className: "payman-v2-spin payman-v2-file-preview-doc-icon" }) : /* @__PURE__ */ jsx(
6004
- FileText,
6005
- {
6006
- size: 16,
6007
- strokeWidth: 1.75,
6008
- className: "payman-v2-file-preview-doc-icon"
6009
- }
6010
- ) }),
6011
- /* @__PURE__ */ jsxs("div", { className: "payman-v2-file-preview-info", children: [
6012
- /* @__PURE__ */ jsx("p", { className: "payman-v2-file-preview-name", children: af.file.name }),
6013
- /* @__PURE__ */ jsx("p", { className: "payman-v2-file-preview-type", children: uploadStatusLabel(uploadStatus, af.file, af.kind) })
6014
- ] })
6015
- ]
6016
- }
6017
- ) : /* @__PURE__ */ jsxs(
6018
- "div",
6019
- {
6020
- className: cn(
6021
- "payman-v2-file-preview-shell",
6022
- hasUploadError && "payman-v2-file-preview-shell-error"
6023
- ),
6024
- children: [
6025
- /* @__PURE__ */ jsx("div", { className: "payman-v2-file-preview-thumb", children: isUploadingFile ? /* @__PURE__ */ jsx(Loader2, { size: 16, className: "payman-v2-spin payman-v2-file-preview-doc-icon" }) : /* @__PURE__ */ jsx(
6026
- FileText,
6027
- {
6028
- size: 16,
6029
- strokeWidth: 1.75,
6030
- className: "payman-v2-file-preview-doc-icon"
6031
- }
6032
- ) }),
6033
- /* @__PURE__ */ jsxs("div", { className: "payman-v2-file-preview-info", children: [
6034
- /* @__PURE__ */ jsx("p", { className: "payman-v2-file-preview-name", children: af.file.name }),
6035
- /* @__PURE__ */ jsx("p", { className: "payman-v2-file-preview-type", children: uploadStatusLabel(uploadStatus, af.file, af.kind) })
6036
- ] })
6037
- ]
6038
- }
6039
- ),
6040
- /* @__PURE__ */ jsx(
6041
- "button",
6042
- {
6043
- type: "button",
6044
- className: "payman-v2-file-preview-remove",
6045
- onClick: () => removeFile(af.id),
6046
- "aria-label": `Remove ${af.file.name}`,
6047
- children: /* @__PURE__ */ jsx(X, { size: 12, strokeWidth: 2.5 })
6048
- }
6049
- )
6050
- ] }, af.id);
6051
- })
6052
- },
6053
- "file-preview"
6054
- ) }),
6055
5206
  /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: editingMessageId && /* @__PURE__ */ jsxs(
6056
5207
  motion.div,
6057
5208
  {
@@ -6283,13 +5434,13 @@ var ChatInputV2 = forwardRef(
6283
5434
  "button",
6284
5435
  {
6285
5436
  type: "button",
6286
- onClick: () => void handleSend(),
5437
+ onClick: handleSend,
6287
5438
  disabled: sendDisabled,
6288
5439
  className: cn(
6289
5440
  "payman-v2-input-send-btn",
6290
5441
  sendDisabled && "payman-v2-input-send-btn-disabled"
6291
5442
  ),
6292
- "aria-label": isUploadingAttachments || isSending ? "Uploading attachments" : "Send message",
5443
+ "aria-label": "Send message",
6293
5444
  children: /* @__PURE__ */ jsx(
6294
5445
  ArrowUp,
6295
5446
  {
@@ -6824,389 +5975,6 @@ function TimelineBars({
6824
5975
  )
6825
5976
  ] });
6826
5977
  }
6827
-
6828
- // src/utils/pdfPreview.ts
6829
- var PdfPreviewError = class extends Error {
6830
- constructor(kind, title, userMessage) {
6831
- super(userMessage);
6832
- __publicField(this, "kind");
6833
- __publicField(this, "title");
6834
- __publicField(this, "userMessage");
6835
- this.name = "PdfPreviewError";
6836
- this.kind = kind;
6837
- this.title = title;
6838
- this.userMessage = userMessage;
6839
- }
6840
- };
6841
- function classifyErrorBody(body, status) {
6842
- const text = body.replace(/\s+/g, " ").trim();
6843
- if (/signed expiry time|must be after signed start time|expired|expir/i.test(text)) {
6844
- return new PdfPreviewError(
6845
- "expired",
6846
- "Link expired",
6847
- "This download link has expired. Ask the assistant to generate a new copy of the document."
6848
- );
6849
- }
6850
- if (/AuthenticationFailed|authorization header|formed correctly including the signature/i.test(
6851
- text
6852
- )) {
6853
- return new PdfPreviewError(
6854
- "forbidden",
6855
- "Link no longer valid",
6856
- "This preview link is no longer valid. Request a fresh download link and try again."
6857
- );
6858
- }
6859
- if (status === 404 || /BlobNotFound|ResourceNotFound|The specified blob does not exist/i.test(text)) {
6860
- return new PdfPreviewError(
6861
- "not_found",
6862
- "Document not found",
6863
- "The file is no longer available. It may have been removed or the link is incorrect."
6864
- );
6865
- }
6866
- if (status === 403) {
6867
- return new PdfPreviewError(
6868
- "forbidden",
6869
- "Can't open document",
6870
- "You may not have access to this file, or the link has expired."
6871
- );
6872
- }
6873
- if (status === 401) {
6874
- return new PdfPreviewError(
6875
- "forbidden",
6876
- "Access denied",
6877
- "This preview link could not be verified. Request a new download link."
6878
- );
6879
- }
6880
- return new PdfPreviewError(
6881
- "unknown",
6882
- "Can't preview document",
6883
- "Something went wrong while opening this file. Try downloading it or request a new link."
6884
- );
6885
- }
6886
- function normalizePdfPreviewError(error) {
6887
- if (error instanceof PdfPreviewError) return error;
6888
- if (error instanceof TypeError) {
6889
- return new PdfPreviewError(
6890
- "network",
6891
- "Connection problem",
6892
- "Could not load the document. Check your connection and try again."
6893
- );
6894
- }
6895
- return new PdfPreviewError(
6896
- "unknown",
6897
- "Can't preview document",
6898
- "Something went wrong while opening this file. Try downloading it or request a new link."
6899
- );
6900
- }
6901
- async function assertPdfBlob(blob, status) {
6902
- if (!blob.size) {
6903
- return Promise.reject(
6904
- new PdfPreviewError(
6905
- "empty",
6906
- "Document unavailable",
6907
- "The file response was empty. Request a new download link and try again."
6908
- )
6909
- );
6910
- }
6911
- const head = await blob.slice(0, Math.min(blob.size, 1024)).text();
6912
- if (head.startsWith("%PDF")) {
6913
- return blob;
6914
- }
6915
- throw classifyErrorBody(head, status);
6916
- }
6917
- async function fetchPdfBlob(src) {
6918
- let response;
6919
- try {
6920
- response = await fetch(src, {
6921
- method: "GET",
6922
- headers: { Accept: "application/pdf,*/*" }
6923
- });
6924
- } catch {
6925
- throw new PdfPreviewError(
6926
- "network",
6927
- "Connection problem",
6928
- "Could not load the document. Check your connection and try again."
6929
- );
6930
- }
6931
- const blob = await response.blob();
6932
- if (!response.ok) {
6933
- const body = await blob.text().catch(() => "");
6934
- throw classifyErrorBody(body, response.status);
6935
- }
6936
- return assertPdfBlob(blob, response.status);
6937
- }
6938
- var MIN_WIDTH = 320;
6939
- var MAX_WIDTH_RATIO = 0.6;
6940
- var DEFAULT_WIDTH = 520;
6941
- var SPRING = { type: "spring", stiffness: 340, damping: 34, mass: 0.85 };
6942
- var SHEET_EXIT = { duration: 0.22, ease: [0.4, 0, 1, 1] };
6943
- function pdfDownloadName(title) {
6944
- const base = title.trim() || "document";
6945
- return base.toLowerCase().endsWith(".pdf") ? base : `${base}.pdf`;
6946
- }
6947
- function clampSplitWidth(w) {
6948
- const maxW = Math.floor(window.innerWidth * MAX_WIDTH_RATIO);
6949
- return Math.max(MIN_WIDTH, Math.min(maxW, w));
6950
- }
6951
- function PdfSheetV2({
6952
- src,
6953
- title,
6954
- onClose,
6955
- mode = "split"
6956
- }) {
6957
- return /* @__PURE__ */ jsx(AnimatePresence, { children: src && /* @__PURE__ */ jsx(
6958
- PdfSheetPanel,
6959
- {
6960
- src,
6961
- title,
6962
- onClose,
6963
- mode
6964
- },
6965
- "pdf-panel"
6966
- ) });
6967
- }
6968
- function PdfSheetPanel({
6969
- src,
6970
- title,
6971
- onClose,
6972
- mode
6973
- }) {
6974
- const isSheet = mode === "sheet";
6975
- const [isLoaded, setIsLoaded] = useState(false);
6976
- const [isDownloading, setIsDownloading] = useState(false);
6977
- const [previewUrl, setPreviewUrl] = useState(null);
6978
- const [previewError, setPreviewError] = useState(null);
6979
- const [loadAttempt, setLoadAttempt] = useState(0);
6980
- const blobRef = useRef(null);
6981
- const objectUrlRef = useRef(null);
6982
- const [panelWidth, setPanelWidth] = useState(DEFAULT_WIDTH);
6983
- const [splitOpened, setSplitOpened] = useState(false);
6984
- const [isDragging, setIsDragging] = useState(false);
6985
- useEffect(() => {
6986
- setIsLoaded(false);
6987
- setPreviewUrl(null);
6988
- setPreviewError(null);
6989
- setPanelWidth(DEFAULT_WIDTH);
6990
- setSplitOpened(false);
6991
- blobRef.current = null;
6992
- if (objectUrlRef.current) {
6993
- URL.revokeObjectURL(objectUrlRef.current);
6994
- objectUrlRef.current = null;
6995
- }
6996
- let cancelled = false;
6997
- const loadPreview = async () => {
6998
- try {
6999
- const blob = await fetchPdfBlob(src);
7000
- if (cancelled) return;
7001
- blobRef.current = blob;
7002
- const objectUrl = URL.createObjectURL(blob);
7003
- objectUrlRef.current = objectUrl;
7004
- setPreviewUrl(objectUrl);
7005
- } catch (error) {
7006
- if (!cancelled) {
7007
- setPreviewError(normalizePdfPreviewError(error));
7008
- }
7009
- }
7010
- };
7011
- void loadPreview();
7012
- return () => {
7013
- cancelled = true;
7014
- if (objectUrlRef.current) {
7015
- URL.revokeObjectURL(objectUrlRef.current);
7016
- objectUrlRef.current = null;
7017
- }
7018
- blobRef.current = null;
7019
- };
7020
- }, [src, loadAttempt]);
7021
- useEffect(() => {
7022
- if (isSheet) return;
7023
- const onWindowResize = () => setPanelWidth((size) => clampSplitWidth(size));
7024
- window.addEventListener("resize", onWindowResize);
7025
- return () => window.removeEventListener("resize", onWindowResize);
7026
- }, [isSheet]);
7027
- const handleKeyDown = useCallback(
7028
- (e) => {
7029
- if (e.key === "Escape") onClose();
7030
- },
7031
- [onClose]
7032
- );
7033
- useEffect(() => {
7034
- document.addEventListener("keydown", handleKeyDown);
7035
- return () => document.removeEventListener("keydown", handleKeyDown);
7036
- }, [handleKeyDown]);
7037
- const canDownload = isLoaded && previewUrl != null && !previewError;
7038
- const handleDownload = async () => {
7039
- const filename = pdfDownloadName(title);
7040
- setIsDownloading(true);
7041
- try {
7042
- const blob = blobRef.current ?? await fetchPdfBlob(src);
7043
- const objectUrl = URL.createObjectURL(blob);
7044
- const a = document.createElement("a");
7045
- a.href = objectUrl;
7046
- a.download = filename;
7047
- a.target = "_blank";
7048
- a.rel = "noopener noreferrer";
7049
- document.body.appendChild(a);
7050
- a.click();
7051
- a.remove();
7052
- URL.revokeObjectURL(objectUrl);
7053
- } catch (error) {
7054
- setPreviewError(normalizePdfPreviewError(error));
7055
- } finally {
7056
- setIsDownloading(false);
7057
- }
7058
- };
7059
- const handleRetry = () => {
7060
- setPreviewError(null);
7061
- setIsLoaded(false);
7062
- setPreviewUrl(null);
7063
- setLoadAttempt((attempt) => attempt + 1);
7064
- };
7065
- const handleOpenInNewTab = () => {
7066
- window.open(src, "_blank", "noopener,noreferrer");
7067
- };
7068
- const onResizeMouseDown = (e) => {
7069
- if (e.button !== 0) return;
7070
- e.preventDefault();
7071
- const startX = e.clientX;
7072
- const startW = panelWidth;
7073
- setIsDragging(true);
7074
- const onMove = (ev) => {
7075
- setPanelWidth(clampSplitWidth(startW + (startX - ev.clientX)));
7076
- };
7077
- const onUp = () => {
7078
- setIsDragging(false);
7079
- document.removeEventListener("mousemove", onMove);
7080
- document.removeEventListener("mouseup", onUp);
7081
- };
7082
- document.addEventListener("mousemove", onMove);
7083
- document.addEventListener("mouseup", onUp);
7084
- };
7085
- return /* @__PURE__ */ jsxs(
7086
- motion.div,
7087
- {
7088
- className: cn(
7089
- "payman-v2-root payman-v2-pdf-sheet-panel",
7090
- isSheet && "payman-v2-pdf-sheet-panel--sheet"
7091
- ),
7092
- style: { minWidth: 0 },
7093
- initial: isSheet ? { x: "100%", opacity: 0 } : { width: 0, opacity: 0 },
7094
- animate: isSheet ? { x: 0, opacity: 1 } : { width: panelWidth, opacity: 1 },
7095
- exit: isSheet ? { x: "100%", opacity: 0, transition: { x: SHEET_EXIT, opacity: SHEET_EXIT } } : { width: 0, opacity: 0, transition: { width: SHEET_EXIT, opacity: SHEET_EXIT } },
7096
- transition: isSheet ? { x: SPRING, opacity: SPRING } : {
7097
- width: splitOpened ? { duration: 0 } : SPRING,
7098
- opacity: SPRING
7099
- },
7100
- onAnimationComplete: () => {
7101
- if (!isSheet && !splitOpened) setSplitOpened(true);
7102
- },
7103
- children: [
7104
- !isSheet && /* @__PURE__ */ jsx(
7105
- "div",
7106
- {
7107
- className: "payman-v2-pdf-sheet-resize-handle",
7108
- onMouseDown: onResizeMouseDown,
7109
- "aria-hidden": "true",
7110
- children: /* @__PURE__ */ jsx("div", { className: "payman-v2-pdf-sheet-resize-grip" })
7111
- }
7112
- ),
7113
- /* @__PURE__ */ jsxs("div", { className: "payman-v2-pdf-sheet-header", children: [
7114
- /* @__PURE__ */ jsxs("div", { className: "payman-v2-pdf-sheet-header-left", children: [
7115
- /* @__PURE__ */ jsx("div", { className: "payman-v2-pdf-sheet-file-icon", children: /* @__PURE__ */ jsx(FileText, { size: 14, strokeWidth: 1.75 }) }),
7116
- /* @__PURE__ */ jsx("span", { className: "payman-v2-pdf-sheet-title", title, children: title || "Document" })
7117
- ] }),
7118
- /* @__PURE__ */ jsxs("div", { className: "payman-v2-pdf-sheet-header-actions", children: [
7119
- /* @__PURE__ */ jsxs(
7120
- "button",
7121
- {
7122
- type: "button",
7123
- className: "payman-v2-pdf-sheet-download-btn",
7124
- "aria-label": "Download PDF",
7125
- disabled: !canDownload || isDownloading,
7126
- onClick: () => void handleDownload(),
7127
- children: [
7128
- isDownloading ? /* @__PURE__ */ jsx(Loader2, { size: 13, strokeWidth: 2, style: { animation: "payman-v2-spin 0.65s linear infinite" } }) : /* @__PURE__ */ jsx(Download, { size: 13, strokeWidth: 2 }),
7129
- /* @__PURE__ */ jsx("span", { children: isDownloading ? "Downloading\u2026" : "Download" })
7130
- ]
7131
- }
7132
- ),
7133
- /* @__PURE__ */ jsx(
7134
- "button",
7135
- {
7136
- type: "button",
7137
- className: "payman-v2-pdf-sheet-close-btn",
7138
- "aria-label": "Close preview",
7139
- onClick: (e) => {
7140
- e.stopPropagation();
7141
- onClose();
7142
- },
7143
- children: /* @__PURE__ */ jsx(X, { size: 15, strokeWidth: 2.25 })
7144
- }
7145
- )
7146
- ] })
7147
- ] }),
7148
- /* @__PURE__ */ jsx("div", { className: "payman-v2-pdf-sheet-body", children: previewError ? /* @__PURE__ */ jsxs("div", { className: "payman-v2-pdf-sheet-error", role: "alert", children: [
7149
- /* @__PURE__ */ jsx("div", { className: "payman-v2-pdf-sheet-error-icon", children: /* @__PURE__ */ jsx(AlertCircle, { size: 22, strokeWidth: 1.75 }) }),
7150
- /* @__PURE__ */ jsx("p", { className: "payman-v2-pdf-sheet-error-title", children: previewError.title }),
7151
- /* @__PURE__ */ jsx("p", { className: "payman-v2-pdf-sheet-error-message", children: previewError.userMessage }),
7152
- /* @__PURE__ */ jsxs("div", { className: "payman-v2-pdf-sheet-error-actions", children: [
7153
- previewError.kind === "network" && /* @__PURE__ */ jsxs(
7154
- "button",
7155
- {
7156
- type: "button",
7157
- className: "payman-v2-pdf-sheet-error-btn",
7158
- onClick: handleRetry,
7159
- children: [
7160
- /* @__PURE__ */ jsx(RefreshCw, { size: 14, strokeWidth: 2 }),
7161
- /* @__PURE__ */ jsx("span", { children: "Try again" })
7162
- ]
7163
- }
7164
- ),
7165
- (previewError.kind === "network" || previewError.kind === "unknown") && /* @__PURE__ */ jsxs(
7166
- "button",
7167
- {
7168
- type: "button",
7169
- className: "payman-v2-pdf-sheet-error-btn payman-v2-pdf-sheet-error-btn-secondary",
7170
- onClick: handleOpenInNewTab,
7171
- children: [
7172
- /* @__PURE__ */ jsx(ExternalLink, { size: 14, strokeWidth: 2 }),
7173
- /* @__PURE__ */ jsx("span", { children: "Open link" })
7174
- ]
7175
- }
7176
- )
7177
- ] })
7178
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
7179
- (!isLoaded || !previewUrl) && /* @__PURE__ */ jsxs("div", { className: "payman-v2-pdf-sheet-loading", children: [
7180
- /* @__PURE__ */ jsx(
7181
- Loader2,
7182
- {
7183
- size: 20,
7184
- strokeWidth: 2,
7185
- style: { animation: "payman-v2-spin 0.65s linear infinite", color: "var(--payman-v2-text-3)" }
7186
- }
7187
- ),
7188
- /* @__PURE__ */ jsx("p", { className: "payman-v2-pdf-sheet-loading-text", children: previewUrl ? "Preparing preview\u2026" : "Opening document\u2026" })
7189
- ] }),
7190
- previewUrl && /* @__PURE__ */ jsx(
7191
- "iframe",
7192
- {
7193
- src: previewUrl,
7194
- title: title || "PDF Preview",
7195
- className: "payman-v2-pdf-sheet-iframe",
7196
- style: {
7197
- opacity: isLoaded ? 1 : 0,
7198
- transition: "opacity 0.3s ease",
7199
- pointerEvents: isDragging ? "none" : "auto"
7200
- },
7201
- onLoad: () => setIsLoaded(true)
7202
- },
7203
- previewUrl
7204
- )
7205
- ] }) })
7206
- ]
7207
- }
7208
- );
7209
- }
7210
5978
  var DEFAULT_USER_ACTION_STATE = {
7211
5979
  prompts: [],
7212
5980
  notifications: []
@@ -7301,8 +6069,7 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
7301
6069
  onLoadMoreMessages,
7302
6070
  isLoadingMoreMessages = false,
7303
6071
  hasMoreMessages = false,
7304
- chat,
7305
- attachmentUpload
6072
+ chat
7306
6073
  }, ref) {
7307
6074
  const [inputValue, setInputValue] = useState("");
7308
6075
  const prevInputValueRef = useRef(inputValue);
@@ -7356,6 +6123,7 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
7356
6123
  const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
7357
6124
  const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
7358
6125
  const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
6126
+ const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
7359
6127
  const dismissNotification = chat.dismissNotification ?? NOOP;
7360
6128
  const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
7361
6129
  const {
@@ -7381,19 +6149,6 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
7381
6149
  }
7382
6150
  }
7383
6151
  );
7384
- const [pdfSheet, setPdfSheet] = useState(null);
7385
- const autoOpenedPdfHrefsRef = useRef(/* @__PURE__ */ new Set());
7386
- const pdfPreviewMode = config.pdfPreviewMode ?? "split";
7387
- const openPdfSheet = useCallback((href, title, options) => {
7388
- if (options?.auto) {
7389
- if (autoOpenedPdfHrefsRef.current.has(href)) return;
7390
- autoOpenedPdfHrefsRef.current.add(href);
7391
- }
7392
- setPdfSheet({ href, title });
7393
- }, []);
7394
- const closePdfSheet = useCallback(() => {
7395
- setPdfSheet(null);
7396
- }, []);
7397
6152
  const contextValue = useMemo(
7398
6153
  () => ({
7399
6154
  resetSession,
@@ -7402,8 +6157,7 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
7402
6157
  cancelStream,
7403
6158
  getSessionId,
7404
6159
  getMessages,
7405
- isWaitingForResponse,
7406
- openPdfSheet
6160
+ isWaitingForResponse
7407
6161
  }),
7408
6162
  [
7409
6163
  resetSession,
@@ -7412,8 +6166,7 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
7412
6166
  cancelStream,
7413
6167
  getSessionId,
7414
6168
  getMessages,
7415
- isWaitingForResponse,
7416
- openPdfSheet
6169
+ isWaitingForResponse
7417
6170
  ]
7418
6171
  );
7419
6172
  const {
@@ -7469,10 +6222,7 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
7469
6222
  setInputValue("");
7470
6223
  setLightboxSrc(null);
7471
6224
  setLightboxAlt("");
7472
- setPdfSheet(null);
7473
- autoOpenedPdfHrefsRef.current.clear();
7474
6225
  chatInputV2Ref.current?.setDraft("");
7475
- chatInputV2Ref.current?.clearAttachments();
7476
6226
  clearTranscript();
7477
6227
  if (isRecording) {
7478
6228
  stopRecording();
@@ -7523,20 +6273,14 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
7523
6273
  emptyStateComponent,
7524
6274
  showResetSession = false,
7525
6275
  enableDeepModeToggle = true,
6276
+ showAttachmentButton = true,
6277
+ showUploadImageButton = true,
6278
+ showAttachFileButton = true,
7526
6279
  messageActions: messageActionsConfig,
7527
6280
  enableSlashCommands = true,
7528
6281
  slashCommands: slashCommandsConfig,
7529
6282
  commandPermissions
7530
6283
  } = config;
7531
- const attachmentSettings = useMemo(
7532
- () => resolveChatAttachmentConfig(config),
7533
- [
7534
- config.attachments,
7535
- config.showAttachmentButton,
7536
- config.showUploadImageButton,
7537
- config.showAttachFileButton
7538
- ]
7539
- );
7540
6284
  const messageActions = useMemo(
7541
6285
  () => ({
7542
6286
  userMessageActions: {
@@ -7624,22 +6368,11 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
7624
6368
  };
7625
6369
  const userActionPrompts = isUserActionSupported ? userActionState.prompts : void 0;
7626
6370
  const notifications = userActionState.notifications;
7627
- const handleAttachmentsChange = useCallback(
7628
- (attachments) => {
7629
- attachmentUpload.syncAttachments(attachments);
7630
- },
7631
- [attachmentUpload]
7632
- );
7633
- const handleV2Send = (text, files = [], attachments = []) => {
6371
+ const handleV2Send = (text) => {
7634
6372
  if (isRecording) stopRecording();
7635
- if ((text.trim() || files.length > 0) && !disableInput && isSessionParamsConfigured) {
7636
- if (files.length > 0 && attachments.length === 0) return;
6373
+ if (text.trim() && !disableInput && isSessionParamsConfigured) {
7637
6374
  setEditingMessageId(null);
7638
- void sendMessage(text.trim(), {
7639
- analysisMode: effectiveAnalysisMode,
7640
- attachments,
7641
- files
7642
- });
6375
+ void sendMessage(text.trim(), { analysisMode: effectiveAnalysisMode });
7643
6376
  }
7644
6377
  };
7645
6378
  const handleVoicePress = useCallback(async () => {
@@ -7687,214 +6420,156 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
7687
6420
  style,
7688
6421
  children: [
7689
6422
  children,
7690
- /* @__PURE__ */ jsxs(
7691
- "div",
6423
+ /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsx(
6424
+ motion.div,
7692
6425
  {
7693
- className: cn(
7694
- "payman-v2-split-layout",
7695
- pdfPreviewMode === "sheet" && "payman-v2-split-layout--sheet"
7696
- ),
7697
- children: [
7698
- /* @__PURE__ */ jsx("div", { className: "payman-v2-chat-column", children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsx(
7699
- motion.div,
7700
- {
7701
- initial: { opacity: 1 },
7702
- exit: { opacity: 0 },
7703
- transition: { duration: 0.3 },
7704
- className: "payman-v2-chat-layout",
7705
- style: { justifyContent: "center", alignItems: "center" },
7706
- children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", flex: 1, width: "100%" }, children: [
7707
- /* @__PURE__ */ jsx(
7708
- MessageList,
7709
- {
7710
- messages,
7711
- isLoading: false,
7712
- emptyStateText,
7713
- showEmptyStateIcon,
7714
- emptyStateComponent,
7715
- layout,
7716
- showTimestamps,
7717
- stage: config.stage || "DEVELOPMENT",
7718
- animated,
7719
- showAgentName,
7720
- agentName,
7721
- showAvatars,
7722
- showUserAvatar,
7723
- showAssistantAvatar,
7724
- showExecutionSteps,
7725
- showStreamingDot,
7726
- streamingStepsText,
7727
- completedStepsText,
7728
- onExecutionTraceClick,
7729
- onLoadMoreMessages,
7730
- isLoadingMoreMessages,
7731
- hasMoreMessages
7732
- }
7733
- ),
7734
- /* @__PURE__ */ jsx(
7735
- motion.div,
7736
- {
7737
- initial: { opacity: 0, y: 12 },
7738
- animate: { opacity: 1, y: 0 },
7739
- transition: { delay: 0.2, duration: 0.4, ease: [0.25, 0.46, 0.45, 0.94] },
7740
- style: { width: "100%" },
7741
- children: hasAskPermission && /* @__PURE__ */ jsx(
7742
- ChatInputV2,
7743
- {
7744
- ref: chatInputV2Ref,
7745
- onSend: handleV2Send,
7746
- onCancel: cancelStream,
7747
- disabled: isV2InputDisabled,
7748
- isStreaming: isWaitingForResponse,
7749
- isUploadingAttachments: attachmentUpload.isUploading,
7750
- attachmentsReady: attachmentUpload.allReady,
7751
- hasAttachmentUploadErrors: attachmentUpload.hasErrors,
7752
- attachmentUploadStatusById: attachmentUpload.statusById,
7753
- uploadedAttachmentPayloads: attachmentUpload.payloads,
7754
- onAttachmentsChange: handleAttachmentsChange,
7755
- placeholder: isRecording ? "Listening..." : placeholder,
7756
- enableVoice: config.enableVoice === true,
7757
- transcribedText: config.enableVoice === true ? transcribedText : "",
7758
- voiceAvailable: config.enableVoice === true && voiceAvailable,
7759
- isRecording,
7760
- onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
7761
- onCancelRecording: handleCancelRecording,
7762
- onConfirmRecording: handleConfirmRecording,
7763
- showResetSession,
7764
- onResetSession: requestResetSession,
7765
- showAttachmentButton: attachmentSettings.showAttachmentButton,
7766
- showUploadImageButton: attachmentSettings.showUploadImageButton,
7767
- showAttachFileButton: attachmentSettings.showAttachFileButton,
7768
- allowedImageExtensions: attachmentSettings.allowedImageExtensions,
7769
- allowedFileExtensions: attachmentSettings.allowedFileExtensions,
7770
- maxCount: attachmentSettings.maxCount,
7771
- maxFileBytes: attachmentSettings.maxFileBytes,
7772
- maxTotalBytes: attachmentSettings.maxTotalBytes,
7773
- onUploadImageClick,
7774
- onAttachFileClick,
7775
- editingMessageId,
7776
- onClearEditing: handleClearEditing,
7777
- analysisMode: enableDeepModeToggle ? analysisMode : void 0,
7778
- onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
7779
- slashCommands
7780
- }
7781
- )
7782
- }
7783
- )
7784
- ] })
7785
- },
7786
- "v2-empty"
7787
- ) : /* @__PURE__ */ jsxs(
7788
- motion.div,
7789
- {
7790
- initial: hasEverSentMessage ? { opacity: 0 } : false,
7791
- animate: { opacity: 1 },
7792
- transition: { duration: 0.3 },
7793
- className: "payman-v2-chat-layout",
7794
- children: [
7795
- /* @__PURE__ */ jsx(
7796
- MessageListV2,
7797
- {
7798
- ref: messageListV2Ref,
7799
- messages,
7800
- isStreaming: isWaitingForResponse,
7801
- sidePanelOpen: !!pdfSheet,
7802
- onEditUserMessage: handleEditMessageDraft,
7803
- onRetryUserMessage: handleRetryUserMessage,
7804
- onImageClick: handleImageClick,
7805
- onExecutionTraceClick,
7806
- messageActions,
7807
- retryDisabled: isWaitingForResponse,
7808
- typingSpeed: config.typingSpeed ?? 4,
7809
- userActionPrompts,
7810
- notifications,
7811
- onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
7812
- onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
7813
- onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
7814
- onDismissNotification: dismissNotification,
7815
- onSubmitFeedback: handleSubmitFeedback
7816
- }
7817
- ),
7818
- /* @__PURE__ */ jsx(
7819
- StreamingIndicatorV2,
7820
- {
7821
- isStreaming: isWaitingForResponse,
7822
- loadingAnimation: config.loadingAnimation
7823
- }
7824
- ),
7825
- hasAskPermission && /* @__PURE__ */ jsx(
7826
- ChatInputV2,
7827
- {
7828
- ref: chatInputV2Ref,
7829
- onSend: handleV2Send,
7830
- onCancel: cancelStream,
7831
- disabled: isV2InputDisabled,
7832
- isStreaming: isWaitingForResponse,
7833
- isUploadingAttachments: attachmentUpload.isUploading,
7834
- attachmentsReady: attachmentUpload.allReady,
7835
- hasAttachmentUploadErrors: attachmentUpload.hasErrors,
7836
- attachmentUploadStatusById: attachmentUpload.statusById,
7837
- uploadedAttachmentPayloads: attachmentUpload.payloads,
7838
- onAttachmentsChange: handleAttachmentsChange,
7839
- placeholder: isRecording ? "Listening..." : placeholder,
7840
- enableVoice: config.enableVoice === true,
7841
- transcribedText: config.enableVoice === true ? transcribedText : "",
7842
- voiceAvailable: config.enableVoice === true && voiceAvailable,
7843
- isRecording,
7844
- onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
7845
- onCancelRecording: handleCancelRecording,
7846
- onConfirmRecording: handleConfirmRecording,
7847
- showResetSession,
7848
- onResetSession: requestResetSession,
7849
- showAttachmentButton: attachmentSettings.showAttachmentButton,
7850
- showUploadImageButton: attachmentSettings.showUploadImageButton,
7851
- showAttachFileButton: attachmentSettings.showAttachFileButton,
7852
- allowedImageExtensions: attachmentSettings.allowedImageExtensions,
7853
- allowedFileExtensions: attachmentSettings.allowedFileExtensions,
7854
- maxCount: attachmentSettings.maxCount,
7855
- maxFileBytes: attachmentSettings.maxFileBytes,
7856
- maxTotalBytes: attachmentSettings.maxTotalBytes,
7857
- onUploadImageClick,
7858
- onAttachFileClick,
7859
- editingMessageId,
7860
- onClearEditing: handleClearEditing,
7861
- analysisMode: enableDeepModeToggle ? analysisMode : void 0,
7862
- onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
7863
- slashCommands
7864
- }
7865
- )
7866
- ]
7867
- },
7868
- "v2-chat"
7869
- ) }) }),
7870
- pdfPreviewMode === "split" && /* @__PURE__ */ jsx(
7871
- PdfSheetV2,
6426
+ initial: { opacity: 1 },
6427
+ exit: { opacity: 0 },
6428
+ transition: { duration: 0.3 },
6429
+ className: "payman-v2-chat-layout",
6430
+ style: { justifyContent: "center", alignItems: "center" },
6431
+ children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", flex: 1, width: "100%" }, children: [
6432
+ /* @__PURE__ */ jsx(
6433
+ MessageList,
7872
6434
  {
7873
- src: pdfSheet?.href ?? null,
7874
- title: pdfSheet?.title ?? "",
7875
- onClose: closePdfSheet,
7876
- mode: "split"
6435
+ messages,
6436
+ isLoading: false,
6437
+ emptyStateText,
6438
+ showEmptyStateIcon,
6439
+ emptyStateComponent,
6440
+ layout,
6441
+ showTimestamps,
6442
+ stage: config.stage || "DEVELOPMENT",
6443
+ animated,
6444
+ showAgentName,
6445
+ agentName,
6446
+ showAvatars,
6447
+ showUserAvatar,
6448
+ showAssistantAvatar,
6449
+ showExecutionSteps,
6450
+ showStreamingDot,
6451
+ streamingStepsText,
6452
+ completedStepsText,
6453
+ onExecutionTraceClick,
6454
+ onLoadMoreMessages,
6455
+ isLoadingMoreMessages,
6456
+ hasMoreMessages
7877
6457
  }
7878
6458
  ),
7879
- pdfPreviewMode === "sheet" && /* @__PURE__ */ jsx(
7880
- "div",
6459
+ /* @__PURE__ */ jsx(
6460
+ motion.div,
7881
6461
  {
7882
- className: "payman-v2-pdf-sheet-top-anchor",
7883
- "aria-hidden": !pdfSheet,
7884
- children: /* @__PURE__ */ jsx(
7885
- PdfSheetV2,
6462
+ initial: { opacity: 0, y: 12 },
6463
+ animate: { opacity: 1, y: 0 },
6464
+ transition: { delay: 0.2, duration: 0.4, ease: [0.25, 0.46, 0.45, 0.94] },
6465
+ style: { width: "100%" },
6466
+ children: hasAskPermission && /* @__PURE__ */ jsx(
6467
+ ChatInputV2,
7886
6468
  {
7887
- src: pdfSheet?.href ?? null,
7888
- title: pdfSheet?.title ?? "",
7889
- onClose: closePdfSheet,
7890
- mode: "sheet"
6469
+ ref: chatInputV2Ref,
6470
+ onSend: handleV2Send,
6471
+ onCancel: cancelStream,
6472
+ disabled: isV2InputDisabled,
6473
+ isStreaming: isWaitingForResponse,
6474
+ placeholder: isRecording ? "Listening..." : placeholder,
6475
+ enableVoice: config.enableVoice === true,
6476
+ transcribedText: config.enableVoice === true ? transcribedText : "",
6477
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
6478
+ isRecording,
6479
+ onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
6480
+ onCancelRecording: handleCancelRecording,
6481
+ onConfirmRecording: handleConfirmRecording,
6482
+ showResetSession,
6483
+ onResetSession: requestResetSession,
6484
+ showAttachmentButton,
6485
+ showUploadImageButton,
6486
+ showAttachFileButton,
6487
+ onUploadImageClick,
6488
+ onAttachFileClick,
6489
+ editingMessageId,
6490
+ onClearEditing: handleClearEditing,
6491
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
6492
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
6493
+ slashCommands
7891
6494
  }
7892
6495
  )
7893
6496
  }
7894
6497
  )
6498
+ ] })
6499
+ },
6500
+ "v2-empty"
6501
+ ) : /* @__PURE__ */ jsxs(
6502
+ motion.div,
6503
+ {
6504
+ initial: hasEverSentMessage ? { opacity: 0 } : false,
6505
+ animate: { opacity: 1 },
6506
+ transition: { duration: 0.3 },
6507
+ className: "payman-v2-chat-layout",
6508
+ children: [
6509
+ /* @__PURE__ */ jsx(
6510
+ MessageListV2,
6511
+ {
6512
+ ref: messageListV2Ref,
6513
+ messages,
6514
+ isStreaming: isWaitingForResponse,
6515
+ onEditUserMessage: handleEditMessageDraft,
6516
+ onRetryUserMessage: handleRetryUserMessage,
6517
+ onImageClick: handleImageClick,
6518
+ onExecutionTraceClick,
6519
+ messageActions,
6520
+ retryDisabled: isWaitingForResponse,
6521
+ typingSpeed: config.typingSpeed ?? 4,
6522
+ userActionPrompts,
6523
+ notifications,
6524
+ onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6525
+ onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6526
+ onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6527
+ onExpireUserAction: isUserActionSupported ? expireUserAction2 : void 0,
6528
+ onDismissNotification: dismissNotification,
6529
+ onSubmitFeedback: handleSubmitFeedback
6530
+ }
6531
+ ),
6532
+ /* @__PURE__ */ jsx(
6533
+ StreamingIndicatorV2,
6534
+ {
6535
+ isStreaming: isWaitingForResponse,
6536
+ loadingAnimation: config.loadingAnimation
6537
+ }
6538
+ ),
6539
+ hasAskPermission && /* @__PURE__ */ jsx(
6540
+ ChatInputV2,
6541
+ {
6542
+ ref: chatInputV2Ref,
6543
+ onSend: handleV2Send,
6544
+ onCancel: cancelStream,
6545
+ disabled: isV2InputDisabled,
6546
+ isStreaming: isWaitingForResponse,
6547
+ placeholder: isRecording ? "Listening..." : placeholder,
6548
+ enableVoice: config.enableVoice === true,
6549
+ transcribedText: config.enableVoice === true ? transcribedText : "",
6550
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
6551
+ isRecording,
6552
+ onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
6553
+ onCancelRecording: handleCancelRecording,
6554
+ onConfirmRecording: handleConfirmRecording,
6555
+ showResetSession,
6556
+ onResetSession: requestResetSession,
6557
+ showAttachmentButton,
6558
+ showUploadImageButton,
6559
+ showAttachFileButton,
6560
+ onUploadImageClick,
6561
+ onAttachFileClick,
6562
+ editingMessageId,
6563
+ onClearEditing: handleClearEditing,
6564
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
6565
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
6566
+ slashCommands
6567
+ }
6568
+ )
7895
6569
  ]
7896
- }
7897
- ),
6570
+ },
6571
+ "v2-chat"
6572
+ ) }),
7898
6573
  /* @__PURE__ */ jsx(
7899
6574
  ImageLightboxV2,
7900
6575
  {
@@ -7929,19 +6604,10 @@ var PaymanChat = forwardRef(
7929
6604
  function PaymanChat2(props, ref) {
7930
6605
  const mergedCallbacks = useSentryChatCallbacks(props.callbacks, props.config);
7931
6606
  const chat = useChatV2(props.config, mergedCallbacks);
7932
- const attachmentUpload = useAttachmentUpload(props.config);
7933
- return /* @__PURE__ */ jsx(
7934
- PaymanChatInner,
7935
- {
7936
- ...props,
7937
- chat,
7938
- attachmentUpload,
7939
- ref
7940
- }
7941
- );
6607
+ return /* @__PURE__ */ jsx(PaymanChatInner, { ...props, chat, ref });
7942
6608
  }
7943
6609
  );
7944
6610
 
7945
- export { PaymanChat, PaymanChatContext, PdfSheetV2, UserActionStaleError, buildSignedUrlEndpoint, cancelUserAction, captureSentryError, cn, formatAttachmentBytes, formatDate, getPdfTitleFromUrl, isPdfUrl, mapExecutionHistoryPageToChatMessages, mapExecutionHistoryToChatMessages, resendUserAction, stripAttachmentsSuffixFromIntent, submitUserAction, uploadAttachment, uploadAttachments, useAttachmentUpload, useChatV2, usePaymanChat, useVoice };
6611
+ export { PaymanChat, PaymanChatContext, UserActionStaleError, cancelUserAction, captureSentryError, cn, formatDate, resendUserAction, submitUserAction, useChatV2, usePaymanChat, useVoice };
7946
6612
  //# sourceMappingURL=index.mjs.map
7947
6613
  //# sourceMappingURL=index.mjs.map