@paymanai/payman-ask-sdk 4.0.26 → 4.0.28

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
@@ -1155,6 +1155,9 @@ function getUserActionSecondsLeft(prompt) {
1155
1155
  return void 0;
1156
1156
  }
1157
1157
  var EMPTY_USER_ACTION_STATE2 = { prompts: [], notifications: [] };
1158
+ function promptSlotKey(p) {
1159
+ return p.toolCallId || p.userActionId;
1160
+ }
1158
1161
  function upsertPrompt(prompts, req) {
1159
1162
  const idx = prompts.findIndex(
1160
1163
  (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
@@ -1201,6 +1204,7 @@ function useChatV2(config, callbacks = {}) {
1201
1204
  configRef.current = config;
1202
1205
  const messagesRef = React.useRef(messages);
1203
1206
  messagesRef.current = messages;
1207
+ const pendingSubmissionsRef = React.useRef(/* @__PURE__ */ new Map());
1204
1208
  const storeAwareSetMessages = React.useCallback(
1205
1209
  (updater) => {
1206
1210
  const streamUserId = streamUserIdRef.current;
@@ -1235,6 +1239,8 @@ function useChatV2(config, callbacks = {}) {
1235
1239
  if (!config.userId) return EMPTY_USER_ACTION_STATE2;
1236
1240
  return activeStreamStore.get(config.userId)?.userActionState ?? EMPTY_USER_ACTION_STATE2;
1237
1241
  });
1242
+ const userActionStateRef = React.useRef(userActionState);
1243
+ userActionStateRef.current = userActionState;
1238
1244
  const storeAwareSetUserActionState = React.useCallback(
1239
1245
  (updater) => {
1240
1246
  const streamUserId = streamUserIdRef.current;
@@ -1262,6 +1268,12 @@ function useChatV2(config, callbacks = {}) {
1262
1268
  onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1263
1269
  onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1264
1270
  onUserActionRequired: (request) => {
1271
+ const requestSlotKey = promptSlotKey(request);
1272
+ for (const [pendingId, slotKey] of pendingSubmissionsRef.current) {
1273
+ if (slotKey === requestSlotKey) {
1274
+ pendingSubmissionsRef.current.delete(pendingId);
1275
+ }
1276
+ }
1265
1277
  storeAwareSetUserActionState((prev) => ({
1266
1278
  ...prev,
1267
1279
  prompts: upsertPrompt(prev.prompts, request)
@@ -1273,6 +1285,28 @@ function useChatV2(config, callbacks = {}) {
1273
1285
  (prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
1274
1286
  );
1275
1287
  callbacksRef.current.onUserNotification?.(notification);
1288
+ },
1289
+ // A submitted-but-unconfirmed prompt (see `submitUserAction`) is only
1290
+ // safe to drop once the stream has demonstrably moved on. `onEvent`
1291
+ // in useStreamManagerV2 calls `onStepsUpdate` for *every* processed
1292
+ // stream event, unconditionally — so the first time this fires
1293
+ // after a submission, the event in hand is either the rejection
1294
+ // retry (a fresh `USER_ACTION_REQUIRED`, which the handler above
1295
+ // already deleted this pending entry for, earlier in the very same
1296
+ // event) or genuine forward progress (RUN_IN_PROGRESS, a tool call,
1297
+ // content deltas, RUN_COMPLETED, ...). Anything still pending by
1298
+ // the time we get here therefore means the run resumed normally —
1299
+ // clear it.
1300
+ onStepsUpdate: (steps) => {
1301
+ if (pendingSubmissionsRef.current.size > 0) {
1302
+ const resolved = [...pendingSubmissionsRef.current.keys()];
1303
+ pendingSubmissionsRef.current.clear();
1304
+ storeAwareSetUserActionState((prev) => ({
1305
+ ...prev,
1306
+ prompts: prev.prompts.filter((p) => !resolved.includes(p.userActionId))
1307
+ }));
1308
+ }
1309
+ callbacksRef.current.onStepsUpdate?.(steps);
1276
1310
  }
1277
1311
  // eslint-disable-next-line react-hooks/exhaustive-deps
1278
1312
  }), []);
@@ -1408,6 +1442,7 @@ function useChatV2(config, callbacks = {}) {
1408
1442
  [storeAwareSetUserActionState]
1409
1443
  );
1410
1444
  const removePrompt = React.useCallback((userActionId) => {
1445
+ pendingSubmissionsRef.current.delete(userActionId);
1411
1446
  storeAwareSetUserActionState((prev) => ({
1412
1447
  ...prev,
1413
1448
  prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
@@ -1418,7 +1453,10 @@ function useChatV2(config, callbacks = {}) {
1418
1453
  setPromptStatus(userActionId, "submitting");
1419
1454
  try {
1420
1455
  await submitUserAction(configRef.current, userActionId, content);
1421
- removePrompt(userActionId);
1456
+ const prompt = userActionStateRef.current.prompts.find(
1457
+ (p) => p.userActionId === userActionId
1458
+ );
1459
+ pendingSubmissionsRef.current.set(userActionId, prompt ? promptSlotKey(prompt) : userActionId);
1422
1460
  } catch (error) {
1423
1461
  if (error instanceof UserActionStaleError) {
1424
1462
  setPromptStatus(userActionId, "stale");
@@ -1429,7 +1467,7 @@ function useChatV2(config, callbacks = {}) {
1429
1467
  throw error;
1430
1468
  }
1431
1469
  },
1432
- [removePrompt, setPromptStatus]
1470
+ [setPromptStatus]
1433
1471
  );
1434
1472
  const cancelUserAction2 = React.useCallback(
1435
1473
  async (userActionId) => {
@@ -1783,6 +1821,284 @@ function useVoice(config = {}, callbacks = {}) {
1783
1821
  reset
1784
1822
  };
1785
1823
  }
1824
+ var DEFAULT_PAGE_SIZE = 20;
1825
+ var DEFAULT_MESSAGES_PAGE_SIZE = 100;
1826
+ function defaultParseSessions(json) {
1827
+ const body = json;
1828
+ const rows = Array.isArray(body?.data) ? body.data : [];
1829
+ const data = rows.map((row) => ({ ...row, title: row.title ?? row.sessionTitle ?? null }));
1830
+ return { data, total: body?.total };
1831
+ }
1832
+ function defaultParseMessages(json) {
1833
+ const body = json;
1834
+ return { data: Array.isArray(body?.data) ? body.data : [] };
1835
+ }
1836
+ function withQuery(path, params) {
1837
+ const qs = Object.entries(params).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`).join("&");
1838
+ if (!qs) return path;
1839
+ return `${path}${path.includes("?") ? "&" : "?"}${qs}`;
1840
+ }
1841
+ function toFeedbackState(feedback) {
1842
+ const value = feedback?.feedback;
1843
+ if (!value) return null;
1844
+ return value === "POSITIVE" ? "up" : "down";
1845
+ }
1846
+ function toMessageDisplay(rows, sessionId) {
1847
+ return rows.flatMap((row) => {
1848
+ const timestamp = row.startTime || row.endTime || (/* @__PURE__ */ new Date()).toISOString();
1849
+ const out = [
1850
+ {
1851
+ id: `${row.id}:user`,
1852
+ sessionId,
1853
+ role: "user",
1854
+ content: row.sessionUserIntent,
1855
+ timestamp
1856
+ }
1857
+ ];
1858
+ if (row.agentResponse) {
1859
+ out.push({
1860
+ id: `${row.id}:assistant`,
1861
+ sessionId,
1862
+ role: "assistant",
1863
+ content: row.agentResponse,
1864
+ timestamp: row.endTime || timestamp,
1865
+ executionId: row.id,
1866
+ isError: row.status === "FAILED",
1867
+ feedback: toFeedbackState(row.feedback)
1868
+ });
1869
+ }
1870
+ return out;
1871
+ });
1872
+ }
1873
+ function useChatSessions({
1874
+ api,
1875
+ sessions,
1876
+ initialSessionId
1877
+ }) {
1878
+ const enabled = sessions?.enabled === true;
1879
+ const listEndpoint = sessions?.listEndpoint ?? "/api/conversations/sessions";
1880
+ const messagesEndpointTemplate = sessions?.messagesEndpoint ?? "/api/conversations/sessions/{sessionId}/messages";
1881
+ const pageSize = sessions?.pageSize ?? DEFAULT_PAGE_SIZE;
1882
+ const messagesPageSize = sessions?.messagesPageSize ?? DEFAULT_MESSAGES_PAGE_SIZE;
1883
+ const parseSessionsResponse = sessions?.parseSessionsResponse ?? defaultParseSessions;
1884
+ const parseMessagesResponse = sessions?.parseMessagesResponse ?? defaultParseMessages;
1885
+ const [sessionsList, setSessionsList] = React.useState([]);
1886
+ const [isLoadingList, setIsLoadingList] = React.useState(enabled);
1887
+ const [isLoadingMore, setIsLoadingMore] = React.useState(false);
1888
+ const [listError, setListError] = React.useState(null);
1889
+ const hasLoadedOnceRef = React.useRef(false);
1890
+ const [page, setPage] = React.useState(0);
1891
+ const [total, setTotal] = React.useState(null);
1892
+ const [lastPageFull, setLastPageFull] = React.useState(false);
1893
+ const [selectedSessionId, setSelectedSessionId] = React.useState(
1894
+ initialSessionId ?? null
1895
+ );
1896
+ const [draftSessionId, setDraftSessionId] = React.useState(generateId);
1897
+ const [selectedMessages, setSelectedMessages] = React.useState([]);
1898
+ const [isLoadingMessages, setIsLoadingMessages] = React.useState(
1899
+ enabled && initialSessionId != null
1900
+ );
1901
+ const [messagesError, setMessagesError] = React.useState(null);
1902
+ const [refetchTick, setRefetchTick] = React.useState(0);
1903
+ const baseUrl = (api.baseUrl || "").replace(/\/+$/, "");
1904
+ const apiHeaders = api.headers;
1905
+ const authToken = api.authToken;
1906
+ const headersKey = JSON.stringify(apiHeaders ?? {});
1907
+ const requestHeaders = React.useMemo(() => {
1908
+ const headers = { Accept: "application/json", ...apiHeaders ?? {} };
1909
+ if (authToken && !headers.Authorization) headers.Authorization = `Bearer ${authToken}`;
1910
+ return headers;
1911
+ }, [headersKey, authToken]);
1912
+ const hasMore = total != null ? sessionsList.length < total : lastPageFull;
1913
+ React.useEffect(() => {
1914
+ setPage(0);
1915
+ hasLoadedOnceRef.current = false;
1916
+ }, [enabled, baseUrl, listEndpoint, pageSize, requestHeaders]);
1917
+ React.useEffect(() => {
1918
+ setPage(0);
1919
+ }, [refetchTick]);
1920
+ React.useEffect(() => {
1921
+ if (!enabled) return;
1922
+ const ctrl = new AbortController();
1923
+ const isFirstPage = page === 0;
1924
+ if (isFirstPage && !hasLoadedOnceRef.current) setIsLoadingList(true);
1925
+ else if (!isFirstPage) setIsLoadingMore(true);
1926
+ setListError(null);
1927
+ fetch(withQuery(`${baseUrl}${listEndpoint}`, { page, limit: pageSize }), {
1928
+ method: "GET",
1929
+ headers: requestHeaders,
1930
+ signal: ctrl.signal
1931
+ }).then(async (res) => {
1932
+ if (!res.ok) throw new Error(`Failed to load chats (${res.status})`);
1933
+ return res.json();
1934
+ }).then((json) => {
1935
+ const parsed = parseSessionsResponse(json);
1936
+ setSessionsList((prev) => isFirstPage ? parsed.data : [...prev, ...parsed.data]);
1937
+ setTotal(parsed.total ?? null);
1938
+ setLastPageFull(parsed.data.length >= pageSize);
1939
+ hasLoadedOnceRef.current = true;
1940
+ }).catch((e) => {
1941
+ if (e.name === "AbortError") return;
1942
+ setListError(e instanceof Error ? e.message : "Failed to load chats");
1943
+ }).finally(() => {
1944
+ setIsLoadingList(false);
1945
+ setIsLoadingMore(false);
1946
+ });
1947
+ return () => ctrl.abort();
1948
+ }, [enabled, baseUrl, listEndpoint, pageSize, requestHeaders, refetchTick, page]);
1949
+ React.useEffect(() => {
1950
+ if (!enabled || !selectedSessionId) {
1951
+ setSelectedMessages([]);
1952
+ return;
1953
+ }
1954
+ const ctrl = new AbortController();
1955
+ setIsLoadingMessages(true);
1956
+ setMessagesError(null);
1957
+ const path = messagesEndpointTemplate.replace(
1958
+ "{sessionId}",
1959
+ encodeURIComponent(selectedSessionId)
1960
+ );
1961
+ fetch(withQuery(`${baseUrl}${path}`, { limit: messagesPageSize }), {
1962
+ method: "GET",
1963
+ headers: requestHeaders,
1964
+ signal: ctrl.signal
1965
+ }).then(async (res) => {
1966
+ if (!res.ok) throw new Error(`Failed to load messages (${res.status})`);
1967
+ return res.json();
1968
+ }).then(
1969
+ (json) => setSelectedMessages(
1970
+ toMessageDisplay(parseMessagesResponse(json).data, selectedSessionId)
1971
+ )
1972
+ ).catch((e) => {
1973
+ if (e.name === "AbortError") return;
1974
+ setMessagesError(e instanceof Error ? e.message : "Failed to load messages");
1975
+ setSelectedMessages([]);
1976
+ }).finally(() => setIsLoadingMessages(false));
1977
+ return () => ctrl.abort();
1978
+ }, [enabled, selectedSessionId, baseUrl, messagesEndpointTemplate, messagesPageSize, requestHeaders]);
1979
+ const loadMore = React.useCallback(() => {
1980
+ setPage((prev) => {
1981
+ if (isLoadingList || isLoadingMore || !hasMore) return prev;
1982
+ return prev + 1;
1983
+ });
1984
+ }, [isLoadingList, isLoadingMore, hasMore]);
1985
+ const selectSession = React.useCallback((sessionId) => {
1986
+ setSelectedSessionId(sessionId);
1987
+ setSelectedMessages([]);
1988
+ }, []);
1989
+ const startNewSession = React.useCallback(() => {
1990
+ setSelectedSessionId(null);
1991
+ setDraftSessionId(generateId());
1992
+ setSelectedMessages([]);
1993
+ }, []);
1994
+ const refetch = React.useCallback(() => setRefetchTick((tick) => tick + 1), []);
1995
+ const noteSentMessage = React.useCallback(
1996
+ (text) => {
1997
+ const id = selectedSessionId ?? draftSessionId;
1998
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1999
+ const trimmed = text.trim();
2000
+ setSessionsList((prev) => {
2001
+ const idx = prev.findIndex((s) => s.sessionId === id);
2002
+ if (idx >= 0) {
2003
+ const existing = prev[idx];
2004
+ const bumped = {
2005
+ ...existing,
2006
+ lastMessageAt: now,
2007
+ messageCount: (existing.messageCount ?? 0) + 1,
2008
+ title: existing.title?.trim() ? existing.title : trimmed
2009
+ };
2010
+ return [bumped, ...prev.slice(0, idx), ...prev.slice(idx + 1)];
2011
+ }
2012
+ const fresh = {
2013
+ sessionId: id,
2014
+ title: trimmed,
2015
+ messageCount: 1,
2016
+ lastMessageAt: now,
2017
+ status: "PROCESSING"
2018
+ };
2019
+ return [fresh, ...prev];
2020
+ });
2021
+ },
2022
+ [selectedSessionId, draftSessionId]
2023
+ );
2024
+ return {
2025
+ enabled,
2026
+ sessionsList,
2027
+ isLoadingList,
2028
+ listError,
2029
+ total,
2030
+ hasMore,
2031
+ isLoadingMore,
2032
+ loadMore,
2033
+ noteSentMessage,
2034
+ selectedSessionId,
2035
+ activeSessionId: selectedSessionId ?? draftSessionId,
2036
+ selectSession,
2037
+ startNewSession,
2038
+ selectedMessages,
2039
+ isLoadingMessages,
2040
+ messagesError,
2041
+ refetch
2042
+ };
2043
+ }
2044
+ function deriveStatus(enabled, blocked) {
2045
+ if (blocked === true) return "blocked";
2046
+ if (enabled === false) return "disabled";
2047
+ if (enabled === true) return "available";
2048
+ return "unknown";
2049
+ }
2050
+ function defaultParse(json) {
2051
+ const body = json;
2052
+ return {
2053
+ available: body?.enabled === true && body?.blocked !== true,
2054
+ status: deriveStatus(body?.enabled, body?.blocked),
2055
+ config: body ?? void 0
2056
+ };
2057
+ }
2058
+ function useAgentConfig({ api, availability }) {
2059
+ const enabled = availability?.enabled === true;
2060
+ const endpoint = availability?.endpoint ?? "/api/agent/config";
2061
+ const parseResponse = availability?.parseResponse ?? defaultParse;
2062
+ const [available, setAvailable] = React.useState(null);
2063
+ const [status, setStatus] = React.useState("unknown");
2064
+ const [config, setConfig] = React.useState(void 0);
2065
+ const [isLoading, setIsLoading] = React.useState(enabled);
2066
+ const [error, setError] = React.useState(null);
2067
+ const [refetchTick, setRefetchTick] = React.useState(0);
2068
+ const baseUrl = (api.baseUrl || "").replace(/\/+$/, "");
2069
+ const apiHeaders = api.headers;
2070
+ const authToken = api.authToken;
2071
+ const requestHeaders = React.useMemo(() => {
2072
+ const headers = { Accept: "application/json", ...apiHeaders ?? {} };
2073
+ if (authToken && !headers.Authorization) headers.Authorization = `Bearer ${authToken}`;
2074
+ return headers;
2075
+ }, [apiHeaders, authToken]);
2076
+ React.useEffect(() => {
2077
+ if (!enabled) return;
2078
+ const ctrl = new AbortController();
2079
+ setIsLoading(true);
2080
+ setError(null);
2081
+ fetch(`${baseUrl}${endpoint}`, {
2082
+ method: "GET",
2083
+ headers: requestHeaders,
2084
+ signal: ctrl.signal
2085
+ }).then(async (res) => {
2086
+ if (!res.ok) throw new Error(`Failed to check availability (${res.status})`);
2087
+ return res.json();
2088
+ }).then((json) => {
2089
+ const parsed = parseResponse(json);
2090
+ setAvailable(parsed.available);
2091
+ setStatus(parsed.status ?? deriveStatus(parsed.available, void 0));
2092
+ setConfig(parsed.config);
2093
+ }).catch((e) => {
2094
+ if (e.name === "AbortError") return;
2095
+ setError(e instanceof Error ? e.message : "Failed to check availability");
2096
+ }).finally(() => setIsLoading(false));
2097
+ return () => ctrl.abort();
2098
+ }, [enabled, baseUrl, endpoint, requestHeaders, refetchTick]);
2099
+ const refetch = React.useCallback(() => setRefetchTick((tick) => tick + 1), []);
2100
+ return { enabled, available, status, config, isLoading, error, refetch };
2101
+ }
1786
2102
  function classifyField(field) {
1787
2103
  if (!field) return "text";
1788
2104
  if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
@@ -3239,11 +3555,10 @@ function UserMessageV2({
3239
3555
  ),
3240
3556
  style: { borderRadius: 9999 },
3241
3557
  children: [
3242
- toast.tone === "success" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3.5 w-3.5", style: { color: "#059669" } }) : /* @__PURE__ */ jsxRuntime.jsx(
3558
+ toast.tone === "success" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { style: { width: 14, height: 14, color: "#059669" } }) : /* @__PURE__ */ jsxRuntime.jsx(
3243
3559
  lucideReact.AlertCircle,
3244
3560
  {
3245
- className: "h-3.5 w-3.5",
3246
- style: { color: "#ef4444" }
3561
+ style: { width: 14, height: 14, color: "#ef4444" }
3247
3562
  }
3248
3563
  ),
3249
3564
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: toast.label })
@@ -3267,8 +3582,13 @@ function UserMessageV2({
3267
3582
  /* @__PURE__ */ jsxRuntime.jsx(
3268
3583
  lucideReact.AlertCircle,
3269
3584
  {
3270
- className: "h-3.5 w-3.5 shrink-0",
3271
- style: { color: "rgba(239, 68, 68, 0.7)", marginTop: "2px" }
3585
+ style: {
3586
+ width: 14,
3587
+ height: 14,
3588
+ flexShrink: 0,
3589
+ color: "rgba(239, 68, 68, 0.7)",
3590
+ marginTop: "2px"
3591
+ }
3272
3592
  }
3273
3593
  ),
3274
3594
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-user-msg-error-text", children: resolvedError })
@@ -3286,10 +3606,9 @@ function UserMessageV2({
3286
3606
  children: copied ? /* @__PURE__ */ jsxRuntime.jsx(
3287
3607
  lucideReact.Check,
3288
3608
  {
3289
- className: "h-3.5 w-3.5",
3290
- style: { color: "#059669" }
3609
+ style: { width: 14, height: 14, color: "#059669" }
3291
3610
  }
3292
- ) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-3.5 w-3.5" })
3611
+ ) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { style: { width: 14, height: 14 } })
3293
3612
  }
3294
3613
  ) }),
3295
3614
  showEditAction && onEdit && /* @__PURE__ */ jsxRuntime.jsx(ActionTooltipV2, { label: "Edit", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -3299,7 +3618,7 @@ function UserMessageV2({
3299
3618
  onClick: () => onEdit(message.id),
3300
3619
  className: "payman-v2-user-msg-action-btn",
3301
3620
  "aria-label": "Edit message",
3302
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" })
3621
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { style: { width: 14, height: 14 } })
3303
3622
  }
3304
3623
  ) }),
3305
3624
  showRetryAction && onRetry && /* @__PURE__ */ jsxRuntime.jsx(ActionTooltipV2, { label: "Retry", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -3310,7 +3629,7 @@ function UserMessageV2({
3310
3629
  disabled: retryDisabled,
3311
3630
  className: "payman-v2-user-msg-action-btn",
3312
3631
  "aria-label": "Retry message",
3313
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "h-3.5 w-3.5" })
3632
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { style: { width: 14, height: 14 } })
3314
3633
  }
3315
3634
  ) })
3316
3635
  ] })
@@ -4109,6 +4428,8 @@ function AssistantMessageV2({
4109
4428
  void 0,
4110
4429
  typingSpeed
4111
4430
  );
4431
+ const displayContentPending = Boolean(rawResponseContent) && !displayContent;
4432
+ const showThinkingBlock = isThinkingStreaming || displayContentPending;
4112
4433
  const stickyLabel = (() => {
4113
4434
  const steps = message.steps;
4114
4435
  if (!steps || steps.length === 0) return void 0;
@@ -4222,14 +4543,12 @@ function AssistantMessageV2({
4222
4543
  toast.tone === "success" ? /* @__PURE__ */ jsxRuntime.jsx(
4223
4544
  lucideReact.Check,
4224
4545
  {
4225
- className: "h-3.5 w-3.5",
4226
- style: { color: "#059669" }
4546
+ style: { width: 14, height: 14, color: "#059669" }
4227
4547
  }
4228
4548
  ) : /* @__PURE__ */ jsxRuntime.jsx(
4229
4549
  lucideReact.AlertCircle,
4230
4550
  {
4231
- className: "h-3.5 w-3.5",
4232
- style: { color: "#ef4444" }
4551
+ style: { width: 14, height: 14, color: "#ef4444" }
4233
4552
  }
4234
4553
  ),
4235
4554
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: toast.label })
@@ -4243,7 +4562,7 @@ function AssistantMessageV2({
4243
4562
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4244
4563
  toastPortal,
4245
4564
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-assistant-msg payman-v2-fade-in", children: [
4246
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: isThinkingStreaming && /* @__PURE__ */ jsxRuntime.jsx(
4565
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showThinkingBlock && /* @__PURE__ */ jsxRuntime.jsx(
4247
4566
  framerMotion.motion.div,
4248
4567
  {
4249
4568
  initial: { opacity: 0, height: 0 },
@@ -4273,6 +4592,10 @@ function AssistantMessageV2({
4273
4592
  isResolvingImages: message.isResolvingImages,
4274
4593
  onImageClick
4275
4594
  }
4595
+ ) : displayContentPending ? (
4596
+ // The intent/status line above is still bridging this
4597
+ // gap (see `showThinkingBlock`) — nothing to add here.
4598
+ null
4276
4599
  ) : !isThinkingStreaming ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-assistant-msg-placeholder", children: "..." }) : null }),
4277
4600
  /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showTrailingProgress && /* @__PURE__ */ jsxRuntime.jsx(
4278
4601
  framerMotion.motion.div,
@@ -4393,6 +4716,112 @@ function AssistantMessageV2({
4393
4716
  )
4394
4717
  ] });
4395
4718
  }
4719
+ var CHAR_VARIANTS = {
4720
+ fade: {
4721
+ initial: { opacity: 0, scale: 0.7 },
4722
+ animate: { opacity: 1, scale: 1 },
4723
+ exit: { opacity: 0, scale: 0.7 },
4724
+ transition: { duration: 0.14, ease: "easeOut" },
4725
+ overflow: "hidden"
4726
+ },
4727
+ blur: {
4728
+ initial: { opacity: 0, filter: "blur(8px)", y: -8 },
4729
+ animate: { opacity: 1, filter: "blur(0px)", y: 0 },
4730
+ exit: { opacity: 0, filter: "blur(8px)", y: 8 },
4731
+ transition: { duration: 0.18, ease: "easeOut" },
4732
+ overflow: "visible"
4733
+ }
4734
+ };
4735
+ function CharSlot({
4736
+ char,
4737
+ charKey,
4738
+ effect
4739
+ }) {
4740
+ if (!/\d/.test(char)) return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "inline-block" }, children: char });
4741
+ const v = CHAR_VARIANTS[effect];
4742
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { position: "relative", display: "inline-block", overflow: v.overflow }, children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "popLayout", initial: false, children: /* @__PURE__ */ jsxRuntime.jsx(
4743
+ framerMotion.motion.span,
4744
+ {
4745
+ initial: v.initial,
4746
+ animate: v.animate,
4747
+ exit: v.exit,
4748
+ transition: v.transition,
4749
+ style: { display: "inline-block" },
4750
+ children: char
4751
+ },
4752
+ charKey
4753
+ ) }) });
4754
+ }
4755
+ function CountUp({
4756
+ to,
4757
+ from = 0,
4758
+ duration = 2,
4759
+ delay = 0,
4760
+ digitEffect = "none",
4761
+ className,
4762
+ startWhen = true,
4763
+ separator = ""
4764
+ }) {
4765
+ const ref = React.useRef(null);
4766
+ const motionValue = framerMotion.useMotionValue(from);
4767
+ const damping = 20 + 40 * (1 / duration);
4768
+ const stiffness = 100 * (1 / duration);
4769
+ const springValue = framerMotion.useSpring(motionValue, { damping, stiffness });
4770
+ const isInView = framerMotion.useInView(ref, { once: true, margin: "0px" });
4771
+ const decimals = React.useMemo(() => {
4772
+ const d = (n) => {
4773
+ const s = n.toString();
4774
+ return s.includes(".") ? s.split(".")[1].length : 0;
4775
+ };
4776
+ return Math.max(d(from), d(to));
4777
+ }, [from, to]);
4778
+ const formatValue = React.useCallback(
4779
+ (latest) => {
4780
+ const formatted = Intl.NumberFormat("en-US", {
4781
+ useGrouping: !!separator,
4782
+ minimumFractionDigits: decimals,
4783
+ maximumFractionDigits: decimals
4784
+ }).format(latest);
4785
+ return separator ? formatted.replace(/,/g, separator) : formatted;
4786
+ },
4787
+ [decimals, separator]
4788
+ );
4789
+ const [chars, setChars] = React.useState(formatValue(from).split(""));
4790
+ React.useEffect(() => {
4791
+ if (isInView && startWhen) {
4792
+ const t = setTimeout(() => motionValue.set(to), delay * 1e3);
4793
+ return () => clearTimeout(t);
4794
+ }
4795
+ }, [isInView, startWhen, motionValue, to, delay]);
4796
+ React.useEffect(() => {
4797
+ const unsub = springValue.on("change", (latest) => {
4798
+ if (digitEffect === "none") {
4799
+ if (ref.current) ref.current.textContent = formatValue(latest);
4800
+ } else {
4801
+ setChars(formatValue(latest).split(""));
4802
+ }
4803
+ });
4804
+ return () => unsub();
4805
+ }, [springValue, formatValue, digitEffect]);
4806
+ if (digitEffect === "none") {
4807
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { ref, className: cn(className), children: formatValue(from) });
4808
+ }
4809
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { ref, className: cn("inline-flex items-center", className), children: chars.map((char, i) => /* @__PURE__ */ jsxRuntime.jsx(CharSlot, { char, charKey: `${i}-${char}`, effect: digitEffect }, i)) });
4810
+ }
4811
+ function AnimatedDuration({ seconds, className }) {
4812
+ const total = Math.max(0, seconds);
4813
+ const m = Math.floor(total / 60);
4814
+ const s = total % 60;
4815
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("tabular-nums", className), children: [
4816
+ m > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4817
+ /* @__PURE__ */ jsxRuntime.jsx(CountUp, { to: m, duration: 0.9, digitEffect: "blur", className: "tabular-nums" }),
4818
+ "m",
4819
+ " "
4820
+ ] }),
4821
+ /* @__PURE__ */ jsxRuntime.jsx(CountUp, { to: s, duration: 0.9, digitEffect: "blur", className: "tabular-nums" }),
4822
+ "s"
4823
+ ] });
4824
+ }
4396
4825
  var DEFAULT_MAX_LENGTH = 6;
4397
4826
  var MAX_SUPPORTED_LENGTH = 12;
4398
4827
  var AUTO_FOCUS_DELAY_MS = 250;
@@ -4519,7 +4948,6 @@ function VerificationInline({
4519
4948
  React.useEffect(() => {
4520
4949
  if (prompt.subAction === "SubmissionInvalid") {
4521
4950
  setErrored(true);
4522
- setCode("");
4523
4951
  lastSubmittedRef.current = null;
4524
4952
  }
4525
4953
  }, [prompt.subAction, prompt.userActionId]);
@@ -4598,7 +5026,7 @@ function VerificationInline({
4598
5026
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-head", children: [
4599
5027
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
4600
5028
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-title", children: "Verification required" }),
4601
- typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
5029
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : /* @__PURE__ */ jsxRuntime.jsx(AnimatedDuration, { seconds: secondsLeft }) })
4602
5030
  ] }),
4603
5031
  renderMarkdown ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-markdown", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRendererV2, { content: description }) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-desc", children: description }),
4604
5032
  stale ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -4670,6 +5098,228 @@ function VerificationInline({
4670
5098
  ] })
4671
5099
  ] });
4672
5100
  }
5101
+ function AmountFieldV2({
5102
+ id,
5103
+ value,
5104
+ onChange,
5105
+ disabled,
5106
+ className,
5107
+ onEnter
5108
+ }) {
5109
+ const inputRef = React.useRef(null);
5110
+ const digits = digitsFromValue(value);
5111
+ const display = formatDigits(digits);
5112
+ React.useLayoutEffect(() => {
5113
+ const el = inputRef.current;
5114
+ if (el && document.activeElement === el) {
5115
+ el.setSelectionRange(el.value.length, el.value.length);
5116
+ }
5117
+ }, [display]);
5118
+ return /* @__PURE__ */ jsxRuntime.jsx(
5119
+ "input",
5120
+ {
5121
+ ref: inputRef,
5122
+ id,
5123
+ type: "text",
5124
+ inputMode: "decimal",
5125
+ autoComplete: "off",
5126
+ className,
5127
+ value: display,
5128
+ placeholder: "0.00",
5129
+ disabled,
5130
+ onChange: (e) => {
5131
+ onChange(valueFromDigits(digitsFromDisplay(e.target.value)));
5132
+ },
5133
+ onKeyDown: (e) => {
5134
+ if (e.key === "Enter") {
5135
+ e.preventDefault();
5136
+ onEnter?.();
5137
+ }
5138
+ },
5139
+ onFocus: (e) => {
5140
+ const el = e.currentTarget;
5141
+ requestAnimationFrame(() => el.setSelectionRange(el.value.length, el.value.length));
5142
+ }
5143
+ }
5144
+ );
5145
+ }
5146
+ function digitsFromDisplay(raw) {
5147
+ return raw.replace(/[^0-9]/g, "").replace(/^0+(?=\d)/, "").slice(-9);
5148
+ }
5149
+ function digitsFromValue(value) {
5150
+ const num = Number(value);
5151
+ if (!value || !Number.isFinite(num) || num <= 0) return "";
5152
+ return String(Math.round(num * 100));
5153
+ }
5154
+ function formatDigits(digits) {
5155
+ if (!digits) return "";
5156
+ const padded = digits.padStart(3, "0");
5157
+ const cents = padded.slice(-2);
5158
+ const whole = padded.slice(0, -2).replace(/^0+(?=\d)/, "") || "0";
5159
+ const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
5160
+ return `${grouped}.${cents}`;
5161
+ }
5162
+ function valueFromDigits(digits) {
5163
+ if (!digits) return "";
5164
+ return (Number(digits) / 100).toString();
5165
+ }
5166
+ function findRootClassName(el) {
5167
+ const root = el?.closest(".payman-v2-root");
5168
+ return root instanceof HTMLElement ? root.className : "";
5169
+ }
5170
+ var MENU_MAX_HEIGHT = 240;
5171
+ var OPTION_HEIGHT = 34;
5172
+ var VIEWPORT_MARGIN = 8;
5173
+ function SelectFieldV2({
5174
+ id,
5175
+ value,
5176
+ options,
5177
+ onChange,
5178
+ disabled,
5179
+ error,
5180
+ placeholder = "Select\u2026"
5181
+ }) {
5182
+ const triggerRef = React.useRef(null);
5183
+ const listRef = React.useRef(null);
5184
+ const [open, setOpen] = React.useState(false);
5185
+ const [rect, setRect] = React.useState(null);
5186
+ const [activeIndex, setActiveIndex] = React.useState(-1);
5187
+ const [rootClassName, setRootClassName] = React.useState("");
5188
+ const selected = options.find((o) => o.const === value);
5189
+ React.useLayoutEffect(() => {
5190
+ if (!open) return;
5191
+ const place = () => {
5192
+ const el = triggerRef.current;
5193
+ if (!el) return;
5194
+ const r = el.getBoundingClientRect();
5195
+ const viewportH = window.innerHeight;
5196
+ const estimatedMenuH = Math.min(
5197
+ options.length * OPTION_HEIGHT + VIEWPORT_MARGIN,
5198
+ MENU_MAX_HEIGHT
5199
+ );
5200
+ const spaceBelow = viewportH - r.bottom;
5201
+ const spaceAbove = r.top;
5202
+ const openUp = spaceBelow < estimatedMenuH && spaceAbove > spaceBelow;
5203
+ setRect({ top: r.bottom, bottom: viewportH - r.top, left: r.left, width: r.width, openUp });
5204
+ setRootClassName(findRootClassName(el));
5205
+ };
5206
+ place();
5207
+ window.addEventListener("resize", place);
5208
+ window.addEventListener("scroll", place, true);
5209
+ return () => {
5210
+ window.removeEventListener("resize", place);
5211
+ window.removeEventListener("scroll", place, true);
5212
+ };
5213
+ }, [open, options.length]);
5214
+ React.useEffect(() => {
5215
+ if (!open) return;
5216
+ setActiveIndex(Math.max(0, options.findIndex((o) => o.const === value)));
5217
+ listRef.current?.focus();
5218
+ }, [open]);
5219
+ React.useEffect(() => {
5220
+ if (!open) return;
5221
+ const onDocDown = (e) => {
5222
+ const target = e.target;
5223
+ if (triggerRef.current?.contains(target) || listRef.current?.contains(target)) return;
5224
+ setOpen(false);
5225
+ };
5226
+ document.addEventListener("mousedown", onDocDown);
5227
+ return () => document.removeEventListener("mousedown", onDocDown);
5228
+ }, [open]);
5229
+ const commit = (opt) => {
5230
+ onChange(opt.const);
5231
+ setOpen(false);
5232
+ triggerRef.current?.focus();
5233
+ };
5234
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5235
+ /* @__PURE__ */ jsxRuntime.jsxs(
5236
+ "button",
5237
+ {
5238
+ ref: triggerRef,
5239
+ id,
5240
+ type: "button",
5241
+ disabled,
5242
+ className: cn("payman-v2-ua-select-trigger", error && "payman-v2-ua-input-error"),
5243
+ "aria-haspopup": "listbox",
5244
+ "aria-expanded": open,
5245
+ onClick: () => setOpen((v) => !v),
5246
+ onKeyDown: (e) => {
5247
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
5248
+ e.preventDefault();
5249
+ setOpen(true);
5250
+ }
5251
+ },
5252
+ children: [
5253
+ /* @__PURE__ */ jsxRuntime.jsx(
5254
+ "span",
5255
+ {
5256
+ className: cn(
5257
+ "payman-v2-ua-select-value",
5258
+ !selected && "payman-v2-ua-select-placeholder"
5259
+ ),
5260
+ children: selected ? selected.title || selected.const : placeholder
5261
+ }
5262
+ ),
5263
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { size: 14, className: "payman-v2-ua-select-chevron", "aria-hidden": true })
5264
+ ]
5265
+ }
5266
+ ),
5267
+ open && rect && typeof document !== "undefined" && reactDom.createPortal(
5268
+ /* @__PURE__ */ jsxRuntime.jsx(
5269
+ "div",
5270
+ {
5271
+ ref: listRef,
5272
+ role: "listbox",
5273
+ className: cn(rootClassName, "payman-v2-ua-select-menu"),
5274
+ tabIndex: -1,
5275
+ style: {
5276
+ position: "fixed",
5277
+ left: rect.left,
5278
+ width: rect.width,
5279
+ height: "fit-content",
5280
+ maxHeight: MENU_MAX_HEIGHT,
5281
+ ...rect.openUp ? { bottom: rect.bottom } : { top: rect.top }
5282
+ },
5283
+ onKeyDown: (e) => {
5284
+ if (e.key === "ArrowDown") {
5285
+ e.preventDefault();
5286
+ setActiveIndex((i) => Math.min(options.length - 1, i + 1));
5287
+ } else if (e.key === "ArrowUp") {
5288
+ e.preventDefault();
5289
+ setActiveIndex((i) => Math.max(0, i - 1));
5290
+ } else if (e.key === "Enter" || e.key === " ") {
5291
+ e.preventDefault();
5292
+ if (activeIndex >= 0) commit(options[activeIndex]);
5293
+ } else if (e.key === "Escape" || e.key === "Tab") {
5294
+ setOpen(false);
5295
+ triggerRef.current?.focus();
5296
+ }
5297
+ },
5298
+ children: options.map((opt, i) => /* @__PURE__ */ jsxRuntime.jsxs(
5299
+ "div",
5300
+ {
5301
+ role: "option",
5302
+ "aria-selected": opt.const === value,
5303
+ className: cn(
5304
+ "payman-v2-ua-select-option",
5305
+ i === activeIndex && "payman-v2-ua-select-option-active",
5306
+ opt.const === value && "payman-v2-ua-select-option-selected"
5307
+ ),
5308
+ onMouseEnter: () => setActiveIndex(i),
5309
+ onClick: () => commit(opt),
5310
+ children: [
5311
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: opt.title || opt.const }),
5312
+ opt.const === value && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 13, "aria-hidden": true })
5313
+ ]
5314
+ },
5315
+ opt.const
5316
+ ))
5317
+ }
5318
+ ),
5319
+ document.body
5320
+ )
5321
+ ] });
5322
+ }
4673
5323
  function SchemaFormInline({
4674
5324
  prompt,
4675
5325
  secondsLeft,
@@ -4715,7 +5365,7 @@ function SchemaFormInline({
4715
5365
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-ua-head", children: [
4716
5366
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "payman-v2-ua-icon", size: 14, strokeWidth: 1.75, "aria-hidden": true }),
4717
5367
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
4718
- typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
5368
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : /* @__PURE__ */ jsxRuntime.jsx(AnimatedDuration, { seconds: secondsLeft }) })
4719
5369
  ] }),
4720
5370
  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 })),
4721
5371
  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]) => {
@@ -4747,26 +5397,33 @@ function SchemaFormInline({
4747
5397
  required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-req", children: "*" })
4748
5398
  ] }),
4749
5399
  field.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-ua-hint", children: field.description }),
4750
- widget === "select" ? /* @__PURE__ */ jsxRuntime.jsxs(
4751
- "select",
5400
+ widget === "select" ? /* @__PURE__ */ jsxRuntime.jsx(
5401
+ SelectFieldV2,
4752
5402
  {
4753
5403
  id: fieldId,
4754
- className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4755
5404
  value: String(values[key] ?? ""),
5405
+ options: getOptions(field),
4756
5406
  disabled: locked,
4757
- onChange: (e) => setValue(key, e.target.value),
4758
- children: [
4759
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select\u2026" }),
4760
- getOptions(field).map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.const, children: opt.title || opt.const }, opt.const))
4761
- ]
5407
+ error: Boolean(err),
5408
+ onChange: (v) => setValue(key, v)
5409
+ }
5410
+ ) : widget === "decimal" ? /* @__PURE__ */ jsxRuntime.jsx(
5411
+ AmountFieldV2,
5412
+ {
5413
+ id: fieldId,
5414
+ value: String(values[key] ?? ""),
5415
+ disabled: locked,
5416
+ className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
5417
+ onChange: (v) => setValue(key, v),
5418
+ onEnter: handleSubmit
4762
5419
  }
4763
5420
  ) : /* @__PURE__ */ jsxRuntime.jsx(
4764
5421
  "input",
4765
5422
  {
4766
5423
  id: fieldId,
4767
- type: widget === "integer" || widget === "decimal" ? "number" : "text",
4768
- inputMode: widget === "integer" ? "numeric" : widget === "decimal" ? "decimal" : void 0,
4769
- step: widget === "decimal" ? "any" : widget === "integer" ? "1" : void 0,
5424
+ type: widget === "integer" ? "number" : "text",
5425
+ inputMode: widget === "integer" ? "numeric" : void 0,
5426
+ step: widget === "integer" ? "1" : void 0,
4770
5427
  className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4771
5428
  value: String(values[key] ?? ""),
4772
5429
  disabled: locked,
@@ -4863,12 +5520,16 @@ function UserActionInline({
4863
5520
  onSubmit,
4864
5521
  onCancel,
4865
5522
  onResend,
4866
- onExpired
5523
+ onExpired,
5524
+ suppressExpiredNote
4867
5525
  }) {
4868
5526
  const [secondsLeft, expired] = useExpiryCountdown(prompt);
4869
5527
  React.useEffect(() => {
4870
5528
  if (expired && prompt.kind !== "notification") onExpired?.();
4871
5529
  }, [expired, onExpired, prompt.kind]);
5530
+ if (expired && prompt.kind !== "notification" && suppressExpiredNote) {
5531
+ return null;
5532
+ }
4872
5533
  let body;
4873
5534
  if (expired && prompt.kind !== "notification") {
4874
5535
  const note = {
@@ -4929,6 +5590,9 @@ function getPromptViewKey(prompt) {
4929
5590
  prompt.message ?? ""
4930
5591
  ].join(PROMPT_KEY_SEPARATOR);
4931
5592
  }
5593
+ function getPromptMountKey(prompt) {
5594
+ return [getPromptSlotKey(prompt), prompt.userActionId].join(PROMPT_KEY_SEPARATOR);
5595
+ }
4932
5596
  var MessageListV2 = React.forwardRef(
4933
5597
  function MessageListV22({
4934
5598
  messages,
@@ -4940,6 +5604,7 @@ var MessageListV2 = React.forwardRef(
4940
5604
  messageActions,
4941
5605
  retryDisabled = false,
4942
5606
  userActionPrompts,
5607
+ dockedUserActionId,
4943
5608
  notifications,
4944
5609
  onSubmitUserAction,
4945
5610
  onCancelUserAction,
@@ -5048,10 +5713,11 @@ var MessageListV2 = React.forwardRef(
5048
5713
  }, [userActionPrompts]);
5049
5714
  const visibleUserActionPrompts = React.useMemo(
5050
5715
  () => userActionPrompts?.filter((prompt) => {
5716
+ if (prompt.userActionId === dockedUserActionId) return false;
5051
5717
  const promptKey = getPromptViewKey(prompt);
5052
5718
  return !expiredPromptViewState[promptKey]?.hidden;
5053
5719
  }),
5054
- [expiredPromptViewState, userActionPrompts]
5720
+ [dockedUserActionId, expiredPromptViewState, userActionPrompts]
5055
5721
  );
5056
5722
  const pinToBottom = React.useCallback((behavior = "instant") => {
5057
5723
  const el = scrollRef.current;
@@ -5200,7 +5866,7 @@ var MessageListV2 = React.forwardRef(
5200
5866
  onResend: onResendUserAction ?? noop,
5201
5867
  onExpired: () => handleUserActionExpired(promptKey, prompt.userActionId)
5202
5868
  },
5203
- promptKey
5869
+ getPromptMountKey(prompt)
5204
5870
  );
5205
5871
  })
5206
5872
  ]
@@ -5232,6 +5898,62 @@ var MessageListV2 = React.forwardRef(
5232
5898
  ] });
5233
5899
  }
5234
5900
  );
5901
+ function IconNewChat({ size = 16, className, style }) {
5902
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, style, "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
5903
+ "path",
5904
+ {
5905
+ d: "M11.9991 14.25V12M11.9991 12V9.75M11.9991 12H9.74936M11.9991 12H14.2489M20.9983 12C20.9983 13.2938 20.7253 14.5238 20.2338 15.6356L21 20.9991L16.4039 19.85C15.1019 20.5823 13.5993 21 11.9991 21C7.02906 21 3 16.9706 3 12C3 7.02944 7.02906 3 11.9991 3C16.9692 3 20.9983 7.02944 20.9983 12Z",
5906
+ stroke: "currentColor",
5907
+ strokeWidth: 2,
5908
+ strokeLinecap: "round",
5909
+ strokeLinejoin: "round"
5910
+ }
5911
+ ) });
5912
+ }
5913
+ function IconPlus({ size = 16, className, style }) {
5914
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, style, "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
5915
+ "path",
5916
+ {
5917
+ d: "M12.0001 4.7998L12 19.1998M19.2 11.9998L4.80005 11.9998",
5918
+ stroke: "currentColor",
5919
+ strokeWidth: 2,
5920
+ strokeLinecap: "round"
5921
+ }
5922
+ ) });
5923
+ }
5924
+ function IconMic({ size = 18, className, style }) {
5925
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, style, "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
5926
+ "path",
5927
+ {
5928
+ d: "M2.49292 13.3764C2.82536 15.6606 3.96908 17.7487 5.71484 19.2588C7.4606 20.7688 9.69168 21.5999 11.9999 21.5999C14.3082 21.5999 16.5393 20.7688 18.285 19.2588C20.0308 17.7487 21.1745 15.6606 21.5069 13.3764M12.0013 2.3999C10.9096 2.3999 9.86265 2.83357 9.09072 3.6055C8.31878 4.37744 7.88512 5.4244 7.88512 6.51608V12.0043C7.88512 13.096 8.31878 14.143 9.09072 14.9149C9.86265 15.6868 10.9096 16.1205 12.0013 16.1205C13.093 16.1205 14.1399 15.6868 14.9119 14.9149C15.6838 14.143 16.1175 13.096 16.1175 12.0043V6.51608C16.1175 5.4244 15.6838 4.37744 14.9119 3.6055C14.1399 2.83357 13.093 2.3999 12.0013 2.3999Z",
5929
+ stroke: "currentColor",
5930
+ strokeWidth: 2,
5931
+ strokeLinecap: "round",
5932
+ strokeLinejoin: "round"
5933
+ }
5934
+ ) });
5935
+ }
5936
+ function IconClock({ size = 12, className, style }) {
5937
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 25 24", fill: "none", className, style, "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
5938
+ "path",
5939
+ {
5940
+ d: "M15.5588 15.1987C16.0827 15.3733 16.649 15.0902 16.8237 14.5662C16.9983 14.0423 16.7152 13.476 16.1912 13.3013L15.5588 15.1987ZM12.5 13.125H11.5C11.5 13.5554 11.7754 13.9376 12.1838 14.0737L12.5 13.125ZM13.5 8.42087C13.5 7.86858 13.0523 7.42087 12.5 7.42087C11.9477 7.42087 11.5 7.86858 11.5 8.42087H13.5ZM16.1912 13.3013L12.8162 12.1763L12.1838 14.0737L15.5588 15.1987L16.1912 13.3013ZM13.5 13.125V8.42087H11.5V13.125H13.5ZM20.5 12C20.5 16.4183 16.9183 20 12.5 20V22C18.0228 22 22.5 17.5228 22.5 12H20.5ZM12.5 20C8.08172 20 4.5 16.4183 4.5 12H2.5C2.5 17.5228 6.97715 22 12.5 22V20ZM4.5 12C4.5 7.58172 8.08172 4 12.5 4V2C6.97715 2 2.5 6.47715 2.5 12H4.5ZM12.5 4C16.9183 4 20.5 7.58172 20.5 12H22.5C22.5 6.47715 18.0228 2 12.5 2V4Z",
5941
+ fill: "currentColor"
5942
+ }
5943
+ ) });
5944
+ }
5945
+ function IconMessage({ size = 12, className, style }) {
5946
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", className, style, "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
5947
+ "path",
5948
+ {
5949
+ d: "M8.39917 8.40002H15.5992M8.39917 13.2H12.5992M21.5992 12C21.5992 13.3801 21.308 14.6921 20.7837 15.878L21.601 21.5991L16.6981 20.3734C15.3091 21.1545 13.7062 21.6 11.9992 21.6C6.69724 21.6 2.39917 17.302 2.39917 12C2.39917 6.69809 6.69724 2.40002 11.9992 2.40002C17.3011 2.40002 21.5992 6.69809 21.5992 12Z",
5950
+ stroke: "currentColor",
5951
+ strokeWidth: 2,
5952
+ strokeLinecap: "round",
5953
+ strokeLinejoin: "round"
5954
+ }
5955
+ ) });
5956
+ }
5235
5957
  var ChatInputV2 = React.forwardRef(
5236
5958
  function ChatInputV22({
5237
5959
  onSend,
@@ -5622,12 +6344,7 @@ var ChatInputV2 = React.forwardRef(
5622
6344
  showActions && "payman-v2-input-attach-btn-active"
5623
6345
  ),
5624
6346
  "aria-label": "Attach",
5625
- children: /* @__PURE__ */ jsxRuntime.jsx(
5626
- lucideReact.Plus,
5627
- {
5628
- style: { width: 20, height: 20, strokeWidth: 2 }
5629
- }
5630
- )
6347
+ children: /* @__PURE__ */ jsxRuntime.jsx(IconPlus, { size: 20 })
5631
6348
  }
5632
6349
  ),
5633
6350
  /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showActions && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -5710,7 +6427,7 @@ var ChatInputV2 = React.forwardRef(
5710
6427
  ),
5711
6428
  "aria-label": isRecording ? "Stop recording" : "Voice input",
5712
6429
  children: [
5713
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, { style: { width: 18, height: 18 } }),
6430
+ /* @__PURE__ */ jsxRuntime.jsx(IconMic, { size: 18 }),
5714
6431
  isRecording && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-input-mic-indicator" })
5715
6432
  ]
5716
6433
  }
@@ -5746,67 +6463,578 @@ var ChatInputV2 = React.forwardRef(
5746
6463
  ] });
5747
6464
  }
5748
6465
  );
5749
-
5750
- // src/assets/thinking-atom.lottie
5751
- var thinking_atom_default = "data:application/zip;base64,UEsDBBQAAAAIAMWrkFzFrMWzUgAAAF4AAAANAAAAbWFuaWZlc3QuanNvbqtWKkstKs7Mz1OyUjJS0lFKT81LLUosyS8C8h1S8kty8ktKMlP14SzdrGIHQz0zPZDaxLzM3MQSoN5iJavoaqXMFKAe38TMPIXgZKApSrWxtQBQSwMEFAAAAAgAxauQXLRghFpXBAAA1RwAABEAAABhL01haW4gU2NlbmUuanNvbu1YWW/jNhD+KwafJVWkqMtPbdEDfdiiQIq+GMZCcei1GvmApLRdGP7v/YYiddqbGNumR4wcomZGHM7B76N0ZLstm7N3Wb6b3a3UTjGHPTw8sLnvsA2bCx/X3811q+qMzY/sAx74stjXda7WeaGqL1alyup9OeNeEnmcnRxWZB9VWbH54sjqj2weOI2bH5+KYibhoirZnONSa0f7AzykGOQYkGP4X2dFpbq13ONx0mTVu6x6bNXZXosf4erI9OLwjwRw7Tv4WWLSP+ALa7I2Rs0RkvkzRhEZQdWz8iHCms48Z54RMEAs2gABkcGRYVFHBqXv8Sh2GBKgRzDNW00SIClag1GzugVNShk5OUd9zxMj4cI/2UhoSdUwVBJpn53EGPMTOd0hh8hAUwuodC1UoVZ1ud+9UjWog+jvb6hIv9D9egxT8aK8kfte5tD5kiTYCfpaqvUPyABb7beH9/57zlUi4zBwE+FnrsyUdDOfC/c+iNVqzVOVKoH06vwLLDsr1Y6q+VZLgX7+dxQDW29SDGmKsVNPr1iLSyB1Bkui0CDGAEdkEmmpeXghQwe/Td0slnRzyDQ5NwdH8L05htXHLGFqIWk0fQ+YPhc9P3+3VpvsoDrSYR9KFNHWpKvWdgfdV998/e3sF2w70Nb35f7pAFNdfn0z49QpNLHDVnqh0FL9IUXU1kG1eZmDO1rYzB06+imrN30/hNG9JmkiPLIVm9flE2ZFuRZoFzf0hIyXzkJfqXcgtLJGCSHyi1SR/URF9pNJYP8b2adeaEwwoqc6AQ2XS+obXTi7bdi6eGmWs8MmXyEN3+UFPaOzQGOdBcQ52BeeDGXqgB3jRNIlirBe7Vs2TYNkXeiGsFtcTS0w3XLPAp8x0MQ86uluEVPse66t9R4ZdbVd88Vw4tNpqTcZIZekEUrFQi/2fAS3hqPIwBMXSIqBp6yqVN1shjHH5NejpvUyOtdZzNyX93lNVfwPn+vGrHjDpiuxSXAvFIHDhSdFRNghvcSPnMQLhKRbo3dbg0bvNgYWsZ4xcz7txuIYrGOfOzz0/JiTUZB4acwdVwiNa43abfVGrbVnMA79fCXG3WGvPdLLlM5nc6czWiBpKFXxq07ptqBOudg7hDI49vRUOAJYuJggZiQDrqFSxA6CMgj2/8PCNqDty6oCjqlViaL8XOb0iC4JjWfU51Xb6CiLunz8CiPop0cnnIb6Ryd7YGpf4bqTUmhPSpSSYXxuSsBtk3ztGkQwPAJS7QevkWdOa2SLNLSn378smfqMTZF0yRzV8Ez4SToNf4i5drmWByfHdk1B5PxGQW+Xgix5jMlhTB5jchlS0CeNOiK74GREQROOGVLQhKFuFHSjoH+CgkSELwM3CrqCgjDJGQoKbhT0pikIH0oE+CAMgOmpF3HAof580grxHaWRdt9opjr9wWUyj6UWHnvSD4yZ1N9pBiIXshuF3Cjktd9izmHo61CIxeSAPpWd/gRQSwECFAAUAAAACADFq5BcxazFs1IAAABeAAAADQAAAAAAAAAAAAAAAAAAAAAAbWFuaWZlc3QuanNvblBLAQIUABQAAAAIAMWrkFy0YIRaVwQAANUcAAARAAAAAAAAAAAAAAAAAH0AAABhL01haW4gU2NlbmUuanNvblBLBQYAAAAAAgACAHoAAAADBQAAAAA=";
5752
- var DEFAULT_SIZE = 36;
5753
- function useElapsedSeconds(isStreaming) {
5754
- const [elapsed, setElapsed] = React.useState(0);
5755
- const startRef = React.useRef(null);
5756
- React.useEffect(() => {
5757
- if (!isStreaming) {
5758
- startRef.current = null;
5759
- setElapsed(0);
5760
- return;
5761
- }
5762
- startRef.current = performance.now();
5763
- setElapsed(0);
5764
- const id = setInterval(() => {
5765
- if (startRef.current == null) return;
5766
- const secs = Math.floor((performance.now() - startRef.current) / 1e3);
5767
- setElapsed(secs);
5768
- }, 100);
5769
- return () => clearInterval(id);
5770
- }, [isStreaming]);
5771
- return elapsed;
6466
+ function dockKey(prompt) {
6467
+ return `${prompt.toolCallId || prompt.userActionId}:${prompt.userActionId}`;
5772
6468
  }
5773
- function StreamingIndicatorV2({
5774
- isStreaming,
5775
- loadingAnimation
6469
+ function UserActionDock({
6470
+ prompt,
6471
+ onSubmit,
6472
+ onCancel,
6473
+ onResend,
6474
+ onExpired,
6475
+ children
5776
6476
  }) {
5777
- const size = loadingAnimation?.size ?? DEFAULT_SIZE;
5778
- const elapsed = useElapsedSeconds(isStreaming);
5779
- return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: isStreaming && /* @__PURE__ */ jsxRuntime.jsxs(
6477
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-ua-dock", children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.motion.div, { layout: "size", transition: { duration: 0.3, ease: [0.2, 0, 0, 1] }, children: /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", initial: false, children: prompt ? /* @__PURE__ */ jsxRuntime.jsx(
5780
6478
  framerMotion.motion.div,
5781
6479
  {
5782
- className: "payman-v2-streaming-indicator",
5783
- initial: { opacity: 0, y: 4 },
5784
- animate: { opacity: 1, y: 0 },
5785
- exit: { opacity: 0, y: 4 },
5786
- transition: { duration: 0.2 },
5787
- "aria-hidden": "true",
5788
- children: [
5789
- /* @__PURE__ */ jsxRuntime.jsx(
5790
- "div",
5791
- {
5792
- className: "payman-v2-streaming-indicator-glyph",
5793
- style: { width: size, height: size },
5794
- children: /* @__PURE__ */ jsxRuntime.jsx(
5795
- dotlottieReact.DotLottieReact,
5796
- {
5797
- src: loadingAnimation?.src ?? thinking_atom_default,
5798
- loop: true,
5799
- autoplay: true,
5800
- style: { width: "100%", height: "100%" }
5801
- }
5802
- )
5803
- }
5804
- ),
5805
- elapsed > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "payman-v2-streaming-indicator-elapsed", children: [
5806
- elapsed,
5807
- "s"
5808
- ] })
5809
- ]
6480
+ initial: { opacity: 0 },
6481
+ animate: { opacity: 1 },
6482
+ exit: { opacity: 0 },
6483
+ transition: { duration: 0.15 },
6484
+ className: "payman-v2-ua-dock-panel",
6485
+ children: /* @__PURE__ */ jsxRuntime.jsx(
6486
+ UserActionInline,
6487
+ {
6488
+ prompt,
6489
+ onSubmit,
6490
+ onCancel,
6491
+ onResend,
6492
+ onExpired: () => onExpired?.(prompt.userActionId),
6493
+ suppressExpiredNote: true
6494
+ }
6495
+ )
6496
+ },
6497
+ dockKey(prompt)
6498
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
6499
+ framerMotion.motion.div,
6500
+ {
6501
+ initial: { opacity: 0 },
6502
+ animate: { opacity: 1 },
6503
+ exit: { opacity: 0 },
6504
+ transition: { duration: 0.15 },
6505
+ children
6506
+ },
6507
+ "composer"
6508
+ ) }) }) });
6509
+ }
6510
+ function clamp(value) {
6511
+ return Math.max(0, Math.min(1, value));
6512
+ }
6513
+ function ensureFrameSize(frame, rows, cols) {
6514
+ const result = [];
6515
+ for (let r = 0; r < rows; r++) {
6516
+ const row = frame[r] || [];
6517
+ result.push([]);
6518
+ for (let c = 0; c < cols; c++) {
6519
+ result[r][c] = row[c] ?? 0;
6520
+ }
6521
+ }
6522
+ return result;
6523
+ }
6524
+ function useAnimation(frames, options) {
6525
+ const [frameIndex, setFrameIndex] = React.useState(0);
6526
+ const [isPlaying, setIsPlaying] = React.useState(options.autoplay);
6527
+ const frameIdRef = React.useRef(void 0);
6528
+ const lastTimeRef = React.useRef(0);
6529
+ const accumulatorRef = React.useRef(0);
6530
+ React.useEffect(() => {
6531
+ if (!frames || frames.length === 0 || !isPlaying) {
6532
+ return;
6533
+ }
6534
+ const frameInterval = 1e3 / options.fps;
6535
+ const animate = (currentTime) => {
6536
+ if (lastTimeRef.current === 0) {
6537
+ lastTimeRef.current = currentTime;
6538
+ }
6539
+ const deltaTime = currentTime - lastTimeRef.current;
6540
+ lastTimeRef.current = currentTime;
6541
+ accumulatorRef.current += deltaTime;
6542
+ if (accumulatorRef.current >= frameInterval) {
6543
+ accumulatorRef.current -= frameInterval;
6544
+ setFrameIndex((prev) => {
6545
+ const next = prev + 1;
6546
+ if (next >= frames.length) {
6547
+ if (options.loop) {
6548
+ options.onFrame?.(0);
6549
+ return 0;
6550
+ } else {
6551
+ setIsPlaying(false);
6552
+ return prev;
6553
+ }
6554
+ }
6555
+ options.onFrame?.(next);
6556
+ return next;
6557
+ });
6558
+ }
6559
+ frameIdRef.current = requestAnimationFrame(animate);
6560
+ };
6561
+ frameIdRef.current = requestAnimationFrame(animate);
6562
+ return () => {
6563
+ if (frameIdRef.current) {
6564
+ cancelAnimationFrame(frameIdRef.current);
6565
+ }
6566
+ };
6567
+ }, [frames, isPlaying, options.fps, options.loop, options.onFrame]);
6568
+ React.useEffect(() => {
6569
+ setFrameIndex(0);
6570
+ setIsPlaying(options.autoplay);
6571
+ lastTimeRef.current = 0;
6572
+ accumulatorRef.current = 0;
6573
+ }, [frames, options.autoplay]);
6574
+ return { frameIndex, isPlaying };
6575
+ }
6576
+ function emptyFrame(rows, cols) {
6577
+ return Array.from({ length: rows }, () => Array(cols).fill(0));
6578
+ }
6579
+ function setPixel(frame, row, col, value) {
6580
+ if (row >= 0 && row < frame.length && col >= 0 && col < frame[0].length) {
6581
+ frame[row][col] = value;
6582
+ }
6583
+ }
6584
+ var loader = (() => {
6585
+ const frames = [];
6586
+ const size = 7;
6587
+ const center = 3;
6588
+ const radius = 2.5;
6589
+ for (let frame = 0; frame < 12; frame++) {
6590
+ const f = emptyFrame(size, size);
6591
+ for (let i = 0; i < 8; i++) {
6592
+ const angle = frame / 12 * Math.PI * 2 + i / 8 * Math.PI * 2;
6593
+ const x = Math.round(center + Math.cos(angle) * radius);
6594
+ const y = Math.round(center + Math.sin(angle) * radius);
6595
+ const brightness = 1 - i / 10;
6596
+ setPixel(f, y, x, Math.max(0.2, brightness));
6597
+ }
6598
+ frames.push(f);
6599
+ }
6600
+ return frames;
6601
+ })();
6602
+ var pulse = (() => {
6603
+ const frames = [];
6604
+ const size = 7;
6605
+ const center = 3;
6606
+ for (let frame = 0; frame < 16; frame++) {
6607
+ const f = emptyFrame(size, size);
6608
+ const phase = frame / 16 * Math.PI * 2;
6609
+ const intensity = (Math.sin(phase) + 1) / 2;
6610
+ setPixel(f, center, center, 1);
6611
+ const radius = Math.floor((1 - intensity) * 3) + 1;
6612
+ for (let dy = -radius; dy <= radius; dy++) {
6613
+ for (let dx = -radius; dx <= radius; dx++) {
6614
+ const dist = Math.sqrt(dx * dx + dy * dy);
6615
+ if (Math.abs(dist - radius) < 0.7) {
6616
+ setPixel(f, center + dy, center + dx, intensity * 0.6);
6617
+ }
6618
+ }
6619
+ }
6620
+ frames.push(f);
6621
+ }
6622
+ return frames;
6623
+ })();
6624
+ function vu(columns, levels) {
6625
+ const rows = 7;
6626
+ const frame = emptyFrame(rows, columns);
6627
+ for (let col = 0; col < Math.min(columns, levels.length); col++) {
6628
+ const level = Math.max(0, Math.min(1, levels[col]));
6629
+ const height = Math.floor(level * rows);
6630
+ for (let row = 0; row < rows; row++) {
6631
+ const rowFromBottom = rows - 1 - row;
6632
+ if (rowFromBottom < height) {
6633
+ let brightness = 1;
6634
+ if (row < rows * 0.3) {
6635
+ brightness = 1;
6636
+ } else if (row < rows * 0.6) {
6637
+ brightness = 0.8;
6638
+ } else {
6639
+ brightness = 0.6;
6640
+ }
6641
+ frame[row][col] = brightness;
6642
+ }
6643
+ }
6644
+ }
6645
+ return frame;
6646
+ }
6647
+ var wave = (() => {
6648
+ const frames = [];
6649
+ const rows = 7;
6650
+ const cols = 7;
6651
+ for (let frame = 0; frame < 24; frame++) {
6652
+ const f = emptyFrame(rows, cols);
6653
+ const phase = frame / 24 * Math.PI * 2;
6654
+ for (let col = 0; col < cols; col++) {
6655
+ const colPhase = col / cols * Math.PI * 2;
6656
+ const height = Math.sin(phase + colPhase) * 2.5 + 3.5;
6657
+ const row = Math.floor(height);
6658
+ if (row >= 0 && row < rows) {
6659
+ setPixel(f, row, col, 1);
6660
+ const frac = height - row;
6661
+ if (row > 0) setPixel(f, row - 1, col, 1 - frac);
6662
+ if (row < rows - 1) setPixel(f, row + 1, col, frac);
6663
+ }
6664
+ }
6665
+ frames.push(f);
6666
+ }
6667
+ return frames;
6668
+ })();
6669
+ var snake = (() => {
6670
+ const frames = [];
6671
+ const rows = 7;
6672
+ const cols = 7;
6673
+ const path = [];
6674
+ let x = 0;
6675
+ let y = 0;
6676
+ let dx = 1;
6677
+ let dy = 0;
6678
+ const visited = /* @__PURE__ */ new Set();
6679
+ while (path.length < rows * cols) {
6680
+ path.push([y, x]);
6681
+ visited.add(`${y},${x}`);
6682
+ const nextX = x + dx;
6683
+ const nextY = y + dy;
6684
+ if (nextX >= 0 && nextX < cols && nextY >= 0 && nextY < rows && !visited.has(`${nextY},${nextX}`)) {
6685
+ x = nextX;
6686
+ y = nextY;
6687
+ } else {
6688
+ const newDx = -dy;
6689
+ const newDy = dx;
6690
+ dx = newDx;
6691
+ dy = newDy;
6692
+ const nextX2 = x + dx;
6693
+ const nextY2 = y + dy;
6694
+ if (nextX2 >= 0 && nextX2 < cols && nextY2 >= 0 && nextY2 < rows && !visited.has(`${nextY2},${nextX2}`)) {
6695
+ x = nextX2;
6696
+ y = nextY2;
6697
+ } else {
6698
+ break;
6699
+ }
6700
+ }
6701
+ }
6702
+ const snakeLength = 5;
6703
+ for (let frame = 0; frame < path.length; frame++) {
6704
+ const f = emptyFrame(rows, cols);
6705
+ for (let i = 0; i < snakeLength; i++) {
6706
+ const idx = frame - i;
6707
+ if (idx >= 0 && idx < path.length) {
6708
+ const [y2, x2] = path[idx];
6709
+ const brightness = 1 - i / snakeLength;
6710
+ setPixel(f, y2, x2, brightness);
6711
+ }
6712
+ }
6713
+ frames.push(f);
6714
+ }
6715
+ return frames;
6716
+ })();
6717
+ var Matrix = React__namespace.forwardRef(
6718
+ ({
6719
+ rows,
6720
+ cols,
6721
+ pattern,
6722
+ frames,
6723
+ fps = 12,
6724
+ autoplay = true,
6725
+ loop = true,
6726
+ size = 10,
6727
+ gap = 2,
6728
+ palette = {
6729
+ on: "currentColor",
6730
+ off: "var(--muted-foreground)"
6731
+ },
6732
+ brightness = 1,
6733
+ ariaLabel,
6734
+ onFrame,
6735
+ mode = "default",
6736
+ levels,
6737
+ className,
6738
+ ...props
6739
+ }, ref) => {
6740
+ const { frameIndex } = useAnimation(frames, {
6741
+ fps,
6742
+ autoplay: autoplay && !pattern,
6743
+ loop,
6744
+ onFrame
6745
+ });
6746
+ const currentFrame = React.useMemo(() => {
6747
+ if (mode === "vu" && levels && levels.length > 0) {
6748
+ return ensureFrameSize(vu(cols, levels), rows, cols);
6749
+ }
6750
+ if (pattern) {
6751
+ return ensureFrameSize(pattern, rows, cols);
6752
+ }
6753
+ if (frames && frames.length > 0) {
6754
+ return ensureFrameSize(frames[frameIndex] || frames[0], rows, cols);
6755
+ }
6756
+ return ensureFrameSize([], rows, cols);
6757
+ }, [pattern, frames, frameIndex, rows, cols, mode, levels]);
6758
+ const cellPositions = React.useMemo(() => {
6759
+ const positions = [];
6760
+ for (let row = 0; row < rows; row++) {
6761
+ positions[row] = [];
6762
+ for (let col = 0; col < cols; col++) {
6763
+ positions[row][col] = {
6764
+ x: col * (size + gap),
6765
+ y: row * (size + gap)
6766
+ };
6767
+ }
6768
+ }
6769
+ return positions;
6770
+ }, [rows, cols, size, gap]);
6771
+ const svgDimensions = React.useMemo(() => {
6772
+ return {
6773
+ width: cols * (size + gap) - gap,
6774
+ height: rows * (size + gap) - gap
6775
+ };
6776
+ }, [rows, cols, size, gap]);
6777
+ const isAnimating = !pattern && frames && frames.length > 0;
6778
+ return /* @__PURE__ */ jsxRuntime.jsx(
6779
+ "div",
6780
+ {
6781
+ ref,
6782
+ role: "img",
6783
+ "aria-label": ariaLabel ?? "matrix display",
6784
+ "aria-live": isAnimating ? "polite" : void 0,
6785
+ className: cn("relative inline-block", className),
6786
+ style: {
6787
+ "--matrix-on": palette.on,
6788
+ "--matrix-off": palette.off,
6789
+ "--matrix-gap": `${gap}px`,
6790
+ "--matrix-size": `${size}px`
6791
+ },
6792
+ ...props,
6793
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
6794
+ "svg",
6795
+ {
6796
+ width: svgDimensions.width,
6797
+ height: svgDimensions.height,
6798
+ viewBox: `0 0 ${svgDimensions.width} ${svgDimensions.height}`,
6799
+ xmlns: "http://www.w3.org/2000/svg",
6800
+ className: "block",
6801
+ style: { overflow: "visible" },
6802
+ children: [
6803
+ /* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
6804
+ /* @__PURE__ */ jsxRuntime.jsxs("radialGradient", { id: "matrix-pixel-on", cx: "50%", cy: "50%", r: "50%", children: [
6805
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "var(--matrix-on)", stopOpacity: "1" }),
6806
+ /* @__PURE__ */ jsxRuntime.jsx(
6807
+ "stop",
6808
+ {
6809
+ offset: "70%",
6810
+ stopColor: "var(--matrix-on)",
6811
+ stopOpacity: "0.85"
6812
+ }
6813
+ ),
6814
+ /* @__PURE__ */ jsxRuntime.jsx(
6815
+ "stop",
6816
+ {
6817
+ offset: "100%",
6818
+ stopColor: "var(--matrix-on)",
6819
+ stopOpacity: "0.6"
6820
+ }
6821
+ )
6822
+ ] }),
6823
+ /* @__PURE__ */ jsxRuntime.jsxs("radialGradient", { id: "matrix-pixel-off", cx: "50%", cy: "50%", r: "50%", children: [
6824
+ /* @__PURE__ */ jsxRuntime.jsx(
6825
+ "stop",
6826
+ {
6827
+ offset: "0%",
6828
+ stopColor: "var(--muted-foreground)",
6829
+ stopOpacity: "1"
6830
+ }
6831
+ ),
6832
+ /* @__PURE__ */ jsxRuntime.jsx(
6833
+ "stop",
6834
+ {
6835
+ offset: "100%",
6836
+ stopColor: "var(--muted-foreground)",
6837
+ stopOpacity: "0.7"
6838
+ }
6839
+ )
6840
+ ] }),
6841
+ /* @__PURE__ */ jsxRuntime.jsxs(
6842
+ "filter",
6843
+ {
6844
+ id: "matrix-glow",
6845
+ x: "-50%",
6846
+ y: "-50%",
6847
+ width: "200%",
6848
+ height: "200%",
6849
+ children: [
6850
+ /* @__PURE__ */ jsxRuntime.jsx("feGaussianBlur", { stdDeviation: "2", result: "blur" }),
6851
+ /* @__PURE__ */ jsxRuntime.jsx("feComposite", { in: "SourceGraphic", in2: "blur", operator: "over" })
6852
+ ]
6853
+ }
6854
+ )
6855
+ ] }),
6856
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
6857
+ .matrix-pixel {
6858
+ transition: opacity 300ms ease-out, transform 150ms ease-out;
6859
+ transform-origin: center;
6860
+ transform-box: fill-box;
6861
+ }
6862
+ .matrix-pixel-active {
6863
+ filter: url(#matrix-glow);
6864
+ }
6865
+ ` }),
6866
+ currentFrame.map(
6867
+ (row, rowIndex) => row.map((value, colIndex) => {
6868
+ const pos = cellPositions[rowIndex]?.[colIndex];
6869
+ if (!pos) return null;
6870
+ const opacity = clamp(brightness * value);
6871
+ const isActive = opacity > 0.5;
6872
+ const isOn = opacity > 0.05;
6873
+ const fill = isOn ? "url(#matrix-pixel-on)" : "url(#matrix-pixel-off)";
6874
+ const scale = isActive ? 1.1 : 1;
6875
+ const radius = size / 2 * 0.9;
6876
+ return /* @__PURE__ */ jsxRuntime.jsx(
6877
+ "circle",
6878
+ {
6879
+ className: cn(
6880
+ "matrix-pixel",
6881
+ isActive && "matrix-pixel-active",
6882
+ !isOn && "opacity-20 dark:opacity-[0.1]"
6883
+ ),
6884
+ cx: pos.x + size / 2,
6885
+ cy: pos.y + size / 2,
6886
+ r: radius,
6887
+ fill,
6888
+ opacity: isOn ? opacity : 0.1,
6889
+ style: {
6890
+ transform: `scale(${scale})`
6891
+ }
6892
+ },
6893
+ `${rowIndex}-${colIndex}`
6894
+ );
6895
+ })
6896
+ )
6897
+ ]
6898
+ }
6899
+ )
6900
+ }
6901
+ );
6902
+ }
6903
+ );
6904
+ Matrix.displayName = "Matrix";
6905
+ var DEFAULT_SIZE = 22;
6906
+ var MATRIX_GRID = 7;
6907
+ var MATRIX_ON = "var(--payman-v2-matrix-color)";
6908
+ var MATRIX_PRESETS = [
6909
+ { frames: loader, fps: 8 },
6910
+ { frames: wave, fps: 14 },
6911
+ { frames: snake, fps: 16 },
6912
+ { frames: pulse, fps: 12 }
6913
+ ];
6914
+ var PRESET_ROTATE_MS = 1e4;
6915
+ function useElapsedSeconds(isStreaming) {
6916
+ const [elapsed, setElapsed] = React.useState(0);
6917
+ const startRef = React.useRef(null);
6918
+ React.useEffect(() => {
6919
+ if (!isStreaming) {
6920
+ startRef.current = null;
6921
+ setElapsed(0);
6922
+ return;
6923
+ }
6924
+ startRef.current = performance.now();
6925
+ setElapsed(0);
6926
+ const id = setInterval(() => {
6927
+ if (startRef.current == null) return;
6928
+ const secs = Math.floor((performance.now() - startRef.current) / 1e3);
6929
+ setElapsed(secs);
6930
+ }, 100);
6931
+ return () => clearInterval(id);
6932
+ }, [isStreaming]);
6933
+ return elapsed;
6934
+ }
6935
+ function useMatrixPresetIndex(isStreaming) {
6936
+ const [index, setIndex] = React.useState(0);
6937
+ React.useEffect(() => {
6938
+ if (!isStreaming) return;
6939
+ setIndex(0);
6940
+ const id = setInterval(() => {
6941
+ setIndex((prev) => (prev + 1) % MATRIX_PRESETS.length);
6942
+ }, PRESET_ROTATE_MS);
6943
+ return () => clearInterval(id);
6944
+ }, [isStreaming]);
6945
+ return index;
6946
+ }
6947
+ function formatElapsed(totalSeconds) {
6948
+ if (totalSeconds < 60) return `${totalSeconds}s`;
6949
+ const minutes = Math.floor(totalSeconds / 60);
6950
+ const seconds = totalSeconds % 60;
6951
+ return `${minutes}m ${seconds}s`;
6952
+ }
6953
+ function StreamingIndicatorV2({
6954
+ isStreaming,
6955
+ loadingAnimation
6956
+ }) {
6957
+ const size = loadingAnimation?.size ?? DEFAULT_SIZE;
6958
+ const elapsed = useElapsedSeconds(isStreaming);
6959
+ const presetIndex = useMatrixPresetIndex(isStreaming);
6960
+ const preset = MATRIX_PRESETS[presetIndex];
6961
+ const gap = 1;
6962
+ const cell = React.useMemo(
6963
+ () => Math.max(2, Math.round((size - (MATRIX_GRID - 1) * gap) / MATRIX_GRID)),
6964
+ [size]
6965
+ );
6966
+ return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: isStreaming && /* @__PURE__ */ jsxRuntime.jsxs(
6967
+ framerMotion.motion.div,
6968
+ {
6969
+ className: "payman-v2-streaming-indicator",
6970
+ initial: { opacity: 0, y: 4 },
6971
+ animate: { opacity: 1, y: 0 },
6972
+ exit: { opacity: 0, y: 4 },
6973
+ transition: { duration: 0.2 },
6974
+ "aria-hidden": "true",
6975
+ children: [
6976
+ /* @__PURE__ */ jsxRuntime.jsx(
6977
+ "div",
6978
+ {
6979
+ className: "payman-v2-streaming-indicator-glyph",
6980
+ style: { width: size, height: size },
6981
+ children: loadingAnimation?.src ? (
6982
+ // Consumer override: render their Lottie animation.
6983
+ /* @__PURE__ */ jsxRuntime.jsx(
6984
+ dotlottieReact.DotLottieReact,
6985
+ {
6986
+ src: loadingAnimation.src,
6987
+ loop: true,
6988
+ autoplay: true,
6989
+ style: { width: "100%", height: "100%" }
6990
+ }
6991
+ )
6992
+ ) : (
6993
+ // Default: dot-matrix glyph, cycling presets +
6994
+ // Payman brand tint. Each preset lives in its own
6995
+ // keyed motion layer so swapping presets crossfades
6996
+ // (old scales/fades out while the new scales/fades
6997
+ // in) instead of hard-cutting. Keying the layer on
6998
+ // `presetIndex` also remounts the Matrix, restarting
6999
+ // its frame counter cleanly at frame 0.
7000
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: /* @__PURE__ */ jsxRuntime.jsx(
7001
+ framerMotion.motion.div,
7002
+ {
7003
+ className: "payman-v2-streaming-indicator-matrix",
7004
+ initial: { opacity: 0, scale: 0.8 },
7005
+ animate: { opacity: 1, scale: 1 },
7006
+ exit: { opacity: 0, scale: 0.8 },
7007
+ transition: { duration: 0.45, ease: [0.2, 0, 0, 1] },
7008
+ children: /* @__PURE__ */ jsxRuntime.jsx(
7009
+ Matrix,
7010
+ {
7011
+ rows: MATRIX_GRID,
7012
+ cols: MATRIX_GRID,
7013
+ frames: preset.frames,
7014
+ fps: preset.fps,
7015
+ size: cell,
7016
+ gap,
7017
+ palette: {
7018
+ on: MATRIX_ON,
7019
+ // Unlit dots use the v2 theme's own muted-text
7020
+ // token (light/dark-aware), not the bare
7021
+ // `--muted-foreground` the vendored Matrix
7022
+ // component falls back to internally — that
7023
+ // variable isn't defined under `.payman-v2-root`
7024
+ // and would otherwise resolve to black.
7025
+ off: "var(--payman-v2-matrix-dot-off)"
7026
+ },
7027
+ ariaLabel: "Working\u2026"
7028
+ }
7029
+ )
7030
+ },
7031
+ presetIndex
7032
+ ) })
7033
+ )
7034
+ }
7035
+ ),
7036
+ elapsed > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-streaming-indicator-elapsed", children: formatElapsed(elapsed) })
7037
+ ]
5810
7038
  },
5811
7039
  "streaming-indicator"
5812
7040
  ) });
@@ -6181,82 +7409,660 @@ function TimelineBars({
6181
7409
  children: /* @__PURE__ */ jsxRuntime.jsx(
6182
7410
  "div",
6183
7411
  {
6184
- style: {
6185
- height: "100%",
6186
- width: `${Math.max(widthPct, 0.3)}%`,
6187
- background: stepColor(step2),
6188
- transition: "width 0.2s ease"
6189
- }
7412
+ style: {
7413
+ height: "100%",
7414
+ width: `${Math.max(widthPct, 0.3)}%`,
7415
+ background: stepColor(step2),
7416
+ transition: "width 0.2s ease"
7417
+ }
7418
+ }
7419
+ )
7420
+ }
7421
+ )
7422
+ ] }, `${step2.step}-${step2.startTime}`);
7423
+ });
7424
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
7425
+ bars,
7426
+ unaccountedMs > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 8 }, children: [
7427
+ /* @__PURE__ */ jsxRuntime.jsxs(
7428
+ "div",
7429
+ {
7430
+ style: {
7431
+ display: "flex",
7432
+ justifyContent: "space-between",
7433
+ fontSize: 12,
7434
+ marginBottom: 4,
7435
+ fontFamily: "ui-monospace, SFMono-Regular, monospace",
7436
+ opacity: 0.7
7437
+ },
7438
+ children: [
7439
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "(unaccounted)" }),
7440
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatMs(unaccountedMs) })
7441
+ ]
7442
+ }
7443
+ ),
7444
+ /* @__PURE__ */ jsxRuntime.jsx(
7445
+ "div",
7446
+ {
7447
+ style: {
7448
+ height: 8,
7449
+ background: "rgba(255,255,255,0.06)",
7450
+ borderRadius: 4,
7451
+ overflow: "hidden"
7452
+ },
7453
+ children: /* @__PURE__ */ jsxRuntime.jsx(
7454
+ "div",
7455
+ {
7456
+ style: {
7457
+ height: "100%",
7458
+ width: `${unaccountedMs / Math.max(totalMs, 1) * 100}%`,
7459
+ background: "repeating-linear-gradient(45deg, rgba(255,255,255,0.18) 0 4px, transparent 4px 8px)"
7460
+ }
7461
+ }
7462
+ )
7463
+ }
7464
+ )
7465
+ ] }),
7466
+ /* @__PURE__ */ jsxRuntime.jsxs(
7467
+ "div",
7468
+ {
7469
+ style: {
7470
+ marginTop: 14,
7471
+ paddingTop: 12,
7472
+ borderTop: "1px solid rgba(255,255,255,0.08)",
7473
+ fontSize: 12,
7474
+ display: "flex",
7475
+ justifyContent: "space-between",
7476
+ opacity: 0.85
7477
+ },
7478
+ children: [
7479
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
7480
+ trace.pipelineSteps.length,
7481
+ " step",
7482
+ trace.pipelineSteps.length === 1 ? "" : "s"
7483
+ ] }),
7484
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontFamily: "ui-monospace, SFMono-Regular, monospace" }, children: [
7485
+ "total: ",
7486
+ formatMs(totalMs)
7487
+ ] })
7488
+ ]
7489
+ }
7490
+ )
7491
+ ] });
7492
+ }
7493
+ var MIN_SIDEBAR_WIDTH = 240;
7494
+ var MAX_SIDEBAR_WIDTH = 520;
7495
+ var DEFAULT_SIDEBAR_WIDTH = 264;
7496
+ var WIDTH_STORAGE_KEY = "payman-v2-sessions-width";
7497
+ function readStoredWidth() {
7498
+ if (typeof window === "undefined") return DEFAULT_SIDEBAR_WIDTH;
7499
+ const raw = Number(window.localStorage?.getItem(WIDTH_STORAGE_KEY));
7500
+ if (Number.isFinite(raw) && raw >= MIN_SIDEBAR_WIDTH && raw <= MAX_SIDEBAR_WIDTH) return raw;
7501
+ return DEFAULT_SIDEBAR_WIDTH;
7502
+ }
7503
+ function formatRelativeTime(iso) {
7504
+ if (!iso) return "";
7505
+ const date = new Date(iso);
7506
+ if (Number.isNaN(date.getTime())) return "";
7507
+ const diffSec = Math.round((Date.now() - date.getTime()) / 1e3);
7508
+ if (diffSec < 60) return "just now";
7509
+ const diffMin = Math.round(diffSec / 60);
7510
+ if (diffMin < 60) return `${diffMin} minute${diffMin === 1 ? "" : "s"} ago`;
7511
+ const diffHr = Math.round(diffMin / 60);
7512
+ if (diffHr < 24) return `${diffHr} hour${diffHr === 1 ? "" : "s"} ago`;
7513
+ const diffDay = Math.round(diffHr / 24);
7514
+ if (diffDay < 7) return `${diffDay} day${diffDay === 1 ? "" : "s"} ago`;
7515
+ return date.toLocaleDateString(void 0, { month: "short", day: "numeric" });
7516
+ }
7517
+ function SessionsSidebarV2({
7518
+ open,
7519
+ onClose,
7520
+ title,
7521
+ newChatLabel,
7522
+ sessions,
7523
+ isLoading,
7524
+ error,
7525
+ onRetry,
7526
+ selectedSessionId,
7527
+ onSelectSession,
7528
+ onNewSession,
7529
+ actions,
7530
+ hasMore,
7531
+ isLoadingMore,
7532
+ onLoadMore
7533
+ }) {
7534
+ const [width, setWidth] = React.useState(readStoredWidth);
7535
+ const [isResizing, setIsResizing] = React.useState(false);
7536
+ const dragState = React.useRef(null);
7537
+ const onResizeMove = React.useCallback((event) => {
7538
+ if (!dragState.current) return;
7539
+ const delta = event.clientX - dragState.current.startX;
7540
+ const next = Math.min(
7541
+ MAX_SIDEBAR_WIDTH,
7542
+ Math.max(MIN_SIDEBAR_WIDTH, dragState.current.startWidth + delta)
7543
+ );
7544
+ setWidth(next);
7545
+ }, []);
7546
+ const onResizeEnd = React.useCallback(() => {
7547
+ dragState.current = null;
7548
+ setIsResizing(false);
7549
+ document.removeEventListener("mousemove", onResizeMove);
7550
+ document.removeEventListener("mouseup", onResizeEnd);
7551
+ document.body.style.removeProperty("user-select");
7552
+ setWidth((w) => {
7553
+ if (typeof window !== "undefined") window.localStorage?.setItem(WIDTH_STORAGE_KEY, String(w));
7554
+ return w;
7555
+ });
7556
+ }, [onResizeMove]);
7557
+ const onResizeStart = React.useCallback(
7558
+ (event) => {
7559
+ event.preventDefault();
7560
+ dragState.current = { startX: event.clientX, startWidth: width };
7561
+ setIsResizing(true);
7562
+ document.body.style.userSelect = "none";
7563
+ document.addEventListener("mousemove", onResizeMove);
7564
+ document.addEventListener("mouseup", onResizeEnd);
7565
+ },
7566
+ [width, onResizeMove, onResizeEnd]
7567
+ );
7568
+ React.useEffect(
7569
+ () => () => {
7570
+ document.removeEventListener("mousemove", onResizeMove);
7571
+ document.removeEventListener("mouseup", onResizeEnd);
7572
+ },
7573
+ [onResizeMove, onResizeEnd]
7574
+ );
7575
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7576
+ "div",
7577
+ {
7578
+ className: cn(
7579
+ "payman-v2-sessions-sidebar",
7580
+ !open && "payman-v2-sessions-sidebar-collapsed",
7581
+ isResizing && "payman-v2-sessions-sidebar-resizing"
7582
+ ),
7583
+ role: "complementary",
7584
+ "aria-label": title,
7585
+ "aria-hidden": !open,
7586
+ style: { "--payman-v2-sessions-width": `${width}px` },
7587
+ children: [
7588
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-sessions-sidebar-inner", children: [
7589
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-sessions-header", children: [
7590
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-sessions-title", children: title }),
7591
+ onClose && /* @__PURE__ */ jsxRuntime.jsx(
7592
+ "button",
7593
+ {
7594
+ type: "button",
7595
+ className: "payman-v2-sessions-close",
7596
+ onClick: onClose,
7597
+ "aria-label": "Close chats panel",
7598
+ tabIndex: open ? 0 : -1,
7599
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 16 })
7600
+ }
7601
+ )
7602
+ ] }),
7603
+ /* @__PURE__ */ jsxRuntime.jsxs(
7604
+ "button",
7605
+ {
7606
+ type: "button",
7607
+ className: "payman-v2-sessions-new-btn",
7608
+ onClick: onNewSession,
7609
+ tabIndex: open ? 0 : -1,
7610
+ children: [
7611
+ /* @__PURE__ */ jsxRuntime.jsx(IconNewChat, { size: 16 }),
7612
+ newChatLabel
7613
+ ]
7614
+ }
7615
+ ),
7616
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-sessions-divider" }),
7617
+ /* @__PURE__ */ jsxRuntime.jsx(
7618
+ SessionsListBody,
7619
+ {
7620
+ sessions,
7621
+ isLoading,
7622
+ error,
7623
+ onRetry,
7624
+ selectedSessionId,
7625
+ onSelectSession,
7626
+ actions,
7627
+ hasMore,
7628
+ isLoadingMore,
7629
+ onLoadMore
7630
+ }
7631
+ )
7632
+ ] }),
7633
+ open && /* @__PURE__ */ jsxRuntime.jsx(
7634
+ "div",
7635
+ {
7636
+ className: "payman-v2-sessions-resizer",
7637
+ onMouseDown: onResizeStart,
7638
+ role: "separator",
7639
+ "aria-orientation": "vertical",
7640
+ "aria-label": "Resize chats panel"
7641
+ }
7642
+ )
7643
+ ]
7644
+ }
7645
+ );
7646
+ }
7647
+ function SessionsListBody({
7648
+ sessions,
7649
+ isLoading,
7650
+ error,
7651
+ onRetry,
7652
+ selectedSessionId,
7653
+ onSelectSession,
7654
+ actions,
7655
+ compact = false,
7656
+ hasMore,
7657
+ isLoadingMore,
7658
+ onLoadMore
7659
+ }) {
7660
+ const [openMenuId, setOpenMenuId] = React.useState(null);
7661
+ const listRef = React.useRef(null);
7662
+ const [shadow, setShadow] = React.useState({ top: false, bottom: false });
7663
+ const updateShadow = React.useCallback(() => {
7664
+ const el = listRef.current;
7665
+ if (!el) return;
7666
+ const top = el.scrollTop > 1;
7667
+ const bottom = Math.ceil(el.scrollTop + el.clientHeight) < el.scrollHeight - 1;
7668
+ setShadow((s) => s.top === top && s.bottom === bottom ? s : { top, bottom });
7669
+ }, []);
7670
+ React.useEffect(() => {
7671
+ updateShadow();
7672
+ const el = listRef.current;
7673
+ if (!el) return;
7674
+ const ro = new ResizeObserver(updateShadow);
7675
+ ro.observe(el);
7676
+ return () => ro.disconnect();
7677
+ }, [sessions, isLoading, updateShadow]);
7678
+ return /* @__PURE__ */ jsxRuntime.jsx(
7679
+ "div",
7680
+ {
7681
+ className: cn(
7682
+ "payman-v2-sessions-list-wrap",
7683
+ shadow.top && "payman-v2-scroll-shadow-top",
7684
+ shadow.bottom && "payman-v2-scroll-shadow-bottom"
7685
+ ),
7686
+ children: /* @__PURE__ */ jsxRuntime.jsx(
7687
+ "div",
7688
+ {
7689
+ ref: listRef,
7690
+ onScroll: updateShadow,
7691
+ className: cn("payman-v2-sessions-list", compact && "payman-v2-sessions-list-compact"),
7692
+ children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(SessionsListSkeleton, {}) : error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-sessions-error", children: [
7693
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { size: 18 }),
7694
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: error }),
7695
+ onRetry && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "payman-v2-sessions-retry-btn", onClick: onRetry, children: "Try again" })
7696
+ ] }) : sessions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-sessions-empty", children: [
7697
+ /* @__PURE__ */ jsxRuntime.jsx(IconMessage, { size: 20 }),
7698
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "No chats yet" })
7699
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7700
+ sessions.map((session) => /* @__PURE__ */ jsxRuntime.jsx(
7701
+ SessionRow,
7702
+ {
7703
+ session,
7704
+ isSelected: session.sessionId === selectedSessionId,
7705
+ isMenuOpen: openMenuId === session.sessionId,
7706
+ onSelect: () => onSelectSession(session.sessionId),
7707
+ onToggleMenu: () => setOpenMenuId((id) => id === session.sessionId ? null : session.sessionId),
7708
+ onCloseMenu: () => setOpenMenuId(null),
7709
+ actions
7710
+ },
7711
+ session.sessionId
7712
+ )),
7713
+ hasMore && onLoadMore && /* @__PURE__ */ jsxRuntime.jsx(
7714
+ "button",
7715
+ {
7716
+ type: "button",
7717
+ className: "payman-v2-sessions-load-more-btn",
7718
+ onClick: onLoadMore,
7719
+ disabled: isLoadingMore,
7720
+ children: isLoadingMore ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-spinner payman-v2-spinner-sm" }) : "Load more"
7721
+ }
7722
+ )
7723
+ ] })
7724
+ }
7725
+ )
7726
+ }
7727
+ );
7728
+ }
7729
+ function SessionsListSkeleton() {
7730
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true, className: "payman-v2-sessions-skeleton", children: [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-sessions-skeleton-row", children: [
7731
+ /* @__PURE__ */ jsxRuntime.jsx(
7732
+ "div",
7733
+ {
7734
+ className: "payman-v2-sessions-skeleton-line payman-v2-sessions-skeleton-line-title",
7735
+ style: { animationDelay: `${i * 90}ms`, width: `${60 + i * 13 % 30}%` }
7736
+ }
7737
+ ),
7738
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-sessions-skeleton-meta", children: [
7739
+ /* @__PURE__ */ jsxRuntime.jsx(
7740
+ "div",
7741
+ {
7742
+ className: "payman-v2-sessions-skeleton-line payman-v2-sessions-skeleton-pill",
7743
+ style: { animationDelay: `${i * 90 + 60}ms` }
7744
+ }
7745
+ ),
7746
+ /* @__PURE__ */ jsxRuntime.jsx(
7747
+ "div",
7748
+ {
7749
+ className: "payman-v2-sessions-skeleton-line payman-v2-sessions-skeleton-pill",
7750
+ style: { animationDelay: `${i * 90 + 120}ms` }
7751
+ }
7752
+ )
7753
+ ] })
7754
+ ] }, i)) });
7755
+ }
7756
+ function SessionRow({
7757
+ session,
7758
+ isSelected,
7759
+ isMenuOpen,
7760
+ onSelect,
7761
+ onToggleMenu,
7762
+ onCloseMenu,
7763
+ actions
7764
+ }) {
7765
+ const menuRef = React.useRef(null);
7766
+ const clipRef = React.useRef(null);
7767
+ const textRef = React.useRef(null);
7768
+ const [overflowPx, setOverflowPx] = React.useState(0);
7769
+ const [scrolling, setScrolling] = React.useState(false);
7770
+ React.useEffect(() => {
7771
+ if (!isMenuOpen) return;
7772
+ const handlePointerDown = (event) => {
7773
+ if (menuRef.current && !menuRef.current.contains(event.target)) {
7774
+ onCloseMenu();
7775
+ }
7776
+ };
7777
+ document.addEventListener("mousedown", handlePointerDown);
7778
+ return () => document.removeEventListener("mousedown", handlePointerDown);
7779
+ }, [isMenuOpen, onCloseMenu]);
7780
+ React.useEffect(() => {
7781
+ const clip = clipRef.current;
7782
+ const text = textRef.current;
7783
+ if (!clip || !text) return;
7784
+ const measure = () => setOverflowPx(Math.max(0, text.scrollWidth - clip.clientWidth));
7785
+ measure();
7786
+ const ro = new ResizeObserver(measure);
7787
+ ro.observe(clip);
7788
+ return () => ro.disconnect();
7789
+ }, [session.title]);
7790
+ const truncated = overflowPx > 2;
7791
+ const messageCount = session.messageCount ?? 0;
7792
+ const title = session.title?.trim() || "New chat";
7793
+ const relativeTime = formatRelativeTime(session.lastMessageAt);
7794
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7795
+ "div",
7796
+ {
7797
+ className: cn(
7798
+ "payman-v2-session-row",
7799
+ isSelected && "payman-v2-session-row-selected"
7800
+ ),
7801
+ children: [
7802
+ /* @__PURE__ */ jsxRuntime.jsxs(
7803
+ "button",
7804
+ {
7805
+ type: "button",
7806
+ className: "payman-v2-session-row-main",
7807
+ onClick: onSelect,
7808
+ title,
7809
+ onMouseEnter: () => truncated && setScrolling(true),
7810
+ onMouseLeave: () => setScrolling(false),
7811
+ children: [
7812
+ /* @__PURE__ */ jsxRuntime.jsx(
7813
+ "span",
7814
+ {
7815
+ ref: clipRef,
7816
+ className: "payman-v2-session-row-title",
7817
+ "data-truncated": truncated ? "true" : void 0,
7818
+ "data-scrolling": scrolling ? "true" : void 0,
7819
+ children: /* @__PURE__ */ jsxRuntime.jsx(
7820
+ "span",
7821
+ {
7822
+ ref: textRef,
7823
+ className: "payman-v2-session-row-title-text",
7824
+ style: scrolling ? {
7825
+ // +16 so the last chars clear the right-edge fade mask.
7826
+ transform: `translateX(-${overflowPx + 16}px)`,
7827
+ // Constant reading pace (~38px/s), so it moves at the same
7828
+ // speed from start to end rather than easing in/out.
7829
+ transitionDuration: `${Math.max(0.9, (overflowPx + 16) / 38)}s`
7830
+ } : void 0,
7831
+ children: title
7832
+ }
7833
+ )
7834
+ }
7835
+ ),
7836
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "payman-v2-session-row-meta", children: [
7837
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "payman-v2-session-row-meta-item", children: [
7838
+ /* @__PURE__ */ jsxRuntime.jsx(IconMessage, { size: 12 }),
7839
+ messageCount,
7840
+ " ",
7841
+ messageCount === 1 ? "message" : "messages"
7842
+ ] }),
7843
+ relativeTime && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "payman-v2-session-row-meta-item", children: [
7844
+ /* @__PURE__ */ jsxRuntime.jsx(IconClock, { size: 12 }),
7845
+ relativeTime
7846
+ ] })
7847
+ ] })
7848
+ ]
7849
+ }
7850
+ ),
7851
+ actions && actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-session-row-menu-wrap", ref: menuRef, children: [
7852
+ /* @__PURE__ */ jsxRuntime.jsx(
7853
+ "button",
7854
+ {
7855
+ type: "button",
7856
+ className: cn(
7857
+ "payman-v2-session-row-menu-btn",
7858
+ isMenuOpen && "payman-v2-session-row-menu-btn-active"
7859
+ ),
7860
+ onClick: onToggleMenu,
7861
+ "aria-label": "Chat options",
7862
+ "aria-expanded": isMenuOpen,
7863
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreHorizontal, { size: 14 })
7864
+ }
7865
+ ),
7866
+ isMenuOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-session-row-menu", role: "menu", children: actions.map((action) => /* @__PURE__ */ jsxRuntime.jsx(
7867
+ "button",
7868
+ {
7869
+ type: "button",
7870
+ role: "menuitem",
7871
+ className: cn(
7872
+ "payman-v2-session-row-menu-item",
7873
+ action.destructive && "payman-v2-session-row-menu-item-destructive"
7874
+ ),
7875
+ onClick: () => {
7876
+ onCloseMenu();
7877
+ void action.onSelect(session);
7878
+ },
7879
+ children: action.label
7880
+ },
7881
+ action.key
7882
+ )) })
7883
+ ] })
7884
+ ]
7885
+ }
7886
+ );
7887
+ }
7888
+ var CLOSE_DELAY_MS = 150;
7889
+ var HOVER_OPEN_DELAY_MS = 500;
7890
+ function SessionsPeekPopoverV2({
7891
+ title,
7892
+ newChatLabel,
7893
+ sessions,
7894
+ isLoading,
7895
+ error,
7896
+ onRetry,
7897
+ selectedSessionId,
7898
+ onSelectSession,
7899
+ onNewSession,
7900
+ onExpand,
7901
+ actions
7902
+ }) {
7903
+ const [open, setOpen] = React.useState(false);
7904
+ const closeTimerRef = React.useRef(null);
7905
+ const openTimerRef = React.useRef(null);
7906
+ const cancelClose = React.useCallback(() => {
7907
+ if (closeTimerRef.current) {
7908
+ clearTimeout(closeTimerRef.current);
7909
+ closeTimerRef.current = null;
7910
+ }
7911
+ }, []);
7912
+ const cancelOpen = React.useCallback(() => {
7913
+ if (openTimerRef.current) {
7914
+ clearTimeout(openTimerRef.current);
7915
+ openTimerRef.current = null;
7916
+ }
7917
+ }, []);
7918
+ const scheduleClose = React.useCallback(() => {
7919
+ cancelOpen();
7920
+ cancelClose();
7921
+ closeTimerRef.current = setTimeout(() => setOpen(false), CLOSE_DELAY_MS);
7922
+ }, [cancelOpen, cancelClose]);
7923
+ const handleEnter = React.useCallback(() => {
7924
+ cancelClose();
7925
+ cancelOpen();
7926
+ openTimerRef.current = setTimeout(() => setOpen(true), HOVER_OPEN_DELAY_MS);
7927
+ }, [cancelClose, cancelOpen]);
7928
+ React.useEffect(
7929
+ () => () => {
7930
+ cancelOpen();
7931
+ cancelClose();
7932
+ },
7933
+ [cancelOpen, cancelClose]
7934
+ );
7935
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7936
+ "div",
7937
+ {
7938
+ className: "payman-v2-sessions-peek-anchor",
7939
+ onMouseEnter: handleEnter,
7940
+ onMouseLeave: scheduleClose,
7941
+ children: [
7942
+ /* @__PURE__ */ jsxRuntime.jsx(
7943
+ "button",
7944
+ {
7945
+ type: "button",
7946
+ className: "payman-v2-sessions-reopen-btn",
7947
+ onClick: onExpand,
7948
+ "aria-label": "Show chats",
7949
+ title: "Show chats",
7950
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelLeft, { size: 16 })
7951
+ }
7952
+ ),
7953
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-sessions-peek", role: "dialog", "aria-label": title, children: [
7954
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-sessions-peek-header", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-sessions-peek-title", children: title }) }),
7955
+ /* @__PURE__ */ jsxRuntime.jsxs(
7956
+ "button",
7957
+ {
7958
+ type: "button",
7959
+ className: "payman-v2-sessions-new-btn payman-v2-sessions-peek-new-btn",
7960
+ onClick: () => {
7961
+ onNewSession();
7962
+ setOpen(false);
7963
+ },
7964
+ children: [
7965
+ /* @__PURE__ */ jsxRuntime.jsx(IconNewChat, { size: 14 }),
7966
+ newChatLabel
7967
+ ]
6190
7968
  }
6191
- )
6192
- }
6193
- )
6194
- ] }, `${step2.step}-${step2.startTime}`);
6195
- });
6196
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
6197
- bars,
6198
- unaccountedMs > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 8 }, children: [
6199
- /* @__PURE__ */ jsxRuntime.jsxs(
6200
- "div",
6201
- {
6202
- style: {
6203
- display: "flex",
6204
- justifyContent: "space-between",
6205
- fontSize: 12,
6206
- marginBottom: 4,
6207
- fontFamily: "ui-monospace, SFMono-Regular, monospace",
6208
- opacity: 0.7
6209
- },
6210
- children: [
6211
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "(unaccounted)" }),
6212
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatMs(unaccountedMs) })
6213
- ]
6214
- }
6215
- ),
6216
- /* @__PURE__ */ jsxRuntime.jsx(
6217
- "div",
6218
- {
6219
- style: {
6220
- height: 8,
6221
- background: "rgba(255,255,255,0.06)",
6222
- borderRadius: 4,
6223
- overflow: "hidden"
6224
- },
6225
- children: /* @__PURE__ */ jsxRuntime.jsx(
6226
- "div",
7969
+ ),
7970
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-sessions-divider" }),
7971
+ /* @__PURE__ */ jsxRuntime.jsx(
7972
+ SessionsListBody,
6227
7973
  {
6228
- style: {
6229
- height: "100%",
6230
- width: `${unaccountedMs / Math.max(totalMs, 1) * 100}%`,
6231
- background: "repeating-linear-gradient(45deg, rgba(255,255,255,0.18) 0 4px, transparent 4px 8px)"
6232
- }
7974
+ sessions,
7975
+ isLoading,
7976
+ error,
7977
+ onRetry,
7978
+ selectedSessionId,
7979
+ onSelectSession: (sessionId) => {
7980
+ onSelectSession(sessionId);
7981
+ setOpen(false);
7982
+ },
7983
+ actions,
7984
+ compact: true
6233
7985
  }
6234
7986
  )
6235
- }
6236
- )
6237
- ] }),
7987
+ ] })
7988
+ ]
7989
+ }
7990
+ );
7991
+ }
7992
+ function SessionsBottomSheetV2({
7993
+ title,
7994
+ newChatLabel,
7995
+ sessions,
7996
+ isLoading,
7997
+ error,
7998
+ onRetry,
7999
+ selectedSessionId,
8000
+ onSelectSession,
8001
+ onNewSession,
8002
+ onClose,
8003
+ actions,
8004
+ hasMore,
8005
+ isLoadingMore,
8006
+ onLoadMore
8007
+ }) {
8008
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8009
+ /* @__PURE__ */ jsxRuntime.jsx(
8010
+ framerMotion.motion.div,
8011
+ {
8012
+ className: "payman-v2-sessions-sheet-backdrop",
8013
+ initial: { opacity: 0 },
8014
+ animate: { opacity: 1 },
8015
+ exit: { opacity: 0 },
8016
+ transition: { duration: 0.2 },
8017
+ onClick: onClose
8018
+ }
8019
+ ),
6238
8020
  /* @__PURE__ */ jsxRuntime.jsxs(
6239
- "div",
8021
+ framerMotion.motion.div,
6240
8022
  {
6241
- style: {
6242
- marginTop: 14,
6243
- paddingTop: 12,
6244
- borderTop: "1px solid rgba(255,255,255,0.08)",
6245
- fontSize: 12,
6246
- display: "flex",
6247
- justifyContent: "space-between",
6248
- opacity: 0.85
6249
- },
8023
+ className: "payman-v2-sessions-sheet",
8024
+ role: "dialog",
8025
+ "aria-modal": "true",
8026
+ "aria-label": title,
8027
+ initial: { y: "100%" },
8028
+ animate: { y: 0 },
8029
+ exit: { y: "100%" },
8030
+ transition: { type: "spring", damping: 32, stiffness: 320 },
6250
8031
  children: [
6251
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
6252
- trace.pipelineSteps.length,
6253
- " step",
6254
- trace.pipelineSteps.length === 1 ? "" : "s"
8032
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-sessions-sheet-grabber", "aria-hidden": true }),
8033
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-sessions-header", children: [
8034
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-sessions-title", children: title }),
8035
+ /* @__PURE__ */ jsxRuntime.jsx(
8036
+ "button",
8037
+ {
8038
+ type: "button",
8039
+ className: "payman-v2-sessions-close",
8040
+ onClick: onClose,
8041
+ "aria-label": "Close chats panel",
8042
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 16 })
8043
+ }
8044
+ )
6255
8045
  ] }),
6256
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontFamily: "ui-monospace, SFMono-Regular, monospace" }, children: [
6257
- "total: ",
6258
- formatMs(totalMs)
6259
- ] })
8046
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "payman-v2-sessions-new-btn", onClick: onNewSession, children: [
8047
+ /* @__PURE__ */ jsxRuntime.jsx(IconNewChat, { size: 16 }),
8048
+ newChatLabel
8049
+ ] }),
8050
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-sessions-divider" }),
8051
+ /* @__PURE__ */ jsxRuntime.jsx(
8052
+ SessionsListBody,
8053
+ {
8054
+ sessions,
8055
+ isLoading,
8056
+ error,
8057
+ onRetry,
8058
+ selectedSessionId,
8059
+ onSelectSession,
8060
+ actions,
8061
+ hasMore,
8062
+ isLoadingMore,
8063
+ onLoadMore
8064
+ }
8065
+ )
6260
8066
  ]
6261
8067
  }
6262
8068
  )
@@ -6356,7 +8162,8 @@ var PaymanChatInner = React.forwardRef(function PaymanChatInner2({
6356
8162
  onLoadMoreMessages,
6357
8163
  isLoadingMoreMessages = false,
6358
8164
  hasMoreMessages = false,
6359
- chat
8165
+ chat,
8166
+ sessionsPanel
6360
8167
  }, ref) {
6361
8168
  const [inputValue, setInputValue] = React.useState("");
6362
8169
  const prevInputValueRef = React.useRef(inputValue);
@@ -6369,6 +8176,25 @@ var PaymanChatInner = React.forwardRef(function PaymanChatInner2({
6369
8176
  const chatInputV2Ref = React.useRef(null);
6370
8177
  const messageListV2Ref = React.useRef(null);
6371
8178
  const resetToEmptyStateRef = React.useRef(false);
8179
+ const rootRef = React.useRef(null);
8180
+ const [isCompact, setIsCompact] = React.useState(false);
8181
+ React.useEffect(() => {
8182
+ const el = rootRef.current;
8183
+ if (!el) return;
8184
+ const ro = new ResizeObserver((entries) => {
8185
+ const width = entries[0]?.contentRect.width;
8186
+ if (width != null) setIsCompact(width < COMPACT_BREAKPOINT_PX);
8187
+ });
8188
+ ro.observe(el);
8189
+ return () => ro.disconnect();
8190
+ }, []);
8191
+ const wasCompactRef = React.useRef(isCompact);
8192
+ React.useEffect(() => {
8193
+ if (isCompact && !wasCompactRef.current && sessionsPanel?.open) {
8194
+ sessionsPanel.onToggle();
8195
+ }
8196
+ wasCompactRef.current = isCompact;
8197
+ }, [isCompact, sessionsPanel]);
6372
8198
  React.useEffect(() => {
6373
8199
  if (config.sentryDsn) {
6374
8200
  initSentryIfNeeded(config.sentryDsn);
@@ -6413,6 +8239,15 @@ var PaymanChatInner = React.forwardRef(function PaymanChatInner2({
6413
8239
  const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
6414
8240
  const dismissNotification = chat.dismissNotification ?? NOOP;
6415
8241
  const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
8242
+ const expiredUserActionIdsRef = React.useRef(/* @__PURE__ */ new Set());
8243
+ const expireUserActionOnce = React.useCallback(
8244
+ (userActionId) => {
8245
+ if (expiredUserActionIdsRef.current.has(userActionId)) return;
8246
+ expiredUserActionIdsRef.current.add(userActionId);
8247
+ void expireUserAction2(userActionId);
8248
+ },
8249
+ [expireUserAction2]
8250
+ );
6416
8251
  const {
6417
8252
  transcribedText,
6418
8253
  isAvailable: voiceAvailable,
@@ -6514,13 +8349,20 @@ var PaymanChatInner = React.forwardRef(function PaymanChatInner2({
6514
8349
  if (isRecording) {
6515
8350
  stopRecording();
6516
8351
  }
6517
- resetSession();
8352
+ if (sessionsPanel?.onNewSession) {
8353
+ sessionsPanel.onNewSession();
8354
+ if (isCompact && sessionsPanel.open) sessionsPanel.onToggle();
8355
+ } else {
8356
+ resetSession();
8357
+ }
6518
8358
  onResetSession?.();
6519
8359
  }, [
6520
8360
  clearTranscript,
6521
8361
  isRecording,
8362
+ isCompact,
6522
8363
  onResetSession,
6523
8364
  resetSession,
8365
+ sessionsPanel,
6524
8366
  stopRecording
6525
8367
  ]);
6526
8368
  const requestResetSession = React.useCallback(() => {
@@ -6534,8 +8376,9 @@ var PaymanChatInner = React.forwardRef(function PaymanChatInner2({
6534
8376
  clearMessages,
6535
8377
  cancelStream,
6536
8378
  getSessionId,
6537
- getMessages
6538
- }), [performResetSession, clearMessages, cancelStream, getSessionId, getMessages]);
8379
+ getMessages,
8380
+ refreshSessions: sessionsPanel?.refetch ?? NOOP
8381
+ }), [performResetSession, clearMessages, cancelStream, getSessionId, getMessages, sessionsPanel?.refetch]);
6539
8382
  const {
6540
8383
  placeholder = "Type your message...",
6541
8384
  emptyStateText = "What can I help with?",
@@ -6655,6 +8498,13 @@ var PaymanChatInner = React.forwardRef(function PaymanChatInner2({
6655
8498
  };
6656
8499
  const userActionPrompts = isUserActionSupported ? userActionState.prompts : void 0;
6657
8500
  const notifications = userActionState.notifications;
8501
+ const dockableUserActionPrompts = userActionPrompts?.filter(
8502
+ (prompt) => prompt.kind !== "notification" && (prompt.status === "pending" || prompt.status === "submitting" || prompt.status === "stale")
8503
+ );
8504
+ const dockedPendingPrompts = dockableUserActionPrompts?.filter(
8505
+ (prompt) => prompt.status === "pending"
8506
+ );
8507
+ const dockedPrompt = dockedPendingPrompts?.[dockedPendingPrompts.length - 1] ?? dockableUserActionPrompts?.[dockableUserActionPrompts.length - 1];
6658
8508
  const handleV2Send = (text) => {
6659
8509
  if (isRecording) stopRecording();
6660
8510
  if (text.trim() && !disableInput && isSessionParamsConfigured) {
@@ -6703,160 +8553,236 @@ var PaymanChatInner = React.forwardRef(function PaymanChatInner2({
6703
8553
  return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
6704
8554
  "div",
6705
8555
  {
8556
+ ref: rootRef,
6706
8557
  className: cn("payman-v2-root", className),
6707
8558
  style,
6708
8559
  children: [
6709
8560
  children,
6710
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsxRuntime.jsx(
6711
- framerMotion.motion.div,
6712
- {
6713
- initial: { opacity: 1 },
6714
- exit: { opacity: 0 },
6715
- transition: { duration: 0.3 },
6716
- className: "payman-v2-chat-layout",
6717
- style: { justifyContent: "center", alignItems: "center" },
6718
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", flex: 1, width: "100%" }, children: [
6719
- /* @__PURE__ */ jsxRuntime.jsx(
6720
- MessageList,
6721
- {
6722
- messages,
6723
- isLoading: false,
6724
- emptyStateText,
6725
- showEmptyStateIcon,
6726
- emptyStateComponent,
6727
- layout,
6728
- showTimestamps,
6729
- stage: config.stage || "DEVELOPMENT",
6730
- animated,
6731
- showAgentName,
6732
- agentName,
6733
- showAvatars,
6734
- showUserAvatar,
6735
- showAssistantAvatar,
6736
- showExecutionSteps,
6737
- showStreamingDot,
6738
- streamingStepsText,
6739
- completedStepsText,
6740
- onExecutionTraceClick,
6741
- onLoadMoreMessages,
6742
- isLoadingMoreMessages,
6743
- hasMoreMessages
6744
- }
6745
- ),
6746
- /* @__PURE__ */ jsxRuntime.jsx(
6747
- framerMotion.motion.div,
6748
- {
6749
- initial: { opacity: 0, y: 12 },
6750
- animate: { opacity: 1, y: 0 },
6751
- transition: { delay: 0.2, duration: 0.4, ease: [0.25, 0.46, 0.45, 0.94] },
6752
- style: { width: "100%" },
6753
- children: hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
6754
- ChatInputV2,
8561
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-body", children: [
8562
+ sessionsPanel && !isCompact && /* @__PURE__ */ jsxRuntime.jsx(
8563
+ SessionsSidebarV2,
8564
+ {
8565
+ open: sessionsPanel.open,
8566
+ onClose: sessionsPanel.onToggle,
8567
+ title: sessionsPanel.title,
8568
+ newChatLabel: sessionsPanel.newChatLabel,
8569
+ sessions: sessionsPanel.sessionsList,
8570
+ isLoading: sessionsPanel.isLoadingList,
8571
+ error: sessionsPanel.listError,
8572
+ onRetry: sessionsPanel.refetch,
8573
+ selectedSessionId: sessionsPanel.activeSessionId,
8574
+ onSelectSession: sessionsPanel.onSelectSession,
8575
+ onNewSession: requestResetSession,
8576
+ actions: sessionsPanel.actions,
8577
+ hasMore: sessionsPanel.hasMore,
8578
+ isLoadingMore: sessionsPanel.isLoadingMore,
8579
+ onLoadMore: sessionsPanel.onLoadMore
8580
+ }
8581
+ ),
8582
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: sessionsPanel && isCompact && sessionsPanel.open && /* @__PURE__ */ jsxRuntime.jsx(
8583
+ SessionsBottomSheetV2,
8584
+ {
8585
+ title: sessionsPanel.title,
8586
+ newChatLabel: sessionsPanel.newChatLabel,
8587
+ sessions: sessionsPanel.sessionsList,
8588
+ isLoading: sessionsPanel.isLoadingList,
8589
+ error: sessionsPanel.listError,
8590
+ onRetry: sessionsPanel.refetch,
8591
+ selectedSessionId: sessionsPanel.activeSessionId,
8592
+ onSelectSession: (sessionId) => {
8593
+ sessionsPanel.onSelectSession(sessionId);
8594
+ sessionsPanel.onToggle();
8595
+ },
8596
+ onNewSession: requestResetSession,
8597
+ onClose: sessionsPanel.onToggle,
8598
+ actions: sessionsPanel.actions,
8599
+ hasMore: sessionsPanel.hasMore,
8600
+ isLoadingMore: sessionsPanel.isLoadingMore,
8601
+ onLoadMore: sessionsPanel.onLoadMore
8602
+ },
8603
+ "sessions-bottom-sheet"
8604
+ ) }),
8605
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-v2-chat-column", children: [
8606
+ sessionsPanel?.isLoadingMessages && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-messages-loading-overlay", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-spinner" }) }),
8607
+ sessionsPanel && !sessionsPanel.open && /* @__PURE__ */ jsxRuntime.jsx(
8608
+ SessionsPeekPopoverV2,
8609
+ {
8610
+ title: sessionsPanel.title,
8611
+ newChatLabel: sessionsPanel.newChatLabel,
8612
+ sessions: sessionsPanel.sessionsList,
8613
+ isLoading: sessionsPanel.isLoadingList,
8614
+ error: sessionsPanel.listError,
8615
+ onRetry: sessionsPanel.refetch,
8616
+ selectedSessionId: sessionsPanel.activeSessionId,
8617
+ onSelectSession: sessionsPanel.onSelectSession,
8618
+ onNewSession: requestResetSession,
8619
+ onExpand: sessionsPanel.onToggle,
8620
+ actions: sessionsPanel.actions
8621
+ }
8622
+ ),
8623
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", children: isEmpty && !hasEverSentMessage ? /* @__PURE__ */ jsxRuntime.jsx(
8624
+ framerMotion.motion.div,
8625
+ {
8626
+ initial: { opacity: 1 },
8627
+ exit: { opacity: 0 },
8628
+ transition: { duration: 0.3 },
8629
+ className: "payman-v2-chat-layout",
8630
+ style: { justifyContent: "center", alignItems: "center" },
8631
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", flex: 1, width: "100%" }, children: [
8632
+ /* @__PURE__ */ jsxRuntime.jsx(
8633
+ MessageList,
8634
+ {
8635
+ messages,
8636
+ isLoading: false,
8637
+ emptyStateText,
8638
+ showEmptyStateIcon,
8639
+ emptyStateComponent,
8640
+ layout,
8641
+ showTimestamps,
8642
+ stage: config.stage || "DEVELOPMENT",
8643
+ animated,
8644
+ showAgentName,
8645
+ agentName,
8646
+ showAvatars,
8647
+ showUserAvatar,
8648
+ showAssistantAvatar,
8649
+ showExecutionSteps,
8650
+ showStreamingDot,
8651
+ streamingStepsText,
8652
+ completedStepsText,
8653
+ onExecutionTraceClick,
8654
+ onLoadMoreMessages,
8655
+ isLoadingMoreMessages,
8656
+ hasMoreMessages
8657
+ }
8658
+ ),
8659
+ /* @__PURE__ */ jsxRuntime.jsx(
8660
+ framerMotion.motion.div,
8661
+ {
8662
+ initial: { opacity: 0 },
8663
+ animate: { opacity: 1 },
8664
+ transition: { duration: 0.18 },
8665
+ style: { width: "100%" },
8666
+ children: hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
8667
+ ChatInputV2,
8668
+ {
8669
+ ref: chatInputV2Ref,
8670
+ onSend: handleV2Send,
8671
+ onCancel: cancelStream,
8672
+ disabled: isV2InputDisabled,
8673
+ isStreaming: isWaitingForResponse,
8674
+ placeholder: isRecording ? "Listening..." : placeholder,
8675
+ enableVoice: config.enableVoice === true,
8676
+ transcribedText: config.enableVoice === true ? transcribedText : "",
8677
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
8678
+ isRecording,
8679
+ onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
8680
+ onCancelRecording: handleCancelRecording,
8681
+ onConfirmRecording: handleConfirmRecording,
8682
+ showResetSession,
8683
+ onResetSession: requestResetSession,
8684
+ showAttachmentButton,
8685
+ showUploadImageButton,
8686
+ showAttachFileButton,
8687
+ onUploadImageClick,
8688
+ onAttachFileClick,
8689
+ editingMessageId,
8690
+ onClearEditing: handleClearEditing,
8691
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
8692
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
8693
+ slashCommands
8694
+ }
8695
+ )
8696
+ }
8697
+ )
8698
+ ] })
8699
+ },
8700
+ "v2-empty"
8701
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
8702
+ framerMotion.motion.div,
8703
+ {
8704
+ initial: { opacity: 0 },
8705
+ animate: { opacity: 1 },
8706
+ transition: { duration: 0.25 },
8707
+ className: "payman-v2-chat-layout",
8708
+ children: [
8709
+ /* @__PURE__ */ jsxRuntime.jsx(
8710
+ MessageListV2,
8711
+ {
8712
+ ref: messageListV2Ref,
8713
+ messages,
8714
+ isStreaming: isWaitingForResponse,
8715
+ onEditUserMessage: handleEditMessageDraft,
8716
+ onRetryUserMessage: handleRetryUserMessage,
8717
+ onImageClick: handleImageClick,
8718
+ onExecutionTraceClick,
8719
+ messageActions,
8720
+ retryDisabled: isWaitingForResponse,
8721
+ typingSpeed: config.typingSpeed ?? 4,
8722
+ userActionPrompts,
8723
+ dockedUserActionId: dockedPrompt?.userActionId,
8724
+ notifications,
8725
+ onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
8726
+ onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
8727
+ onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
8728
+ onExpireUserAction: isUserActionSupported ? async (id) => expireUserActionOnce(id) : void 0,
8729
+ onDismissNotification: dismissNotification,
8730
+ onSubmitFeedback: handleSubmitFeedback
8731
+ }
8732
+ ),
8733
+ /* @__PURE__ */ jsxRuntime.jsx(
8734
+ StreamingIndicatorV2,
6755
8735
  {
6756
- ref: chatInputV2Ref,
6757
- onSend: handleV2Send,
6758
- onCancel: cancelStream,
6759
- disabled: isV2InputDisabled,
6760
8736
  isStreaming: isWaitingForResponse,
6761
- placeholder: isRecording ? "Listening..." : placeholder,
6762
- enableVoice: config.enableVoice === true,
6763
- transcribedText: config.enableVoice === true ? transcribedText : "",
6764
- voiceAvailable: config.enableVoice === true && voiceAvailable,
6765
- isRecording,
6766
- onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
6767
- onCancelRecording: handleCancelRecording,
6768
- onConfirmRecording: handleConfirmRecording,
6769
- showResetSession,
6770
- onResetSession: requestResetSession,
6771
- showAttachmentButton,
6772
- showUploadImageButton,
6773
- showAttachFileButton,
6774
- onUploadImageClick,
6775
- onAttachFileClick,
6776
- editingMessageId,
6777
- onClearEditing: handleClearEditing,
6778
- analysisMode: enableDeepModeToggle ? analysisMode : void 0,
6779
- onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
6780
- slashCommands
8737
+ loadingAnimation: config.loadingAnimation
8738
+ }
8739
+ ),
8740
+ hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
8741
+ UserActionDock,
8742
+ {
8743
+ prompt: isUserActionSupported ? dockedPrompt : void 0,
8744
+ onSubmit: submitUserAction2,
8745
+ onCancel: cancelUserAction2,
8746
+ onResend: resendUserAction2,
8747
+ onExpired: expireUserActionOnce,
8748
+ children: /* @__PURE__ */ jsxRuntime.jsx(
8749
+ ChatInputV2,
8750
+ {
8751
+ ref: chatInputV2Ref,
8752
+ onSend: handleV2Send,
8753
+ onCancel: cancelStream,
8754
+ disabled: isV2InputDisabled,
8755
+ isStreaming: isWaitingForResponse,
8756
+ placeholder: isRecording ? "Listening..." : placeholder,
8757
+ enableVoice: config.enableVoice === true,
8758
+ transcribedText: config.enableVoice === true ? transcribedText : "",
8759
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
8760
+ isRecording,
8761
+ onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
8762
+ onCancelRecording: handleCancelRecording,
8763
+ onConfirmRecording: handleConfirmRecording,
8764
+ showResetSession,
8765
+ onResetSession: requestResetSession,
8766
+ showAttachmentButton,
8767
+ showUploadImageButton,
8768
+ showAttachFileButton,
8769
+ onUploadImageClick,
8770
+ onAttachFileClick,
8771
+ editingMessageId,
8772
+ onClearEditing: handleClearEditing,
8773
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
8774
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
8775
+ slashCommands
8776
+ }
8777
+ )
6781
8778
  }
6782
8779
  )
6783
- }
6784
- )
6785
- ] })
6786
- },
6787
- "v2-empty"
6788
- ) : /* @__PURE__ */ jsxRuntime.jsxs(
6789
- framerMotion.motion.div,
6790
- {
6791
- initial: hasEverSentMessage ? { opacity: 0 } : false,
6792
- animate: { opacity: 1 },
6793
- transition: { duration: 0.3 },
6794
- className: "payman-v2-chat-layout",
6795
- children: [
6796
- /* @__PURE__ */ jsxRuntime.jsx(
6797
- MessageListV2,
6798
- {
6799
- ref: messageListV2Ref,
6800
- messages,
6801
- isStreaming: isWaitingForResponse,
6802
- onEditUserMessage: handleEditMessageDraft,
6803
- onRetryUserMessage: handleRetryUserMessage,
6804
- onImageClick: handleImageClick,
6805
- onExecutionTraceClick,
6806
- messageActions,
6807
- retryDisabled: isWaitingForResponse,
6808
- typingSpeed: config.typingSpeed ?? 4,
6809
- userActionPrompts,
6810
- notifications,
6811
- onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6812
- onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6813
- onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6814
- onExpireUserAction: isUserActionSupported ? expireUserAction2 : void 0,
6815
- onDismissNotification: dismissNotification,
6816
- onSubmitFeedback: handleSubmitFeedback
6817
- }
6818
- ),
6819
- /* @__PURE__ */ jsxRuntime.jsx(
6820
- StreamingIndicatorV2,
6821
- {
6822
- isStreaming: isWaitingForResponse,
6823
- loadingAnimation: config.loadingAnimation
6824
- }
6825
- ),
6826
- hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
6827
- ChatInputV2,
6828
- {
6829
- ref: chatInputV2Ref,
6830
- onSend: handleV2Send,
6831
- onCancel: cancelStream,
6832
- disabled: isV2InputDisabled,
6833
- isStreaming: isWaitingForResponse,
6834
- placeholder: isRecording ? "Listening..." : placeholder,
6835
- enableVoice: config.enableVoice === true,
6836
- transcribedText: config.enableVoice === true ? transcribedText : "",
6837
- voiceAvailable: config.enableVoice === true && voiceAvailable,
6838
- isRecording,
6839
- onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
6840
- onCancelRecording: handleCancelRecording,
6841
- onConfirmRecording: handleConfirmRecording,
6842
- showResetSession,
6843
- onResetSession: requestResetSession,
6844
- showAttachmentButton,
6845
- showUploadImageButton,
6846
- showAttachFileButton,
6847
- onUploadImageClick,
6848
- onAttachFileClick,
6849
- editingMessageId,
6850
- onClearEditing: handleClearEditing,
6851
- analysisMode: enableDeepModeToggle ? analysisMode : void 0,
6852
- onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
6853
- slashCommands
6854
- }
6855
- )
6856
- ]
6857
- },
6858
- "v2-chat"
6859
- ) }),
8780
+ ]
8781
+ },
8782
+ "v2-chat"
8783
+ ) })
8784
+ ] })
8785
+ ] }),
6860
8786
  /* @__PURE__ */ jsxRuntime.jsx(
6861
8787
  ImageLightboxV2,
6862
8788
  {
@@ -6887,13 +8813,162 @@ var PaymanChatInner = React.forwardRef(function PaymanChatInner2({
6887
8813
  }
6888
8814
  ) });
6889
8815
  });
6890
- var PaymanChat = React.forwardRef(
6891
- function PaymanChat2(props, ref) {
8816
+ var PaymanChatWithChat = React.forwardRef(
8817
+ function PaymanChatWithChat2(props, ref) {
6892
8818
  const mergedCallbacks = useSentryChatCallbacks(props.callbacks, props.config);
6893
8819
  const chat = useChatV2(props.config, mergedCallbacks);
6894
8820
  return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatInner, { ...props, chat, ref });
6895
8821
  }
6896
8822
  );
8823
+ function AvailabilityNotice({ message }) {
8824
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center p-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-muted-foreground text-sm", children: message }) }) });
8825
+ }
8826
+ var DEFAULT_AGENT_DISABLED_MESSAGE = "Chat is currently unavailable. Please try again later.";
8827
+ var DEFAULT_USER_BLOCKED_MESSAGE = "Chat isn't available for your account right now.";
8828
+ var COMPACT_BREAKPOINT_PX = 640;
8829
+ var PaymanChat = React.forwardRef(
8830
+ function PaymanChat2(props, ref) {
8831
+ const { config } = props;
8832
+ const sessionsConfig = config.sessions;
8833
+ const availabilityData = useAgentConfig({
8834
+ api: config.api,
8835
+ availability: config.availability
8836
+ });
8837
+ const agentUnavailable = availabilityData.enabled && availabilityData.available === false;
8838
+ const availabilityStatus = availabilityData.status;
8839
+ const availabilityGateOpen = !availabilityData.enabled || availabilityData.available === true || availabilityData.available === null && !availabilityData.isLoading;
8840
+ const gatedSessionsConfig = React.useMemo(
8841
+ () => sessionsConfig ? {
8842
+ ...sessionsConfig,
8843
+ enabled: sessionsConfig.enabled === true && availabilityGateOpen
8844
+ } : sessionsConfig,
8845
+ [sessionsConfig, availabilityGateOpen]
8846
+ );
8847
+ const sessionsData = useChatSessions({
8848
+ api: config.api,
8849
+ sessions: gatedSessionsConfig,
8850
+ initialSessionId: config.initialSessionId
8851
+ });
8852
+ const [sidebarOpen, setSidebarOpen] = React.useState(
8853
+ sessionsConfig?.defaultOpen !== false
8854
+ );
8855
+ const toggleSidebar = React.useCallback(() => setSidebarOpen((open) => !open), []);
8856
+ const effectiveConfig = React.useMemo(() => {
8857
+ if (!sessionsData.enabled) return config;
8858
+ return {
8859
+ ...config,
8860
+ initialSessionId: sessionsData.activeSessionId,
8861
+ initialMessages: sessionsData.selectedMessages,
8862
+ autoGenerateSessionId: false,
8863
+ // Scope the message store per active session. The base userId alone
8864
+ // is shared across every session, so its chatStore entry would leak
8865
+ // the previous chat's messages into a newly-selected or brand-new
8866
+ // session (getStoredOrInitialMessages prefers the store over
8867
+ // initialMessages). Per-session keys give each chat a clean slate.
8868
+ userId: config.userId ? `${config.userId}::${sessionsData.activeSessionId}` : void 0
8869
+ };
8870
+ }, [
8871
+ config,
8872
+ sessionsData.enabled,
8873
+ sessionsData.activeSessionId,
8874
+ sessionsData.selectedMessages
8875
+ ]);
8876
+ const sessionsPanel = React.useMemo(() => {
8877
+ if (!sessionsData.enabled) return void 0;
8878
+ return {
8879
+ open: sidebarOpen,
8880
+ onToggle: toggleSidebar,
8881
+ title: sessionsConfig?.title ?? "Chats",
8882
+ newChatLabel: sessionsConfig?.newChatLabel ?? "Start New Chat",
8883
+ sessionsList: sessionsData.sessionsList,
8884
+ isLoadingList: sessionsData.isLoadingList,
8885
+ listError: sessionsData.listError,
8886
+ activeSessionId: sessionsData.activeSessionId,
8887
+ onSelectSession: sessionsData.selectSession,
8888
+ onNewSession: sessionsData.startNewSession,
8889
+ actions: sessionsConfig?.actions,
8890
+ refetch: sessionsData.refetch,
8891
+ hasMore: sessionsData.hasMore,
8892
+ isLoadingMore: sessionsData.isLoadingMore,
8893
+ onLoadMore: sessionsData.loadMore,
8894
+ isLoadingMessages: sessionsData.isLoadingMessages
8895
+ };
8896
+ }, [
8897
+ sessionsData.enabled,
8898
+ sessionsData.sessionsList,
8899
+ sessionsData.isLoadingList,
8900
+ sessionsData.listError,
8901
+ sessionsData.activeSessionId,
8902
+ sessionsData.selectSession,
8903
+ sessionsData.startNewSession,
8904
+ sessionsData.refetch,
8905
+ sessionsData.hasMore,
8906
+ sessionsData.isLoadingMore,
8907
+ sessionsData.loadMore,
8908
+ sessionsData.isLoadingMessages,
8909
+ sidebarOpen,
8910
+ toggleSidebar,
8911
+ sessionsConfig?.title,
8912
+ sessionsConfig?.newChatLabel,
8913
+ sessionsConfig?.actions
8914
+ ]);
8915
+ const callbacksWithSessionsRefresh = React.useMemo(() => {
8916
+ if (!sessionsData.enabled) return props.callbacks;
8917
+ return {
8918
+ ...props.callbacks,
8919
+ // Reflect the message in the sidebar the instant it's sent, before
8920
+ // the server round-trip — the refetch below reconciles on complete.
8921
+ onMessageSent: (message) => {
8922
+ props.callbacks?.onMessageSent?.(message);
8923
+ sessionsData.noteSentMessage(message);
8924
+ },
8925
+ onStreamComplete: (message) => {
8926
+ props.callbacks?.onStreamComplete?.(message);
8927
+ sessionsData.refetch();
8928
+ }
8929
+ };
8930
+ }, [sessionsData.enabled, props.callbacks, sessionsData.refetch, sessionsData.noteSentMessage]);
8931
+ const availabilityPending = availabilityData.enabled && availabilityData.isLoading && availabilityData.available === null;
8932
+ if (availabilityPending) {
8933
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8934
+ "div",
8935
+ {
8936
+ className: cn("bg-background overflow-hidden flex flex-col flex-[4]", props.className),
8937
+ style: props.style,
8938
+ children: [
8939
+ props.children,
8940
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-v2-chat-loading", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-v2-spinner" }) })
8941
+ ]
8942
+ }
8943
+ );
8944
+ }
8945
+ if (agentUnavailable) {
8946
+ const disabledBody = availabilityStatus === "blocked" ? config.availability?.blockedComponent ?? /* @__PURE__ */ jsxRuntime.jsx(AvailabilityNotice, { message: DEFAULT_USER_BLOCKED_MESSAGE }) : config.availability?.disabledComponent ?? /* @__PURE__ */ jsxRuntime.jsx(AvailabilityNotice, { message: DEFAULT_AGENT_DISABLED_MESSAGE });
8947
+ return /* @__PURE__ */ jsxRuntime.jsxs(
8948
+ "div",
8949
+ {
8950
+ className: cn("bg-background overflow-hidden flex flex-col flex-[4]", props.className),
8951
+ style: props.style,
8952
+ children: [
8953
+ props.children,
8954
+ disabledBody
8955
+ ]
8956
+ }
8957
+ );
8958
+ }
8959
+ return /* @__PURE__ */ jsxRuntime.jsx(
8960
+ PaymanChatWithChat,
8961
+ {
8962
+ ...props,
8963
+ config: effectiveConfig,
8964
+ callbacks: callbacksWithSessionsRefresh,
8965
+ sessionsPanel,
8966
+ ref
8967
+ },
8968
+ sessionsData.enabled ? sessionsData.activeSessionId : void 0
8969
+ );
8970
+ }
8971
+ );
6897
8972
 
6898
8973
  exports.PaymanChat = PaymanChat;
6899
8974
  exports.PaymanChatContext = PaymanChatContext;
@@ -6904,6 +8979,7 @@ exports.cn = cn;
6904
8979
  exports.formatDate = formatDate;
6905
8980
  exports.resendUserAction = resendUserAction;
6906
8981
  exports.submitUserAction = submitUserAction;
8982
+ exports.useAgentConfig = useAgentConfig;
6907
8983
  exports.useChatV2 = useChatV2;
6908
8984
  exports.usePaymanChat = usePaymanChat;
6909
8985
  exports.useVoice = useVoice;