@paymanai/payman-ask-sdk 4.0.20 → 4.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -40,10 +40,7 @@ var remarkBreaks__default = /*#__PURE__*/_interopDefault(remarkBreaks);
40
40
 
41
41
  var __defProp = Object.defineProperty;
42
42
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
43
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
44
- var __defProp2 = Object.defineProperty;
45
- var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
46
- var __publicField2 = (obj, key, value) => __defNormalProp2(obj, key + "", value);
43
+ var __publicField = (obj, key, value) => __defNormalProp(obj, key + "", value);
47
44
  function generateId() {
48
45
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
49
46
  const r = Math.random() * 16 | 0;
@@ -1154,7 +1151,7 @@ async function uploadAttachments(config, files, signal) {
1154
1151
  var UserActionStaleError = class extends Error {
1155
1152
  constructor(userActionId, message = "User action is no longer actionable") {
1156
1153
  super(message);
1157
- __publicField2(this, "userActionId");
1154
+ __publicField(this, "userActionId");
1158
1155
  this.name = "UserActionStaleError";
1159
1156
  this.userActionId = userActionId;
1160
1157
  }
@@ -1835,102 +1832,6 @@ function useVoice(config = {}, callbacks = {}) {
1835
1832
  reset
1836
1833
  };
1837
1834
  }
1838
- function useAttachmentUpload(config) {
1839
- const configRef = react.useRef(config);
1840
- configRef.current = config;
1841
- const [entries, setEntries] = react.useState(/* @__PURE__ */ new Map());
1842
- const [orderedIds, setOrderedIds] = react.useState([]);
1843
- const abortControllersRef = react.useRef(/* @__PURE__ */ new Map());
1844
- const activeIdsRef = react.useRef(/* @__PURE__ */ new Set());
1845
- const startUpload = react.useCallback((id, file) => {
1846
- if (activeIdsRef.current.has(id)) return;
1847
- activeIdsRef.current.add(id);
1848
- const controller = new AbortController();
1849
- abortControllersRef.current.set(id, controller);
1850
- setEntries((prev) => {
1851
- const next = new Map(prev);
1852
- next.set(id, { id, status: "uploading" });
1853
- return next;
1854
- });
1855
- void uploadAttachment(configRef.current, file, controller.signal).then((payload) => {
1856
- if (controller.signal.aborted) return;
1857
- setEntries((prev) => {
1858
- const next = new Map(prev);
1859
- next.set(id, { id, status: "done", payload });
1860
- return next;
1861
- });
1862
- }).catch((error) => {
1863
- if (error.name === "AbortError") return;
1864
- setEntries((prev) => {
1865
- const next = new Map(prev);
1866
- next.set(id, {
1867
- id,
1868
- status: "error",
1869
- error: error.message || "Upload failed"
1870
- });
1871
- return next;
1872
- });
1873
- }).finally(() => {
1874
- abortControllersRef.current.delete(id);
1875
- });
1876
- }, []);
1877
- const syncAttachments = react.useCallback(
1878
- (attachments) => {
1879
- const nextIds = attachments.map((attachment) => attachment.id);
1880
- setOrderedIds(nextIds);
1881
- const nextIdSet = new Set(nextIds);
1882
- for (const id of activeIdsRef.current) {
1883
- if (nextIdSet.has(id)) continue;
1884
- abortControllersRef.current.get(id)?.abort();
1885
- abortControllersRef.current.delete(id);
1886
- activeIdsRef.current.delete(id);
1887
- }
1888
- setEntries((prev) => {
1889
- const next = new Map(prev);
1890
- for (const id of next.keys()) {
1891
- if (!nextIdSet.has(id)) next.delete(id);
1892
- }
1893
- return next;
1894
- });
1895
- for (const attachment of attachments) {
1896
- startUpload(attachment.id, attachment.file);
1897
- }
1898
- },
1899
- [startUpload]
1900
- );
1901
- const clearAll = react.useCallback(() => {
1902
- for (const controller of abortControllersRef.current.values()) {
1903
- controller.abort();
1904
- }
1905
- abortControllersRef.current.clear();
1906
- activeIdsRef.current.clear();
1907
- setOrderedIds([]);
1908
- setEntries(/* @__PURE__ */ new Map());
1909
- }, []);
1910
- const entryList = react.useMemo(
1911
- () => orderedIds.map((id) => entries.get(id)).filter((entry) => entry != null),
1912
- [entries, orderedIds]
1913
- );
1914
- const isUploading = entryList.some((entry) => entry.status === "uploading");
1915
- const hasErrors = entryList.some((entry) => entry.status === "error");
1916
- const allReady = orderedIds.length === 0 || orderedIds.every((id) => entries.get(id)?.status === "done");
1917
- const payloads = entryList.filter((entry) => entry.status === "done" && entry.payload).map((entry) => entry.payload);
1918
- const statusById = react.useMemo(
1919
- () => Object.fromEntries(
1920
- entryList.map((entry) => [entry.id, entry.status])
1921
- ),
1922
- [entryList]
1923
- );
1924
- return {
1925
- syncAttachments,
1926
- clearAll,
1927
- isUploading,
1928
- hasErrors,
1929
- allReady,
1930
- payloads,
1931
- statusById
1932
- };
1933
- }
1934
1835
  function classifyField(field) {
1935
1836
  if (!field) return "text";
1936
1837
  if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
@@ -2047,66 +1948,6 @@ function buildContent(schema, values) {
2047
1948
  }
2048
1949
  return content;
2049
1950
  }
2050
- var ATTACHMENTS_SUFFIX_RE = /\n\n\[Attachments:[^\]]*\]\s*$/;
2051
- function stripAttachmentsSuffixFromIntent(intent) {
2052
- return intent.replace(ATTACHMENTS_SUFFIX_RE, "").trimEnd();
2053
- }
2054
- function mapFeedback(feedback) {
2055
- if (!feedback?.feedback) return null;
2056
- return feedback.feedback === "POSITIVE" ? "up" : "down";
2057
- }
2058
- function mapHistoryAttachments(executionId, attachments) {
2059
- const mapped = (attachments ?? []).map((attachment, index) => ({
2060
- id: `${executionId}:att:${index}`,
2061
- filename: attachment.filename,
2062
- mimeType: attachment.mimeType,
2063
- url: attachment.url ?? void 0,
2064
- kind: attachment.mimeType.startsWith("image/") ? "image" : "file"
2065
- }));
2066
- return mapped.length > 0 ? mapped : void 0;
2067
- }
2068
- function mapExecutionHistoryToChatMessages(message) {
2069
- const timestamp = message.startTime || message.endTime || (/* @__PURE__ */ new Date()).toISOString();
2070
- const executionId = message.executionId ?? message.traceId ?? message.id;
2071
- const attachments = mapHistoryAttachments(message.id, message.attachments);
2072
- const rows = [
2073
- {
2074
- id: `${message.id}:user`,
2075
- role: "user",
2076
- content: stripAttachmentsSuffixFromIntent(message.sessionUserIntent),
2077
- timestamp,
2078
- attachments
2079
- }
2080
- ];
2081
- if (message.agentResponse) {
2082
- rows.push({
2083
- id: `${message.id}:assistant`,
2084
- role: "assistant",
2085
- content: message.agentResponse,
2086
- timestamp: message.endTime || timestamp,
2087
- isError: message.status === "FAILED",
2088
- executionId,
2089
- feedback: mapFeedback(message.feedback)
2090
- });
2091
- }
2092
- return rows;
2093
- }
2094
- function mapExecutionHistoryPageToChatMessages(messages) {
2095
- return messages.flatMap(mapExecutionHistoryToChatMessages);
2096
- }
2097
- function attachmentDisplayUrl(attachment) {
2098
- return attachment.previewUrl ?? attachment.url;
2099
- }
2100
- function isImageAttachment(attachment) {
2101
- const displayUrl = attachmentDisplayUrl(attachment);
2102
- return attachment.kind === "image" || Boolean(displayUrl) && attachment.mimeType.startsWith("image/");
2103
- }
2104
- function isPdfAttachmentMeta(attachment) {
2105
- return attachment.mimeType === "application/pdf" || /\.pdf$/i.test(attachment.filename);
2106
- }
2107
- function isPdfFile(file) {
2108
- return file.type === "application/pdf" || /\.pdf$/i.test(file.name);
2109
- }
2110
1951
  var PaymanChatContext = react.createContext(void 0);
2111
1952
  function usePaymanChat() {
2112
1953
  const context = react.useContext(PaymanChatContext);
@@ -2217,111 +2058,6 @@ function subscribeToCfRay(urlPattern, listener) {
2217
2058
  };
2218
2059
  }
2219
2060
 
2220
- // src/utils/attachmentConfig.ts
2221
- var DEFAULT_IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp"];
2222
- var DEFAULT_DOCUMENT_EXTENSIONS = ["pdf", "docx", "xlsx", "xls"];
2223
- function resolveChatAttachmentConfig(config) {
2224
- const nested = config.attachments;
2225
- const enabled = nested?.enabled ?? config.showAttachmentButton ?? true;
2226
- const uploadImage = nested?.uploadImage ?? config.showUploadImageButton ?? true;
2227
- const attachFile = nested?.attachFile ?? config.showAttachFileButton ?? true;
2228
- return {
2229
- showAttachmentButton: enabled && (uploadImage || attachFile),
2230
- showUploadImageButton: enabled && uploadImage,
2231
- showAttachFileButton: enabled && attachFile,
2232
- maxCount: nested?.maxCount ?? nested?.maxImages ?? nested?.maxDocuments,
2233
- maxFileBytes: nested?.maxFileBytes,
2234
- maxTotalBytes: nested?.maxTotalBytes,
2235
- allowedImageExtensions: nested?.imageExtensions ?? DEFAULT_IMAGE_EXTENSIONS,
2236
- allowedFileExtensions: nested?.documentExtensions ?? DEFAULT_DOCUMENT_EXTENSIONS
2237
- };
2238
- }
2239
-
2240
- // src/utils/formatAttachmentBytes.ts
2241
- function formatAttachmentBytes(bytes) {
2242
- if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
2243
- const units = ["B", "KB", "MB", "GB"];
2244
- let value = bytes;
2245
- let unitIndex = 0;
2246
- while (value >= 1024 && unitIndex < units.length - 1) {
2247
- value /= 1024;
2248
- unitIndex += 1;
2249
- }
2250
- const rounded = unitIndex === 0 ? String(Math.round(value)) : value >= 10 ? value.toFixed(0) : value.toFixed(1).replace(/\.0$/, "");
2251
- return `${rounded} ${units[unitIndex]}`;
2252
- }
2253
-
2254
- // src/utils/pdfLink.ts
2255
- function filenameFromContentDisposition(value) {
2256
- if (!value) return void 0;
2257
- let decoded = value;
2258
- try {
2259
- decoded = decodeURIComponent(value);
2260
- } catch {
2261
- decoded = value;
2262
- }
2263
- const quoted = decoded.match(/filename\*=(?:UTF-8''([^;]+)|"([^"]+)")/i);
2264
- if (quoted?.[1] || quoted?.[2]) {
2265
- return (quoted[1] ?? quoted[2])?.trim();
2266
- }
2267
- const unquoted = decoded.match(/filename=([^;]+)/i);
2268
- return unquoted?.[1]?.replace(/"/g, "").trim();
2269
- }
2270
- function filenameFromUrlPath(href) {
2271
- try {
2272
- const parts = new URL(href).pathname.split("/").filter(Boolean);
2273
- const last = parts[parts.length - 1];
2274
- return last ? decodeURIComponent(last) : void 0;
2275
- } catch {
2276
- return void 0;
2277
- }
2278
- }
2279
- function isPdfUrl(href) {
2280
- if (!href) return false;
2281
- const lower = href.toLowerCase();
2282
- if (lower.includes(".pdf")) return true;
2283
- try {
2284
- const url = new URL(href);
2285
- if (url.pathname.toLowerCase().endsWith(".pdf")) return true;
2286
- const filename = url.searchParams.get("filename");
2287
- if (filename?.toLowerCase().includes(".pdf")) return true;
2288
- for (const key of ["rscd", "response-content-disposition"]) {
2289
- const fromDisposition = filenameFromContentDisposition(
2290
- url.searchParams.get(key)
2291
- );
2292
- if (fromDisposition?.toLowerCase().includes(".pdf")) return true;
2293
- }
2294
- } catch {
2295
- return lower.endsWith(".pdf");
2296
- }
2297
- return false;
2298
- }
2299
- function getPdfTitleFromUrl(href, linkText) {
2300
- const text = linkText?.trim();
2301
- if (text) return text;
2302
- try {
2303
- const url = new URL(href);
2304
- const filename = url.searchParams.get("filename");
2305
- if (filename) {
2306
- return filename.replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
2307
- }
2308
- for (const key of ["rscd", "response-content-disposition"]) {
2309
- const fromDisposition = filenameFromContentDisposition(
2310
- url.searchParams.get(key)
2311
- );
2312
- if (fromDisposition) {
2313
- return fromDisposition.replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
2314
- }
2315
- }
2316
- } catch {
2317
- }
2318
- const fromPath = filenameFromUrlPath(href);
2319
- if (fromPath) {
2320
- return fromPath.replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
2321
- }
2322
- return "Document";
2323
- }
2324
-
2325
2061
  // src/utils/slashCommands.ts
2326
2062
  var DEFAULT_SLASH_COMMANDS = [
2327
2063
  {
@@ -3473,108 +3209,6 @@ function ActionTooltipV2({ label, children }) {
3473
3209
  ] }) })
3474
3210
  ] });
3475
3211
  }
3476
- function FilePreviewShell({
3477
- filename,
3478
- typeLabel,
3479
- onClick,
3480
- thumbnail,
3481
- className,
3482
- "aria-label": ariaLabel
3483
- }) {
3484
- const shellClass = cn(
3485
- "payman-v2-file-preview-shell",
3486
- "payman-v2-file-preview-shell-sent",
3487
- onClick && "payman-v2-file-preview-shell-clickable",
3488
- className
3489
- );
3490
- const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3491
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-file-preview-thumb", children: thumbnail ?? /* @__PURE__ */ jsxRuntime.jsx(
3492
- lucideReact.FileText,
3493
- {
3494
- size: 16,
3495
- strokeWidth: 1.75,
3496
- className: "payman-v2-file-preview-doc-icon"
3497
- }
3498
- ) }),
3499
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-file-preview-info", children: [
3500
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-file-preview-name", children: filename }),
3501
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-file-preview-type", children: typeLabel })
3502
- ] })
3503
- ] });
3504
- if (onClick) {
3505
- return /* @__PURE__ */ jsxRuntime.jsx(
3506
- "button",
3507
- {
3508
- type: "button",
3509
- className: shellClass,
3510
- onClick,
3511
- "aria-label": ariaLabel ?? `Open ${filename}`,
3512
- children: body
3513
- }
3514
- );
3515
- }
3516
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: shellClass, children: body });
3517
- }
3518
- function FilePreviewBlockLayout({
3519
- children,
3520
- className,
3521
- ...rest
3522
- }) {
3523
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("payman-v2-file-preview-item", className), ...rest, children });
3524
- }
3525
- function attachmentTypeLabel(attachment) {
3526
- if (isImageAttachment(attachment)) {
3527
- return "Image";
3528
- }
3529
- const ext = attachment.filename.split(".").pop()?.toUpperCase();
3530
- return ext || "Document";
3531
- }
3532
- function isPdfAttachment(attachment) {
3533
- const displayUrl = attachmentDisplayUrl(attachment);
3534
- return isPdfAttachmentMeta(attachment) || Boolean(displayUrl) && isPdfUrl(displayUrl);
3535
- }
3536
- function AttachmentPreviewBlock({
3537
- attachment,
3538
- onImageClick
3539
- }) {
3540
- const chatContext = react.useContext(PaymanChatContext);
3541
- const displayUrl = attachmentDisplayUrl(attachment);
3542
- const typeLabel = attachmentTypeLabel(attachment);
3543
- const isImage = isImageAttachment(attachment) && displayUrl;
3544
- const openAttachment = () => {
3545
- if (!displayUrl) return;
3546
- if (isImageAttachment(attachment)) {
3547
- onImageClick?.(displayUrl, attachment.filename);
3548
- return;
3549
- }
3550
- if (isPdfAttachment(attachment)) {
3551
- chatContext?.openPdfSheet(displayUrl, attachment.filename);
3552
- return;
3553
- }
3554
- window.open(displayUrl, "_blank", "noopener,noreferrer");
3555
- };
3556
- if (isImage) {
3557
- return /* @__PURE__ */ jsxRuntime.jsx(FilePreviewBlockLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(
3558
- FilePreviewShell,
3559
- {
3560
- filename: attachment.filename,
3561
- typeLabel,
3562
- onClick: openAttachment,
3563
- "aria-label": `Preview ${attachment.filename}`,
3564
- thumbnail: /* @__PURE__ */ jsxRuntime.jsx("img", { src: displayUrl, alt: "", draggable: false })
3565
- }
3566
- ) });
3567
- }
3568
- return /* @__PURE__ */ jsxRuntime.jsx(FilePreviewBlockLayout, { children: /* @__PURE__ */ jsxRuntime.jsx(
3569
- FilePreviewShell,
3570
- {
3571
- filename: attachment.filename,
3572
- typeLabel,
3573
- onClick: displayUrl ? openAttachment : void 0,
3574
- "aria-label": `Open ${attachment.filename}`
3575
- }
3576
- ) });
3577
- }
3578
3212
  function formatMessageTime(timestamp) {
3579
3213
  const value = new Date(timestamp);
3580
3214
  if (Number.isNaN(value.getTime())) return "";
@@ -3587,7 +3221,6 @@ function UserMessageV2({
3587
3221
  message,
3588
3222
  onEdit,
3589
3223
  onRetry,
3590
- onImageClick,
3591
3224
  retryDisabled = false,
3592
3225
  actions
3593
3226
  }) {
@@ -3670,15 +3303,7 @@ function UserMessageV2({
3670
3303
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3671
3304
  toastPortal,
3672
3305
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-user-msg payman-v2-fade-in", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-user-msg-group", children: [
3673
- message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-file-preview payman-v2-user-msg-attachments", children: message.attachments.map((attachment) => /* @__PURE__ */ jsxRuntime.jsx(
3674
- AttachmentPreviewBlock,
3675
- {
3676
- attachment,
3677
- onImageClick
3678
- },
3679
- attachment.id
3680
- )) }),
3681
- (message.content.trim().length > 0 || !message.attachments?.length) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-user-msg-bubble", children: parsedCommand ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-user-msg-command", children: [
3306
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-user-msg-bubble", children: parsedCommand ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-user-msg-command", children: [
3682
3307
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-user-msg-command-chip", children: parsedCommand.command }),
3683
3308
  parsedCommand.body.trim() ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-user-msg-text", children: parsedCommand.body }) : null
3684
3309
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-user-msg-text", children: message.content }) }),
@@ -3903,44 +3528,9 @@ function MarkdownImageV2({
3903
3528
  }
3904
3529
  ) });
3905
3530
  }
3906
- function PdfBlockV2({ title, href, onOpen, autoOpen = false }) {
3907
- const autoOpenedHrefRef = react.useRef(null);
3908
- react.useEffect(() => {
3909
- if (!autoOpen || autoOpenedHrefRef.current === href) return;
3910
- autoOpenedHrefRef.current = href;
3911
- onOpen(href, title, { auto: true });
3912
- }, [href, autoOpen]);
3913
- return /* @__PURE__ */ jsxRuntime.jsx(FilePreviewBlockLayout, { "data-payman-file-block": true, children: /* @__PURE__ */ jsxRuntime.jsx(
3914
- FilePreviewShell,
3915
- {
3916
- filename: title,
3917
- typeLabel: "PDF",
3918
- onClick: () => onOpen(href, title),
3919
- "aria-label": `Open PDF: ${title}`
3920
- }
3921
- ) });
3922
- }
3923
- function childrenToText(children) {
3924
- if (typeof children === "string") return children;
3925
- if (typeof children === "number") return String(children);
3926
- if (Array.isArray(children)) return children.map(childrenToText).join("");
3927
- if (children && typeof children === "object" && "props" in children) {
3928
- return childrenToText(children.props.children);
3929
- }
3930
- return "";
3931
- }
3932
- function isFileBlockChild(child) {
3933
- return react.isValidElement(child) && typeof child.props === "object" && child.props !== null && "data-payman-file-block" in child.props;
3934
- }
3935
- function buildComponents(onImageClick, isResolvingRef, onPdfClick, autoOpenPdf) {
3531
+ function buildComponents(onImageClick, isResolvingRef) {
3936
3532
  return {
3937
- p: ({ children }) => {
3938
- const childArray = react.Children.toArray(children);
3939
- if (childArray.length === 1 && isFileBlockChild(childArray[0])) {
3940
- return childArray[0];
3941
- }
3942
- return /* @__PURE__ */ jsxRuntime.jsx("p", { children });
3943
- },
3533
+ p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { children }),
3944
3534
  code: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("code", { children }),
3945
3535
  pre: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-markdown-pre", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { children }) }),
3946
3536
  ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { children }),
@@ -3953,15 +3543,7 @@ function buildComponents(onImageClick, isResolvingRef, onPdfClick, autoOpenPdf)
3953
3543
  em: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("em", { children }),
3954
3544
  blockquote: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("blockquote", { children }),
3955
3545
  hr: () => /* @__PURE__ */ jsxRuntime.jsx("hr", {}),
3956
- a: ({ href, children }) => {
3957
- const url = href ?? "";
3958
- if (onPdfClick && isPdfUrl(url)) {
3959
- const linkText = childrenToText(children).trim();
3960
- const title = getPdfTitleFromUrl(url, linkText);
3961
- return /* @__PURE__ */ jsxRuntime.jsx(PdfBlockV2, { href: url, title, onOpen: onPdfClick, autoOpen: autoOpenPdf });
3962
- }
3963
- return /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children });
3964
- },
3546
+ a: ({ href, children }) => /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noopener noreferrer", children }),
3965
3547
  img: ({ src, alt }) => /* @__PURE__ */ jsxRuntime.jsx(
3966
3548
  MarkdownImageV2,
3967
3549
  {
@@ -3982,15 +3564,13 @@ function MarkdownRendererV2({
3982
3564
  content,
3983
3565
  isStreaming,
3984
3566
  isResolvingImages,
3985
- onImageClick,
3986
- onPdfClick,
3987
- autoOpenPdf
3567
+ onImageClick
3988
3568
  }) {
3989
3569
  const isResolvingRef = react.useRef(isResolvingImages);
3990
3570
  isResolvingRef.current = isResolvingImages;
3991
3571
  const components = react.useMemo(
3992
- () => buildComponents(onImageClick, isResolvingRef, onPdfClick, autoOpenPdf),
3993
- [onImageClick, onPdfClick, autoOpenPdf]
3572
+ () => buildComponents(onImageClick, isResolvingRef),
3573
+ [onImageClick]
3994
3574
  );
3995
3575
  return /* @__PURE__ */ jsxRuntime.jsx(
3996
3576
  "div",
@@ -4359,17 +3939,6 @@ function stripIncompleteImageToken(text) {
4359
3939
  if (/^!\[[^\]]*\]\([^)]*\)/.test(after)) return text;
4360
3940
  return text.slice(0, lastBang);
4361
3941
  }
4362
- function stripIncompleteLinkToken(text) {
4363
- const lastBracket = text.lastIndexOf("[");
4364
- if (lastBracket === -1) return text;
4365
- if (lastBracket > 0 && text[lastBracket - 1] === "!") return text;
4366
- const after = text.slice(lastBracket);
4367
- if (/^\[[^\]]*\]\([^)]*\)/.test(after)) return text;
4368
- return text.slice(0, lastBracket);
4369
- }
4370
- function stripIncompleteMarkdownTokens(text) {
4371
- return stripIncompleteLinkToken(stripIncompleteImageToken(text));
4372
- }
4373
3942
  function getFeedbackState(message) {
4374
3943
  const feedback = message.feedback;
4375
3944
  if (feedback === "up" || feedback === "down") return feedback;
@@ -4393,12 +3962,6 @@ function AssistantMessageV2({
4393
3962
  () => getFeedbackState(message)
4394
3963
  );
4395
3964
  const [reasonModalOpen, setReasonModalOpen] = react.useState(false);
4396
- const chatContext = react.useContext(PaymanChatContext);
4397
- const chatContextRef = react.useRef(chatContext);
4398
- chatContextRef.current = chatContext;
4399
- const handlePdfClick = react.useCallback((href, title, options) => {
4400
- chatContextRef.current?.openPdfSheet(href, title, options);
4401
- }, []);
4402
3965
  const canSubmitFeedback = !!onSubmitFeedback && !!message.executionId;
4403
3966
  const [toast, setToast] = react.useState(null);
4404
3967
  const copyResetTimerRef = react.useRef(null);
@@ -4423,7 +3986,7 @@ function AssistantMessageV2({
4423
3986
  const raw = message.isStreaming ? message.streamingContent || message.content : message.content;
4424
3987
  if (!raw) return "";
4425
3988
  const normalized = raw.replace(/\\n/g, "\n");
4426
- return message.isStreaming ? stripIncompleteMarkdownTokens(normalized) : normalized;
3989
+ return message.isStreaming ? stripIncompleteImageToken(normalized) : normalized;
4427
3990
  })();
4428
3991
  const isThinkingStreaming = !!message.isStreaming && !rawResponseContent && !message.isError;
4429
3992
  const responseTypingEnabled = hasEverStreamed.current && Boolean(rawResponseContent) && !message.isError;
@@ -4593,12 +4156,10 @@ function AssistantMessageV2({
4593
4156
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-assistant-msg-content-area", children: displayContent ? /* @__PURE__ */ jsxRuntime.jsx(
4594
4157
  MarkdownRendererV2,
4595
4158
  {
4596
- content: message.isStreaming && !isCancelled || isResponseTyping ? stripIncompleteMarkdownTokens(displayContent) : displayContent,
4159
+ content: displayContent,
4597
4160
  isStreaming: message.isStreaming && !isCancelled || isResponseTyping,
4598
4161
  isResolvingImages: message.isResolvingImages,
4599
- onImageClick,
4600
- onPdfClick: handlePdfClick,
4601
- autoOpenPdf: hasEverStreamed.current
4162
+ onImageClick
4602
4163
  }
4603
4164
  ) : !isThinkingStreaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-assistant-msg-placeholder", children: "..." }) : null }),
4604
4165
  isCancelled && message.isStreaming && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-assistant-msg-paused", children: [
@@ -4815,6 +4376,7 @@ function VerificationInline({
4815
4376
  const [code, setCode] = react.useState("");
4816
4377
  const [errored, setErrored] = react.useState(false);
4817
4378
  const [resendSec, setResendSec] = react.useState(0);
4379
+ const [hiddenAfterResend, setHiddenAfterResend] = react.useState(false);
4818
4380
  const lastSubmittedRef = react.useRef(null);
4819
4381
  const resendTimerRef = react.useRef(void 0);
4820
4382
  const status = prompt.status;
@@ -4828,6 +4390,9 @@ function VerificationInline({
4828
4390
  lastSubmittedRef.current = null;
4829
4391
  }
4830
4392
  }, [prompt.subAction, prompt.userActionId]);
4393
+ react.useEffect(() => {
4394
+ setHiddenAfterResend(false);
4395
+ }, [prompt.expirySeconds, prompt.message, prompt.subAction, prompt.userActionId]);
4831
4396
  react.useEffect(() => {
4832
4397
  if (code.length < codeLen) lastSubmittedRef.current = null;
4833
4398
  }, [code, codeLen]);
@@ -4878,11 +4443,13 @@ function VerificationInline({
4878
4443
  if (locked || resendSec > 0) return;
4879
4444
  setErrored(false);
4880
4445
  setCode("");
4446
+ setHiddenAfterResend(true);
4881
4447
  lastSubmittedRef.current = null;
4882
4448
  try {
4883
4449
  await onResend(prompt.userActionId);
4884
4450
  startResendCooldown();
4885
4451
  } catch {
4452
+ setHiddenAfterResend(false);
4886
4453
  }
4887
4454
  }, [locked, onResend, prompt.userActionId, resendSec, startResendCooldown]);
4888
4455
  const handleCancel = react.useCallback(() => {
@@ -4890,6 +4457,7 @@ function VerificationInline({
4890
4457
  void onCancel(prompt.userActionId);
4891
4458
  }, [busy, onCancel, prompt.userActionId]);
4892
4459
  const description = prompt.message?.trim() || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
4460
+ if (hiddenAfterResend) return null;
4893
4461
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Verification required", children: [
4894
4462
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-head", children: [
4895
4463
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
@@ -4985,6 +4553,9 @@ function SchemaFormInline({
4985
4553
  const busy = status === "submitting";
4986
4554
  const stale = status === "stale";
4987
4555
  const locked = busy || stale || expired;
4556
+ const isUserConfirmation = prompt.subAction === "UserConfirmation";
4557
+ const messageFormat = prompt.metadata?.["payman/messageFormat"];
4558
+ const renderMarkdown = messageFormat === "markdown";
4988
4559
  const setValue = (key, value) => {
4989
4560
  setValues((prev) => ({ ...prev, [key]: value }));
4990
4561
  setErrors((prev) => {
@@ -5010,8 +4581,8 @@ function SchemaFormInline({
5010
4581
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
5011
4582
  typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
5012
4583
  ] }),
5013
- prompt.message?.trim() && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: prompt.message }),
5014
- stale ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: "This action has no inputs to fill." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
4584
+ prompt.message?.trim() && (renderMarkdown ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-markdown", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRendererV2, { content: prompt.message }) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: prompt.message })),
4585
+ stale ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
5015
4586
  const widget = classifyField(field);
5016
4587
  const label = field.title || key;
5017
4588
  const required = isRequired(schema, key);
@@ -5084,7 +4655,7 @@ function SchemaFormInline({
5084
4655
  className: "payman-v2-ua-btn payman-v2-ua-btn-primary",
5085
4656
  disabled: locked,
5086
4657
  onClick: handleSubmit,
5087
- children: busy ? "Submitting\u2026" : "Submit"
4658
+ children: busy ? "Submitting\u2026" : isUserConfirmation ? "Confirm" : "Submit"
5088
4659
  }
5089
4660
  ),
5090
4661
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5140,10 +4711,25 @@ function useExpiryCountdown(prompt) {
5140
4711
  const expired = initial !== void 0 && secondsLeft === 0;
5141
4712
  return [secondsLeft, expired];
5142
4713
  }
5143
- function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
4714
+ function UserActionInline({
4715
+ prompt,
4716
+ onSubmit,
4717
+ onCancel,
4718
+ onResend,
4719
+ onExpired
4720
+ }) {
5144
4721
  const [secondsLeft, expired] = useExpiryCountdown(prompt);
4722
+ react.useEffect(() => {
4723
+ if (expired && prompt.kind !== "notification") onExpired?.();
4724
+ }, [expired, onExpired, prompt.kind]);
5145
4725
  let body;
5146
- if (prompt.kind === "verification") {
4726
+ if (expired && prompt.kind !== "notification") {
4727
+ const note = {
4728
+ id: `${prompt.userActionId}-expired`,
4729
+ message: prompt.kind === "verification" ? "Verification Request Expired" : "User Form Expired"
4730
+ };
4731
+ body = /* @__PURE__ */ jsxRuntime.jsx(NotificationInline, { notification: note });
4732
+ } else if (prompt.kind === "verification") {
5147
4733
  body = /* @__PURE__ */ jsxRuntime.jsx(
5148
4734
  VerificationInline,
5149
4735
  {
@@ -5183,10 +4769,23 @@ function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
5183
4769
  }
5184
4770
  var SCROLL_THRESHOLD2 = 100;
5185
4771
  var USER_SCROLL_UP_EPSILON = 4;
4772
+ var PROMPT_KEY_SEPARATOR = "";
4773
+ function getPromptSlotKey(prompt) {
4774
+ return prompt.toolCallId || prompt.userActionId;
4775
+ }
4776
+ function getPromptViewKey(prompt) {
4777
+ return [
4778
+ getPromptSlotKey(prompt),
4779
+ prompt.userActionId,
4780
+ prompt.subAction ?? "",
4781
+ prompt.expirySeconds ?? "",
4782
+ prompt.message ?? ""
4783
+ ].join(PROMPT_KEY_SEPARATOR);
4784
+ }
5186
4785
  var MessageListV2 = react.forwardRef(
5187
4786
  function MessageListV22({
5188
4787
  messages,
5189
- isStreaming: _isStreaming = false,
4788
+ isStreaming = false,
5190
4789
  onEditUserMessage,
5191
4790
  onRetryUserMessage,
5192
4791
  onImageClick,
@@ -5198,10 +4797,10 @@ var MessageListV2 = react.forwardRef(
5198
4797
  onSubmitUserAction,
5199
4798
  onCancelUserAction,
5200
4799
  onResendUserAction,
4800
+ onExpireUserAction,
5201
4801
  onDismissNotification,
5202
4802
  onSubmitFeedback,
5203
- typingSpeed = 4,
5204
- sidePanelOpen = false
4803
+ typingSpeed = 4
5205
4804
  }, ref) {
5206
4805
  const noop = react.useCallback(async () => {
5207
4806
  }, []);
@@ -5209,11 +4808,11 @@ var MessageListV2 = react.forwardRef(
5209
4808
  const scrollInnerRef = react.useRef(null);
5210
4809
  const isNearBottomRef = react.useRef(true);
5211
4810
  const [showScrollBtn, setShowScrollBtn] = react.useState(false);
4811
+ const [expiredPromptViewState, setExpiredPromptViewState] = react.useState({});
4812
+ const expiredUserActionIdsRef = react.useRef(/* @__PURE__ */ new Set());
5212
4813
  const prevCountRef = react.useRef(messages.length);
5213
4814
  const pauseStickUntilUserMessageRef = react.useRef(false);
5214
4815
  const followingBottomRef = react.useRef(true);
5215
- const sidePanelOpenRef = react.useRef(sidePanelOpen);
5216
- sidePanelOpenRef.current = sidePanelOpen;
5217
4816
  const lastPinAtRef = react.useRef(0);
5218
4817
  const prevScrollTopRef = react.useRef(0);
5219
4818
  const getDistanceFromBottom = react.useCallback(() => {
@@ -5221,6 +4820,92 @@ var MessageListV2 = react.forwardRef(
5221
4820
  if (!el) return 0;
5222
4821
  return el.scrollHeight - el.scrollTop - el.clientHeight;
5223
4822
  }, []);
4823
+ const messageActivityFingerprint = react.useMemo(() => {
4824
+ const last = messages[messages.length - 1];
4825
+ const promptFingerprint = (userActionPrompts ?? []).map((prompt) => `${getPromptViewKey(prompt)}:${prompt.status}`).join(PROMPT_KEY_SEPARATOR);
4826
+ const notificationFingerprint = (notifications ?? []).map((notification) => notification.id).join(PROMPT_KEY_SEPARATOR);
4827
+ if (!last) {
4828
+ return [
4829
+ 0,
4830
+ isStreaming ? "streaming" : "idle",
4831
+ promptFingerprint,
4832
+ notificationFingerprint
4833
+ ].join(PROMPT_KEY_SEPARATOR);
4834
+ }
4835
+ return [
4836
+ messages.length,
4837
+ isStreaming ? "streaming" : "idle",
4838
+ last.id,
4839
+ last.role,
4840
+ last.content ?? "",
4841
+ last.isStreaming ? "streaming" : "done",
4842
+ last.streamProgress ?? "",
4843
+ last.steps?.length ?? 0,
4844
+ last.errorDetails ?? "",
4845
+ promptFingerprint,
4846
+ notificationFingerprint
4847
+ ].join(PROMPT_KEY_SEPARATOR);
4848
+ }, [isStreaming, messages, notifications, userActionPrompts]);
4849
+ const handleUserActionExpired = react.useCallback(
4850
+ (promptKey, userActionId) => {
4851
+ setExpiredPromptViewState((prev) => {
4852
+ if (prev[promptKey]) return prev;
4853
+ return {
4854
+ ...prev,
4855
+ [promptKey]: { baseline: messageActivityFingerprint, hidden: false }
4856
+ };
4857
+ });
4858
+ if (!expiredUserActionIdsRef.current.has(userActionId)) {
4859
+ expiredUserActionIdsRef.current.add(userActionId);
4860
+ void onExpireUserAction?.(userActionId)?.catch(() => {
4861
+ });
4862
+ }
4863
+ },
4864
+ [messageActivityFingerprint, onExpireUserAction]
4865
+ );
4866
+ react.useEffect(() => {
4867
+ setExpiredPromptViewState((prev) => {
4868
+ let changed = false;
4869
+ const next = {};
4870
+ for (const [key, state] of Object.entries(prev)) {
4871
+ if (!state.hidden && state.baseline !== messageActivityFingerprint) {
4872
+ next[key] = { ...state, hidden: true };
4873
+ changed = true;
4874
+ } else {
4875
+ next[key] = state;
4876
+ }
4877
+ }
4878
+ return changed ? next : prev;
4879
+ });
4880
+ }, [messageActivityFingerprint]);
4881
+ react.useEffect(() => {
4882
+ const livePromptKeys = new Set((userActionPrompts ?? []).map(getPromptViewKey));
4883
+ const liveUserActionIds = new Set((userActionPrompts ?? []).map((p) => p.userActionId));
4884
+ for (const userActionId of expiredUserActionIdsRef.current) {
4885
+ if (!liveUserActionIds.has(userActionId)) {
4886
+ expiredUserActionIdsRef.current.delete(userActionId);
4887
+ }
4888
+ }
4889
+ setExpiredPromptViewState((prev) => {
4890
+ let changed = false;
4891
+ const next = {};
4892
+ for (const [key, state] of Object.entries(prev)) {
4893
+ if (livePromptKeys.has(key)) {
4894
+ next[key] = state;
4895
+ } else {
4896
+ changed = true;
4897
+ }
4898
+ }
4899
+ return changed ? next : prev;
4900
+ });
4901
+ }, [userActionPrompts]);
4902
+ const visibleUserActionPrompts = react.useMemo(
4903
+ () => userActionPrompts?.filter((prompt) => {
4904
+ const promptKey = getPromptViewKey(prompt);
4905
+ return !expiredPromptViewState[promptKey]?.hidden;
4906
+ }),
4907
+ [expiredPromptViewState, userActionPrompts]
4908
+ );
5224
4909
  const pinToBottom = react.useCallback((behavior = "instant") => {
5225
4910
  const el = scrollRef.current;
5226
4911
  if (!el) return;
@@ -5261,25 +4946,17 @@ var MessageListV2 = react.forwardRef(
5261
4946
  const nearBottom = distance <= SCROLL_THRESHOLD2;
5262
4947
  isNearBottomRef.current = nearBottom;
5263
4948
  setShowScrollBtn(!nearBottom);
4949
+ const sincePin = performance.now() - lastPinAtRef.current;
4950
+ if (sincePin < 250) return;
5264
4951
  const scrolledUp = currentScrollTop < prevScrollTop - USER_SCROLL_UP_EPSILON;
5265
4952
  if (scrolledUp) {
5266
4953
  followingBottomRef.current = false;
5267
4954
  pauseStickUntilUserMessageRef.current = true;
5268
- return;
5269
- }
5270
- const sincePin = performance.now() - lastPinAtRef.current;
5271
- if (sincePin < 250) return;
5272
- if (nearBottom) {
4955
+ } else if (nearBottom) {
5273
4956
  followingBottomRef.current = true;
5274
4957
  pauseStickUntilUserMessageRef.current = false;
5275
4958
  }
5276
4959
  }, [getDistanceFromBottom]);
5277
- const handleWheel = react.useCallback((e) => {
5278
- if (e.deltaY < 0) {
5279
- followingBottomRef.current = false;
5280
- pauseStickUntilUserMessageRef.current = true;
5281
- }
5282
- }, []);
5283
4960
  react.useEffect(() => {
5284
4961
  const prevCount = prevCountRef.current;
5285
4962
  prevCountRef.current = messages.length;
@@ -5289,7 +4966,7 @@ var MessageListV2 = react.forwardRef(
5289
4966
  pauseStickUntilUserMessageRef.current = false;
5290
4967
  followingBottomRef.current = true;
5291
4968
  requestAnimationFrame(() => scrollToBottom());
5292
- } else if (!sidePanelOpenRef.current && !pauseStickUntilUserMessageRef.current && followingBottomRef.current) {
4969
+ } else if (!pauseStickUntilUserMessageRef.current && followingBottomRef.current) {
5293
4970
  requestAnimationFrame(() => scrollToBottom("instant"));
5294
4971
  }
5295
4972
  }
@@ -5298,16 +4975,27 @@ var MessageListV2 = react.forwardRef(
5298
4975
  const inner = scrollInnerRef.current;
5299
4976
  if (!inner) return;
5300
4977
  const pinIfFollowing = () => {
5301
- if (sidePanelOpenRef.current) return;
5302
4978
  if (pauseStickUntilUserMessageRef.current) return;
5303
4979
  if (!followingBottomRef.current) return;
5304
- if (getDistanceFromBottom() > SCROLL_THRESHOLD2) return;
5305
4980
  pinToBottom("instant");
5306
4981
  };
5307
- const ro = new ResizeObserver(pinIfFollowing);
4982
+ const ro = new ResizeObserver(() => {
4983
+ pinIfFollowing();
4984
+ });
5308
4985
  ro.observe(inner);
5309
- return () => ro.disconnect();
5310
- }, [pinToBottom, getDistanceFromBottom]);
4986
+ const mo = new MutationObserver(() => {
4987
+ pinIfFollowing();
4988
+ });
4989
+ mo.observe(inner, {
4990
+ childList: true,
4991
+ subtree: true,
4992
+ characterData: true
4993
+ });
4994
+ return () => {
4995
+ ro.disconnect();
4996
+ mo.disconnect();
4997
+ };
4998
+ }, [pinToBottom]);
5311
4999
  react.useEffect(() => {
5312
5000
  if (messages.length > 0) {
5313
5001
  setTimeout(() => scrollToBottom("instant"), 50);
@@ -5319,7 +5007,6 @@ var MessageListV2 = react.forwardRef(
5319
5007
  {
5320
5008
  ref: scrollRef,
5321
5009
  onScroll: handleScroll,
5322
- onWheel: handleWheel,
5323
5010
  className: "payman-v2-message-scroll payman-v2-scrollbar",
5324
5011
  children: /* @__PURE__ */ jsxRuntime.jsxs(
5325
5012
  "div",
@@ -5333,7 +5020,6 @@ var MessageListV2 = react.forwardRef(
5333
5020
  message,
5334
5021
  onEdit: onEditUserMessage,
5335
5022
  onRetry: onRetryUserMessage,
5336
- onImageClick,
5337
5023
  retryDisabled,
5338
5024
  actions: messageActions?.userMessageActions
5339
5025
  }
@@ -5356,16 +5042,20 @@ var MessageListV2 = react.forwardRef(
5356
5042
  },
5357
5043
  note.id
5358
5044
  )),
5359
- userActionPrompts?.map((prompt) => /* @__PURE__ */ jsxRuntime.jsx(
5360
- UserActionInline,
5361
- {
5362
- prompt,
5363
- onSubmit: onSubmitUserAction ?? noop,
5364
- onCancel: onCancelUserAction ?? noop,
5365
- onResend: onResendUserAction ?? noop
5366
- },
5367
- prompt.toolCallId || prompt.userActionId
5368
- ))
5045
+ visibleUserActionPrompts?.map((prompt) => {
5046
+ const promptKey = getPromptViewKey(prompt);
5047
+ return /* @__PURE__ */ jsxRuntime.jsx(
5048
+ UserActionInline,
5049
+ {
5050
+ prompt,
5051
+ onSubmit: onSubmitUserAction ?? noop,
5052
+ onCancel: onCancelUserAction ?? noop,
5053
+ onResend: onResendUserAction ?? noop,
5054
+ onExpired: () => handleUserActionExpired(promptKey, prompt.userActionId)
5055
+ },
5056
+ promptKey
5057
+ );
5058
+ })
5369
5059
  ]
5370
5060
  }
5371
5061
  )
@@ -5395,121 +5085,11 @@ var MessageListV2 = react.forwardRef(
5395
5085
  ] });
5396
5086
  }
5397
5087
  );
5398
- function FilePreviewModal({ src, name, onClose }) {
5399
- const [isMounted, setIsMounted] = react.useState(false);
5400
- const [isLoaded, setIsLoaded] = react.useState(false);
5401
- react.useEffect(() => {
5402
- setIsMounted(true);
5403
- return () => setIsMounted(false);
5404
- }, []);
5405
- react.useEffect(() => {
5406
- setIsLoaded(false);
5407
- }, [src]);
5408
- const handleKeyDown = react.useCallback(
5409
- (e) => {
5410
- if (e.key === "Escape") onClose();
5411
- },
5412
- [onClose]
5413
- );
5414
- react.useEffect(() => {
5415
- if (!src || typeof document === "undefined") return;
5416
- document.addEventListener("keydown", handleKeyDown);
5417
- const prev = document.body.style.overflow;
5418
- document.body.style.overflow = "hidden";
5419
- return () => {
5420
- document.removeEventListener("keydown", handleKeyDown);
5421
- document.body.style.overflow = prev;
5422
- };
5423
- }, [src, handleKeyDown]);
5424
- if (!isMounted || typeof document === "undefined") return null;
5425
- return reactDom.createPortal(
5426
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: src ? /* @__PURE__ */ jsxRuntime.jsxs(
5427
- framerMotion.motion.div,
5428
- {
5429
- className: "payman-v2-file-preview-overlay",
5430
- initial: { opacity: 0 },
5431
- animate: { opacity: 1 },
5432
- exit: { opacity: 0 },
5433
- transition: { duration: 0.18 },
5434
- onClick: onClose,
5435
- role: "dialog",
5436
- "aria-modal": "true",
5437
- "aria-label": `Preview: ${name}`,
5438
- children: [
5439
- /* @__PURE__ */ jsxRuntime.jsx(
5440
- "button",
5441
- {
5442
- type: "button",
5443
- className: "payman-v2-file-preview-close",
5444
- "aria-label": "Close preview",
5445
- onClick: (e) => {
5446
- e.stopPropagation();
5447
- onClose();
5448
- },
5449
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 18, strokeWidth: 2 })
5450
- }
5451
- ),
5452
- /* @__PURE__ */ jsxRuntime.jsx(
5453
- framerMotion.motion.div,
5454
- {
5455
- className: "payman-v2-file-preview-inner",
5456
- initial: { scale: 0.93, opacity: 0 },
5457
- animate: { scale: isLoaded ? 1 : 0.93, opacity: isLoaded ? 1 : 0 },
5458
- exit: { scale: 0.93, opacity: 0 },
5459
- transition: { duration: 0.2 },
5460
- onClick: (e) => e.stopPropagation(),
5461
- children: /* @__PURE__ */ jsxRuntime.jsx(
5462
- "img",
5463
- {
5464
- src,
5465
- alt: name,
5466
- className: "payman-v2-file-preview-img",
5467
- draggable: false,
5468
- onLoad: () => setIsLoaded(true)
5469
- }
5470
- )
5471
- }
5472
- )
5473
- ]
5474
- },
5475
- "file-preview"
5476
- ) : null }),
5477
- document.body
5478
- );
5479
- }
5480
- function normalizeExtension(ext) {
5481
- return ext.replace(/^\./, "").toLowerCase();
5482
- }
5483
- function extOf(filename) {
5484
- return filename.split(".").pop()?.toLowerCase() ?? "";
5485
- }
5486
- function isAllowedImage(file, allowedExtensions) {
5487
- const allowed = new Set(allowedExtensions.map(normalizeExtension));
5488
- const ext = extOf(file.name);
5489
- if (allowed.has(ext)) return true;
5490
- if (!file.type.startsWith("image/")) return false;
5491
- const mimeSubtype = file.type.slice("image/".length).toLowerCase();
5492
- return allowed.has(mimeSubtype) || mimeSubtype === "jpeg" && allowed.has("jpg");
5493
- }
5494
- function isAllowedDocument(file, allowedExtensions) {
5495
- const allowed = new Set(allowedExtensions.map(normalizeExtension));
5496
- return allowed.has(extOf(file.name));
5497
- }
5498
- function fileTypeLabel(file, kind) {
5499
- if (kind === "image") return "Image";
5500
- const ext = extOf(file.name);
5501
- return ext ? ext.toUpperCase() : "Document";
5502
- }
5503
5088
  var ChatInputV2 = react.forwardRef(
5504
5089
  function ChatInputV22({
5505
5090
  onSend,
5506
5091
  disabled = false,
5507
5092
  isStreaming = false,
5508
- isUploadingAttachments = false,
5509
- attachmentsReady = true,
5510
- hasAttachmentUploadErrors = false,
5511
- attachmentUploadStatusById = {},
5512
- uploadedAttachmentPayloads = [],
5513
5093
  placeholder = "Reply...",
5514
5094
  enableVoice = false,
5515
5095
  voiceAvailable = false,
@@ -5523,13 +5103,6 @@ var ChatInputV2 = react.forwardRef(
5523
5103
  showAttachFileButton = true,
5524
5104
  onUploadImageClick,
5525
5105
  onAttachFileClick,
5526
- allowedImageExtensions = ["png", "jpg", "jpeg", "gif", "webp"],
5527
- allowedFileExtensions = ["pdf", "docx", "xlsx", "xls"],
5528
- maxCount,
5529
- maxFileBytes,
5530
- maxTotalBytes,
5531
- onFilesChange,
5532
- onAttachmentsChange,
5533
5106
  editingMessageId = null,
5534
5107
  onClearEditing,
5535
5108
  analysisMode,
@@ -5543,140 +5116,21 @@ var ChatInputV2 = react.forwardRef(
5543
5116
  const [selectedCommandIndex, setSelectedCommandIndex] = react.useState(0);
5544
5117
  const [inlineHint, setInlineHint] = react.useState(null);
5545
5118
  const [commandMenuDismissed, setCommandMenuDismissed] = react.useState(false);
5546
- const [attachedFiles, setAttachedFiles] = react.useState([]);
5547
- const [previewFile, setPreviewFile] = react.useState(null);
5548
- const [isSending, setIsSending] = react.useState(false);
5549
5119
  const textareaRef = react.useRef(null);
5550
5120
  const actionsRef = react.useRef(null);
5551
- const imageInputRef = react.useRef(null);
5552
- const fileInputRef = react.useRef(null);
5553
5121
  const preRecordTextRef = react.useRef("");
5554
5122
  const voiceTooltipTimerRef = react.useRef(
5555
5123
  null
5556
5124
  );
5557
5125
  const voiceDraftSyncActiveRef = react.useRef(false);
5558
- const chatContext = react.useContext(PaymanChatContext);
5559
- react.useEffect(() => {
5560
- return () => {
5561
- attachedFiles.forEach((f) => URL.revokeObjectURL(f.objectUrl));
5562
- };
5563
- }, []);
5564
- const notifyAttachmentList = react.useCallback(
5565
- (files) => {
5566
- onFilesChange?.(files.map((f) => f.file));
5567
- onAttachmentsChange?.(files.map((f) => ({ id: f.id, file: f.file })));
5568
- },
5569
- [onAttachmentsChange, onFilesChange]
5570
- );
5571
- const clearAttachmentsFromInput = react.useCallback(() => {
5572
- setAttachedFiles((prev) => {
5573
- prev.forEach((f) => URL.revokeObjectURL(f.objectUrl));
5574
- return [];
5575
- });
5576
- setPreviewFile(null);
5577
- onFilesChange?.([]);
5578
- onAttachmentsChange?.([]);
5579
- }, [onAttachmentsChange, onFilesChange]);
5580
- const addFiles = react.useCallback(
5581
- (incoming, source) => {
5582
- const isImage = source === "image";
5583
- const allowedExtensions = isImage ? allowedImageExtensions : allowedFileExtensions;
5584
- const isAllowed = isImage ? isAllowedImage : isAllowedDocument;
5585
- const kindLabel = isImage ? "image" : "document";
5586
- setAttachedFiles((prev) => {
5587
- const accepted = [];
5588
- let rejectedType = false;
5589
- let hitCountLimit = false;
5590
- let hitFileSizeLimit = false;
5591
- let hitTotalSizeLimit = false;
5592
- let oversizedName = "";
5593
- const currentTotalBytes = prev.reduce(
5594
- (sum, item) => sum + item.file.size,
5595
- 0
5596
- );
5597
- let addedBytes = 0;
5598
- for (const file of incoming) {
5599
- if (!isAllowed(file, allowedExtensions)) {
5600
- rejectedType = true;
5601
- continue;
5602
- }
5603
- if (maxFileBytes != null && file.size > maxFileBytes) {
5604
- hitFileSizeLimit = true;
5605
- oversizedName = file.name;
5606
- continue;
5607
- }
5608
- if (maxTotalBytes != null && currentTotalBytes + addedBytes + file.size > maxTotalBytes) {
5609
- hitTotalSizeLimit = true;
5610
- break;
5611
- }
5612
- if (maxCount != null && prev.length + accepted.length >= maxCount) {
5613
- hitCountLimit = true;
5614
- break;
5615
- }
5616
- accepted.push({
5617
- id: `${Date.now()}-${Math.random()}`,
5618
- file,
5619
- kind: source,
5620
- objectUrl: URL.createObjectURL(file)
5621
- });
5622
- addedBytes += file.size;
5623
- }
5624
- if (rejectedType) {
5625
- setInlineHint(
5626
- `Unsupported ${kindLabel} type. Allowed: ${allowedExtensions.map(normalizeExtension).join(", ")}.`
5627
- );
5628
- } else if (hitFileSizeLimit) {
5629
- setInlineHint(
5630
- maxFileBytes != null ? `"${oversizedName}" exceeds the ${formatAttachmentBytes(maxFileBytes)} per-file limit.` : "File exceeds the maximum allowed size."
5631
- );
5632
- } else if (hitTotalSizeLimit) {
5633
- setInlineHint(
5634
- maxTotalBytes != null ? `Total attachment size would exceed ${formatAttachmentBytes(maxTotalBytes)}.` : "Total attachment size exceeds the limit."
5635
- );
5636
- } else if (hitCountLimit) {
5637
- setInlineHint(
5638
- maxCount === 1 ? "Only 1 attachment is allowed per message." : `Maximum ${maxCount} attachments allowed per message.`
5639
- );
5640
- }
5641
- if (accepted.length === 0) return prev;
5642
- const updated = [...prev, ...accepted];
5643
- notifyAttachmentList(updated);
5644
- return updated;
5645
- });
5646
- },
5647
- [
5648
- allowedFileExtensions,
5649
- allowedImageExtensions,
5650
- maxCount,
5651
- maxFileBytes,
5652
- maxTotalBytes,
5653
- notifyAttachmentList
5654
- ]
5655
- );
5656
- const removeFile = react.useCallback(
5657
- (id) => {
5658
- setAttachedFiles((prev) => {
5659
- const target = prev.find((f) => f.id === id);
5660
- if (target) URL.revokeObjectURL(target.objectUrl);
5661
- const updated = prev.filter((f) => f.id !== id);
5662
- notifyAttachmentList(updated);
5663
- return updated;
5664
- });
5665
- setPreviewFile((p) => p?.id === id ? null : p);
5666
- },
5667
- [notifyAttachmentList]
5668
- );
5669
- const imageAccept = allowedImageExtensions.map((e) => `.${e.replace(/^\./, "")}`).join(",");
5670
- const fileAccept = allowedFileExtensions.map((e) => `.${e.replace(/^\./, "")}`).join(",");
5671
- const isInputBusy = isSending || isUploadingAttachments;
5672
- const isInputLocked = disabled || isRecording || isInputBusy;
5673
- const hasAttachmentOptions = showUploadImageButton || showAttachFileButton;
5674
- const showAttachmentMenuButton = showAttachmentButton && hasAttachmentOptions;
5675
- const showVoiceButton = enableVoice && onVoicePress != null;
5676
- const isVoiceButtonDisabled = disabled || !voiceAvailable;
5677
- const commandQuery = value.startsWith("/") && !value.includes("\n") && !/\s/.test(value) ? value.toLowerCase() : null;
5678
- const commandSuggestions = commandQuery == null ? [] : slashCommands.filter(
5679
- (command) => command.name.toLowerCase().startsWith(commandQuery)
5126
+ const isInputLocked = disabled || isRecording;
5127
+ const hasAttachmentOptions = showUploadImageButton || showAttachFileButton;
5128
+ const showAttachmentMenuButton = showAttachmentButton && hasAttachmentOptions;
5129
+ const showVoiceButton = enableVoice && onVoicePress != null;
5130
+ const isVoiceButtonDisabled = disabled || !voiceAvailable;
5131
+ const commandQuery = value.startsWith("/") && !value.includes("\n") && !/\s/.test(value) ? value.toLowerCase() : null;
5132
+ const commandSuggestions = commandQuery == null ? [] : slashCommands.filter(
5133
+ (command) => command.name.toLowerCase().startsWith(commandQuery)
5680
5134
  );
5681
5135
  const showCommandSuggestions = !commandMenuDismissed && !isRecording && !disabled && commandSuggestions.length > 0;
5682
5136
  react.useEffect(() => {
@@ -5709,10 +5163,9 @@ var ChatInputV2 = react.forwardRef(
5709
5163
  const end = message.length;
5710
5164
  textarea.setSelectionRange(end, end);
5711
5165
  });
5712
- },
5713
- clearAttachments: clearAttachmentsFromInput
5166
+ }
5714
5167
  }),
5715
- [disabled, clearAttachmentsFromInput]
5168
+ [disabled]
5716
5169
  );
5717
5170
  react.useEffect(() => {
5718
5171
  if (!showActions) return;
@@ -5746,23 +5199,8 @@ var ChatInputV2 = react.forwardRef(
5746
5199
  const separator = base && !base.endsWith(" ") && transcribedText ? " " : "";
5747
5200
  setValue(`${base}${separator}${transcribedText}`);
5748
5201
  }, [isRecording, transcribedText]);
5749
- function uploadStatusLabel(status, file, kind) {
5750
- if (status === "uploading") return "Uploading\u2026";
5751
- if (status === "error") return "Upload failed";
5752
- return fileTypeLabel(file, kind);
5753
- }
5754
- const handleSend = react.useCallback(async () => {
5755
- const hasText = value.trim().length > 0;
5756
- const hasFiles = attachedFiles.length > 0;
5757
- if (!hasText && !hasFiles || disabled || isSending || isUploadingAttachments) return;
5758
- if (hasFiles && !attachmentsReady) {
5759
- setInlineHint("Waiting for attachments to finish uploading.");
5760
- return;
5761
- }
5762
- if (hasFiles && hasAttachmentUploadErrors) {
5763
- setInlineHint("Remove or re-add attachments that failed to upload.");
5764
- return;
5765
- }
5202
+ const handleSend = react.useCallback(() => {
5203
+ if (!value.trim() || disabled) return;
5766
5204
  const commandHint = getSlashCommandValidationHint(value);
5767
5205
  if (commandHint) {
5768
5206
  setInlineHint(commandHint);
@@ -5772,38 +5210,15 @@ var ChatInputV2 = react.forwardRef(
5772
5210
  preRecordTextRef.current = "";
5773
5211
  setInlineHint(null);
5774
5212
  onClearEditing?.();
5775
- const textToSend = value.trim();
5776
- const filesToSend = attachedFiles.map((f) => f.file);
5777
- const attachmentsToSend = [...uploadedAttachmentPayloads];
5213
+ onSend(value.trim());
5778
5214
  setValue("");
5779
- clearAttachmentsFromInput();
5780
5215
  requestAnimationFrame(() => {
5781
5216
  if (textareaRef.current) {
5782
5217
  textareaRef.current.style.height = "auto";
5783
5218
  textareaRef.current.focus();
5784
5219
  }
5785
5220
  });
5786
- setIsSending(true);
5787
- try {
5788
- await onSend(textToSend, filesToSend, attachmentsToSend);
5789
- } catch {
5790
- setInlineHint("Failed to send message. Please try again.");
5791
- } finally {
5792
- setIsSending(false);
5793
- }
5794
- }, [
5795
- value,
5796
- attachedFiles,
5797
- uploadedAttachmentPayloads,
5798
- disabled,
5799
- isSending,
5800
- isUploadingAttachments,
5801
- attachmentsReady,
5802
- hasAttachmentUploadErrors,
5803
- onClearEditing,
5804
- onSend,
5805
- clearAttachmentsFromInput
5806
- ]);
5221
+ }, [value, disabled, onClearEditing, onSend]);
5807
5222
  const selectCommand = react.useCallback(
5808
5223
  (command) => {
5809
5224
  const insertText = command.insertText ?? `${command.name} `;
@@ -5848,30 +5263,18 @@ var ChatInputV2 = react.forwardRef(
5848
5263
  }
5849
5264
  if (e.key === "Enter" && !e.shiftKey) {
5850
5265
  e.preventDefault();
5851
- if (isStreaming || isSending || isUploadingAttachments) return;
5852
- void handleSend();
5266
+ if (isStreaming) return;
5267
+ handleSend();
5853
5268
  }
5854
5269
  };
5855
5270
  const handleUploadImageClick = () => {
5856
- imageInputRef.current?.click();
5857
5271
  onUploadImageClick?.();
5858
5272
  setShowActions(false);
5859
5273
  };
5860
5274
  const handleAttachFileClick = () => {
5861
- fileInputRef.current?.click();
5862
5275
  onAttachFileClick?.();
5863
5276
  setShowActions(false);
5864
5277
  };
5865
- const handleImageFilesSelected = (e) => {
5866
- const files = Array.from(e.target.files ?? []);
5867
- if (files.length) addFiles(files, "image");
5868
- e.target.value = "";
5869
- };
5870
- const handleDocFilesSelected = (e) => {
5871
- const files = Array.from(e.target.files ?? []);
5872
- if (files.length) addFiles(files, "file");
5873
- e.target.value = "";
5874
- };
5875
5278
  const hideVoiceTooltip = react.useCallback(() => {
5876
5279
  if (voiceTooltipTimerRef.current) {
5877
5280
  clearTimeout(voiceTooltipTimerRef.current);
@@ -5899,39 +5302,9 @@ var ChatInputV2 = react.forwardRef(
5899
5302
  }
5900
5303
  onVoicePress();
5901
5304
  };
5902
- const canSend = (value.trim().length > 0 || attachedFiles.length > 0) && !disabled;
5903
- const sendDisabled = !canSend || isStreaming || isUploadingAttachments || !attachmentsReady || hasAttachmentUploadErrors || isSending;
5305
+ const canSend = value.trim().length > 0 && !disabled;
5306
+ const sendDisabled = !canSend || isStreaming;
5904
5307
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-input-container", children: [
5905
- /* @__PURE__ */ jsxRuntime.jsx(
5906
- "input",
5907
- {
5908
- ref: imageInputRef,
5909
- type: "file",
5910
- accept: imageAccept,
5911
- multiple: maxCount == null || maxCount > 1,
5912
- style: { display: "none" },
5913
- onChange: handleImageFilesSelected
5914
- }
5915
- ),
5916
- /* @__PURE__ */ jsxRuntime.jsx(
5917
- "input",
5918
- {
5919
- ref: fileInputRef,
5920
- type: "file",
5921
- accept: fileAccept,
5922
- multiple: maxCount == null || maxCount > 1,
5923
- style: { display: "none" },
5924
- onChange: handleDocFilesSelected
5925
- }
5926
- ),
5927
- /* @__PURE__ */ jsxRuntime.jsx(
5928
- FilePreviewModal,
5929
- {
5930
- src: previewFile?.kind === "image" ? previewFile.objectUrl : null,
5931
- name: previewFile?.file.name ?? "",
5932
- onClose: () => setPreviewFile(null)
5933
- }
5934
- ),
5935
5308
  /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showCommandSuggestions && /* @__PURE__ */ jsxRuntime.jsx(
5936
5309
  framerMotion.motion.div,
5937
5310
  {
@@ -5970,115 +5343,6 @@ var ChatInputV2 = react.forwardRef(
5970
5343
  ),
5971
5344
  children: [
5972
5345
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-input-body", children: [
5973
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: attachedFiles.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
5974
- framerMotion.motion.div,
5975
- {
5976
- initial: { opacity: 0, height: 0 },
5977
- animate: { opacity: 1, height: "auto" },
5978
- exit: { opacity: 0, height: 0 },
5979
- transition: { duration: 0.15, ease: "easeOut" },
5980
- className: "payman-v2-file-preview",
5981
- children: attachedFiles.map((af) => {
5982
- const uploadStatus = attachmentUploadStatusById[af.id];
5983
- const isUploadingFile = uploadStatus === "uploading";
5984
- const hasUploadError = uploadStatus === "error";
5985
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-file-preview-item", children: [
5986
- af.kind === "image" ? /* @__PURE__ */ jsxRuntime.jsxs(
5987
- "button",
5988
- {
5989
- type: "button",
5990
- className: cn(
5991
- "payman-v2-file-preview-shell payman-v2-file-preview-shell-clickable",
5992
- hasUploadError && "payman-v2-file-preview-shell-error"
5993
- ),
5994
- onClick: () => setPreviewFile(af),
5995
- disabled: isUploadingFile,
5996
- "aria-label": `Preview ${af.file.name}`,
5997
- children: [
5998
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-file-preview-thumb", children: [
5999
- /* @__PURE__ */ jsxRuntime.jsx(
6000
- "img",
6001
- {
6002
- src: af.objectUrl,
6003
- alt: "",
6004
- draggable: false
6005
- }
6006
- ),
6007
- isUploadingFile && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-file-preview-uploading", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 14, className: "payman-v2-spin" }) })
6008
- ] }),
6009
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-file-preview-info", children: [
6010
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-file-preview-name", children: af.file.name }),
6011
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-file-preview-type", children: uploadStatusLabel(uploadStatus, af.file, af.kind) })
6012
- ] })
6013
- ]
6014
- }
6015
- ) : isPdfFile(af.file) ? /* @__PURE__ */ jsxRuntime.jsxs(
6016
- "button",
6017
- {
6018
- type: "button",
6019
- className: cn(
6020
- "payman-v2-file-preview-shell payman-v2-file-preview-shell-clickable",
6021
- hasUploadError && "payman-v2-file-preview-shell-error"
6022
- ),
6023
- onClick: () => chatContext?.openPdfSheet(
6024
- af.objectUrl,
6025
- af.file.name
6026
- ),
6027
- disabled: isUploadingFile,
6028
- "aria-label": `Preview ${af.file.name}`,
6029
- children: [
6030
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-file-preview-thumb", children: isUploadingFile ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 16, className: "payman-v2-spin payman-v2-file-preview-doc-icon" }) : /* @__PURE__ */ jsxRuntime.jsx(
6031
- lucideReact.FileText,
6032
- {
6033
- size: 16,
6034
- strokeWidth: 1.75,
6035
- className: "payman-v2-file-preview-doc-icon"
6036
- }
6037
- ) }),
6038
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-file-preview-info", children: [
6039
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-file-preview-name", children: af.file.name }),
6040
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-file-preview-type", children: uploadStatusLabel(uploadStatus, af.file, af.kind) })
6041
- ] })
6042
- ]
6043
- }
6044
- ) : /* @__PURE__ */ jsxRuntime.jsxs(
6045
- "div",
6046
- {
6047
- className: cn(
6048
- "payman-v2-file-preview-shell",
6049
- hasUploadError && "payman-v2-file-preview-shell-error"
6050
- ),
6051
- children: [
6052
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-file-preview-thumb", children: isUploadingFile ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 16, className: "payman-v2-spin payman-v2-file-preview-doc-icon" }) : /* @__PURE__ */ jsxRuntime.jsx(
6053
- lucideReact.FileText,
6054
- {
6055
- size: 16,
6056
- strokeWidth: 1.75,
6057
- className: "payman-v2-file-preview-doc-icon"
6058
- }
6059
- ) }),
6060
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-file-preview-info", children: [
6061
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-file-preview-name", children: af.file.name }),
6062
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-file-preview-type", children: uploadStatusLabel(uploadStatus, af.file, af.kind) })
6063
- ] })
6064
- ]
6065
- }
6066
- ),
6067
- /* @__PURE__ */ jsxRuntime.jsx(
6068
- "button",
6069
- {
6070
- type: "button",
6071
- className: "payman-v2-file-preview-remove",
6072
- onClick: () => removeFile(af.id),
6073
- "aria-label": `Remove ${af.file.name}`,
6074
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 12, strokeWidth: 2.5 })
6075
- }
6076
- )
6077
- ] }, af.id);
6078
- })
6079
- },
6080
- "file-preview"
6081
- ) }),
6082
5346
  /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: editingMessageId && /* @__PURE__ */ jsxRuntime.jsxs(
6083
5347
  framerMotion.motion.div,
6084
5348
  {
@@ -6310,13 +5574,13 @@ var ChatInputV2 = react.forwardRef(
6310
5574
  "button",
6311
5575
  {
6312
5576
  type: "button",
6313
- onClick: () => void handleSend(),
5577
+ onClick: handleSend,
6314
5578
  disabled: sendDisabled,
6315
5579
  className: cn(
6316
5580
  "payman-v2-input-send-btn",
6317
5581
  sendDisabled && "payman-v2-input-send-btn-disabled"
6318
5582
  ),
6319
- "aria-label": isUploadingAttachments || isSending ? "Uploading attachments" : "Send message",
5583
+ "aria-label": "Send message",
6320
5584
  children: /* @__PURE__ */ jsxRuntime.jsx(
6321
5585
  lucideReact.ArrowUp,
6322
5586
  {
@@ -6851,389 +6115,6 @@ function TimelineBars({
6851
6115
  )
6852
6116
  ] });
6853
6117
  }
6854
-
6855
- // src/utils/pdfPreview.ts
6856
- var PdfPreviewError = class extends Error {
6857
- constructor(kind, title, userMessage) {
6858
- super(userMessage);
6859
- __publicField(this, "kind");
6860
- __publicField(this, "title");
6861
- __publicField(this, "userMessage");
6862
- this.name = "PdfPreviewError";
6863
- this.kind = kind;
6864
- this.title = title;
6865
- this.userMessage = userMessage;
6866
- }
6867
- };
6868
- function classifyErrorBody(body, status) {
6869
- const text = body.replace(/\s+/g, " ").trim();
6870
- if (/signed expiry time|must be after signed start time|expired|expir/i.test(text)) {
6871
- return new PdfPreviewError(
6872
- "expired",
6873
- "Link expired",
6874
- "This download link has expired. Ask the assistant to generate a new copy of the document."
6875
- );
6876
- }
6877
- if (/AuthenticationFailed|authorization header|formed correctly including the signature/i.test(
6878
- text
6879
- )) {
6880
- return new PdfPreviewError(
6881
- "forbidden",
6882
- "Link no longer valid",
6883
- "This preview link is no longer valid. Request a fresh download link and try again."
6884
- );
6885
- }
6886
- if (status === 404 || /BlobNotFound|ResourceNotFound|The specified blob does not exist/i.test(text)) {
6887
- return new PdfPreviewError(
6888
- "not_found",
6889
- "Document not found",
6890
- "The file is no longer available. It may have been removed or the link is incorrect."
6891
- );
6892
- }
6893
- if (status === 403) {
6894
- return new PdfPreviewError(
6895
- "forbidden",
6896
- "Can't open document",
6897
- "You may not have access to this file, or the link has expired."
6898
- );
6899
- }
6900
- if (status === 401) {
6901
- return new PdfPreviewError(
6902
- "forbidden",
6903
- "Access denied",
6904
- "This preview link could not be verified. Request a new download link."
6905
- );
6906
- }
6907
- return new PdfPreviewError(
6908
- "unknown",
6909
- "Can't preview document",
6910
- "Something went wrong while opening this file. Try downloading it or request a new link."
6911
- );
6912
- }
6913
- function normalizePdfPreviewError(error) {
6914
- if (error instanceof PdfPreviewError) return error;
6915
- if (error instanceof TypeError) {
6916
- return new PdfPreviewError(
6917
- "network",
6918
- "Connection problem",
6919
- "Could not load the document. Check your connection and try again."
6920
- );
6921
- }
6922
- return new PdfPreviewError(
6923
- "unknown",
6924
- "Can't preview document",
6925
- "Something went wrong while opening this file. Try downloading it or request a new link."
6926
- );
6927
- }
6928
- async function assertPdfBlob(blob, status) {
6929
- if (!blob.size) {
6930
- return Promise.reject(
6931
- new PdfPreviewError(
6932
- "empty",
6933
- "Document unavailable",
6934
- "The file response was empty. Request a new download link and try again."
6935
- )
6936
- );
6937
- }
6938
- const head = await blob.slice(0, Math.min(blob.size, 1024)).text();
6939
- if (head.startsWith("%PDF")) {
6940
- return blob;
6941
- }
6942
- throw classifyErrorBody(head, status);
6943
- }
6944
- async function fetchPdfBlob(src) {
6945
- let response;
6946
- try {
6947
- response = await fetch(src, {
6948
- method: "GET",
6949
- headers: { Accept: "application/pdf,*/*" }
6950
- });
6951
- } catch {
6952
- throw new PdfPreviewError(
6953
- "network",
6954
- "Connection problem",
6955
- "Could not load the document. Check your connection and try again."
6956
- );
6957
- }
6958
- const blob = await response.blob();
6959
- if (!response.ok) {
6960
- const body = await blob.text().catch(() => "");
6961
- throw classifyErrorBody(body, response.status);
6962
- }
6963
- return assertPdfBlob(blob, response.status);
6964
- }
6965
- var MIN_WIDTH = 320;
6966
- var MAX_WIDTH_RATIO = 0.6;
6967
- var DEFAULT_WIDTH = 520;
6968
- var SPRING = { type: "spring", stiffness: 340, damping: 34, mass: 0.85 };
6969
- var SHEET_EXIT = { duration: 0.22, ease: [0.4, 0, 1, 1] };
6970
- function pdfDownloadName(title) {
6971
- const base = title.trim() || "document";
6972
- return base.toLowerCase().endsWith(".pdf") ? base : `${base}.pdf`;
6973
- }
6974
- function clampSplitWidth(w) {
6975
- const maxW = Math.floor(window.innerWidth * MAX_WIDTH_RATIO);
6976
- return Math.max(MIN_WIDTH, Math.min(maxW, w));
6977
- }
6978
- function PdfSheetV2({
6979
- src,
6980
- title,
6981
- onClose,
6982
- mode = "split"
6983
- }) {
6984
- return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: src && /* @__PURE__ */ jsxRuntime.jsx(
6985
- PdfSheetPanel,
6986
- {
6987
- src,
6988
- title,
6989
- onClose,
6990
- mode
6991
- },
6992
- "pdf-panel"
6993
- ) });
6994
- }
6995
- function PdfSheetPanel({
6996
- src,
6997
- title,
6998
- onClose,
6999
- mode
7000
- }) {
7001
- const isSheet = mode === "sheet";
7002
- const [isLoaded, setIsLoaded] = react.useState(false);
7003
- const [isDownloading, setIsDownloading] = react.useState(false);
7004
- const [previewUrl, setPreviewUrl] = react.useState(null);
7005
- const [previewError, setPreviewError] = react.useState(null);
7006
- const [loadAttempt, setLoadAttempt] = react.useState(0);
7007
- const blobRef = react.useRef(null);
7008
- const objectUrlRef = react.useRef(null);
7009
- const [panelWidth, setPanelWidth] = react.useState(DEFAULT_WIDTH);
7010
- const [splitOpened, setSplitOpened] = react.useState(false);
7011
- const [isDragging, setIsDragging] = react.useState(false);
7012
- react.useEffect(() => {
7013
- setIsLoaded(false);
7014
- setPreviewUrl(null);
7015
- setPreviewError(null);
7016
- setPanelWidth(DEFAULT_WIDTH);
7017
- setSplitOpened(false);
7018
- blobRef.current = null;
7019
- if (objectUrlRef.current) {
7020
- URL.revokeObjectURL(objectUrlRef.current);
7021
- objectUrlRef.current = null;
7022
- }
7023
- let cancelled = false;
7024
- const loadPreview = async () => {
7025
- try {
7026
- const blob = await fetchPdfBlob(src);
7027
- if (cancelled) return;
7028
- blobRef.current = blob;
7029
- const objectUrl = URL.createObjectURL(blob);
7030
- objectUrlRef.current = objectUrl;
7031
- setPreviewUrl(objectUrl);
7032
- } catch (error) {
7033
- if (!cancelled) {
7034
- setPreviewError(normalizePdfPreviewError(error));
7035
- }
7036
- }
7037
- };
7038
- void loadPreview();
7039
- return () => {
7040
- cancelled = true;
7041
- if (objectUrlRef.current) {
7042
- URL.revokeObjectURL(objectUrlRef.current);
7043
- objectUrlRef.current = null;
7044
- }
7045
- blobRef.current = null;
7046
- };
7047
- }, [src, loadAttempt]);
7048
- react.useEffect(() => {
7049
- if (isSheet) return;
7050
- const onWindowResize = () => setPanelWidth((size) => clampSplitWidth(size));
7051
- window.addEventListener("resize", onWindowResize);
7052
- return () => window.removeEventListener("resize", onWindowResize);
7053
- }, [isSheet]);
7054
- const handleKeyDown = react.useCallback(
7055
- (e) => {
7056
- if (e.key === "Escape") onClose();
7057
- },
7058
- [onClose]
7059
- );
7060
- react.useEffect(() => {
7061
- document.addEventListener("keydown", handleKeyDown);
7062
- return () => document.removeEventListener("keydown", handleKeyDown);
7063
- }, [handleKeyDown]);
7064
- const canDownload = isLoaded && previewUrl != null && !previewError;
7065
- const handleDownload = async () => {
7066
- const filename = pdfDownloadName(title);
7067
- setIsDownloading(true);
7068
- try {
7069
- const blob = blobRef.current ?? await fetchPdfBlob(src);
7070
- const objectUrl = URL.createObjectURL(blob);
7071
- const a = document.createElement("a");
7072
- a.href = objectUrl;
7073
- a.download = filename;
7074
- a.target = "_blank";
7075
- a.rel = "noopener noreferrer";
7076
- document.body.appendChild(a);
7077
- a.click();
7078
- a.remove();
7079
- URL.revokeObjectURL(objectUrl);
7080
- } catch (error) {
7081
- setPreviewError(normalizePdfPreviewError(error));
7082
- } finally {
7083
- setIsDownloading(false);
7084
- }
7085
- };
7086
- const handleRetry = () => {
7087
- setPreviewError(null);
7088
- setIsLoaded(false);
7089
- setPreviewUrl(null);
7090
- setLoadAttempt((attempt) => attempt + 1);
7091
- };
7092
- const handleOpenInNewTab = () => {
7093
- window.open(src, "_blank", "noopener,noreferrer");
7094
- };
7095
- const onResizeMouseDown = (e) => {
7096
- if (e.button !== 0) return;
7097
- e.preventDefault();
7098
- const startX = e.clientX;
7099
- const startW = panelWidth;
7100
- setIsDragging(true);
7101
- const onMove = (ev) => {
7102
- setPanelWidth(clampSplitWidth(startW + (startX - ev.clientX)));
7103
- };
7104
- const onUp = () => {
7105
- setIsDragging(false);
7106
- document.removeEventListener("mousemove", onMove);
7107
- document.removeEventListener("mouseup", onUp);
7108
- };
7109
- document.addEventListener("mousemove", onMove);
7110
- document.addEventListener("mouseup", onUp);
7111
- };
7112
- return /* @__PURE__ */ jsxRuntime.jsxs(
7113
- framerMotion.motion.div,
7114
- {
7115
- className: cn(
7116
- "payman-v2-root payman-v2-pdf-sheet-panel",
7117
- isSheet && "payman-v2-pdf-sheet-panel--sheet"
7118
- ),
7119
- style: { minWidth: 0 },
7120
- initial: isSheet ? { x: "100%", opacity: 0 } : { width: 0, opacity: 0 },
7121
- animate: isSheet ? { x: 0, opacity: 1 } : { width: panelWidth, opacity: 1 },
7122
- exit: isSheet ? { x: "100%", opacity: 0, transition: { x: SHEET_EXIT, opacity: SHEET_EXIT } } : { width: 0, opacity: 0, transition: { width: SHEET_EXIT, opacity: SHEET_EXIT } },
7123
- transition: isSheet ? { x: SPRING, opacity: SPRING } : {
7124
- width: splitOpened ? { duration: 0 } : SPRING,
7125
- opacity: SPRING
7126
- },
7127
- onAnimationComplete: () => {
7128
- if (!isSheet && !splitOpened) setSplitOpened(true);
7129
- },
7130
- children: [
7131
- !isSheet && /* @__PURE__ */ jsxRuntime.jsx(
7132
- "div",
7133
- {
7134
- className: "payman-v2-pdf-sheet-resize-handle",
7135
- onMouseDown: onResizeMouseDown,
7136
- "aria-hidden": "true",
7137
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-pdf-sheet-resize-grip" })
7138
- }
7139
- ),
7140
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-header", children: [
7141
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-header-left", children: [
7142
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-pdf-sheet-file-icon", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { size: 14, strokeWidth: 1.75 }) }),
7143
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-pdf-sheet-title", title, children: title || "Document" })
7144
- ] }),
7145
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-header-actions", children: [
7146
- /* @__PURE__ */ jsxRuntime.jsxs(
7147
- "button",
7148
- {
7149
- type: "button",
7150
- className: "payman-v2-pdf-sheet-download-btn",
7151
- "aria-label": "Download PDF",
7152
- disabled: !canDownload || isDownloading,
7153
- onClick: () => void handleDownload(),
7154
- children: [
7155
- isDownloading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { size: 13, strokeWidth: 2, style: { animation: "payman-v2-spin 0.65s linear infinite" } }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { size: 13, strokeWidth: 2 }),
7156
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: isDownloading ? "Downloading\u2026" : "Download" })
7157
- ]
7158
- }
7159
- ),
7160
- /* @__PURE__ */ jsxRuntime.jsx(
7161
- "button",
7162
- {
7163
- type: "button",
7164
- className: "payman-v2-pdf-sheet-close-btn",
7165
- "aria-label": "Close preview",
7166
- onClick: (e) => {
7167
- e.stopPropagation();
7168
- onClose();
7169
- },
7170
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 15, strokeWidth: 2.25 })
7171
- }
7172
- )
7173
- ] })
7174
- ] }),
7175
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-pdf-sheet-body", children: previewError ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-error", role: "alert", children: [
7176
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-pdf-sheet-error-icon", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { size: 22, strokeWidth: 1.75 }) }),
7177
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-pdf-sheet-error-title", children: previewError.title }),
7178
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-pdf-sheet-error-message", children: previewError.userMessage }),
7179
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-error-actions", children: [
7180
- previewError.kind === "network" && /* @__PURE__ */ jsxRuntime.jsxs(
7181
- "button",
7182
- {
7183
- type: "button",
7184
- className: "payman-v2-pdf-sheet-error-btn",
7185
- onClick: handleRetry,
7186
- children: [
7187
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: 14, strokeWidth: 2 }),
7188
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Try again" })
7189
- ]
7190
- }
7191
- ),
7192
- (previewError.kind === "network" || previewError.kind === "unknown") && /* @__PURE__ */ jsxRuntime.jsxs(
7193
- "button",
7194
- {
7195
- type: "button",
7196
- className: "payman-v2-pdf-sheet-error-btn payman-v2-pdf-sheet-error-btn-secondary",
7197
- onClick: handleOpenInNewTab,
7198
- children: [
7199
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 14, strokeWidth: 2 }),
7200
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Open link" })
7201
- ]
7202
- }
7203
- )
7204
- ] })
7205
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7206
- (!isLoaded || !previewUrl) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-pdf-sheet-loading", children: [
7207
- /* @__PURE__ */ jsxRuntime.jsx(
7208
- lucideReact.Loader2,
7209
- {
7210
- size: 20,
7211
- strokeWidth: 2,
7212
- style: { animation: "payman-v2-spin 0.65s linear infinite", color: "var(--payman-v2-text-3)" }
7213
- }
7214
- ),
7215
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-pdf-sheet-loading-text", children: previewUrl ? "Preparing preview\u2026" : "Opening document\u2026" })
7216
- ] }),
7217
- previewUrl && /* @__PURE__ */ jsxRuntime.jsx(
7218
- "iframe",
7219
- {
7220
- src: previewUrl,
7221
- title: title || "PDF Preview",
7222
- className: "payman-v2-pdf-sheet-iframe",
7223
- style: {
7224
- opacity: isLoaded ? 1 : 0,
7225
- transition: "opacity 0.3s ease",
7226
- pointerEvents: isDragging ? "none" : "auto"
7227
- },
7228
- onLoad: () => setIsLoaded(true)
7229
- },
7230
- previewUrl
7231
- )
7232
- ] }) })
7233
- ]
7234
- }
7235
- );
7236
- }
7237
6118
  var DEFAULT_USER_ACTION_STATE = {
7238
6119
  prompts: [],
7239
6120
  notifications: []
@@ -7328,8 +6209,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7328
6209
  onLoadMoreMessages,
7329
6210
  isLoadingMoreMessages = false,
7330
6211
  hasMoreMessages = false,
7331
- chat,
7332
- attachmentUpload
6212
+ chat
7333
6213
  }, ref) {
7334
6214
  const [inputValue, setInputValue] = react.useState("");
7335
6215
  const prevInputValueRef = react.useRef(inputValue);
@@ -7383,6 +6263,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7383
6263
  const submitUserAction2 = chat.submitUserAction ?? NOOP_ASYNC;
7384
6264
  const cancelUserAction2 = chat.cancelUserAction ?? NOOP_ASYNC;
7385
6265
  const resendUserAction2 = chat.resendUserAction ?? NOOP_ASYNC;
6266
+ const expireUserAction = chat.expireUserAction ?? NOOP_ASYNC;
7386
6267
  const dismissNotification = chat.dismissNotification ?? NOOP;
7387
6268
  const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
7388
6269
  const {
@@ -7408,19 +6289,6 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7408
6289
  }
7409
6290
  }
7410
6291
  );
7411
- const [pdfSheet, setPdfSheet] = react.useState(null);
7412
- const autoOpenedPdfHrefsRef = react.useRef(/* @__PURE__ */ new Set());
7413
- const pdfPreviewMode = config.pdfPreviewMode ?? "split";
7414
- const openPdfSheet = react.useCallback((href, title, options) => {
7415
- if (options?.auto) {
7416
- if (autoOpenedPdfHrefsRef.current.has(href)) return;
7417
- autoOpenedPdfHrefsRef.current.add(href);
7418
- }
7419
- setPdfSheet({ href, title });
7420
- }, []);
7421
- const closePdfSheet = react.useCallback(() => {
7422
- setPdfSheet(null);
7423
- }, []);
7424
6292
  const contextValue = react.useMemo(
7425
6293
  () => ({
7426
6294
  resetSession,
@@ -7429,8 +6297,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7429
6297
  cancelStream,
7430
6298
  getSessionId,
7431
6299
  getMessages,
7432
- isWaitingForResponse,
7433
- openPdfSheet
6300
+ isWaitingForResponse
7434
6301
  }),
7435
6302
  [
7436
6303
  resetSession,
@@ -7439,8 +6306,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7439
6306
  cancelStream,
7440
6307
  getSessionId,
7441
6308
  getMessages,
7442
- isWaitingForResponse,
7443
- openPdfSheet
6309
+ isWaitingForResponse
7444
6310
  ]
7445
6311
  );
7446
6312
  const {
@@ -7496,10 +6362,7 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7496
6362
  setInputValue("");
7497
6363
  setLightboxSrc(null);
7498
6364
  setLightboxAlt("");
7499
- setPdfSheet(null);
7500
- autoOpenedPdfHrefsRef.current.clear();
7501
6365
  chatInputV2Ref.current?.setDraft("");
7502
- chatInputV2Ref.current?.clearAttachments();
7503
6366
  clearTranscript();
7504
6367
  if (isRecording) {
7505
6368
  stopRecording();
@@ -7550,20 +6413,14 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7550
6413
  emptyStateComponent,
7551
6414
  showResetSession = false,
7552
6415
  enableDeepModeToggle = true,
6416
+ showAttachmentButton = true,
6417
+ showUploadImageButton = true,
6418
+ showAttachFileButton = true,
7553
6419
  messageActions: messageActionsConfig,
7554
6420
  enableSlashCommands = true,
7555
6421
  slashCommands: slashCommandsConfig,
7556
6422
  commandPermissions
7557
6423
  } = config;
7558
- const attachmentSettings = react.useMemo(
7559
- () => resolveChatAttachmentConfig(config),
7560
- [
7561
- config.attachments,
7562
- config.showAttachmentButton,
7563
- config.showUploadImageButton,
7564
- config.showAttachFileButton
7565
- ]
7566
- );
7567
6424
  const messageActions = react.useMemo(
7568
6425
  () => ({
7569
6426
  userMessageActions: {
@@ -7651,22 +6508,11 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7651
6508
  };
7652
6509
  const userActionPrompts = isUserActionSupported ? userActionState.prompts : void 0;
7653
6510
  const notifications = userActionState.notifications;
7654
- const handleAttachmentsChange = react.useCallback(
7655
- (attachments) => {
7656
- attachmentUpload.syncAttachments(attachments);
7657
- },
7658
- [attachmentUpload]
7659
- );
7660
- const handleV2Send = (text, files = [], attachments = []) => {
6511
+ const handleV2Send = (text) => {
7661
6512
  if (isRecording) stopRecording();
7662
- if ((text.trim() || files.length > 0) && !disableInput && isSessionParamsConfigured) {
7663
- if (files.length > 0 && attachments.length === 0) return;
6513
+ if (text.trim() && !disableInput && isSessionParamsConfigured) {
7664
6514
  setEditingMessageId(null);
7665
- void sendMessage(text.trim(), {
7666
- analysisMode: effectiveAnalysisMode,
7667
- attachments,
7668
- files
7669
- });
6515
+ void sendMessage(text.trim(), { analysisMode: effectiveAnalysisMode });
7670
6516
  }
7671
6517
  };
7672
6518
  const handleVoicePress = react.useCallback(async () => {
@@ -7714,214 +6560,156 @@ var PaymanChatInner = react.forwardRef(function PaymanChatInner2({
7714
6560
  style,
7715
6561
  children: [
7716
6562
  children,
7717
- /* @__PURE__ */ jsxRuntime.jsxs(
7718
- "div",
6563
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsxRuntime.jsx(
6564
+ framerMotion.motion.div,
7719
6565
  {
7720
- className: cn(
7721
- "payman-v2-split-layout",
7722
- pdfPreviewMode === "sheet" && "payman-v2-split-layout--sheet"
7723
- ),
7724
- children: [
7725
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-chat-column", children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsxRuntime.jsx(
7726
- framerMotion.motion.div,
7727
- {
7728
- initial: { opacity: 1 },
7729
- exit: { opacity: 0 },
7730
- transition: { duration: 0.3 },
7731
- className: "payman-v2-chat-layout",
7732
- style: { justifyContent: "center", alignItems: "center" },
7733
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", flex: 1, width: "100%" }, children: [
7734
- /* @__PURE__ */ jsxRuntime.jsx(
7735
- MessageList,
7736
- {
7737
- messages,
7738
- isLoading: false,
7739
- emptyStateText,
7740
- showEmptyStateIcon,
7741
- emptyStateComponent,
7742
- layout,
7743
- showTimestamps,
7744
- stage: config.stage || "DEVELOPMENT",
7745
- animated,
7746
- showAgentName,
7747
- agentName,
7748
- showAvatars,
7749
- showUserAvatar,
7750
- showAssistantAvatar,
7751
- showExecutionSteps,
7752
- showStreamingDot,
7753
- streamingStepsText,
7754
- completedStepsText,
7755
- onExecutionTraceClick,
7756
- onLoadMoreMessages,
7757
- isLoadingMoreMessages,
7758
- hasMoreMessages
7759
- }
7760
- ),
7761
- /* @__PURE__ */ jsxRuntime.jsx(
7762
- framerMotion.motion.div,
7763
- {
7764
- initial: { opacity: 0, y: 12 },
7765
- animate: { opacity: 1, y: 0 },
7766
- transition: { delay: 0.2, duration: 0.4, ease: [0.25, 0.46, 0.45, 0.94] },
7767
- style: { width: "100%" },
7768
- children: hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
7769
- ChatInputV2,
7770
- {
7771
- ref: chatInputV2Ref,
7772
- onSend: handleV2Send,
7773
- onCancel: cancelStream,
7774
- disabled: isV2InputDisabled,
7775
- isStreaming: isWaitingForResponse,
7776
- isUploadingAttachments: attachmentUpload.isUploading,
7777
- attachmentsReady: attachmentUpload.allReady,
7778
- hasAttachmentUploadErrors: attachmentUpload.hasErrors,
7779
- attachmentUploadStatusById: attachmentUpload.statusById,
7780
- uploadedAttachmentPayloads: attachmentUpload.payloads,
7781
- onAttachmentsChange: handleAttachmentsChange,
7782
- placeholder: isRecording ? "Listening..." : placeholder,
7783
- enableVoice: config.enableVoice === true,
7784
- transcribedText: config.enableVoice === true ? transcribedText : "",
7785
- voiceAvailable: config.enableVoice === true && voiceAvailable,
7786
- isRecording,
7787
- onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
7788
- onCancelRecording: handleCancelRecording,
7789
- onConfirmRecording: handleConfirmRecording,
7790
- showResetSession,
7791
- onResetSession: requestResetSession,
7792
- showAttachmentButton: attachmentSettings.showAttachmentButton,
7793
- showUploadImageButton: attachmentSettings.showUploadImageButton,
7794
- showAttachFileButton: attachmentSettings.showAttachFileButton,
7795
- allowedImageExtensions: attachmentSettings.allowedImageExtensions,
7796
- allowedFileExtensions: attachmentSettings.allowedFileExtensions,
7797
- maxCount: attachmentSettings.maxCount,
7798
- maxFileBytes: attachmentSettings.maxFileBytes,
7799
- maxTotalBytes: attachmentSettings.maxTotalBytes,
7800
- onUploadImageClick,
7801
- onAttachFileClick,
7802
- editingMessageId,
7803
- onClearEditing: handleClearEditing,
7804
- analysisMode: enableDeepModeToggle ? analysisMode : void 0,
7805
- onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
7806
- slashCommands
7807
- }
7808
- )
7809
- }
7810
- )
7811
- ] })
7812
- },
7813
- "v2-empty"
7814
- ) : /* @__PURE__ */ jsxRuntime.jsxs(
7815
- framerMotion.motion.div,
7816
- {
7817
- initial: hasEverSentMessage ? { opacity: 0 } : false,
7818
- animate: { opacity: 1 },
7819
- transition: { duration: 0.3 },
7820
- className: "payman-v2-chat-layout",
7821
- children: [
7822
- /* @__PURE__ */ jsxRuntime.jsx(
7823
- MessageListV2,
7824
- {
7825
- ref: messageListV2Ref,
7826
- messages,
7827
- isStreaming: isWaitingForResponse,
7828
- sidePanelOpen: !!pdfSheet,
7829
- onEditUserMessage: handleEditMessageDraft,
7830
- onRetryUserMessage: handleRetryUserMessage,
7831
- onImageClick: handleImageClick,
7832
- onExecutionTraceClick,
7833
- messageActions,
7834
- retryDisabled: isWaitingForResponse,
7835
- typingSpeed: config.typingSpeed ?? 4,
7836
- userActionPrompts,
7837
- notifications,
7838
- onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
7839
- onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
7840
- onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
7841
- onDismissNotification: dismissNotification,
7842
- onSubmitFeedback: handleSubmitFeedback
7843
- }
7844
- ),
7845
- /* @__PURE__ */ jsxRuntime.jsx(
7846
- StreamingIndicatorV2,
7847
- {
7848
- isStreaming: isWaitingForResponse,
7849
- loadingAnimation: config.loadingAnimation
7850
- }
7851
- ),
7852
- hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
7853
- ChatInputV2,
7854
- {
7855
- ref: chatInputV2Ref,
7856
- onSend: handleV2Send,
7857
- onCancel: cancelStream,
7858
- disabled: isV2InputDisabled,
7859
- isStreaming: isWaitingForResponse,
7860
- isUploadingAttachments: attachmentUpload.isUploading,
7861
- attachmentsReady: attachmentUpload.allReady,
7862
- hasAttachmentUploadErrors: attachmentUpload.hasErrors,
7863
- attachmentUploadStatusById: attachmentUpload.statusById,
7864
- uploadedAttachmentPayloads: attachmentUpload.payloads,
7865
- onAttachmentsChange: handleAttachmentsChange,
7866
- placeholder: isRecording ? "Listening..." : placeholder,
7867
- enableVoice: config.enableVoice === true,
7868
- transcribedText: config.enableVoice === true ? transcribedText : "",
7869
- voiceAvailable: config.enableVoice === true && voiceAvailable,
7870
- isRecording,
7871
- onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
7872
- onCancelRecording: handleCancelRecording,
7873
- onConfirmRecording: handleConfirmRecording,
7874
- showResetSession,
7875
- onResetSession: requestResetSession,
7876
- showAttachmentButton: attachmentSettings.showAttachmentButton,
7877
- showUploadImageButton: attachmentSettings.showUploadImageButton,
7878
- showAttachFileButton: attachmentSettings.showAttachFileButton,
7879
- allowedImageExtensions: attachmentSettings.allowedImageExtensions,
7880
- allowedFileExtensions: attachmentSettings.allowedFileExtensions,
7881
- maxCount: attachmentSettings.maxCount,
7882
- maxFileBytes: attachmentSettings.maxFileBytes,
7883
- maxTotalBytes: attachmentSettings.maxTotalBytes,
7884
- onUploadImageClick,
7885
- onAttachFileClick,
7886
- editingMessageId,
7887
- onClearEditing: handleClearEditing,
7888
- analysisMode: enableDeepModeToggle ? analysisMode : void 0,
7889
- onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
7890
- slashCommands
7891
- }
7892
- )
7893
- ]
7894
- },
7895
- "v2-chat"
7896
- ) }) }),
7897
- pdfPreviewMode === "split" && /* @__PURE__ */ jsxRuntime.jsx(
7898
- PdfSheetV2,
6566
+ initial: { opacity: 1 },
6567
+ exit: { opacity: 0 },
6568
+ transition: { duration: 0.3 },
6569
+ className: "payman-v2-chat-layout",
6570
+ style: { justifyContent: "center", alignItems: "center" },
6571
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", flex: 1, width: "100%" }, children: [
6572
+ /* @__PURE__ */ jsxRuntime.jsx(
6573
+ MessageList,
7899
6574
  {
7900
- src: pdfSheet?.href ?? null,
7901
- title: pdfSheet?.title ?? "",
7902
- onClose: closePdfSheet,
7903
- mode: "split"
6575
+ messages,
6576
+ isLoading: false,
6577
+ emptyStateText,
6578
+ showEmptyStateIcon,
6579
+ emptyStateComponent,
6580
+ layout,
6581
+ showTimestamps,
6582
+ stage: config.stage || "DEVELOPMENT",
6583
+ animated,
6584
+ showAgentName,
6585
+ agentName,
6586
+ showAvatars,
6587
+ showUserAvatar,
6588
+ showAssistantAvatar,
6589
+ showExecutionSteps,
6590
+ showStreamingDot,
6591
+ streamingStepsText,
6592
+ completedStepsText,
6593
+ onExecutionTraceClick,
6594
+ onLoadMoreMessages,
6595
+ isLoadingMoreMessages,
6596
+ hasMoreMessages
7904
6597
  }
7905
6598
  ),
7906
- pdfPreviewMode === "sheet" && /* @__PURE__ */ jsxRuntime.jsx(
7907
- "div",
6599
+ /* @__PURE__ */ jsxRuntime.jsx(
6600
+ framerMotion.motion.div,
7908
6601
  {
7909
- className: "payman-v2-pdf-sheet-top-anchor",
7910
- "aria-hidden": !pdfSheet,
7911
- children: /* @__PURE__ */ jsxRuntime.jsx(
7912
- PdfSheetV2,
6602
+ initial: { opacity: 0, y: 12 },
6603
+ animate: { opacity: 1, y: 0 },
6604
+ transition: { delay: 0.2, duration: 0.4, ease: [0.25, 0.46, 0.45, 0.94] },
6605
+ style: { width: "100%" },
6606
+ children: hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
6607
+ ChatInputV2,
7913
6608
  {
7914
- src: pdfSheet?.href ?? null,
7915
- title: pdfSheet?.title ?? "",
7916
- onClose: closePdfSheet,
7917
- mode: "sheet"
6609
+ ref: chatInputV2Ref,
6610
+ onSend: handleV2Send,
6611
+ onCancel: cancelStream,
6612
+ disabled: isV2InputDisabled,
6613
+ isStreaming: isWaitingForResponse,
6614
+ placeholder: isRecording ? "Listening..." : placeholder,
6615
+ enableVoice: config.enableVoice === true,
6616
+ transcribedText: config.enableVoice === true ? transcribedText : "",
6617
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
6618
+ isRecording,
6619
+ onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
6620
+ onCancelRecording: handleCancelRecording,
6621
+ onConfirmRecording: handleConfirmRecording,
6622
+ showResetSession,
6623
+ onResetSession: requestResetSession,
6624
+ showAttachmentButton,
6625
+ showUploadImageButton,
6626
+ showAttachFileButton,
6627
+ onUploadImageClick,
6628
+ onAttachFileClick,
6629
+ editingMessageId,
6630
+ onClearEditing: handleClearEditing,
6631
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
6632
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
6633
+ slashCommands
7918
6634
  }
7919
6635
  )
7920
6636
  }
7921
6637
  )
6638
+ ] })
6639
+ },
6640
+ "v2-empty"
6641
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
6642
+ framerMotion.motion.div,
6643
+ {
6644
+ initial: hasEverSentMessage ? { opacity: 0 } : false,
6645
+ animate: { opacity: 1 },
6646
+ transition: { duration: 0.3 },
6647
+ className: "payman-v2-chat-layout",
6648
+ children: [
6649
+ /* @__PURE__ */ jsxRuntime.jsx(
6650
+ MessageListV2,
6651
+ {
6652
+ ref: messageListV2Ref,
6653
+ messages,
6654
+ isStreaming: isWaitingForResponse,
6655
+ onEditUserMessage: handleEditMessageDraft,
6656
+ onRetryUserMessage: handleRetryUserMessage,
6657
+ onImageClick: handleImageClick,
6658
+ onExecutionTraceClick,
6659
+ messageActions,
6660
+ retryDisabled: isWaitingForResponse,
6661
+ typingSpeed: config.typingSpeed ?? 4,
6662
+ userActionPrompts,
6663
+ notifications,
6664
+ onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6665
+ onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6666
+ onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6667
+ onExpireUserAction: isUserActionSupported ? expireUserAction : void 0,
6668
+ onDismissNotification: dismissNotification,
6669
+ onSubmitFeedback: handleSubmitFeedback
6670
+ }
6671
+ ),
6672
+ /* @__PURE__ */ jsxRuntime.jsx(
6673
+ StreamingIndicatorV2,
6674
+ {
6675
+ isStreaming: isWaitingForResponse,
6676
+ loadingAnimation: config.loadingAnimation
6677
+ }
6678
+ ),
6679
+ hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
6680
+ ChatInputV2,
6681
+ {
6682
+ ref: chatInputV2Ref,
6683
+ onSend: handleV2Send,
6684
+ onCancel: cancelStream,
6685
+ disabled: isV2InputDisabled,
6686
+ isStreaming: isWaitingForResponse,
6687
+ placeholder: isRecording ? "Listening..." : placeholder,
6688
+ enableVoice: config.enableVoice === true,
6689
+ transcribedText: config.enableVoice === true ? transcribedText : "",
6690
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
6691
+ isRecording,
6692
+ onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
6693
+ onCancelRecording: handleCancelRecording,
6694
+ onConfirmRecording: handleConfirmRecording,
6695
+ showResetSession,
6696
+ onResetSession: requestResetSession,
6697
+ showAttachmentButton,
6698
+ showUploadImageButton,
6699
+ showAttachFileButton,
6700
+ onUploadImageClick,
6701
+ onAttachFileClick,
6702
+ editingMessageId,
6703
+ onClearEditing: handleClearEditing,
6704
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
6705
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
6706
+ slashCommands
6707
+ }
6708
+ )
7922
6709
  ]
7923
- }
7924
- ),
6710
+ },
6711
+ "v2-chat"
6712
+ ) }),
7925
6713
  /* @__PURE__ */ jsxRuntime.jsx(
7926
6714
  ImageLightboxV2,
7927
6715
  {
@@ -7956,39 +6744,19 @@ var PaymanChat = react.forwardRef(
7956
6744
  function PaymanChat2(props, ref) {
7957
6745
  const mergedCallbacks = useSentryChatCallbacks(props.callbacks, props.config);
7958
6746
  const chat = useChatV2(props.config, mergedCallbacks);
7959
- const attachmentUpload = useAttachmentUpload(props.config);
7960
- return /* @__PURE__ */ jsxRuntime.jsx(
7961
- PaymanChatInner,
7962
- {
7963
- ...props,
7964
- chat,
7965
- attachmentUpload,
7966
- ref
7967
- }
7968
- );
6747
+ return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatInner, { ...props, chat, ref });
7969
6748
  }
7970
6749
  );
7971
6750
 
7972
6751
  exports.PaymanChat = PaymanChat;
7973
6752
  exports.PaymanChatContext = PaymanChatContext;
7974
- exports.PdfSheetV2 = PdfSheetV2;
7975
6753
  exports.UserActionStaleError = UserActionStaleError;
7976
- exports.buildSignedUrlEndpoint = buildSignedUrlEndpoint;
7977
6754
  exports.cancelUserAction = cancelUserAction;
7978
6755
  exports.captureSentryError = captureSentryError;
7979
6756
  exports.cn = cn;
7980
- exports.formatAttachmentBytes = formatAttachmentBytes;
7981
6757
  exports.formatDate = formatDate;
7982
- exports.getPdfTitleFromUrl = getPdfTitleFromUrl;
7983
- exports.isPdfUrl = isPdfUrl;
7984
- exports.mapExecutionHistoryPageToChatMessages = mapExecutionHistoryPageToChatMessages;
7985
- exports.mapExecutionHistoryToChatMessages = mapExecutionHistoryToChatMessages;
7986
6758
  exports.resendUserAction = resendUserAction;
7987
- exports.stripAttachmentsSuffixFromIntent = stripAttachmentsSuffixFromIntent;
7988
6759
  exports.submitUserAction = submitUserAction;
7989
- exports.uploadAttachment = uploadAttachment;
7990
- exports.uploadAttachments = uploadAttachments;
7991
- exports.useAttachmentUpload = useAttachmentUpload;
7992
6760
  exports.useChatV2 = useChatV2;
7993
6761
  exports.usePaymanChat = usePaymanChat;
7994
6762
  exports.useVoice = useVoice;