@paymanai/payman-ask-sdk 4.0.26 → 4.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { createContext, forwardRef, useCallback, useRef, useState, useMemo, useEffect, useImperativeHandle, useLayoutEffect, useContext } from 'react';
3
- import { AnimatePresence, motion } from 'framer-motion';
3
+ import { AnimatePresence, motion, useMotionValue, useSpring, useInView } from 'framer-motion';
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import * as Sentry from '@sentry/react';
@@ -1128,6 +1128,9 @@ function getUserActionSecondsLeft(prompt) {
1128
1128
  return void 0;
1129
1129
  }
1130
1130
  var EMPTY_USER_ACTION_STATE2 = { prompts: [], notifications: [] };
1131
+ function promptSlotKey(p) {
1132
+ return p.toolCallId || p.userActionId;
1133
+ }
1131
1134
  function upsertPrompt(prompts, req) {
1132
1135
  const idx = prompts.findIndex(
1133
1136
  (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
@@ -1174,6 +1177,7 @@ function useChatV2(config, callbacks = {}) {
1174
1177
  configRef.current = config;
1175
1178
  const messagesRef = useRef(messages);
1176
1179
  messagesRef.current = messages;
1180
+ const pendingSubmissionsRef = useRef(/* @__PURE__ */ new Map());
1177
1181
  const storeAwareSetMessages = useCallback(
1178
1182
  (updater) => {
1179
1183
  const streamUserId = streamUserIdRef.current;
@@ -1208,6 +1212,8 @@ function useChatV2(config, callbacks = {}) {
1208
1212
  if (!config.userId) return EMPTY_USER_ACTION_STATE2;
1209
1213
  return activeStreamStore.get(config.userId)?.userActionState ?? EMPTY_USER_ACTION_STATE2;
1210
1214
  });
1215
+ const userActionStateRef = useRef(userActionState);
1216
+ userActionStateRef.current = userActionState;
1211
1217
  const storeAwareSetUserActionState = useCallback(
1212
1218
  (updater) => {
1213
1219
  const streamUserId = streamUserIdRef.current;
@@ -1235,6 +1241,12 @@ function useChatV2(config, callbacks = {}) {
1235
1241
  onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1236
1242
  onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1237
1243
  onUserActionRequired: (request) => {
1244
+ const requestSlotKey = promptSlotKey(request);
1245
+ for (const [pendingId, slotKey] of pendingSubmissionsRef.current) {
1246
+ if (slotKey === requestSlotKey) {
1247
+ pendingSubmissionsRef.current.delete(pendingId);
1248
+ }
1249
+ }
1238
1250
  storeAwareSetUserActionState((prev) => ({
1239
1251
  ...prev,
1240
1252
  prompts: upsertPrompt(prev.prompts, request)
@@ -1246,6 +1258,28 @@ function useChatV2(config, callbacks = {}) {
1246
1258
  (prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
1247
1259
  );
1248
1260
  callbacksRef.current.onUserNotification?.(notification);
1261
+ },
1262
+ // A submitted-but-unconfirmed prompt (see `submitUserAction`) is only
1263
+ // safe to drop once the stream has demonstrably moved on. `onEvent`
1264
+ // in useStreamManagerV2 calls `onStepsUpdate` for *every* processed
1265
+ // stream event, unconditionally — so the first time this fires
1266
+ // after a submission, the event in hand is either the rejection
1267
+ // retry (a fresh `USER_ACTION_REQUIRED`, which the handler above
1268
+ // already deleted this pending entry for, earlier in the very same
1269
+ // event) or genuine forward progress (RUN_IN_PROGRESS, a tool call,
1270
+ // content deltas, RUN_COMPLETED, ...). Anything still pending by
1271
+ // the time we get here therefore means the run resumed normally —
1272
+ // clear it.
1273
+ onStepsUpdate: (steps) => {
1274
+ if (pendingSubmissionsRef.current.size > 0) {
1275
+ const resolved = [...pendingSubmissionsRef.current.keys()];
1276
+ pendingSubmissionsRef.current.clear();
1277
+ storeAwareSetUserActionState((prev) => ({
1278
+ ...prev,
1279
+ prompts: prev.prompts.filter((p) => !resolved.includes(p.userActionId))
1280
+ }));
1281
+ }
1282
+ callbacksRef.current.onStepsUpdate?.(steps);
1249
1283
  }
1250
1284
  // eslint-disable-next-line react-hooks/exhaustive-deps
1251
1285
  }), []);
@@ -1381,6 +1415,7 @@ function useChatV2(config, callbacks = {}) {
1381
1415
  [storeAwareSetUserActionState]
1382
1416
  );
1383
1417
  const removePrompt = useCallback((userActionId) => {
1418
+ pendingSubmissionsRef.current.delete(userActionId);
1384
1419
  storeAwareSetUserActionState((prev) => ({
1385
1420
  ...prev,
1386
1421
  prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
@@ -1391,7 +1426,10 @@ function useChatV2(config, callbacks = {}) {
1391
1426
  setPromptStatus(userActionId, "submitting");
1392
1427
  try {
1393
1428
  await submitUserAction(configRef.current, userActionId, content);
1394
- removePrompt(userActionId);
1429
+ const prompt = userActionStateRef.current.prompts.find(
1430
+ (p) => p.userActionId === userActionId
1431
+ );
1432
+ pendingSubmissionsRef.current.set(userActionId, prompt ? promptSlotKey(prompt) : userActionId);
1395
1433
  } catch (error) {
1396
1434
  if (error instanceof UserActionStaleError) {
1397
1435
  setPromptStatus(userActionId, "stale");
@@ -1402,7 +1440,7 @@ function useChatV2(config, callbacks = {}) {
1402
1440
  throw error;
1403
1441
  }
1404
1442
  },
1405
- [removePrompt, setPromptStatus]
1443
+ [setPromptStatus]
1406
1444
  );
1407
1445
  const cancelUserAction2 = useCallback(
1408
1446
  async (userActionId) => {
@@ -4082,6 +4120,8 @@ function AssistantMessageV2({
4082
4120
  void 0,
4083
4121
  typingSpeed
4084
4122
  );
4123
+ const displayContentPending = Boolean(rawResponseContent) && !displayContent;
4124
+ const showThinkingBlock = isThinkingStreaming || displayContentPending;
4085
4125
  const stickyLabel = (() => {
4086
4126
  const steps = message.steps;
4087
4127
  if (!steps || steps.length === 0) return void 0;
@@ -4216,7 +4256,7 @@ function AssistantMessageV2({
4216
4256
  return /* @__PURE__ */ jsxs(Fragment, { children: [
4217
4257
  toastPortal,
4218
4258
  /* @__PURE__ */ jsxs("div", { className: "payman-v2-assistant-msg payman-v2-fade-in", children: [
4219
- /* @__PURE__ */ jsx(AnimatePresence, { children: isThinkingStreaming && /* @__PURE__ */ jsx(
4259
+ /* @__PURE__ */ jsx(AnimatePresence, { children: showThinkingBlock && /* @__PURE__ */ jsx(
4220
4260
  motion.div,
4221
4261
  {
4222
4262
  initial: { opacity: 0, height: 0 },
@@ -4246,6 +4286,10 @@ function AssistantMessageV2({
4246
4286
  isResolvingImages: message.isResolvingImages,
4247
4287
  onImageClick
4248
4288
  }
4289
+ ) : displayContentPending ? (
4290
+ // The intent/status line above is still bridging this
4291
+ // gap (see `showThinkingBlock`) — nothing to add here.
4292
+ null
4249
4293
  ) : !isThinkingStreaming ? /* @__PURE__ */ jsx("span", { className: "payman-v2-assistant-msg-placeholder", children: "..." }) : null }),
4250
4294
  /* @__PURE__ */ jsx(AnimatePresence, { children: showTrailingProgress && /* @__PURE__ */ jsx(
4251
4295
  motion.div,
@@ -4366,6 +4410,112 @@ function AssistantMessageV2({
4366
4410
  )
4367
4411
  ] });
4368
4412
  }
4413
+ var CHAR_VARIANTS = {
4414
+ fade: {
4415
+ initial: { opacity: 0, scale: 0.7 },
4416
+ animate: { opacity: 1, scale: 1 },
4417
+ exit: { opacity: 0, scale: 0.7 },
4418
+ transition: { duration: 0.14, ease: "easeOut" },
4419
+ overflow: "hidden"
4420
+ },
4421
+ blur: {
4422
+ initial: { opacity: 0, filter: "blur(8px)", y: -8 },
4423
+ animate: { opacity: 1, filter: "blur(0px)", y: 0 },
4424
+ exit: { opacity: 0, filter: "blur(8px)", y: 8 },
4425
+ transition: { duration: 0.18, ease: "easeOut" },
4426
+ overflow: "visible"
4427
+ }
4428
+ };
4429
+ function CharSlot({
4430
+ char,
4431
+ charKey,
4432
+ effect
4433
+ }) {
4434
+ if (!/\d/.test(char)) return /* @__PURE__ */ jsx("span", { style: { display: "inline-block" }, children: char });
4435
+ const v = CHAR_VARIANTS[effect];
4436
+ return /* @__PURE__ */ jsx("span", { style: { position: "relative", display: "inline-block", overflow: v.overflow }, children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "popLayout", initial: false, children: /* @__PURE__ */ jsx(
4437
+ motion.span,
4438
+ {
4439
+ initial: v.initial,
4440
+ animate: v.animate,
4441
+ exit: v.exit,
4442
+ transition: v.transition,
4443
+ style: { display: "inline-block" },
4444
+ children: char
4445
+ },
4446
+ charKey
4447
+ ) }) });
4448
+ }
4449
+ function CountUp({
4450
+ to,
4451
+ from = 0,
4452
+ duration = 2,
4453
+ delay = 0,
4454
+ digitEffect = "none",
4455
+ className,
4456
+ startWhen = true,
4457
+ separator = ""
4458
+ }) {
4459
+ const ref = useRef(null);
4460
+ const motionValue = useMotionValue(from);
4461
+ const damping = 20 + 40 * (1 / duration);
4462
+ const stiffness = 100 * (1 / duration);
4463
+ const springValue = useSpring(motionValue, { damping, stiffness });
4464
+ const isInView = useInView(ref, { once: true, margin: "0px" });
4465
+ const decimals = useMemo(() => {
4466
+ const d = (n) => {
4467
+ const s = n.toString();
4468
+ return s.includes(".") ? s.split(".")[1].length : 0;
4469
+ };
4470
+ return Math.max(d(from), d(to));
4471
+ }, [from, to]);
4472
+ const formatValue = useCallback(
4473
+ (latest) => {
4474
+ const formatted = Intl.NumberFormat("en-US", {
4475
+ useGrouping: !!separator,
4476
+ minimumFractionDigits: decimals,
4477
+ maximumFractionDigits: decimals
4478
+ }).format(latest);
4479
+ return separator ? formatted.replace(/,/g, separator) : formatted;
4480
+ },
4481
+ [decimals, separator]
4482
+ );
4483
+ const [chars, setChars] = useState(formatValue(from).split(""));
4484
+ useEffect(() => {
4485
+ if (isInView && startWhen) {
4486
+ const t = setTimeout(() => motionValue.set(to), delay * 1e3);
4487
+ return () => clearTimeout(t);
4488
+ }
4489
+ }, [isInView, startWhen, motionValue, to, delay]);
4490
+ useEffect(() => {
4491
+ const unsub = springValue.on("change", (latest) => {
4492
+ if (digitEffect === "none") {
4493
+ if (ref.current) ref.current.textContent = formatValue(latest);
4494
+ } else {
4495
+ setChars(formatValue(latest).split(""));
4496
+ }
4497
+ });
4498
+ return () => unsub();
4499
+ }, [springValue, formatValue, digitEffect]);
4500
+ if (digitEffect === "none") {
4501
+ return /* @__PURE__ */ jsx("span", { ref, className: cn(className), children: formatValue(from) });
4502
+ }
4503
+ return /* @__PURE__ */ jsx("span", { ref, className: cn("inline-flex items-center", className), children: chars.map((char, i) => /* @__PURE__ */ jsx(CharSlot, { char, charKey: `${i}-${char}`, effect: digitEffect }, i)) });
4504
+ }
4505
+ function AnimatedDuration({ seconds, className }) {
4506
+ const total = Math.max(0, seconds);
4507
+ const m = Math.floor(total / 60);
4508
+ const s = total % 60;
4509
+ return /* @__PURE__ */ jsxs("span", { className: cn("tabular-nums", className), children: [
4510
+ m > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
4511
+ /* @__PURE__ */ jsx(CountUp, { to: m, duration: 0.9, digitEffect: "blur", className: "tabular-nums" }),
4512
+ "m",
4513
+ " "
4514
+ ] }),
4515
+ /* @__PURE__ */ jsx(CountUp, { to: s, duration: 0.9, digitEffect: "blur", className: "tabular-nums" }),
4516
+ "s"
4517
+ ] });
4518
+ }
4369
4519
  var DEFAULT_MAX_LENGTH = 6;
4370
4520
  var MAX_SUPPORTED_LENGTH = 12;
4371
4521
  var AUTO_FOCUS_DELAY_MS = 250;
@@ -4492,7 +4642,6 @@ function VerificationInline({
4492
4642
  useEffect(() => {
4493
4643
  if (prompt.subAction === "SubmissionInvalid") {
4494
4644
  setErrored(true);
4495
- setCode("");
4496
4645
  lastSubmittedRef.current = null;
4497
4646
  }
4498
4647
  }, [prompt.subAction, prompt.userActionId]);
@@ -4571,7 +4720,7 @@ function VerificationInline({
4571
4720
  /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-head", children: [
4572
4721
  /* @__PURE__ */ jsx(ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
4573
4722
  /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-title", children: "Verification required" }),
4574
- typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
4723
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : /* @__PURE__ */ jsx(AnimatedDuration, { seconds: secondsLeft }) })
4575
4724
  ] }),
4576
4725
  renderMarkdown ? /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-markdown", children: /* @__PURE__ */ jsx(MarkdownRendererV2, { content: description }) }) : /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: description }),
4577
4726
  stale ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -4643,6 +4792,228 @@ function VerificationInline({
4643
4792
  ] })
4644
4793
  ] });
4645
4794
  }
4795
+ function AmountFieldV2({
4796
+ id,
4797
+ value,
4798
+ onChange,
4799
+ disabled,
4800
+ className,
4801
+ onEnter
4802
+ }) {
4803
+ const inputRef = useRef(null);
4804
+ const digits = digitsFromValue(value);
4805
+ const display = formatDigits(digits);
4806
+ useLayoutEffect(() => {
4807
+ const el = inputRef.current;
4808
+ if (el && document.activeElement === el) {
4809
+ el.setSelectionRange(el.value.length, el.value.length);
4810
+ }
4811
+ }, [display]);
4812
+ return /* @__PURE__ */ jsx(
4813
+ "input",
4814
+ {
4815
+ ref: inputRef,
4816
+ id,
4817
+ type: "text",
4818
+ inputMode: "decimal",
4819
+ autoComplete: "off",
4820
+ className,
4821
+ value: display,
4822
+ placeholder: "0.00",
4823
+ disabled,
4824
+ onChange: (e) => {
4825
+ onChange(valueFromDigits(digitsFromDisplay(e.target.value)));
4826
+ },
4827
+ onKeyDown: (e) => {
4828
+ if (e.key === "Enter") {
4829
+ e.preventDefault();
4830
+ onEnter?.();
4831
+ }
4832
+ },
4833
+ onFocus: (e) => {
4834
+ const el = e.currentTarget;
4835
+ requestAnimationFrame(() => el.setSelectionRange(el.value.length, el.value.length));
4836
+ }
4837
+ }
4838
+ );
4839
+ }
4840
+ function digitsFromDisplay(raw) {
4841
+ return raw.replace(/[^0-9]/g, "").replace(/^0+(?=\d)/, "").slice(-9);
4842
+ }
4843
+ function digitsFromValue(value) {
4844
+ const num = Number(value);
4845
+ if (!value || !Number.isFinite(num) || num <= 0) return "";
4846
+ return String(Math.round(num * 100));
4847
+ }
4848
+ function formatDigits(digits) {
4849
+ if (!digits) return "";
4850
+ const padded = digits.padStart(3, "0");
4851
+ const cents = padded.slice(-2);
4852
+ const whole = padded.slice(0, -2).replace(/^0+(?=\d)/, "") || "0";
4853
+ const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
4854
+ return `${grouped}.${cents}`;
4855
+ }
4856
+ function valueFromDigits(digits) {
4857
+ if (!digits) return "";
4858
+ return (Number(digits) / 100).toString();
4859
+ }
4860
+ function findRootClassName(el) {
4861
+ const root = el?.closest(".payman-v2-root");
4862
+ return root instanceof HTMLElement ? root.className : "";
4863
+ }
4864
+ var MENU_MAX_HEIGHT = 240;
4865
+ var OPTION_HEIGHT = 34;
4866
+ var VIEWPORT_MARGIN = 8;
4867
+ function SelectFieldV2({
4868
+ id,
4869
+ value,
4870
+ options,
4871
+ onChange,
4872
+ disabled,
4873
+ error,
4874
+ placeholder = "Select\u2026"
4875
+ }) {
4876
+ const triggerRef = useRef(null);
4877
+ const listRef = useRef(null);
4878
+ const [open, setOpen] = useState(false);
4879
+ const [rect, setRect] = useState(null);
4880
+ const [activeIndex, setActiveIndex] = useState(-1);
4881
+ const [rootClassName, setRootClassName] = useState("");
4882
+ const selected = options.find((o) => o.const === value);
4883
+ useLayoutEffect(() => {
4884
+ if (!open) return;
4885
+ const place = () => {
4886
+ const el = triggerRef.current;
4887
+ if (!el) return;
4888
+ const r = el.getBoundingClientRect();
4889
+ const viewportH = window.innerHeight;
4890
+ const estimatedMenuH = Math.min(
4891
+ options.length * OPTION_HEIGHT + VIEWPORT_MARGIN,
4892
+ MENU_MAX_HEIGHT
4893
+ );
4894
+ const spaceBelow = viewportH - r.bottom;
4895
+ const spaceAbove = r.top;
4896
+ const openUp = spaceBelow < estimatedMenuH && spaceAbove > spaceBelow;
4897
+ setRect({ top: r.bottom, bottom: viewportH - r.top, left: r.left, width: r.width, openUp });
4898
+ setRootClassName(findRootClassName(el));
4899
+ };
4900
+ place();
4901
+ window.addEventListener("resize", place);
4902
+ window.addEventListener("scroll", place, true);
4903
+ return () => {
4904
+ window.removeEventListener("resize", place);
4905
+ window.removeEventListener("scroll", place, true);
4906
+ };
4907
+ }, [open, options.length]);
4908
+ useEffect(() => {
4909
+ if (!open) return;
4910
+ setActiveIndex(Math.max(0, options.findIndex((o) => o.const === value)));
4911
+ listRef.current?.focus();
4912
+ }, [open]);
4913
+ useEffect(() => {
4914
+ if (!open) return;
4915
+ const onDocDown = (e) => {
4916
+ const target = e.target;
4917
+ if (triggerRef.current?.contains(target) || listRef.current?.contains(target)) return;
4918
+ setOpen(false);
4919
+ };
4920
+ document.addEventListener("mousedown", onDocDown);
4921
+ return () => document.removeEventListener("mousedown", onDocDown);
4922
+ }, [open]);
4923
+ const commit = (opt) => {
4924
+ onChange(opt.const);
4925
+ setOpen(false);
4926
+ triggerRef.current?.focus();
4927
+ };
4928
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4929
+ /* @__PURE__ */ jsxs(
4930
+ "button",
4931
+ {
4932
+ ref: triggerRef,
4933
+ id,
4934
+ type: "button",
4935
+ disabled,
4936
+ className: cn("payman-v2-ua-select-trigger", error && "payman-v2-ua-input-error"),
4937
+ "aria-haspopup": "listbox",
4938
+ "aria-expanded": open,
4939
+ onClick: () => setOpen((v) => !v),
4940
+ onKeyDown: (e) => {
4941
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
4942
+ e.preventDefault();
4943
+ setOpen(true);
4944
+ }
4945
+ },
4946
+ children: [
4947
+ /* @__PURE__ */ jsx(
4948
+ "span",
4949
+ {
4950
+ className: cn(
4951
+ "payman-v2-ua-select-value",
4952
+ !selected && "payman-v2-ua-select-placeholder"
4953
+ ),
4954
+ children: selected ? selected.title || selected.const : placeholder
4955
+ }
4956
+ ),
4957
+ /* @__PURE__ */ jsx(ChevronDown, { size: 14, className: "payman-v2-ua-select-chevron", "aria-hidden": true })
4958
+ ]
4959
+ }
4960
+ ),
4961
+ open && rect && typeof document !== "undefined" && createPortal(
4962
+ /* @__PURE__ */ jsx(
4963
+ "div",
4964
+ {
4965
+ ref: listRef,
4966
+ role: "listbox",
4967
+ className: cn(rootClassName, "payman-v2-ua-select-menu"),
4968
+ tabIndex: -1,
4969
+ style: {
4970
+ position: "fixed",
4971
+ left: rect.left,
4972
+ width: rect.width,
4973
+ height: "fit-content",
4974
+ maxHeight: MENU_MAX_HEIGHT,
4975
+ ...rect.openUp ? { bottom: rect.bottom } : { top: rect.top }
4976
+ },
4977
+ onKeyDown: (e) => {
4978
+ if (e.key === "ArrowDown") {
4979
+ e.preventDefault();
4980
+ setActiveIndex((i) => Math.min(options.length - 1, i + 1));
4981
+ } else if (e.key === "ArrowUp") {
4982
+ e.preventDefault();
4983
+ setActiveIndex((i) => Math.max(0, i - 1));
4984
+ } else if (e.key === "Enter" || e.key === " ") {
4985
+ e.preventDefault();
4986
+ if (activeIndex >= 0) commit(options[activeIndex]);
4987
+ } else if (e.key === "Escape" || e.key === "Tab") {
4988
+ setOpen(false);
4989
+ triggerRef.current?.focus();
4990
+ }
4991
+ },
4992
+ children: options.map((opt, i) => /* @__PURE__ */ jsxs(
4993
+ "div",
4994
+ {
4995
+ role: "option",
4996
+ "aria-selected": opt.const === value,
4997
+ className: cn(
4998
+ "payman-v2-ua-select-option",
4999
+ i === activeIndex && "payman-v2-ua-select-option-active",
5000
+ opt.const === value && "payman-v2-ua-select-option-selected"
5001
+ ),
5002
+ onMouseEnter: () => setActiveIndex(i),
5003
+ onClick: () => commit(opt),
5004
+ children: [
5005
+ /* @__PURE__ */ jsx("span", { children: opt.title || opt.const }),
5006
+ opt.const === value && /* @__PURE__ */ jsx(Check, { size: 13, "aria-hidden": true })
5007
+ ]
5008
+ },
5009
+ opt.const
5010
+ ))
5011
+ }
5012
+ ),
5013
+ document.body
5014
+ )
5015
+ ] });
5016
+ }
4646
5017
  function SchemaFormInline({
4647
5018
  prompt,
4648
5019
  secondsLeft,
@@ -4688,7 +5059,7 @@ function SchemaFormInline({
4688
5059
  /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-head", children: [
4689
5060
  /* @__PURE__ */ jsx(Pencil, { className: "payman-v2-ua-icon", size: 14, strokeWidth: 1.75, "aria-hidden": true }),
4690
5061
  /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
4691
- typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
5062
+ typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : /* @__PURE__ */ jsx(AnimatedDuration, { seconds: secondsLeft }) })
4692
5063
  ] }),
4693
5064
  prompt.message?.trim() && (renderMarkdown ? /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-markdown", children: /* @__PURE__ */ jsx(MarkdownRendererV2, { content: prompt.message }) }) : /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: prompt.message })),
4694
5065
  stale ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? null : /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
@@ -4720,26 +5091,33 @@ function SchemaFormInline({
4720
5091
  required && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-req", children: "*" })
4721
5092
  ] }),
4722
5093
  field.description && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-hint", children: field.description }),
4723
- widget === "select" ? /* @__PURE__ */ jsxs(
4724
- "select",
5094
+ widget === "select" ? /* @__PURE__ */ jsx(
5095
+ SelectFieldV2,
4725
5096
  {
4726
5097
  id: fieldId,
4727
- className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4728
5098
  value: String(values[key] ?? ""),
5099
+ options: getOptions(field),
4729
5100
  disabled: locked,
4730
- onChange: (e) => setValue(key, e.target.value),
4731
- children: [
4732
- /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select\u2026" }),
4733
- getOptions(field).map((opt) => /* @__PURE__ */ jsx("option", { value: opt.const, children: opt.title || opt.const }, opt.const))
4734
- ]
5101
+ error: Boolean(err),
5102
+ onChange: (v) => setValue(key, v)
5103
+ }
5104
+ ) : widget === "decimal" ? /* @__PURE__ */ jsx(
5105
+ AmountFieldV2,
5106
+ {
5107
+ id: fieldId,
5108
+ value: String(values[key] ?? ""),
5109
+ disabled: locked,
5110
+ className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
5111
+ onChange: (v) => setValue(key, v),
5112
+ onEnter: handleSubmit
4735
5113
  }
4736
5114
  ) : /* @__PURE__ */ jsx(
4737
5115
  "input",
4738
5116
  {
4739
5117
  id: fieldId,
4740
- type: widget === "integer" || widget === "decimal" ? "number" : "text",
4741
- inputMode: widget === "integer" ? "numeric" : widget === "decimal" ? "decimal" : void 0,
4742
- step: widget === "decimal" ? "any" : widget === "integer" ? "1" : void 0,
5118
+ type: widget === "integer" ? "number" : "text",
5119
+ inputMode: widget === "integer" ? "numeric" : void 0,
5120
+ step: widget === "integer" ? "1" : void 0,
4743
5121
  className: cn("payman-v2-ua-input", err && "payman-v2-ua-input-error"),
4744
5122
  value: String(values[key] ?? ""),
4745
5123
  disabled: locked,
@@ -4836,12 +5214,16 @@ function UserActionInline({
4836
5214
  onSubmit,
4837
5215
  onCancel,
4838
5216
  onResend,
4839
- onExpired
5217
+ onExpired,
5218
+ suppressExpiredNote
4840
5219
  }) {
4841
5220
  const [secondsLeft, expired] = useExpiryCountdown(prompt);
4842
5221
  useEffect(() => {
4843
5222
  if (expired && prompt.kind !== "notification") onExpired?.();
4844
5223
  }, [expired, onExpired, prompt.kind]);
5224
+ if (expired && prompt.kind !== "notification" && suppressExpiredNote) {
5225
+ return null;
5226
+ }
4845
5227
  let body;
4846
5228
  if (expired && prompt.kind !== "notification") {
4847
5229
  const note = {
@@ -4902,6 +5284,9 @@ function getPromptViewKey(prompt) {
4902
5284
  prompt.message ?? ""
4903
5285
  ].join(PROMPT_KEY_SEPARATOR);
4904
5286
  }
5287
+ function getPromptMountKey(prompt) {
5288
+ return [getPromptSlotKey(prompt), prompt.userActionId].join(PROMPT_KEY_SEPARATOR);
5289
+ }
4905
5290
  var MessageListV2 = forwardRef(
4906
5291
  function MessageListV22({
4907
5292
  messages,
@@ -4913,6 +5298,7 @@ var MessageListV2 = forwardRef(
4913
5298
  messageActions,
4914
5299
  retryDisabled = false,
4915
5300
  userActionPrompts,
5301
+ dockedUserActionId,
4916
5302
  notifications,
4917
5303
  onSubmitUserAction,
4918
5304
  onCancelUserAction,
@@ -5021,10 +5407,11 @@ var MessageListV2 = forwardRef(
5021
5407
  }, [userActionPrompts]);
5022
5408
  const visibleUserActionPrompts = useMemo(
5023
5409
  () => userActionPrompts?.filter((prompt) => {
5410
+ if (prompt.userActionId === dockedUserActionId) return false;
5024
5411
  const promptKey = getPromptViewKey(prompt);
5025
5412
  return !expiredPromptViewState[promptKey]?.hidden;
5026
5413
  }),
5027
- [expiredPromptViewState, userActionPrompts]
5414
+ [dockedUserActionId, expiredPromptViewState, userActionPrompts]
5028
5415
  );
5029
5416
  const pinToBottom = useCallback((behavior = "instant") => {
5030
5417
  const el = scrollRef.current;
@@ -5173,7 +5560,7 @@ var MessageListV2 = forwardRef(
5173
5560
  onResend: onResendUserAction ?? noop,
5174
5561
  onExpired: () => handleUserActionExpired(promptKey, prompt.userActionId)
5175
5562
  },
5176
- promptKey
5563
+ getPromptMountKey(prompt)
5177
5564
  );
5178
5565
  })
5179
5566
  ]
@@ -5719,10 +6106,455 @@ var ChatInputV2 = forwardRef(
5719
6106
  ] });
5720
6107
  }
5721
6108
  );
5722
-
5723
- // src/assets/thinking-atom.lottie
5724
- 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=";
5725
- var DEFAULT_SIZE = 36;
6109
+ function dockKey(prompt) {
6110
+ return `${prompt.toolCallId || prompt.userActionId}:${prompt.userActionId}`;
6111
+ }
6112
+ function UserActionDock({
6113
+ prompt,
6114
+ onSubmit,
6115
+ onCancel,
6116
+ onResend,
6117
+ onExpired,
6118
+ children
6119
+ }) {
6120
+ return /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-dock", children: /* @__PURE__ */ jsx(motion.div, { layout: "size", transition: { duration: 0.3, ease: [0.2, 0, 0, 1] }, children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", initial: false, children: prompt ? /* @__PURE__ */ jsx(
6121
+ motion.div,
6122
+ {
6123
+ initial: { opacity: 0 },
6124
+ animate: { opacity: 1 },
6125
+ exit: { opacity: 0 },
6126
+ transition: { duration: 0.15 },
6127
+ className: "payman-v2-ua-dock-panel",
6128
+ children: /* @__PURE__ */ jsx(
6129
+ UserActionInline,
6130
+ {
6131
+ prompt,
6132
+ onSubmit,
6133
+ onCancel,
6134
+ onResend,
6135
+ onExpired: () => onExpired?.(prompt.userActionId),
6136
+ suppressExpiredNote: true
6137
+ }
6138
+ )
6139
+ },
6140
+ dockKey(prompt)
6141
+ ) : /* @__PURE__ */ jsx(
6142
+ motion.div,
6143
+ {
6144
+ initial: { opacity: 0 },
6145
+ animate: { opacity: 1 },
6146
+ exit: { opacity: 0 },
6147
+ transition: { duration: 0.15 },
6148
+ children
6149
+ },
6150
+ "composer"
6151
+ ) }) }) });
6152
+ }
6153
+ function clamp(value) {
6154
+ return Math.max(0, Math.min(1, value));
6155
+ }
6156
+ function ensureFrameSize(frame, rows, cols) {
6157
+ const result = [];
6158
+ for (let r = 0; r < rows; r++) {
6159
+ const row = frame[r] || [];
6160
+ result.push([]);
6161
+ for (let c = 0; c < cols; c++) {
6162
+ result[r][c] = row[c] ?? 0;
6163
+ }
6164
+ }
6165
+ return result;
6166
+ }
6167
+ function useAnimation(frames, options) {
6168
+ const [frameIndex, setFrameIndex] = useState(0);
6169
+ const [isPlaying, setIsPlaying] = useState(options.autoplay);
6170
+ const frameIdRef = useRef(void 0);
6171
+ const lastTimeRef = useRef(0);
6172
+ const accumulatorRef = useRef(0);
6173
+ useEffect(() => {
6174
+ if (!frames || frames.length === 0 || !isPlaying) {
6175
+ return;
6176
+ }
6177
+ const frameInterval = 1e3 / options.fps;
6178
+ const animate = (currentTime) => {
6179
+ if (lastTimeRef.current === 0) {
6180
+ lastTimeRef.current = currentTime;
6181
+ }
6182
+ const deltaTime = currentTime - lastTimeRef.current;
6183
+ lastTimeRef.current = currentTime;
6184
+ accumulatorRef.current += deltaTime;
6185
+ if (accumulatorRef.current >= frameInterval) {
6186
+ accumulatorRef.current -= frameInterval;
6187
+ setFrameIndex((prev) => {
6188
+ const next = prev + 1;
6189
+ if (next >= frames.length) {
6190
+ if (options.loop) {
6191
+ options.onFrame?.(0);
6192
+ return 0;
6193
+ } else {
6194
+ setIsPlaying(false);
6195
+ return prev;
6196
+ }
6197
+ }
6198
+ options.onFrame?.(next);
6199
+ return next;
6200
+ });
6201
+ }
6202
+ frameIdRef.current = requestAnimationFrame(animate);
6203
+ };
6204
+ frameIdRef.current = requestAnimationFrame(animate);
6205
+ return () => {
6206
+ if (frameIdRef.current) {
6207
+ cancelAnimationFrame(frameIdRef.current);
6208
+ }
6209
+ };
6210
+ }, [frames, isPlaying, options.fps, options.loop, options.onFrame]);
6211
+ useEffect(() => {
6212
+ setFrameIndex(0);
6213
+ setIsPlaying(options.autoplay);
6214
+ lastTimeRef.current = 0;
6215
+ accumulatorRef.current = 0;
6216
+ }, [frames, options.autoplay]);
6217
+ return { frameIndex, isPlaying };
6218
+ }
6219
+ function emptyFrame(rows, cols) {
6220
+ return Array.from({ length: rows }, () => Array(cols).fill(0));
6221
+ }
6222
+ function setPixel(frame, row, col, value) {
6223
+ if (row >= 0 && row < frame.length && col >= 0 && col < frame[0].length) {
6224
+ frame[row][col] = value;
6225
+ }
6226
+ }
6227
+ var loader = (() => {
6228
+ const frames = [];
6229
+ const size = 7;
6230
+ const center = 3;
6231
+ const radius = 2.5;
6232
+ for (let frame = 0; frame < 12; frame++) {
6233
+ const f = emptyFrame(size, size);
6234
+ for (let i = 0; i < 8; i++) {
6235
+ const angle = frame / 12 * Math.PI * 2 + i / 8 * Math.PI * 2;
6236
+ const x = Math.round(center + Math.cos(angle) * radius);
6237
+ const y = Math.round(center + Math.sin(angle) * radius);
6238
+ const brightness = 1 - i / 10;
6239
+ setPixel(f, y, x, Math.max(0.2, brightness));
6240
+ }
6241
+ frames.push(f);
6242
+ }
6243
+ return frames;
6244
+ })();
6245
+ var pulse = (() => {
6246
+ const frames = [];
6247
+ const size = 7;
6248
+ const center = 3;
6249
+ for (let frame = 0; frame < 16; frame++) {
6250
+ const f = emptyFrame(size, size);
6251
+ const phase = frame / 16 * Math.PI * 2;
6252
+ const intensity = (Math.sin(phase) + 1) / 2;
6253
+ setPixel(f, center, center, 1);
6254
+ const radius = Math.floor((1 - intensity) * 3) + 1;
6255
+ for (let dy = -radius; dy <= radius; dy++) {
6256
+ for (let dx = -radius; dx <= radius; dx++) {
6257
+ const dist = Math.sqrt(dx * dx + dy * dy);
6258
+ if (Math.abs(dist - radius) < 0.7) {
6259
+ setPixel(f, center + dy, center + dx, intensity * 0.6);
6260
+ }
6261
+ }
6262
+ }
6263
+ frames.push(f);
6264
+ }
6265
+ return frames;
6266
+ })();
6267
+ function vu(columns, levels) {
6268
+ const rows = 7;
6269
+ const frame = emptyFrame(rows, columns);
6270
+ for (let col = 0; col < Math.min(columns, levels.length); col++) {
6271
+ const level = Math.max(0, Math.min(1, levels[col]));
6272
+ const height = Math.floor(level * rows);
6273
+ for (let row = 0; row < rows; row++) {
6274
+ const rowFromBottom = rows - 1 - row;
6275
+ if (rowFromBottom < height) {
6276
+ let brightness = 1;
6277
+ if (row < rows * 0.3) {
6278
+ brightness = 1;
6279
+ } else if (row < rows * 0.6) {
6280
+ brightness = 0.8;
6281
+ } else {
6282
+ brightness = 0.6;
6283
+ }
6284
+ frame[row][col] = brightness;
6285
+ }
6286
+ }
6287
+ }
6288
+ return frame;
6289
+ }
6290
+ var wave = (() => {
6291
+ const frames = [];
6292
+ const rows = 7;
6293
+ const cols = 7;
6294
+ for (let frame = 0; frame < 24; frame++) {
6295
+ const f = emptyFrame(rows, cols);
6296
+ const phase = frame / 24 * Math.PI * 2;
6297
+ for (let col = 0; col < cols; col++) {
6298
+ const colPhase = col / cols * Math.PI * 2;
6299
+ const height = Math.sin(phase + colPhase) * 2.5 + 3.5;
6300
+ const row = Math.floor(height);
6301
+ if (row >= 0 && row < rows) {
6302
+ setPixel(f, row, col, 1);
6303
+ const frac = height - row;
6304
+ if (row > 0) setPixel(f, row - 1, col, 1 - frac);
6305
+ if (row < rows - 1) setPixel(f, row + 1, col, frac);
6306
+ }
6307
+ }
6308
+ frames.push(f);
6309
+ }
6310
+ return frames;
6311
+ })();
6312
+ var snake = (() => {
6313
+ const frames = [];
6314
+ const rows = 7;
6315
+ const cols = 7;
6316
+ const path = [];
6317
+ let x = 0;
6318
+ let y = 0;
6319
+ let dx = 1;
6320
+ let dy = 0;
6321
+ const visited = /* @__PURE__ */ new Set();
6322
+ while (path.length < rows * cols) {
6323
+ path.push([y, x]);
6324
+ visited.add(`${y},${x}`);
6325
+ const nextX = x + dx;
6326
+ const nextY = y + dy;
6327
+ if (nextX >= 0 && nextX < cols && nextY >= 0 && nextY < rows && !visited.has(`${nextY},${nextX}`)) {
6328
+ x = nextX;
6329
+ y = nextY;
6330
+ } else {
6331
+ const newDx = -dy;
6332
+ const newDy = dx;
6333
+ dx = newDx;
6334
+ dy = newDy;
6335
+ const nextX2 = x + dx;
6336
+ const nextY2 = y + dy;
6337
+ if (nextX2 >= 0 && nextX2 < cols && nextY2 >= 0 && nextY2 < rows && !visited.has(`${nextY2},${nextX2}`)) {
6338
+ x = nextX2;
6339
+ y = nextY2;
6340
+ } else {
6341
+ break;
6342
+ }
6343
+ }
6344
+ }
6345
+ const snakeLength = 5;
6346
+ for (let frame = 0; frame < path.length; frame++) {
6347
+ const f = emptyFrame(rows, cols);
6348
+ for (let i = 0; i < snakeLength; i++) {
6349
+ const idx = frame - i;
6350
+ if (idx >= 0 && idx < path.length) {
6351
+ const [y2, x2] = path[idx];
6352
+ const brightness = 1 - i / snakeLength;
6353
+ setPixel(f, y2, x2, brightness);
6354
+ }
6355
+ }
6356
+ frames.push(f);
6357
+ }
6358
+ return frames;
6359
+ })();
6360
+ var Matrix = React.forwardRef(
6361
+ ({
6362
+ rows,
6363
+ cols,
6364
+ pattern,
6365
+ frames,
6366
+ fps = 12,
6367
+ autoplay = true,
6368
+ loop = true,
6369
+ size = 10,
6370
+ gap = 2,
6371
+ palette = {
6372
+ on: "currentColor",
6373
+ off: "var(--muted-foreground)"
6374
+ },
6375
+ brightness = 1,
6376
+ ariaLabel,
6377
+ onFrame,
6378
+ mode = "default",
6379
+ levels,
6380
+ className,
6381
+ ...props
6382
+ }, ref) => {
6383
+ const { frameIndex } = useAnimation(frames, {
6384
+ fps,
6385
+ autoplay: autoplay && !pattern,
6386
+ loop,
6387
+ onFrame
6388
+ });
6389
+ const currentFrame = useMemo(() => {
6390
+ if (mode === "vu" && levels && levels.length > 0) {
6391
+ return ensureFrameSize(vu(cols, levels), rows, cols);
6392
+ }
6393
+ if (pattern) {
6394
+ return ensureFrameSize(pattern, rows, cols);
6395
+ }
6396
+ if (frames && frames.length > 0) {
6397
+ return ensureFrameSize(frames[frameIndex] || frames[0], rows, cols);
6398
+ }
6399
+ return ensureFrameSize([], rows, cols);
6400
+ }, [pattern, frames, frameIndex, rows, cols, mode, levels]);
6401
+ const cellPositions = useMemo(() => {
6402
+ const positions = [];
6403
+ for (let row = 0; row < rows; row++) {
6404
+ positions[row] = [];
6405
+ for (let col = 0; col < cols; col++) {
6406
+ positions[row][col] = {
6407
+ x: col * (size + gap),
6408
+ y: row * (size + gap)
6409
+ };
6410
+ }
6411
+ }
6412
+ return positions;
6413
+ }, [rows, cols, size, gap]);
6414
+ const svgDimensions = useMemo(() => {
6415
+ return {
6416
+ width: cols * (size + gap) - gap,
6417
+ height: rows * (size + gap) - gap
6418
+ };
6419
+ }, [rows, cols, size, gap]);
6420
+ const isAnimating = !pattern && frames && frames.length > 0;
6421
+ return /* @__PURE__ */ jsx(
6422
+ "div",
6423
+ {
6424
+ ref,
6425
+ role: "img",
6426
+ "aria-label": ariaLabel ?? "matrix display",
6427
+ "aria-live": isAnimating ? "polite" : void 0,
6428
+ className: cn("relative inline-block", className),
6429
+ style: {
6430
+ "--matrix-on": palette.on,
6431
+ "--matrix-off": palette.off,
6432
+ "--matrix-gap": `${gap}px`,
6433
+ "--matrix-size": `${size}px`
6434
+ },
6435
+ ...props,
6436
+ children: /* @__PURE__ */ jsxs(
6437
+ "svg",
6438
+ {
6439
+ width: svgDimensions.width,
6440
+ height: svgDimensions.height,
6441
+ viewBox: `0 0 ${svgDimensions.width} ${svgDimensions.height}`,
6442
+ xmlns: "http://www.w3.org/2000/svg",
6443
+ className: "block",
6444
+ style: { overflow: "visible" },
6445
+ children: [
6446
+ /* @__PURE__ */ jsxs("defs", { children: [
6447
+ /* @__PURE__ */ jsxs("radialGradient", { id: "matrix-pixel-on", cx: "50%", cy: "50%", r: "50%", children: [
6448
+ /* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "var(--matrix-on)", stopOpacity: "1" }),
6449
+ /* @__PURE__ */ jsx(
6450
+ "stop",
6451
+ {
6452
+ offset: "70%",
6453
+ stopColor: "var(--matrix-on)",
6454
+ stopOpacity: "0.85"
6455
+ }
6456
+ ),
6457
+ /* @__PURE__ */ jsx(
6458
+ "stop",
6459
+ {
6460
+ offset: "100%",
6461
+ stopColor: "var(--matrix-on)",
6462
+ stopOpacity: "0.6"
6463
+ }
6464
+ )
6465
+ ] }),
6466
+ /* @__PURE__ */ jsxs("radialGradient", { id: "matrix-pixel-off", cx: "50%", cy: "50%", r: "50%", children: [
6467
+ /* @__PURE__ */ jsx(
6468
+ "stop",
6469
+ {
6470
+ offset: "0%",
6471
+ stopColor: "var(--muted-foreground)",
6472
+ stopOpacity: "1"
6473
+ }
6474
+ ),
6475
+ /* @__PURE__ */ jsx(
6476
+ "stop",
6477
+ {
6478
+ offset: "100%",
6479
+ stopColor: "var(--muted-foreground)",
6480
+ stopOpacity: "0.7"
6481
+ }
6482
+ )
6483
+ ] }),
6484
+ /* @__PURE__ */ jsxs(
6485
+ "filter",
6486
+ {
6487
+ id: "matrix-glow",
6488
+ x: "-50%",
6489
+ y: "-50%",
6490
+ width: "200%",
6491
+ height: "200%",
6492
+ children: [
6493
+ /* @__PURE__ */ jsx("feGaussianBlur", { stdDeviation: "2", result: "blur" }),
6494
+ /* @__PURE__ */ jsx("feComposite", { in: "SourceGraphic", in2: "blur", operator: "over" })
6495
+ ]
6496
+ }
6497
+ )
6498
+ ] }),
6499
+ /* @__PURE__ */ jsx("style", { children: `
6500
+ .matrix-pixel {
6501
+ transition: opacity 300ms ease-out, transform 150ms ease-out;
6502
+ transform-origin: center;
6503
+ transform-box: fill-box;
6504
+ }
6505
+ .matrix-pixel-active {
6506
+ filter: url(#matrix-glow);
6507
+ }
6508
+ ` }),
6509
+ currentFrame.map(
6510
+ (row, rowIndex) => row.map((value, colIndex) => {
6511
+ const pos = cellPositions[rowIndex]?.[colIndex];
6512
+ if (!pos) return null;
6513
+ const opacity = clamp(brightness * value);
6514
+ const isActive = opacity > 0.5;
6515
+ const isOn = opacity > 0.05;
6516
+ const fill = isOn ? "url(#matrix-pixel-on)" : "url(#matrix-pixel-off)";
6517
+ const scale = isActive ? 1.1 : 1;
6518
+ const radius = size / 2 * 0.9;
6519
+ return /* @__PURE__ */ jsx(
6520
+ "circle",
6521
+ {
6522
+ className: cn(
6523
+ "matrix-pixel",
6524
+ isActive && "matrix-pixel-active",
6525
+ !isOn && "opacity-20 dark:opacity-[0.1]"
6526
+ ),
6527
+ cx: pos.x + size / 2,
6528
+ cy: pos.y + size / 2,
6529
+ r: radius,
6530
+ fill,
6531
+ opacity: isOn ? opacity : 0.1,
6532
+ style: {
6533
+ transform: `scale(${scale})`
6534
+ }
6535
+ },
6536
+ `${rowIndex}-${colIndex}`
6537
+ );
6538
+ })
6539
+ )
6540
+ ]
6541
+ }
6542
+ )
6543
+ }
6544
+ );
6545
+ }
6546
+ );
6547
+ Matrix.displayName = "Matrix";
6548
+ var DEFAULT_SIZE = 22;
6549
+ var MATRIX_GRID = 7;
6550
+ var MATRIX_ON = "var(--payman-v2-matrix-color)";
6551
+ var MATRIX_PRESETS = [
6552
+ { frames: loader, fps: 8 },
6553
+ { frames: wave, fps: 14 },
6554
+ { frames: snake, fps: 16 },
6555
+ { frames: pulse, fps: 12 }
6556
+ ];
6557
+ var PRESET_ROTATE_MS = 1e4;
5726
6558
  function useElapsedSeconds(isStreaming) {
5727
6559
  const [elapsed, setElapsed] = useState(0);
5728
6560
  const startRef = useRef(null);
@@ -5743,12 +6575,37 @@ function useElapsedSeconds(isStreaming) {
5743
6575
  }, [isStreaming]);
5744
6576
  return elapsed;
5745
6577
  }
6578
+ function useMatrixPresetIndex(isStreaming) {
6579
+ const [index, setIndex] = useState(0);
6580
+ useEffect(() => {
6581
+ if (!isStreaming) return;
6582
+ setIndex(0);
6583
+ const id = setInterval(() => {
6584
+ setIndex((prev) => (prev + 1) % MATRIX_PRESETS.length);
6585
+ }, PRESET_ROTATE_MS);
6586
+ return () => clearInterval(id);
6587
+ }, [isStreaming]);
6588
+ return index;
6589
+ }
6590
+ function formatElapsed(totalSeconds) {
6591
+ if (totalSeconds < 60) return `${totalSeconds}s`;
6592
+ const minutes = Math.floor(totalSeconds / 60);
6593
+ const seconds = totalSeconds % 60;
6594
+ return `${minutes}m ${seconds}s`;
6595
+ }
5746
6596
  function StreamingIndicatorV2({
5747
6597
  isStreaming,
5748
6598
  loadingAnimation
5749
6599
  }) {
5750
6600
  const size = loadingAnimation?.size ?? DEFAULT_SIZE;
5751
6601
  const elapsed = useElapsedSeconds(isStreaming);
6602
+ const presetIndex = useMatrixPresetIndex(isStreaming);
6603
+ const preset = MATRIX_PRESETS[presetIndex];
6604
+ const gap = 1;
6605
+ const cell = useMemo(
6606
+ () => Math.max(2, Math.round((size - (MATRIX_GRID - 1) * gap) / MATRIX_GRID)),
6607
+ [size]
6608
+ );
5752
6609
  return /* @__PURE__ */ jsx(AnimatePresence, { children: isStreaming && /* @__PURE__ */ jsxs(
5753
6610
  motion.div,
5754
6611
  {
@@ -5764,21 +6621,62 @@ function StreamingIndicatorV2({
5764
6621
  {
5765
6622
  className: "payman-v2-streaming-indicator-glyph",
5766
6623
  style: { width: size, height: size },
5767
- children: /* @__PURE__ */ jsx(
5768
- DotLottieReact,
5769
- {
5770
- src: loadingAnimation?.src ?? thinking_atom_default,
5771
- loop: true,
5772
- autoplay: true,
5773
- style: { width: "100%", height: "100%" }
5774
- }
6624
+ children: loadingAnimation?.src ? (
6625
+ // Consumer override: render their Lottie animation.
6626
+ /* @__PURE__ */ jsx(
6627
+ DotLottieReact,
6628
+ {
6629
+ src: loadingAnimation.src,
6630
+ loop: true,
6631
+ autoplay: true,
6632
+ style: { width: "100%", height: "100%" }
6633
+ }
6634
+ )
6635
+ ) : (
6636
+ // Default: dot-matrix glyph, cycling presets +
6637
+ // Payman brand tint. Each preset lives in its own
6638
+ // keyed motion layer so swapping presets crossfades
6639
+ // (old scales/fades out while the new scales/fades
6640
+ // in) instead of hard-cutting. Keying the layer on
6641
+ // `presetIndex` also remounts the Matrix, restarting
6642
+ // its frame counter cleanly at frame 0.
6643
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: /* @__PURE__ */ jsx(
6644
+ motion.div,
6645
+ {
6646
+ className: "payman-v2-streaming-indicator-matrix",
6647
+ initial: { opacity: 0, scale: 0.8 },
6648
+ animate: { opacity: 1, scale: 1 },
6649
+ exit: { opacity: 0, scale: 0.8 },
6650
+ transition: { duration: 0.45, ease: [0.2, 0, 0, 1] },
6651
+ children: /* @__PURE__ */ jsx(
6652
+ Matrix,
6653
+ {
6654
+ rows: MATRIX_GRID,
6655
+ cols: MATRIX_GRID,
6656
+ frames: preset.frames,
6657
+ fps: preset.fps,
6658
+ size: cell,
6659
+ gap,
6660
+ palette: {
6661
+ on: MATRIX_ON,
6662
+ // Unlit dots use the v2 theme's own muted-text
6663
+ // token (light/dark-aware), not the bare
6664
+ // `--muted-foreground` the vendored Matrix
6665
+ // component falls back to internally — that
6666
+ // variable isn't defined under `.payman-v2-root`
6667
+ // and would otherwise resolve to black.
6668
+ off: "var(--payman-v2-matrix-dot-off)"
6669
+ },
6670
+ ariaLabel: "Working\u2026"
6671
+ }
6672
+ )
6673
+ },
6674
+ presetIndex
6675
+ ) })
5775
6676
  )
5776
6677
  }
5777
6678
  ),
5778
- elapsed > 0 && /* @__PURE__ */ jsxs("span", { className: "payman-v2-streaming-indicator-elapsed", children: [
5779
- elapsed,
5780
- "s"
5781
- ] })
6679
+ elapsed > 0 && /* @__PURE__ */ jsx("span", { className: "payman-v2-streaming-indicator-elapsed", children: formatElapsed(elapsed) })
5782
6680
  ]
5783
6681
  },
5784
6682
  "streaming-indicator"
@@ -6386,6 +7284,15 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6386
7284
  const expireUserAction2 = chat.expireUserAction ?? NOOP_ASYNC;
6387
7285
  const dismissNotification = chat.dismissNotification ?? NOOP;
6388
7286
  const isUserActionSupported = typeof chat.submitUserAction === "function" && typeof chat.cancelUserAction === "function" && typeof chat.resendUserAction === "function";
7287
+ const expiredUserActionIdsRef = useRef(/* @__PURE__ */ new Set());
7288
+ const expireUserActionOnce = useCallback(
7289
+ (userActionId) => {
7290
+ if (expiredUserActionIdsRef.current.has(userActionId)) return;
7291
+ expiredUserActionIdsRef.current.add(userActionId);
7292
+ void expireUserAction2(userActionId);
7293
+ },
7294
+ [expireUserAction2]
7295
+ );
6389
7296
  const {
6390
7297
  transcribedText,
6391
7298
  isAvailable: voiceAvailable,
@@ -6628,6 +7535,9 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6628
7535
  };
6629
7536
  const userActionPrompts = isUserActionSupported ? userActionState.prompts : void 0;
6630
7537
  const notifications = userActionState.notifications;
7538
+ const dockedPrompt = userActionPrompts?.find(
7539
+ (prompt) => prompt.kind !== "notification" && (prompt.status === "pending" || prompt.status === "submitting" || prompt.status === "stale")
7540
+ );
6631
7541
  const handleV2Send = (text) => {
6632
7542
  if (isRecording) stopRecording();
6633
7543
  if (text.trim() && !disableInput && isSessionParamsConfigured) {
@@ -6780,11 +7690,12 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6780
7690
  retryDisabled: isWaitingForResponse,
6781
7691
  typingSpeed: config.typingSpeed ?? 4,
6782
7692
  userActionPrompts,
7693
+ dockedUserActionId: dockedPrompt?.userActionId,
6783
7694
  notifications,
6784
7695
  onSubmitUserAction: isUserActionSupported ? submitUserAction2 : void 0,
6785
7696
  onCancelUserAction: isUserActionSupported ? cancelUserAction2 : void 0,
6786
7697
  onResendUserAction: isUserActionSupported ? resendUserAction2 : void 0,
6787
- onExpireUserAction: isUserActionSupported ? expireUserAction2 : void 0,
7698
+ onExpireUserAction: isUserActionSupported ? async (id) => expireUserActionOnce(id) : void 0,
6788
7699
  onDismissNotification: dismissNotification,
6789
7700
  onSubmitFeedback: handleSubmitFeedback
6790
7701
  }
@@ -6797,33 +7708,43 @@ var PaymanChatInner = forwardRef(function PaymanChatInner2({
6797
7708
  }
6798
7709
  ),
6799
7710
  hasAskPermission && /* @__PURE__ */ jsx(
6800
- ChatInputV2,
7711
+ UserActionDock,
6801
7712
  {
6802
- ref: chatInputV2Ref,
6803
- onSend: handleV2Send,
6804
- onCancel: cancelStream,
6805
- disabled: isV2InputDisabled,
6806
- isStreaming: isWaitingForResponse,
6807
- placeholder: isRecording ? "Listening..." : placeholder,
6808
- enableVoice: config.enableVoice === true,
6809
- transcribedText: config.enableVoice === true ? transcribedText : "",
6810
- voiceAvailable: config.enableVoice === true && voiceAvailable,
6811
- isRecording,
6812
- onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
6813
- onCancelRecording: handleCancelRecording,
6814
- onConfirmRecording: handleConfirmRecording,
6815
- showResetSession,
6816
- onResetSession: requestResetSession,
6817
- showAttachmentButton,
6818
- showUploadImageButton,
6819
- showAttachFileButton,
6820
- onUploadImageClick,
6821
- onAttachFileClick,
6822
- editingMessageId,
6823
- onClearEditing: handleClearEditing,
6824
- analysisMode: enableDeepModeToggle ? analysisMode : void 0,
6825
- onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
6826
- slashCommands
7713
+ prompt: isUserActionSupported ? dockedPrompt : void 0,
7714
+ onSubmit: submitUserAction2,
7715
+ onCancel: cancelUserAction2,
7716
+ onResend: resendUserAction2,
7717
+ onExpired: expireUserActionOnce,
7718
+ children: /* @__PURE__ */ jsx(
7719
+ ChatInputV2,
7720
+ {
7721
+ ref: chatInputV2Ref,
7722
+ onSend: handleV2Send,
7723
+ onCancel: cancelStream,
7724
+ disabled: isV2InputDisabled,
7725
+ isStreaming: isWaitingForResponse,
7726
+ placeholder: isRecording ? "Listening..." : placeholder,
7727
+ enableVoice: config.enableVoice === true,
7728
+ transcribedText: config.enableVoice === true ? transcribedText : "",
7729
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
7730
+ isRecording,
7731
+ onVoicePress: config.enableVoice === true ? handleVoicePress : void 0,
7732
+ onCancelRecording: handleCancelRecording,
7733
+ onConfirmRecording: handleConfirmRecording,
7734
+ showResetSession,
7735
+ onResetSession: requestResetSession,
7736
+ showAttachmentButton,
7737
+ showUploadImageButton,
7738
+ showAttachFileButton,
7739
+ onUploadImageClick,
7740
+ onAttachFileClick,
7741
+ editingMessageId,
7742
+ onClearEditing: handleClearEditing,
7743
+ analysisMode: enableDeepModeToggle ? analysisMode : void 0,
7744
+ onAnalysisModeChange: enableDeepModeToggle ? setAnalysisMode : void 0,
7745
+ slashCommands
7746
+ }
7747
+ )
6827
7748
  }
6828
7749
  )
6829
7750
  ]