@paymanai/payman-ask-sdk 1.2.3 → 1.2.5

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
@@ -4,7 +4,7 @@ import { createContext, useContext, useState, useRef, useMemo, useEffect, useCal
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import { motion, AnimatePresence } from 'framer-motion';
7
- import { Bot, Mic, Pause, Send, ShieldCheck, Loader2, User, CheckCircle2, XCircle, X, ChevronUp, ChevronDown, Check, Binoculars } from 'lucide-react';
7
+ import { MessageCircle, ArrowDown, Square, Mic, ArrowUp, ShieldCheck, Loader2, X, Check, RefreshCw, User, Clock, Sparkles, Binoculars, ChevronDown, ChevronRight } from 'lucide-react';
8
8
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
9
  import ReactMarkdown from 'react-markdown';
10
10
  import remarkGfm from 'remark-gfm';
@@ -47,6 +47,11 @@ function formatDate(timestamp) {
47
47
  year: date.getFullYear() !== now.getFullYear() ? "numeric" : void 0
48
48
  });
49
49
  }
50
+ function formatElapsedTime(ms) {
51
+ if (!ms) return "";
52
+ if (ms < 1e3) return `${ms}ms`;
53
+ return `${(ms / 1e3).toFixed(1)}s`;
54
+ }
50
55
  function ChatInput({
51
56
  value,
52
57
  onChange,
@@ -69,92 +74,181 @@ function ChatInput({
69
74
  if (textareaRef.current) {
70
75
  textareaRef.current.style.height = "auto";
71
76
  const scrollHeight = textareaRef.current.scrollHeight;
72
- const maxHeight = 200;
73
- textareaRef.current.style.height = `${Math.min(scrollHeight, maxHeight)}px`;
77
+ const maxHeight = 160;
78
+ textareaRef.current.style.height = `${Math.min(
79
+ scrollHeight,
80
+ maxHeight
81
+ )}px`;
74
82
  }
75
83
  }, [value]);
76
84
  const handleKeyDown = (e) => {
77
85
  if (e.key === "Enter" && !e.shiftKey) {
78
86
  e.preventDefault();
79
- onSend();
87
+ if (value.trim() && !isInputDisabled) onSend();
80
88
  }
81
89
  };
82
90
  const isInputDisabled = disabled || isWaitingForResponse;
83
91
  const showPauseButton = isWaitingForResponse && onPause;
84
92
  const showVoiceButton = enableVoice && onVoicePress != null;
85
93
  const isVoiceButtonDisabled = isWaitingForResponse || !voiceAvailable || !isSessionParamsConfigured;
94
+ const canSend = !isInputDisabled && !!value.trim();
86
95
  const getPlaceholder = () => {
87
- if (!hasSelectedSession) {
88
- return "Select a version to start chatting";
89
- }
90
- if (!isSessionParamsConfigured) {
91
- return "Configure User ID and User Label in Session Params";
92
- }
96
+ if (!hasSelectedSession) return "Select a version to start chatting";
97
+ if (!isSessionParamsConfigured) return "Configure session params to begin";
93
98
  return placeholder;
94
99
  };
95
- return /* @__PURE__ */ jsx("div", { className: cn("flex-shrink-0 p-4 w-full", className), style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx("div", { className: "flex justify-center w-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full px-0", style: { maxWidth: "672px" }, children: /* @__PURE__ */ jsxs(
96
- motion.div,
100
+ return /* @__PURE__ */ jsx(
101
+ "div",
97
102
  {
98
- initial: false,
99
- className: "flex items-end overflow-hidden transition-colors bg-input border border-border shadow-sm",
100
- style: { borderRadius: "24px" },
101
- children: [
102
- /* @__PURE__ */ jsx(
103
- "textarea",
104
- {
105
- ref: textareaRef,
106
- value,
107
- onChange: (e) => onChange(e.target.value),
108
- onKeyDown: handleKeyDown,
109
- onClick,
110
- disabled: isInputDisabled,
111
- placeholder: getPlaceholder(),
112
- className: "bg-transparent text-foreground focus:outline-none resize-none overflow-y-auto disabled:opacity-50 disabled:cursor-not-allowed flex-1 min-w-0 text-sm placeholder:text-muted-foreground",
113
- style: {
114
- minHeight: "48px",
115
- maxHeight: "200px",
116
- padding: "12px 12px 12px 16px"
117
- },
118
- rows: 1
119
- }
120
- ),
121
- /* @__PURE__ */ jsxs("div", { className: "flex flex-row items-center gap-1 shrink-0 mb-2 mr-2", children: [
122
- showVoiceButton && /* @__PURE__ */ jsx(
123
- "button",
124
- {
125
- type: "button",
126
- onClick: onVoicePress,
127
- disabled: isVoiceButtonDisabled,
128
- className: `flex items-center justify-center flex-shrink-0 transition-all rounded-full w-8 h-8 min-w-[32px] ${isRecording ? "animate-pulse" : "hover:opacity-80"} disabled:opacity-50 disabled:cursor-not-allowed`,
129
- "aria-label": isRecording ? "Stop recording" : "Voice input",
130
- children: /* @__PURE__ */ jsx(Mic, { className: `w-5 h-5 shrink-0 ${isRecording ? "text-red-500" : "text-foreground"}` })
131
- }
103
+ className: cn("flex-shrink-0 w-full", className),
104
+ style: { flexShrink: 0 },
105
+ children: /* @__PURE__ */ jsx("div", { className: "px-3 pb-3 pt-1.5 w-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full max-w-2xl mx-auto", children: /* @__PURE__ */ jsxs(
106
+ motion.div,
107
+ {
108
+ initial: false,
109
+ className: cn(
110
+ "flex items-end overflow-hidden",
111
+ "payman-chat-input"
132
112
  ),
133
- showPauseButton ? /* @__PURE__ */ jsx(
134
- "button",
135
- {
136
- type: "button",
137
- onClick: onPause,
138
- className: "flex items-center justify-center flex-shrink-0 transition-all rounded-full w-8 h-8 bg-primary text-primary-foreground hover:bg-primary/90",
139
- "aria-label": "Pause request",
140
- children: /* @__PURE__ */ jsx(Pause, { className: "w-4 h-4" })
141
- }
142
- ) : /* @__PURE__ */ jsx(
143
- "button",
113
+ children: [
114
+ /* @__PURE__ */ jsx(
115
+ "textarea",
116
+ {
117
+ ref: textareaRef,
118
+ value,
119
+ onChange: (e) => onChange(e.target.value),
120
+ onKeyDown: handleKeyDown,
121
+ onClick,
122
+ disabled: isInputDisabled,
123
+ placeholder: getPlaceholder(),
124
+ className: cn(
125
+ "payman-chat-input-field",
126
+ "focus:outline-none resize-none overflow-y-auto",
127
+ "flex-1 min-w-0 py-3"
128
+ ),
129
+ style: {
130
+ minHeight: "44px",
131
+ maxHeight: "160px",
132
+ paddingLeft: "18px",
133
+ paddingRight: "8px"
134
+ },
135
+ rows: 1
136
+ }
137
+ ),
138
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0 p-2", children: [
139
+ showVoiceButton && /* @__PURE__ */ jsxs(
140
+ "button",
141
+ {
142
+ type: "button",
143
+ onClick: onVoicePress,
144
+ disabled: isVoiceButtonDisabled,
145
+ className: cn(
146
+ "relative flex items-center justify-center",
147
+ "w-8 h-8 rounded-full transition-all duration-200",
148
+ "payman-chat-input-btn-voice",
149
+ isRecording && "recording"
150
+ ),
151
+ "aria-label": isRecording ? "Stop recording" : "Voice input",
152
+ children: [
153
+ isRecording && /* @__PURE__ */ jsx(
154
+ "span",
155
+ {
156
+ className: "absolute inset-0 rounded-full border-2 animate-ping opacity-40",
157
+ style: {
158
+ borderColor: "var(--payman-input-btn-voice-recording-ring)"
159
+ }
160
+ }
161
+ ),
162
+ isRecording ? /* @__PURE__ */ jsx(
163
+ Square,
164
+ {
165
+ className: "w-3 h-3 relative z-10",
166
+ fill: "currentColor"
167
+ }
168
+ ) : /* @__PURE__ */ jsx(Mic, { className: "w-4 h-4" })
169
+ ]
170
+ }
171
+ ),
172
+ showPauseButton ? /* @__PURE__ */ jsx(
173
+ "button",
174
+ {
175
+ type: "button",
176
+ onClick: onPause,
177
+ className: cn(
178
+ "flex items-center justify-center",
179
+ "w-8 h-8 rounded-full",
180
+ "payman-chat-input-btn-pause",
181
+ "hover:opacity-90 active:scale-95",
182
+ "transition-all duration-150"
183
+ ),
184
+ "aria-label": "Stop response",
185
+ children: /* @__PURE__ */ jsx(Square, { className: "w-3.5 h-3.5", fill: "currentColor" })
186
+ }
187
+ ) : /* @__PURE__ */ jsx(
188
+ "button",
189
+ {
190
+ type: "button",
191
+ onClick: onSend,
192
+ disabled: !canSend,
193
+ className: cn(
194
+ "flex items-center justify-center",
195
+ "w-8 h-8 rounded-full",
196
+ "payman-chat-input-btn-send",
197
+ "hover:opacity-90 active:scale-95",
198
+ "transition-all duration-150"
199
+ ),
200
+ "aria-label": "Send message",
201
+ children: /* @__PURE__ */ jsx(ArrowUp, { className: "w-4 h-4", strokeWidth: 2.5 })
202
+ }
203
+ )
204
+ ] })
205
+ ]
206
+ }
207
+ ) }) })
208
+ }
209
+ );
210
+ }
211
+ function ThinkingBlock({ text }) {
212
+ const [isOpen, setIsOpen] = useState(false);
213
+ const hasContent = typeof text === "string" && text.trim().length > 0;
214
+ if (!hasContent) return null;
215
+ return /* @__PURE__ */ jsxs("div", { className: "mt-1.5 mb-1.5", children: [
216
+ /* @__PURE__ */ jsxs(
217
+ "button",
218
+ {
219
+ type: "button",
220
+ onClick: () => setIsOpen((prev) => !prev),
221
+ className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground/50 hover:text-muted-foreground/80 transition-colors",
222
+ "aria-expanded": isOpen,
223
+ "aria-label": isOpen ? "Collapse thought process" : "Expand thought process",
224
+ children: [
225
+ /* @__PURE__ */ jsx(
226
+ motion.div,
144
227
  {
145
- type: "button",
146
- onClick: onSend,
147
- disabled: isInputDisabled || !value.trim(),
148
- className: "flex items-center justify-center flex-shrink-0 transition-all rounded-full w-8 h-8 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed",
149
- "aria-label": "Send message",
150
- children: /* @__PURE__ */ jsx(Send, { className: "w-4 h-4" })
228
+ animate: { rotate: isOpen ? 180 : 0 },
229
+ transition: { duration: 0.15 },
230
+ className: "shrink-0",
231
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" })
151
232
  }
152
- )
153
- ] })
154
- ]
155
- }
156
- ) }) }) });
233
+ ),
234
+ /* @__PURE__ */ jsx("span", { children: "Thought process" })
235
+ ]
236
+ }
237
+ ),
238
+ /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isOpen && /* @__PURE__ */ jsx(
239
+ motion.div,
240
+ {
241
+ initial: { height: 0, opacity: 0 },
242
+ animate: { height: "auto", opacity: 1 },
243
+ exit: { height: 0, opacity: 0 },
244
+ transition: { duration: 0.2, ease: "easeInOut" },
245
+ className: "overflow-hidden",
246
+ children: /* @__PURE__ */ jsx("div", { className: "mt-1 bg-muted/50 rounded-md p-2 overflow-y-auto overflow-x-hidden", children: /* @__PURE__ */ jsx("p", { className: "m-0 text-xs text-muted-foreground/70 whitespace-pre-wrap leading-relaxed", children: text }) })
247
+ }
248
+ ) })
249
+ ] });
157
250
  }
251
+ var FRIENDLY_ERROR_MESSAGE = "Oops, something went wrong. Please try again.";
158
252
  function AgentMessage({
159
253
  message,
160
254
  animated = false,
@@ -174,310 +268,364 @@ function AgentMessage({
174
268
  const isError = message.isError ?? (message.streamProgress === "error" || !!message.errorDetails);
175
269
  const isCancelled = message.isCancelled ?? false;
176
270
  const currentExecutingStepId = message.currentExecutingStepId;
177
- const [isStepsExpanded, setIsStepsExpanded] = useState(isStreaming && hasSteps);
271
+ const [isStepsExpanded, setIsStepsExpanded] = useState(false);
272
+ const wasStreamingRef = useRef(isStreaming);
178
273
  useEffect(() => {
179
274
  if (isStreaming && hasSteps) {
180
275
  setIsStepsExpanded(true);
181
276
  }
277
+ if (wasStreamingRef.current && !isStreaming) {
278
+ setIsStepsExpanded(false);
279
+ }
280
+ wasStreamingRef.current = isStreaming;
182
281
  }, [isStreaming, hasSteps]);
183
282
  const totalElapsedMs = hasSteps ? message.steps.reduce((sum, step) => sum + (step.elapsedMs || 0), 0) : 0;
184
283
  const currentMessage = message.currentMessage;
185
284
  const rawContent = message.streamingContent || message.content || "";
186
285
  const content = rawContent.replace(/\\n/g, "\n");
187
- const getStreamingStepsText = (expanded) => {
188
- const defaultText = `${expanded ? "Hide" : "View"} progress ({count} ${message.steps.length === 1 ? "step" : "steps"})`;
189
- const text = streamingStepsText || defaultText;
190
- const prefix = expanded ? "Hide" : "View";
191
- return text.replace("{count}", message.steps.length.toString()).replace(/^(View|Hide)/, prefix);
192
- };
193
- const getCompletedStepsText = (expanded) => {
286
+ const activeThinkingText = message.activeThinkingText;
287
+ const allThinkingText = message.allThinkingText;
288
+ const currentStep = useMemo(
289
+ () => message.steps?.find(
290
+ (s) => s.id === currentExecutingStepId && s.status === "in_progress"
291
+ ),
292
+ [message.steps, currentExecutingStepId]
293
+ );
294
+ const getStepsLabel = (streaming) => {
295
+ const count = message.steps.length;
296
+ const stepWord = count === 1 ? "step" : "steps";
194
297
  if (completedStepsText) {
195
- const result = completedStepsText.replace("{count}", message.steps.length.toString()).replace("{time}", (totalElapsedMs / 1e3).toFixed(1));
196
- return result;
298
+ return completedStepsText.replace("{count}", count.toString()).replace("{time}", (totalElapsedMs / 1e3).toFixed(1));
197
299
  }
198
- const prefix = expanded ? "Hide" : "View";
199
- const stepWord = message.steps.length === 1 ? "step" : "steps";
200
- return `${prefix} execution steps (${message.steps.length} ${stepWord})`;
300
+ return `${count} ${stepWord} completed`;
301
+ };
302
+ const renderStepIcon = (step, isCurrentlyExecuting) => {
303
+ const wrapper = "h-4 w-4 flex items-center justify-center shrink-0";
304
+ if (isCurrentlyExecuting)
305
+ return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 payman-agent-step-icon--active animate-spin" }) });
306
+ if (step.status === "pending" && isCancelled)
307
+ return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full payman-agent-step-icon--pending" }) });
308
+ if (step.status === "pending" || step.status === "in_progress")
309
+ return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 payman-agent-step-icon--in-progress-dim animate-spin" }) });
310
+ if (step.status === "completed")
311
+ return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(
312
+ Check,
313
+ {
314
+ className: cn(
315
+ "h-3.5 w-3.5",
316
+ step.eventType === "USER_ACTION_SUCCESS" ? "payman-agent-step-icon--success" : "payman-agent-step-icon--success-dim"
317
+ )
318
+ }
319
+ ) });
320
+ if (step.status === "error")
321
+ return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5 payman-agent-step-icon--error" }) });
322
+ return /* @__PURE__ */ jsx("div", { className: wrapper, children: /* @__PURE__ */ jsx("div", { className: "h-1.5 w-1.5 rounded-full payman-agent-step-icon--pending-dim" }) });
201
323
  };
202
- const messageContent = /* @__PURE__ */ jsxs("div", { className: "flex gap-4 justify-start items-start w-full", children: [
203
- showAvatar && /* @__PURE__ */ jsx("div", { className: "h-8 w-8 shrink-0 mt-0.5", children: isStreaming && showStreamingDot ? (
204
- // Animated dot indicator while streaming
205
- /* @__PURE__ */ jsx("div", { className: "h-8 w-8 flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
206
- /* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-primary animate-pulse" }),
207
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 h-2 w-2 rounded-full bg-primary animate-ping opacity-75" })
208
- ] }) })
209
- ) : (
210
- // Avatar after completion or if streaming dot is disabled
211
- /* @__PURE__ */ jsx("div", { className: "h-8 w-8 rounded-full bg-muted flex items-center justify-center", children: /* @__PURE__ */ jsx(Bot, { className: "h-4 w-4 text-muted-foreground" }) })
212
- ) }),
213
- /* @__PURE__ */ jsxs("div", { className: cn(
214
- "min-w-0 flex flex-col",
215
- layout === "centered" ? "max-w-[85%]" : showAvatar ? "max-w-[85%]" : "max-w-[80%]"
216
- ), children: [
217
- message.userActionResult && /* @__PURE__ */ jsx("div", { className: "mb-2", children: message.userActionResult === "approved" ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-1 text-xs font-medium rounded-full bg-green-100 text-green-700", children: [
218
- /* @__PURE__ */ jsx(CheckCircle2, { className: "w-3 h-3" }),
219
- " OTP Verified"
220
- ] }) : /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-1 text-xs font-medium rounded-full bg-red-100 text-red-700", children: [
221
- /* @__PURE__ */ jsx(XCircle, { className: "w-3 h-3" }),
222
- " Rejected"
223
- ] }) }),
324
+ const stepsListContent = hasSteps && /* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: isStepsExpanded && /* @__PURE__ */ jsxs(
325
+ motion.div,
326
+ {
327
+ initial: { height: 0, opacity: 0 },
328
+ animate: { height: "auto", opacity: 1 },
329
+ exit: { height: 0, opacity: 0 },
330
+ transition: { duration: 0.2, ease: "easeInOut" },
331
+ className: "overflow-hidden",
332
+ children: [
333
+ !isStreaming && allThinkingText?.trim() && /* @__PURE__ */ jsx(ThinkingBlock, { text: allThinkingText }),
334
+ /* @__PURE__ */ jsx("div", { className: "pt-1.5", children: message.steps.map((step) => {
335
+ const isCurrentlyExecuting = step.id === currentExecutingStepId && step.status === "in_progress" && isStreaming && !isCancelled;
336
+ const hasTime = step.elapsedMs != null && step.elapsedMs > 0;
337
+ return /* @__PURE__ */ jsxs("div", { className: "mb-1.5", children: [
338
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-start", children: [
339
+ /* @__PURE__ */ jsx("div", { className: "mt-px", children: renderStepIcon(step, isCurrentlyExecuting) }),
340
+ /* @__PURE__ */ jsx(
341
+ "span",
342
+ {
343
+ className: cn(
344
+ "text-xs leading-relaxed min-w-0 break-words",
345
+ isCurrentlyExecuting && "shimmer-text font-medium",
346
+ !isCurrentlyExecuting && step.status === "error" && "text-destructive payman-agent-step-text--error",
347
+ !isCurrentlyExecuting && step.eventType === "USER_ACTION_SUCCESS" && "text-emerald-600 payman-agent-step-text--success",
348
+ !isCurrentlyExecuting && step.status === "completed" && "text-muted-foreground payman-agent-step-text--completed",
349
+ !isCurrentlyExecuting && step.status === "pending" && "text-muted-foreground/40 payman-agent-step-text--pending",
350
+ !isCurrentlyExecuting && step.status === "in_progress" && "text-muted-foreground/60 payman-agent-step-text--in-progress"
351
+ ),
352
+ children: step.message
353
+ }
354
+ )
355
+ ] }),
356
+ hasTime && /* @__PURE__ */ jsx("div", { className: "pl-[22px] mt-1", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded-md bg-muted border border-border/40 leading-none payman-agent-step-time", children: [
357
+ /* @__PURE__ */ jsx(Clock, { className: "h-2.5 w-2.5 text-muted-foreground/60 shrink-0 payman-agent-step-time-icon" }),
358
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-muted-foreground/60", children: formatElapsedTime(step.elapsedMs) })
359
+ ] }) })
360
+ ] }, step.id);
361
+ }) })
362
+ ]
363
+ }
364
+ ) });
365
+ const stepsToggleRef = useRef(null);
366
+ const handleStepsToggle = useCallback(() => {
367
+ setIsStepsExpanded((prev) => {
368
+ const next = !prev;
369
+ if (next) {
370
+ requestAnimationFrame(() => {
371
+ stepsToggleRef.current?.closest(".payman-scrollbar")?.dispatchEvent(new CustomEvent("payman-steps-toggle"));
372
+ });
373
+ }
374
+ return next;
375
+ });
376
+ }, []);
377
+ const stepsToggle = (streaming) => /* @__PURE__ */ jsxs(
378
+ "button",
379
+ {
380
+ ref: stepsToggleRef,
381
+ onClick: handleStepsToggle,
382
+ className: "inline-flex items-center gap-1.5 text-xs payman-agent-step-toggle transition-colors",
383
+ children: [
384
+ /* @__PURE__ */ jsx(
385
+ motion.div,
386
+ {
387
+ animate: { rotate: isStepsExpanded ? 90 : 0 },
388
+ transition: { duration: 0.15 },
389
+ className: "shrink-0",
390
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-3 w-3" })
391
+ }
392
+ ),
393
+ /* @__PURE__ */ jsx("span", { children: getStepsLabel() })
394
+ ]
395
+ }
396
+ );
397
+ const showSteps = showExecutionSteps && hasSteps && !isStreaming && !isError;
398
+ const messageContent = /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
399
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3 justify-start items-start w-full", children: [
400
+ showAvatar && /* @__PURE__ */ jsx("div", { className: "shrink-0 mt-0.5", children: isStreaming && showStreamingDot ? /* @__PURE__ */ jsx("div", { className: "h-8 w-8 flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
401
+ /* @__PURE__ */ jsx("div", { className: "h-2.5 w-2.5 rounded-full payman-agent-avatar-dot animate-pulse" }),
402
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 h-2.5 w-2.5 rounded-full payman-agent-avatar-dot-ping animate-ping" })
403
+ ] }) }) : /* @__PURE__ */ jsx("div", { className: "h-8 w-8 rounded-full payman-agent-avatar flex items-center justify-center", children: /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 payman-agent-avatar-icon" }) }) }),
224
404
  /* @__PURE__ */ jsxs(
225
405
  "div",
226
406
  {
227
407
  className: cn(
228
- "overflow-hidden w-full",
229
- layout === "centered" ? cn(
230
- "bg-white border transition-all duration-200 p-3",
231
- hasSteps ? "rounded-t-2xl rounded-tl-sm border-b-0" : "rounded-2xl rounded-tl-sm",
232
- message.isStreaming ? "border-neutral-300 shadow-sm" : "border-neutral-200",
233
- isError && "border-red-200"
234
- ) : cn(
235
- "p-3",
236
- hasSteps && !isStreaming ? "rounded-t-md" : "rounded-md",
237
- isError ? "bg-destructive/10 border border-destructive/30" : "bg-muted"
238
- )
408
+ "min-w-0",
409
+ layout === "centered" ? "max-w-[85%]" : showAvatar ? "max-w-[85%]" : "max-w-[80%]"
239
410
  ),
240
411
  children: [
241
- showAgentName && /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold mb-1.5 opacity-60", children: agentName }),
242
- /* @__PURE__ */ jsx("div", { className: cn("text-sm leading-relaxed", layout === "centered" ? "p-4" : "", showAgentName && "mt-1"), children: isStreaming && !content ? !showAvatar ? (
243
- // Without avatars (yaak): show spinner
244
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
245
- /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 text-primary animate-spin flex-shrink-0" }),
246
- /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: currentMessage || /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center", children: [
247
- "Thinking",
248
- /* @__PURE__ */ jsxs("span", { className: "ml-1 inline-flex", children: [
249
- /* @__PURE__ */ jsx("span", { className: "thinking-dot", children: "." }),
250
- /* @__PURE__ */ jsx("span", { className: "thinking-dot", children: "." }),
251
- /* @__PURE__ */ jsx("span", { className: "thinking-dot", children: "." })
252
- ] })
253
- ] }) })
254
- ] })
255
- ) : (
256
- // With avatars (paygent-central): no spinner in message (dot avatar handles it)
257
- /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: currentMessage || /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center", children: [
258
- "Thinking",
259
- /* @__PURE__ */ jsxs("span", { className: "ml-1 inline-flex", children: [
260
- /* @__PURE__ */ jsx("span", { className: "thinking-dot", children: "." }),
261
- /* @__PURE__ */ jsx("span", { className: "thinking-dot", children: "." }),
262
- /* @__PURE__ */ jsx("span", { className: "thinking-dot", children: "." })
263
- ] })
264
- ] }) })
265
- ) : isCancelled && !content ? (
266
- // Show cancelled state only when message is cancelled and has no content
267
- !showAvatar ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
268
- /* @__PURE__ */ jsx(X, { className: "w-4 h-4 text-muted-foreground flex-shrink-0" }),
269
- /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: currentMessage || "The request was stopped." })
270
- ] }) : /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: currentMessage || "The request was stopped." })
271
- ) : /* @__PURE__ */ jsx("div", { className: cn(
272
- "prose prose-sm dark:prose-invert max-w-none",
273
- isError && "text-destructive"
274
- ), children: /* @__PURE__ */ jsx(
275
- ReactMarkdown,
412
+ message.userActionResult && /* @__PURE__ */ jsx("div", { className: "mb-2", children: message.userActionResult === "approved" ? /* @__PURE__ */ jsxs(
413
+ "span",
276
414
  {
277
- remarkPlugins: [remarkGfm],
278
- components: {
279
- p: ({ children }) => /* @__PURE__ */ jsx("p", { className: cn("mb-3 last:mb-0 text-sm leading-relaxed", isError ? "text-destructive" : "text-foreground"), children }),
280
- code: ({ className: codeClassName, children }) => {
281
- const isInline = !codeClassName;
282
- return isInline ? /* @__PURE__ */ jsx("code", { className: cn("px-1.5 py-0.5 rounded text-xs font-mono break-all", isError ? "bg-destructive/10 text-destructive" : "bg-muted"), children }) : /* @__PURE__ */ jsx("code", { className: cn("block p-3 rounded text-xs font-mono overflow-x-auto my-2 whitespace-pre", isError ? "bg-destructive/10 text-destructive" : "bg-muted"), children });
283
- },
284
- pre: ({ children }) => /* @__PURE__ */ jsx("pre", { className: cn("my-2 overflow-x-auto max-w-full", isError && "bg-destructive/10"), children }),
285
- ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: cn("list-disc ml-4 mb-3 space-y-1 text-sm", isError && "text-destructive"), children }),
286
- ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: cn("list-decimal ml-4 mb-3 space-y-1 text-sm", isError && "text-destructive"), children }),
287
- li: ({ children }) => /* @__PURE__ */ jsx("li", { className: cn("text-sm leading-relaxed", isError ? "text-destructive" : "text-foreground"), children }),
288
- h1: ({ children }) => /* @__PURE__ */ jsx("h1", { className: cn("text-lg font-semibold mb-2 mt-4 first:mt-0", isError ? "text-destructive" : "text-foreground"), children }),
289
- h2: ({ children }) => /* @__PURE__ */ jsx("h2", { className: cn("text-base font-semibold mb-2 mt-3 first:mt-0", isError ? "text-destructive" : "text-foreground"), children }),
290
- h3: ({ children }) => /* @__PURE__ */ jsx("h3", { className: cn("text-sm font-semibold mb-1 mt-2 first:mt-0", isError ? "text-destructive" : "text-foreground"), children }),
291
- strong: ({ children }) => /* @__PURE__ */ jsx("strong", { className: cn("font-semibold", isError ? "text-destructive" : "text-foreground"), children }),
292
- em: ({ children }) => /* @__PURE__ */ jsx("em", { className: cn("italic", isError ? "text-destructive" : "text-foreground"), children }),
293
- blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", { className: "border-l-4 border-border pl-4 my-2 italic text-muted-foreground", children }),
294
- hr: () => /* @__PURE__ */ jsx("hr", { className: "my-4 border-border" }),
295
- a: ({ href, children }) => /* @__PURE__ */ jsx(
296
- "a",
297
- {
298
- href,
299
- target: "_blank",
300
- rel: "noopener noreferrer",
301
- className: cn("underline underline-offset-2", isError ? "text-destructive hover:text-destructive/80" : "text-primary hover:text-primary/80"),
302
- children
303
- }
304
- ),
305
- table: ({ children }) => /* @__PURE__ */ jsx("div", { className: "relative w-full overflow-x-auto my-4 -mx-1", children: /* @__PURE__ */ jsx("table", { className: "min-w-full caption-bottom text-sm border border-border rounded-md", children }) }),
306
- thead: ({ children }) => /* @__PURE__ */ jsx("thead", { className: "[&_tr]:border-b bg-muted/50", children }),
307
- tbody: ({ children }) => /* @__PURE__ */ jsx("tbody", { className: "[&_tr:last-child]:border-0", children }),
308
- tr: ({ children }) => /* @__PURE__ */ jsx("tr", { className: "hover:bg-muted/50 border-b transition-colors", children }),
309
- th: ({ children }) => /* @__PURE__ */ jsx("th", { className: "text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap", children }),
310
- td: ({ children }) => /* @__PURE__ */ jsx("td", { className: "p-2 align-middle text-foreground", children })
415
+ className: "inline-flex items-center gap-1.5 px-4 py-1.5 text-xs font-semibold rounded-full payman-agent-approved shrink-0",
416
+ style: {
417
+ background: "var(--payman-agent-approved-bg, var(--payman-success-light, #ecfdf5))",
418
+ color: "var(--payman-agent-approved-text, var(--payman-success-dark, #047857))",
419
+ border: "1px solid var(--payman-agent-approved-border, var(--payman-success, #059669))"
311
420
  },
312
- children: content || (isStreaming ? "Thinking..." : isCancelled ? "The request was stopped." : isError ? "Oops, something went wrong. Please try again." : "")
421
+ children: [
422
+ /* @__PURE__ */ jsx(Check, { className: "w-3.5 h-3.5 shrink-0", strokeWidth: 2.5 }),
423
+ "Verified"
424
+ ]
313
425
  }
314
- ) }) })
315
- ]
316
- }
317
- ),
318
- showExecutionSteps && hasSteps && !isStreaming && !isError && /* @__PURE__ */ jsxs("div", { className: cn(
319
- "overflow-hidden",
320
- layout === "centered" ? "bg-white border border-t-0 border-neutral-200 rounded-b-2xl" : "bg-muted border-t border-border/30 rounded-b-md"
321
- ), children: [
322
- /* @__PURE__ */ jsxs(
323
- "button",
324
- {
325
- onClick: () => setIsStepsExpanded(!isStepsExpanded),
326
- className: cn(
327
- "w-full px-4 py-2 flex items-center justify-between text-xs transition-colors",
328
- layout === "centered" ? "text-neutral-500 hover:bg-neutral-50" : "text-muted-foreground hover:bg-muted/70"
329
- ),
330
- children: [
331
- /* @__PURE__ */ jsx("span", { children: getCompletedStepsText(!!isStepsExpanded) }),
332
- isStepsExpanded ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" })
333
- ]
334
- }
335
- ),
336
- isStepsExpanded && /* @__PURE__ */ jsx(AnimatePresence, { children: /* @__PURE__ */ jsx(
337
- motion.div,
338
- {
339
- initial: { height: 0, opacity: 0 },
340
- animate: { height: "auto", opacity: 1 },
341
- exit: { height: 0, opacity: 0 },
342
- transition: { duration: 0.2 },
343
- className: "px-4 pb-3 pt-2 space-y-2 bg-background/30",
344
- children: message.steps.map((step) => {
345
- const isCurrentlyExecuting = step.id === currentExecutingStepId && step.status === "in_progress" && isStreaming && !isCancelled;
346
- return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 text-xs", children: [
347
- isCurrentlyExecuting && /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 text-primary animate-spin shrink-0 mt-0.5" }),
348
- step.status === "pending" && isCancelled && /* @__PURE__ */ jsx(XCircle, { className: "h-3 w-3 text-muted-foreground shrink-0 mt-0.5" }),
349
- step.status === "pending" && !isCancelled && /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 text-primary animate-spin shrink-0 mt-0.5" }),
350
- step.status === "in_progress" && !isCurrentlyExecuting && /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 text-primary animate-spin shrink-0 mt-0.5" }),
351
- step.status === "completed" && /* @__PURE__ */ jsx(Check, { className: cn("h-3 w-3 shrink-0 mt-0.5", step.eventType === "USER_ACTION_SUCCESS" ? "text-green-600" : "text-primary") }),
352
- step.status === "error" && /* @__PURE__ */ jsx(X, { className: "h-3 w-3 text-destructive shrink-0 mt-0.5" }),
353
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
354
- /* @__PURE__ */ jsx(
355
- "span",
426
+ ) : /* @__PURE__ */ jsxs(
427
+ "span",
428
+ {
429
+ className: "inline-flex items-center gap-1.5 px-4 py-1.5 text-xs font-semibold rounded-full payman-agent-rejected shrink-0",
430
+ style: {
431
+ background: "var(--payman-agent-rejected-bg, var(--payman-error-light, #fef2f2))",
432
+ color: "var(--payman-agent-rejected-text, var(--payman-error-dark, #b91c1c))",
433
+ border: "1px solid var(--payman-agent-rejected-border, var(--payman-destructive, #dc2626))"
434
+ },
435
+ children: [
436
+ /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5 shrink-0", strokeWidth: 2.5 }),
437
+ "Rejected"
438
+ ]
439
+ }
440
+ ) }),
441
+ /* @__PURE__ */ jsxs(
442
+ "div",
443
+ {
444
+ className: cn(
445
+ "overflow-hidden w-full min-w-0 px-4 py-3 rounded-2xl rounded-tl-md transition-all duration-200",
446
+ layout === "centered" ? cn(
447
+ "payman-agent-bubble payman-agent-bubble--centered border shadow-sm",
448
+ isStreaming && "streaming",
449
+ isError && "error"
450
+ ) : cn(
451
+ "payman-agent-bubble",
452
+ isError && "payman-agent-bubble--error"
453
+ )
454
+ ),
455
+ children: [
456
+ showAgentName && /* @__PURE__ */ jsx(
457
+ "p",
356
458
  {
357
459
  className: cn(
358
- "text-muted-foreground break-words",
359
- step.status === "error" && "text-destructive",
360
- step.eventType === "USER_ACTION_SUCCESS" && "text-green-600"
460
+ "text-sm font-semibold mb-1 leading-none text-muted-foreground/70 payman-agent-name",
461
+ isStreaming && !content && "animate-pulse"
361
462
  ),
362
- children: step.message
463
+ children: isStreaming && !content ? "Thought Process" : agentName
363
464
  }
364
465
  ),
365
- step.elapsedMs && /* @__PURE__ */ jsxs("span", { className: "ml-1.5 text-[10px] opacity-50", children: [
366
- "(",
367
- (step.elapsedMs / 1e3).toFixed(1),
368
- "s)"
369
- ] })
370
- ] })
371
- ] }, step.id);
372
- })
373
- }
374
- ) })
375
- ] }),
376
- hasSteps && isStreaming && /* @__PURE__ */ jsxs("div", { className: cn(
377
- "overflow-hidden",
378
- layout === "centered" ? "bg-white border border-t-0 border-neutral-300 rounded-b-2xl" : "bg-muted border-t border-border/30 rounded-b-md"
379
- ), children: [
380
- /* @__PURE__ */ jsxs(
381
- "button",
382
- {
383
- onClick: () => setIsStepsExpanded(!isStepsExpanded),
384
- className: cn(
385
- "w-full px-4 py-2 flex items-center justify-between text-xs transition-colors",
386
- layout === "centered" ? "text-neutral-500 hover:bg-neutral-50" : "text-muted-foreground hover:bg-muted/70"
387
- ),
388
- children: [
389
- /* @__PURE__ */ jsx("span", { children: getStreamingStepsText(!!isStepsExpanded) }),
390
- isStepsExpanded ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3" })
391
- ]
392
- }
393
- ),
394
- isStepsExpanded && /* @__PURE__ */ jsx(AnimatePresence, { children: /* @__PURE__ */ jsx(
395
- motion.div,
396
- {
397
- initial: { height: 0, opacity: 0 },
398
- animate: { height: "auto", opacity: 1 },
399
- exit: { height: 0, opacity: 0 },
400
- transition: { duration: 0.2 },
401
- className: "px-4 pb-3 pt-2 space-y-2 bg-background/30",
402
- children: message.steps.map((step) => {
403
- const isCurrentlyExecuting = step.id === currentExecutingStepId && step.status === "in_progress" && isStreaming && !isCancelled;
404
- return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 text-xs", children: [
405
- isCurrentlyExecuting && /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 text-primary animate-spin shrink-0 mt-0.5" }),
406
- step.status === "pending" && isCancelled && /* @__PURE__ */ jsx(XCircle, { className: "h-3 w-3 text-muted-foreground shrink-0 mt-0.5" }),
407
- step.status === "pending" && !isCancelled && /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 text-primary animate-spin shrink-0 mt-0.5" }),
408
- step.status === "in_progress" && !isCurrentlyExecuting && /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 text-primary animate-spin shrink-0 mt-0.5" }),
409
- step.status === "completed" && /* @__PURE__ */ jsx(Check, { className: cn("h-3 w-3 shrink-0 mt-0.5", step.eventType === "USER_ACTION_SUCCESS" ? "text-green-600" : "text-primary") }),
410
- step.status === "error" && /* @__PURE__ */ jsx(X, { className: "h-3 w-3 text-destructive shrink-0 mt-0.5" }),
411
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
412
466
  /* @__PURE__ */ jsx(
413
- "span",
467
+ "div",
414
468
  {
415
469
  className: cn(
416
- "text-muted-foreground break-words",
417
- step.status === "error" && "text-destructive",
418
- step.eventType === "USER_ACTION_SUCCESS" && "text-green-600"
470
+ "text-sm leading-relaxed min-w-0 w-full break-words overflow-wrap-anywhere",
471
+ showAgentName && "mt-1"
419
472
  ),
420
- children: step.message
473
+ children: isStreaming && !content ? (
474
+ // One active item at a time, by event order: thinking stream OR current step with loading (same bullet UI as dropdown)
475
+ activeThinkingText ? /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-start", children: [
476
+ /* @__PURE__ */ jsx("div", { className: "mt-px", children: /* @__PURE__ */ jsx("div", { className: "h-4 w-4 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 text-primary animate-spin payman-agent-thinking-spinner" }) }) }),
477
+ /* @__PURE__ */ jsxs("span", { className: "text-sm leading-relaxed min-w-0 break-words text-muted-foreground whitespace-pre-wrap flex-1 payman-agent-thinking-text", children: [
478
+ activeThinkingText,
479
+ /* @__PURE__ */ jsx("span", { className: "inline-block w-0.5 h-3.5 bg-muted-foreground/50 animate-pulse ml-0.5 align-text-bottom" })
480
+ ] })
481
+ ] }) : currentStep ? /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-start", children: [
482
+ /* @__PURE__ */ jsx("div", { className: "mt-px", children: renderStepIcon(currentStep, true) }),
483
+ /* @__PURE__ */ jsx("span", { className: "text-sm leading-relaxed min-w-0 break-words shimmer-text font-medium", children: currentStep.message })
484
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
485
+ !showAvatar && /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 text-primary/70 animate-spin flex-shrink-0 payman-agent-thinking-spinner" }),
486
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground whitespace-nowrap payman-agent-thinking-text", children: currentMessage || "Thinking..." })
487
+ ] })
488
+ ) : isCancelled && !content ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
489
+ /* @__PURE__ */ jsx(X, { className: "w-4 h-4 payman-agent-cancelled-icon flex-shrink-0" }),
490
+ /* @__PURE__ */ jsx("span", { className: "text-sm payman-agent-cancelled-text italic", children: currentMessage || "Request was stopped." })
491
+ ] }) : /* @__PURE__ */ jsx(
492
+ "div",
493
+ {
494
+ className: cn(
495
+ "payman-markdown payman-agent-markdown prose prose-sm dark:prose-invert max-w-none min-w-0 w-full break-words overflow-wrap-anywhere",
496
+ isError && "payman-agent-markdown--error",
497
+ isStreaming && content && "streaming-cursor"
498
+ ),
499
+ children: /* @__PURE__ */ jsx(
500
+ ReactMarkdown,
501
+ {
502
+ remarkPlugins: [remarkGfm],
503
+ components: markdownComponents(),
504
+ children: isError ? FRIENDLY_ERROR_MESSAGE : content || (isStreaming ? "Thinking..." : isCancelled ? "Request was stopped." : "")
505
+ }
506
+ )
507
+ }
508
+ )
421
509
  }
422
- ),
423
- step.elapsedMs && /* @__PURE__ */ jsxs("span", { className: "ml-1.5 text-[10px] opacity-50", children: [
424
- "(",
425
- (step.elapsedMs / 1e3).toFixed(1),
426
- "s)"
427
- ] })
428
- ] })
429
- ] }, step.id);
430
- })
431
- }
432
- ) })
433
- ] })
510
+ )
511
+ ]
512
+ }
513
+ )
514
+ ]
515
+ }
516
+ ),
517
+ hasTraceData && onExecutionTraceClick && /* @__PURE__ */ jsx(
518
+ "button",
519
+ {
520
+ onClick: () => onExecutionTraceClick({
521
+ message,
522
+ tracingData: message.tracingData,
523
+ executionId: message.executionId
524
+ }),
525
+ className: cn(
526
+ "p-2 rounded-lg shrink-0 payman-agent-trace",
527
+ "active:scale-95 transition-all duration-150",
528
+ message.userActionResult ? "mt-8" : "mt-0.5"
529
+ ),
530
+ title: "View execution trace",
531
+ "aria-label": "View execution trace",
532
+ children: /* @__PURE__ */ jsx(Binoculars, { className: "w-4 h-4 payman-agent-trace-icon" })
533
+ }
534
+ )
434
535
  ] }),
435
- hasTraceData && onExecutionTraceClick && /* @__PURE__ */ jsx(
436
- "button",
437
- {
438
- onClick: () => onExecutionTraceClick({
439
- message,
440
- tracingData: message.tracingData,
441
- executionId: message.executionId
442
- }),
443
- className: cn(
444
- "p-2 bg-white/5 border border-white/20 rounded-lg shadow-sm hover:bg-white/10 hover:border-highlight transition-colors duration-200 flex-shrink-0",
445
- message.userActionResult ? "mt-8" : "mt-0.5"
446
- ),
447
- title: "View execution trace",
448
- "aria-label": "View execution trace",
449
- children: /* @__PURE__ */ jsx(Binoculars, { className: "w-4 h-4 text-foreground" })
450
- }
451
- )
536
+ showSteps && /* @__PURE__ */ jsxs("div", { className: cn("mt-1.5", showAvatar ? "ml-11" : "ml-0"), children: [
537
+ showExecutionSteps && hasSteps && !isStreaming && !isError && stepsToggle(),
538
+ stepsListContent
539
+ ] })
452
540
  ] });
453
541
  if (animated) {
454
542
  return /* @__PURE__ */ jsx(
455
543
  motion.div,
456
544
  {
457
- initial: { opacity: 0, y: 10 },
458
- animate: { opacity: 1, y: 0 },
459
- transition: { duration: 0.2 },
545
+ initial: { opacity: 0, y: 8, scale: 0.98 },
546
+ animate: { opacity: 1, y: 0, scale: 1 },
547
+ transition: { duration: 0.25, ease: [0.25, 0.46, 0.45, 0.94] },
460
548
  children: messageContent
461
549
  }
462
550
  );
463
551
  }
464
552
  return messageContent;
465
553
  }
466
- function UserMessage({ message, animated = false, showAvatar = false }) {
467
- const content = /* @__PURE__ */ jsxs("div", { className: "flex gap-4 justify-end w-full", children: [
468
- /* @__PURE__ */ jsxs("div", { className: "max-w-[85%] min-w-0 flex flex-col items-end", children: [
469
- /* @__PURE__ */ jsx("div", { className: "rounded-lg p-3 bg-primary text-primary-foreground", children: /* @__PURE__ */ jsx("p", { className: "text-sm whitespace-pre-wrap break-words", children: message.content }) }),
470
- /* @__PURE__ */ jsx("p", { className: "text-[10px] mt-1 text-muted-foreground", children: formatDate(message.timestamp) })
554
+ function markdownComponents(_isError) {
555
+ return {
556
+ p: ({ children }) => /* @__PURE__ */ jsx("p", { className: "mb-3 last:mb-0 text-sm leading-relaxed", children }),
557
+ code: ({ className: codeClassName, children }) => {
558
+ const isInline = !codeClassName;
559
+ return isInline ? /* @__PURE__ */ jsx("code", { className: "px-1.5 py-0.5 rounded-md text-xs font-mono break-all", children }) : /* @__PURE__ */ jsx("code", { className: "block p-3 rounded-lg text-xs font-mono overflow-x-auto my-2 whitespace-pre", children });
560
+ },
561
+ pre: ({ children }) => /* @__PURE__ */ jsx("pre", { className: "my-2 overflow-x-auto max-w-full rounded-lg", children }),
562
+ ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: "list-disc ml-4 mb-3 space-y-1 text-sm", children }),
563
+ ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: "list-decimal ml-4 mb-3 space-y-1 text-sm", children }),
564
+ li: ({ children }) => /* @__PURE__ */ jsx("li", { className: "text-sm leading-relaxed", children }),
565
+ h1: ({ children }) => /* @__PURE__ */ jsx("h1", { className: "text-lg font-semibold mb-2 mt-4 first:mt-0", children }),
566
+ h2: ({ children }) => /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold mb-2 mt-3 first:mt-0", children }),
567
+ h3: ({ children }) => /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold mb-1 mt-2 first:mt-0", children }),
568
+ strong: ({ children }) => /* @__PURE__ */ jsx("strong", { className: "font-semibold", children }),
569
+ em: ({ children }) => /* @__PURE__ */ jsx("em", { className: "italic", children }),
570
+ blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", { className: "pl-4 my-2 italic", children }),
571
+ hr: () => /* @__PURE__ */ jsx("hr", { className: "my-4" }),
572
+ a: ({ href, children }) => /* @__PURE__ */ jsx(
573
+ "a",
574
+ {
575
+ href,
576
+ target: "_blank",
577
+ rel: "noopener noreferrer",
578
+ className: "underline underline-offset-2 decoration-1",
579
+ children
580
+ }
581
+ ),
582
+ table: ({ children }) => /* @__PURE__ */ jsx("div", { className: "relative w-full overflow-x-auto my-4 -mx-1", children: /* @__PURE__ */ jsx("table", { className: "min-w-full caption-bottom text-sm border rounded-lg overflow-hidden", children }) }),
583
+ thead: ({ children }) => /* @__PURE__ */ jsx("thead", { className: "[&_tr]:border-b", children }),
584
+ tbody: ({ children }) => /* @__PURE__ */ jsx("tbody", { className: "[&_tr:last-child]:border-0", children }),
585
+ tr: ({ children }) => /* @__PURE__ */ jsx("tr", { className: "border-b transition-colors", children }),
586
+ th: ({ children }) => /* @__PURE__ */ jsx("th", { className: "h-10 px-3 text-left align-middle font-medium whitespace-nowrap text-xs", children }),
587
+ td: ({ children }) => /* @__PURE__ */ jsx("td", { className: "p-3 align-middle text-sm", children })
588
+ };
589
+ }
590
+ function UserMessage({
591
+ message,
592
+ animated = false,
593
+ showAvatar = false
594
+ }) {
595
+ const content = /* @__PURE__ */ jsxs("div", { className: "flex gap-3 justify-end w-full", children: [
596
+ /* @__PURE__ */ jsxs("div", { className: "max-w-[80%] min-w-0 flex flex-col items-end", children: [
597
+ /* @__PURE__ */ jsx(
598
+ "div",
599
+ {
600
+ className: cn(
601
+ "rounded-2xl rounded-br-md px-4 py-3",
602
+ "bg-primary text-primary-foreground",
603
+ "shadow-sm"
604
+ ),
605
+ children: /* @__PURE__ */ jsx("p", { className: "text-sm leading-relaxed whitespace-pre-wrap break-words", children: message.content })
606
+ }
607
+ ),
608
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] mt-1.5 mr-1 text-muted-foreground/70 select-none", children: formatDate(message.timestamp) })
471
609
  ] }),
472
- showAvatar && /* @__PURE__ */ jsx("div", { className: "h-8 w-8 shrink-0 mt-0.5", children: /* @__PURE__ */ jsx("div", { className: "h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center", children: /* @__PURE__ */ jsx(User, { className: "h-4 w-4 text-primary" }) }) })
610
+ showAvatar && /* @__PURE__ */ jsx("div", { className: "shrink-0 mt-0.5", children: /* @__PURE__ */ jsx(
611
+ "div",
612
+ {
613
+ className: cn(
614
+ "h-8 w-8 rounded-full",
615
+ "bg-primary/10 border border-primary/20",
616
+ "flex items-center justify-center"
617
+ ),
618
+ children: /* @__PURE__ */ jsx(User, { className: "h-3.5 w-3.5 text-primary" })
619
+ }
620
+ ) })
473
621
  ] });
474
622
  if (animated) {
475
623
  return /* @__PURE__ */ jsx(
476
624
  motion.div,
477
625
  {
478
- initial: { opacity: 0, y: 10 },
479
- animate: { opacity: 1, y: 0 },
480
- transition: { duration: 0.3, ease: "easeOut" },
626
+ initial: { opacity: 0, y: 8, scale: 0.98 },
627
+ animate: { opacity: 1, y: 0, scale: 1 },
628
+ transition: { duration: 0.25, ease: [0.25, 0.46, 0.45, 0.94] },
481
629
  children: content
482
630
  }
483
631
  );
@@ -489,7 +637,7 @@ function MessageRow({
489
637
  stage = "DEV",
490
638
  animated = false,
491
639
  showAgentName = false,
492
- agentName = "Paygent",
640
+ agentName = "Assistant",
493
641
  showAvatars = false,
494
642
  showUserAvatar,
495
643
  showAssistantAvatar,
@@ -504,7 +652,14 @@ function MessageRow({
504
652
  const actualShowUserAvatar = showUserAvatar !== void 0 ? showUserAvatar : showAvatars;
505
653
  const actualShowAssistantAvatar = showAssistantAvatar !== void 0 ? showAssistantAvatar : showAvatars;
506
654
  if (message.role === "user") {
507
- return /* @__PURE__ */ jsx(UserMessage, { message, animated, showAvatar: actualShowUserAvatar });
655
+ return /* @__PURE__ */ jsx(
656
+ UserMessage,
657
+ {
658
+ message,
659
+ animated,
660
+ showAvatar: actualShowUserAvatar
661
+ }
662
+ );
508
663
  }
509
664
  return /* @__PURE__ */ jsx(
510
665
  AgentMessage,
@@ -531,30 +686,58 @@ function MessageRowSkeleton({
531
686
  return /* @__PURE__ */ jsx(
532
687
  "div",
533
688
  {
534
- className: `flex ${isRightAligned ? "justify-end" : "justify-start"} animate-pulse`,
689
+ className: cn(
690
+ "flex w-full",
691
+ isRightAligned ? "justify-end" : "justify-start"
692
+ ),
535
693
  children: /* @__PURE__ */ jsxs(
536
694
  "div",
537
695
  {
538
- className: `flex flex-col ${isRightAligned ? "items-end" : "items-start"} w-full max-w-[80%]`,
696
+ className: cn(
697
+ "flex flex-col gap-2 w-full",
698
+ isRightAligned ? "items-end max-w-[70%]" : "items-start max-w-[80%]"
699
+ ),
539
700
  children: [
540
701
  /* @__PURE__ */ jsx(
541
702
  "div",
542
703
  {
543
- className: `rounded-lg p-3 ${isRightAligned ? "bg-primary/20" : "bg-muted"} w-full`,
544
- children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
545
- /* @__PURE__ */ jsx("div", { className: "h-3 bg-muted-foreground/20 rounded w-3/4" }),
546
- /* @__PURE__ */ jsx("div", { className: "h-3 bg-muted-foreground/20 rounded w-full" }),
547
- /* @__PURE__ */ jsx("div", { className: "h-3 bg-muted-foreground/20 rounded w-5/6" })
704
+ className: cn(
705
+ "rounded-2xl p-4 w-full",
706
+ isRightAligned ? "bg-primary/8 rounded-br-md" : "bg-muted/70 rounded-bl-md"
707
+ ),
708
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-2.5", children: [
709
+ /* @__PURE__ */ jsx("div", { className: "h-3 skeleton-pulse rounded-full bg-muted-foreground/10 w-[85%]" }),
710
+ /* @__PURE__ */ jsx(
711
+ "div",
712
+ {
713
+ className: "h-3 skeleton-pulse rounded-full bg-muted-foreground/10 w-full",
714
+ style: { animationDelay: "0.1s" }
715
+ }
716
+ ),
717
+ !isRightAligned && /* @__PURE__ */ jsx(
718
+ "div",
719
+ {
720
+ className: "h-3 skeleton-pulse rounded-full bg-muted-foreground/10 w-[60%]",
721
+ style: { animationDelay: "0.2s" }
722
+ }
723
+ )
548
724
  ] })
549
725
  }
550
726
  ),
551
- /* @__PURE__ */ jsx("div", { className: "h-2 bg-muted-foreground/20 rounded w-16 mt-1" })
727
+ /* @__PURE__ */ jsx(
728
+ "div",
729
+ {
730
+ className: "h-2 skeleton-pulse rounded-full bg-muted-foreground/8 w-12 mx-1",
731
+ style: { animationDelay: "0.3s" }
732
+ }
733
+ )
552
734
  ]
553
735
  }
554
736
  )
555
737
  }
556
738
  );
557
739
  }
740
+ var SCROLL_THRESHOLD = 150;
558
741
  function MessageList({
559
742
  messages,
560
743
  isLoading = false,
@@ -565,7 +748,7 @@ function MessageList({
565
748
  stage = "DEV",
566
749
  animated = false,
567
750
  showAgentName = false,
568
- agentName = "Paygent",
751
+ agentName = "Assistant",
569
752
  showAvatars = false,
570
753
  showUserAvatar,
571
754
  showAssistantAvatar,
@@ -576,106 +759,204 @@ function MessageList({
576
759
  onExecutionTraceClick,
577
760
  className
578
761
  }) {
579
- const messagesContainerRef = useRef(null);
580
- const scrollToBottom = useCallback(() => {
581
- const el = messagesContainerRef.current;
582
- if (el) {
583
- el.scrollTop = el.scrollHeight;
762
+ const scrollRef = useRef(null);
763
+ const isNearBottomRef = useRef(true);
764
+ const [showScrollBtn, setShowScrollBtn] = useState(false);
765
+ const prevMessageCountRef = useRef(messages.length);
766
+ const getDistanceFromBottom = useCallback(() => {
767
+ const el = scrollRef.current;
768
+ if (!el) return 0;
769
+ return el.scrollHeight - el.scrollTop - el.clientHeight;
770
+ }, []);
771
+ const scrollToBottom = useCallback((behavior = "smooth") => {
772
+ const el = scrollRef.current;
773
+ if (!el) return;
774
+ el.scrollTo({ top: el.scrollHeight, behavior });
775
+ }, []);
776
+ const handleScroll = useCallback(() => {
777
+ const distance = getDistanceFromBottom();
778
+ const nearBottom = distance <= SCROLL_THRESHOLD;
779
+ isNearBottomRef.current = nearBottom;
780
+ setShowScrollBtn(!nearBottom);
781
+ }, [getDistanceFromBottom]);
782
+ useEffect(() => {
783
+ const prevCount = prevMessageCountRef.current;
784
+ prevMessageCountRef.current = messages.length;
785
+ if (messages.length > prevCount && isNearBottomRef.current) {
786
+ requestAnimationFrame(() => scrollToBottom());
787
+ }
788
+ }, [messages.length, scrollToBottom]);
789
+ useEffect(() => {
790
+ const lastMsg = messages[messages.length - 1];
791
+ if (!lastMsg?.isStreaming) return;
792
+ if (!isNearBottomRef.current) return;
793
+ requestAnimationFrame(() => scrollToBottom());
794
+ });
795
+ useEffect(() => {
796
+ if (messages.length > 0) {
797
+ setTimeout(() => scrollToBottom("instant"), 50);
584
798
  }
585
799
  }, []);
586
800
  useEffect(() => {
587
- if (messages.length > 0) scrollToBottom();
588
- }, [messages, scrollToBottom]);
801
+ const handleStepsToggle = () => {
802
+ requestAnimationFrame(() => {
803
+ requestAnimationFrame(() => {
804
+ const distance = getDistanceFromBottom();
805
+ if (distance > 0 && distance < 600) {
806
+ scrollToBottom();
807
+ }
808
+ });
809
+ });
810
+ };
811
+ const el = scrollRef.current;
812
+ if (el) {
813
+ el.addEventListener("payman-steps-toggle", handleStepsToggle);
814
+ return () => el.removeEventListener("payman-steps-toggle", handleStepsToggle);
815
+ }
816
+ }, [getDistanceFromBottom, scrollToBottom]);
817
+ const handleScrollToBottom = useCallback(() => {
818
+ scrollToBottom();
819
+ }, [scrollToBottom]);
589
820
  if (isLoading) {
590
- return /* @__PURE__ */ jsx("div", { className: cn("flex-1 overflow-y-auto min-h-0", className), children: /* @__PURE__ */ jsx("div", { className: "p-4 space-y-4", children: Array.from({ length: 6 }).map((_, index) => /* @__PURE__ */ jsx(
591
- MessageRowSkeleton,
592
- {
593
- isRightAligned: index % 3 === 0
594
- },
595
- `skeleton-${index}`
596
- )) }) });
597
- }
598
- if (messages.length === 0) {
599
821
  return /* @__PURE__ */ jsx(
600
822
  "div",
601
823
  {
602
824
  className: cn(
603
- "flex-1 overflow-y-auto min-h-0 flex flex-col items-center justify-center h-full text-center p-4",
825
+ "flex-1 overflow-y-auto min-h-0 payman-scrollbar",
604
826
  className
605
827
  ),
606
- children: /* @__PURE__ */ jsxs(
607
- motion.div,
828
+ children: /* @__PURE__ */ jsx(
829
+ "div",
608
830
  {
609
- initial: { opacity: 0, y: 20 },
610
- animate: { opacity: 1, y: 0 },
611
- transition: {
612
- duration: 0.5,
613
- ease: "easeOut"
614
- },
615
- className: "flex flex-col items-center gap-4",
616
- children: [
617
- showEmptyStateIcon && /* @__PURE__ */ jsx("div", { className: "flex h-12 w-12 items-center justify-center", children: /* @__PURE__ */ jsx(Bot, { className: "h-6 w-6 text-muted-foreground" }) }),
618
- emptyStateText.split("\n").map((line, index) => /* @__PURE__ */ jsx(
619
- "h2",
620
- {
621
- className: cn(
622
- index === 0 ? layout === "centered" ? "text-2xl font-semibold text-primary" : "text-2xl font-semibold text-foreground" : "text-sm text-muted-foreground"
623
- ),
624
- children: line
625
- },
626
- index
627
- ))
628
- ]
831
+ className: cn(
832
+ "space-y-5",
833
+ layout === "centered" ? "max-w-2xl mx-auto px-4 py-6" : "p-4"
834
+ ),
835
+ children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsx(
836
+ MessageRowSkeleton,
837
+ {
838
+ isRightAligned: i % 3 === 0
839
+ },
840
+ `skel-${i}`
841
+ ))
629
842
  }
630
843
  )
631
844
  }
632
845
  );
633
846
  }
634
- return /* @__PURE__ */ jsx(
847
+ if (messages.length === 0) {
848
+ const lines = emptyStateText.split("\n").map((l) => l.trim()).filter(Boolean);
849
+ return /* @__PURE__ */ jsx("div", { className: cn("payman-empty-root p-8", className), children: /* @__PURE__ */ jsxs(
850
+ motion.div,
851
+ {
852
+ initial: { opacity: 0, y: 20 },
853
+ animate: { opacity: 1, y: 0 },
854
+ transition: { duration: 0.5, ease: [0.25, 0.46, 0.45, 0.94] },
855
+ className: "payman-empty-content",
856
+ children: [
857
+ showEmptyStateIcon && /* @__PURE__ */ jsx(
858
+ motion.div,
859
+ {
860
+ initial: { scale: 0.85, opacity: 0 },
861
+ animate: { scale: 1, opacity: 1 },
862
+ transition: { delay: 0.08, duration: 0.45, ease: [0.25, 0.46, 0.45, 0.94] },
863
+ className: "payman-empty-icon-wrap",
864
+ children: /* @__PURE__ */ jsx(MessageCircle, { className: "h-7 w-7 payman-empty-icon", strokeWidth: 1.5 })
865
+ }
866
+ ),
867
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1.5", children: lines.map((line, i) => /* @__PURE__ */ jsx(
868
+ motion.p,
869
+ {
870
+ initial: { opacity: 0, y: 6 },
871
+ animate: { opacity: 1, y: 0 },
872
+ transition: { delay: 0.12 + i * 0.06, duration: 0.4 },
873
+ className: i === 0 ? "payman-empty-title" : "payman-empty-subtitle",
874
+ children: line
875
+ },
876
+ i
877
+ )) })
878
+ ]
879
+ }
880
+ ) });
881
+ }
882
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
635
883
  "div",
636
884
  {
637
- ref: messagesContainerRef,
638
- className: cn("flex-1 overflow-y-auto min-h-0", className),
639
- children: /* @__PURE__ */ jsx("div", { className: cn(
640
- "space-y-4",
641
- layout === "centered" ? "max-w-2xl mx-auto px-4 py-6" : "p-4"
642
- ), children: messages.map((message) => /* @__PURE__ */ jsx(
643
- MessageRow,
644
- {
645
- message,
646
- stage,
647
- animated,
648
- showAgentName,
649
- agentName,
650
- showAvatars,
651
- showUserAvatar,
652
- showAssistantAvatar,
653
- layout,
654
- showTimestamps,
655
- showExecutionSteps,
656
- showStreamingDot,
657
- streamingStepsText,
658
- completedStepsText,
659
- onExecutionTraceClick
660
- },
661
- message.id
662
- )) })
885
+ ref: scrollRef,
886
+ onScroll: handleScroll,
887
+ className: cn(
888
+ "flex-1 min-h-0 overflow-y-auto payman-scrollbar relative",
889
+ className
890
+ ),
891
+ children: [
892
+ /* @__PURE__ */ jsx(
893
+ "div",
894
+ {
895
+ className: cn(
896
+ "space-y-4",
897
+ layout === "centered" ? "max-w-2xl mx-auto px-4 py-6" : "p-4"
898
+ ),
899
+ children: messages.map((message) => /* @__PURE__ */ jsx(
900
+ MessageRow,
901
+ {
902
+ message,
903
+ stage,
904
+ animated,
905
+ showAgentName,
906
+ agentName,
907
+ showAvatars,
908
+ showUserAvatar,
909
+ showAssistantAvatar,
910
+ layout,
911
+ showTimestamps,
912
+ showExecutionSteps,
913
+ showStreamingDot,
914
+ streamingStepsText,
915
+ completedStepsText,
916
+ onExecutionTraceClick
917
+ },
918
+ message.id
919
+ ))
920
+ }
921
+ ),
922
+ showScrollBtn && /* @__PURE__ */ jsx("div", { className: "sticky bottom-0 z-20 flex justify-center pb-3 -mt-11", children: /* @__PURE__ */ jsx(
923
+ "button",
924
+ {
925
+ onClick: handleScrollToBottom,
926
+ className: cn(
927
+ "w-8 h-8 rounded-full",
928
+ "bg-card border border-border shadow-lg",
929
+ "flex items-center justify-center",
930
+ "hover:bg-muted active:scale-95",
931
+ "transition-all duration-150",
932
+ "cursor-pointer"
933
+ ),
934
+ "aria-label": "Scroll to bottom",
935
+ children: /* @__PURE__ */ jsx(ArrowDown, { className: "w-4 h-4 text-muted-foreground" })
936
+ }
937
+ ) })
938
+ ]
663
939
  }
664
- );
940
+ ) });
665
941
  }
666
942
  var DEFAULT_MAX_LENGTH = 6;
667
943
  var MAX_SUPPORTED_LENGTH = 12;
668
944
  var AUTO_FOCUS_DELAY_MS = 250;
669
- function OtpInput({ value, onChange, maxLength, disabled = false }) {
945
+ function OtpInput({
946
+ value,
947
+ onChange,
948
+ maxLength,
949
+ disabled = false
950
+ }) {
670
951
  const inputRefs = useRef([]);
671
952
  const safeMaxLength = Number.isInteger(maxLength) && maxLength > 0 ? Math.min(maxLength, MAX_SUPPORTED_LENGTH) : DEFAULT_MAX_LENGTH;
672
953
  const digits = value.split("").concat(Array(safeMaxLength).fill("")).slice(0, safeMaxLength);
673
954
  useEffect(() => {
674
955
  if (disabled) return;
675
- const focusTimer = window.setTimeout(() => {
956
+ const timer = window.setTimeout(() => {
676
957
  inputRefs.current[0]?.focus();
677
958
  }, AUTO_FOCUS_DELAY_MS);
678
- return () => window.clearTimeout(focusTimer);
959
+ return () => window.clearTimeout(timer);
679
960
  }, [disabled]);
680
961
  const focusInput = (index) => {
681
962
  if (index >= 0 && index < safeMaxLength) {
@@ -690,9 +971,7 @@ function OtpInput({ value, onChange, maxLength, disabled = false }) {
690
971
  const newDigits = [...digits];
691
972
  newDigits[index] = char;
692
973
  updateValue(newDigits);
693
- if (char && index < safeMaxLength - 1) {
694
- focusInput(index + 1);
695
- }
974
+ if (char && index < safeMaxLength - 1) focusInput(index + 1);
696
975
  };
697
976
  const handleKeyDown = (index, e) => {
698
977
  if (e.key === "Enter") {
@@ -729,7 +1008,7 @@ function OtpInput({ value, onChange, maxLength, disabled = false }) {
729
1008
  updateValue(newDigits);
730
1009
  focusInput(Math.min(pasted.length, safeMaxLength - 1));
731
1010
  };
732
- return /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "8px", justifyContent: "center" }, children: digits.map((digit, i) => /* @__PURE__ */ jsx(
1011
+ return /* @__PURE__ */ jsx("div", { className: "flex gap-2.5 justify-center", children: digits.map((digit, i) => /* @__PURE__ */ jsx(
733
1012
  "input",
734
1013
  {
735
1014
  ref: (el) => {
@@ -745,7 +1024,12 @@ function OtpInput({ value, onChange, maxLength, disabled = false }) {
745
1024
  onPaste: handlePaste,
746
1025
  onFocus: (e) => e.target.select(),
747
1026
  "aria-label": `Digit ${i + 1}`,
748
- className: `w-10 h-12 text-center text-xl font-semibold border border-border rounded-lg outline-none text-foreground ${disabled ? "bg-muted opacity-50" : "bg-transparent"}`
1027
+ className: cn(
1028
+ "w-12 h-14 text-center text-xl font-semibold rounded-xl",
1029
+ "payman-otp-input otp-input",
1030
+ "transition-all duration-150 cursor-text",
1031
+ disabled && "cursor-not-allowed"
1032
+ )
749
1033
  },
750
1034
  i
751
1035
  )) });
@@ -814,9 +1098,10 @@ function UserActionModal({
814
1098
  }, [isOpen, resetActionState]);
815
1099
  useEffect(() => {
816
1100
  if (resendCooldownRemaining <= 0) return;
817
- const timer = setTimeout(() => {
818
- setResendCooldownRemaining((prev) => prev - 1);
819
- }, 1e3);
1101
+ const timer = setTimeout(
1102
+ () => setResendCooldownRemaining((p) => p - 1),
1103
+ 1e3
1104
+ );
820
1105
  return () => clearTimeout(timer);
821
1106
  }, [resendCooldownRemaining]);
822
1107
  useEffect(() => {
@@ -828,9 +1113,7 @@ function UserActionModal({
828
1113
  useEffect(() => {
829
1114
  if (!isOpen || !isSubmitting) return;
830
1115
  if (actionType !== "approve" && actionType !== "reject") return;
831
- const timeout = setTimeout(() => {
832
- resetActionState();
833
- }, ACTION_PENDING_TIMEOUT_MS);
1116
+ const timeout = setTimeout(resetActionState, ACTION_PENDING_TIMEOUT_MS);
834
1117
  return () => clearTimeout(timeout);
835
1118
  }, [isOpen, isSubmitting, actionType, resetActionState]);
836
1119
  useEffect(() => {
@@ -840,39 +1123,39 @@ function UserActionModal({
840
1123
  previousFocusedRef.current = document.activeElement;
841
1124
  const originalOverflow = document.body.style.overflow;
842
1125
  document.body.style.overflow = "hidden";
843
- const getFocusableElements = () => Array.from(
1126
+ const getFocusable = () => Array.from(
844
1127
  dialog.querySelectorAll(
845
1128
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
846
1129
  )
847
1130
  ).filter((el) => !el.hasAttribute("disabled") && el.tabIndex !== -1);
848
- const focusables = getFocusableElements();
1131
+ const focusables = getFocusable();
849
1132
  (focusables[0] ?? dialog).focus();
850
- const handleKeyDown = (event) => {
851
- if (event.key === "Escape") {
852
- event.preventDefault();
1133
+ const handleKeyDown = (e) => {
1134
+ if (e.key === "Escape") {
1135
+ e.preventDefault();
853
1136
  return;
854
1137
  }
855
- if (event.key !== "Tab") return;
856
- const currentFocusables = getFocusableElements();
857
- if (currentFocusables.length === 0) {
858
- event.preventDefault();
1138
+ if (e.key !== "Tab") return;
1139
+ const items = getFocusable();
1140
+ if (!items.length) {
1141
+ e.preventDefault();
859
1142
  dialog.focus();
860
1143
  return;
861
1144
  }
862
- const first = currentFocusables[0];
863
- const last = currentFocusables[currentFocusables.length - 1];
864
- const activeElement = document.activeElement;
865
- const isInDialog = activeElement ? dialog.contains(activeElement) : false;
866
- if (event.shiftKey) {
867
- if (!isInDialog || activeElement === first) {
868
- event.preventDefault();
1145
+ const first = items[0];
1146
+ const last = items[items.length - 1];
1147
+ const active = document.activeElement;
1148
+ const inDialog = active ? dialog.contains(active) : false;
1149
+ if (e.shiftKey) {
1150
+ if (!inDialog || active === first) {
1151
+ e.preventDefault();
869
1152
  last.focus();
870
1153
  }
871
- return;
872
- }
873
- if (!isInDialog || activeElement === last) {
874
- event.preventDefault();
875
- first.focus();
1154
+ } else {
1155
+ if (!inDialog || active === last) {
1156
+ e.preventDefault();
1157
+ first.focus();
1158
+ }
876
1159
  }
877
1160
  };
878
1161
  document.addEventListener("keydown", handleKeyDown);
@@ -916,155 +1199,191 @@ function UserActionModal({
916
1199
  }, [resendCooldownRemaining, onResend]);
917
1200
  if (!isOpen || !userActionRequest) return null;
918
1201
  const isOtpValid = otp.length === schema.maxLength && /^\d+$/.test(otp);
919
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
1202
+ return /* @__PURE__ */ jsxs(
920
1203
  "div",
921
1204
  {
1205
+ className: "payman-modal-backdrop",
922
1206
  style: {
923
1207
  position: "fixed",
924
1208
  inset: 0,
925
- backgroundColor: "rgba(0, 0, 0, 0.5)",
926
1209
  zIndex: 1e4,
927
1210
  display: "flex",
928
1211
  alignItems: "center",
929
- justifyContent: "center"
1212
+ justifyContent: "center",
1213
+ padding: 16,
1214
+ boxSizing: "border-box"
930
1215
  },
931
- children: /* @__PURE__ */ jsxs(
932
- "div",
933
- {
934
- ref: dialogRef,
935
- role: "dialog",
936
- "aria-modal": "true",
937
- className: "bg-card rounded-xl p-6 shadow-2xl",
938
- style: { width: "100%", maxWidth: "420px", margin: "0 16px" },
939
- tabIndex: -1,
940
- children: [
941
- /* @__PURE__ */ jsxs("div", { className: "text-center mb-4", children: [
942
- /* @__PURE__ */ jsx("div", { className: "inline-flex items-center justify-center w-12 h-12 rounded-full bg-muted mb-3", children: /* @__PURE__ */ jsx(ShieldCheck, { className: "w-6 h-6 text-primary" }) }),
943
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-foreground m-0", children: MODAL_CONTENT.TITLE })
944
- ] }),
945
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground text-center mb-6 leading-relaxed", children: userActionRequest.message }),
946
- /* @__PURE__ */ jsx("div", { className: "mb-6", children: /* @__PURE__ */ jsx(
947
- OtpInput,
948
- {
949
- value: otp,
950
- onChange: setOtp,
951
- maxLength: schema.maxLength,
952
- disabled: isSubmitting
953
- }
954
- ) }),
955
- /* @__PURE__ */ jsxs(
1216
+ children: [
1217
+ /* @__PURE__ */ jsx(
1218
+ "div",
1219
+ {
1220
+ className: "absolute inset-0 backdrop-blur-sm payman-modal-backdrop-overlay payman-modal-animate",
1221
+ "aria-hidden": true,
1222
+ style: {
1223
+ background: "var(--payman-modal-backdrop-bg, rgba(0, 0, 0, 0.4))"
1224
+ }
1225
+ }
1226
+ ),
1227
+ /* @__PURE__ */ jsx(
1228
+ "div",
1229
+ {
1230
+ style: {
1231
+ flexShrink: 0,
1232
+ width: "100%",
1233
+ maxWidth: 400,
1234
+ minWidth: 0
1235
+ },
1236
+ children: /* @__PURE__ */ jsxs(
956
1237
  "div",
957
1238
  {
1239
+ ref: dialogRef,
1240
+ role: "dialog",
1241
+ "aria-modal": "true",
1242
+ "aria-labelledby": "payman-modal-title",
1243
+ className: cn(
1244
+ "relative w-full rounded-2xl p-6 shadow-2xl",
1245
+ "payman-modal-dialog payman-modal-animate"
1246
+ ),
958
1247
  style: {
959
- display: "flex",
960
- flexDirection: "column",
961
- gap: "10px"
1248
+ width: "100%",
1249
+ maxWidth: 400,
1250
+ boxSizing: "border-box",
1251
+ background: "var(--payman-modal-dialog-bg, var(--payman-card, #ffffff))",
1252
+ border: "1px solid var(--payman-modal-dialog-border, var(--payman-border, #e4e4e7))"
962
1253
  },
1254
+ tabIndex: -1,
963
1255
  children: [
964
- /* @__PURE__ */ jsxs(
965
- "div",
966
- {
967
- style: {
968
- display: "flex",
969
- gap: "8px"
970
- },
971
- children: [
972
- /* @__PURE__ */ jsxs(
973
- "button",
974
- {
975
- onClick: handleReject,
976
- disabled: isSubmitting,
977
- className: cn(
978
- "rounded-lg font-medium transition-colors cursor-pointer",
979
- "bg-destructive/10 text-destructive hover:bg-destructive/30 hover:text-destructive",
980
- "disabled:opacity-50 disabled:cursor-not-allowed"
981
- ),
982
- style: {
983
- flex: 1,
984
- display: "flex",
985
- alignItems: "center",
986
- justifyContent: "center",
987
- gap: "6px",
988
- whiteSpace: "nowrap",
989
- minWidth: 0,
990
- padding: "10px 16px",
991
- fontSize: "14px",
992
- lineHeight: "20px",
993
- border: "none"
994
- },
995
- children: [
996
- actionType === "reject" && /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 animate-spin", style: { flexShrink: 0 } }),
997
- actionType === "reject" ? MODAL_CONTENT.LOADING_REJECT : BUTTON_LABELS.REJECT
998
- ]
999
- }
1000
- ),
1001
- /* @__PURE__ */ jsxs(
1002
- "button",
1256
+ /* @__PURE__ */ jsxs("div", { className: "text-center mb-5", children: [
1257
+ /* @__PURE__ */ jsx(
1258
+ "div",
1259
+ {
1260
+ className: "inline-flex items-center justify-center w-12 h-12 rounded-2xl payman-modal-icon-wrap mb-3",
1261
+ style: {
1262
+ background: "var(--payman-modal-icon-bg, color-mix(in srgb, var(--payman-primary, #18181b) 10%, transparent))",
1263
+ border: "1px solid var(--payman-modal-icon-border, color-mix(in srgb, var(--payman-primary, #18181b) 20%, transparent))"
1264
+ },
1265
+ children: /* @__PURE__ */ jsx(
1266
+ ShieldCheck,
1003
1267
  {
1004
- onClick: handleApprove,
1005
- disabled: !isOtpValid || isSubmitting,
1006
- className: cn(
1007
- "rounded-lg font-medium transition-colors cursor-pointer",
1008
- "bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80",
1009
- "disabled:opacity-50 disabled:cursor-not-allowed"
1010
- ),
1268
+ className: "w-6 h-6 payman-modal-icon",
1011
1269
  style: {
1012
- flex: 1,
1013
- display: "flex",
1014
- alignItems: "center",
1015
- justifyContent: "center",
1016
- gap: "6px",
1017
- whiteSpace: "nowrap",
1018
- minWidth: 0,
1019
- padding: "10px 16px",
1020
- fontSize: "14px",
1021
- lineHeight: "20px",
1022
- border: "none"
1023
- },
1024
- children: [
1025
- actionType === "approve" && /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 animate-spin", style: { flexShrink: 0 } }),
1026
- actionType === "approve" ? MODAL_CONTENT.LOADING_APPROVE : BUTTON_LABELS.APPROVE
1027
- ]
1270
+ color: "var(--payman-modal-icon-fg, var(--payman-primary, #18181b))"
1271
+ }
1028
1272
  }
1029
1273
  )
1030
- ]
1031
- }
1032
- ),
1033
- /* @__PURE__ */ jsxs(
1034
- "button",
1274
+ }
1275
+ ),
1276
+ /* @__PURE__ */ jsx(
1277
+ "h2",
1278
+ {
1279
+ id: "payman-modal-title",
1280
+ className: "text-lg font-semibold payman-modal-title tracking-tight",
1281
+ style: {
1282
+ color: "var(--payman-modal-title-fg, var(--payman-foreground, #18181b))"
1283
+ },
1284
+ children: MODAL_CONTENT.TITLE
1285
+ }
1286
+ )
1287
+ ] }),
1288
+ /* @__PURE__ */ jsx(
1289
+ "p",
1035
1290
  {
1036
- onClick: handleResend,
1037
- disabled: isSubmitting || resendCooldownRemaining > 0,
1038
- className: cn(
1039
- "rounded-lg font-medium transition-colors cursor-pointer",
1040
- "border border-border text-muted-foreground hover:text-foreground hover:border-muted-foreground/40 hover:bg-muted/50",
1041
- "disabled:opacity-50 disabled:cursor-not-allowed"
1042
- ),
1291
+ className: "text-sm payman-modal-desc text-center mb-6 leading-relaxed px-0.5",
1043
1292
  style: {
1044
- display: "flex",
1045
- alignItems: "center",
1046
- justifyContent: "center",
1047
- gap: "6px",
1048
- whiteSpace: "nowrap",
1049
- width: "100%",
1050
- padding: "8px 16px",
1051
- fontSize: "12px",
1052
- lineHeight: "16px"
1293
+ color: "var(--payman-modal-desc-fg, var(--payman-muted-foreground, #71717a))"
1053
1294
  },
1054
- children: [
1055
- actionType === "resend" && /* @__PURE__ */ jsx(Loader2, { className: "w-3.5 h-3.5 animate-spin", style: { flexShrink: 0 } }),
1056
- actionType === "resend" ? MODAL_CONTENT.LOADING_RESEND : resendCooldownRemaining > 0 ? `${MODAL_CONTENT.RESEND_AVAILABLE_IN} ${resendCooldownRemaining}s` : BUTTON_LABELS.RESEND
1057
- ]
1295
+ children: userActionRequest.message
1058
1296
  }
1059
- )
1297
+ ),
1298
+ /* @__PURE__ */ jsx("div", { className: "mb-6", children: /* @__PURE__ */ jsx(
1299
+ OtpInput,
1300
+ {
1301
+ value: otp,
1302
+ onChange: setOtp,
1303
+ maxLength: schema.maxLength,
1304
+ disabled: isSubmitting
1305
+ }
1306
+ ) }),
1307
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
1308
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5", children: [
1309
+ /* @__PURE__ */ jsxs(
1310
+ "button",
1311
+ {
1312
+ type: "button",
1313
+ onClick: handleReject,
1314
+ disabled: isSubmitting,
1315
+ className: cn(
1316
+ "flex-1 flex items-center justify-center gap-2",
1317
+ "min-h-[48px] py-3 px-4 rounded-xl text-sm font-medium border-0 cursor-pointer",
1318
+ "payman-modal-btn-reject",
1319
+ "active:scale-[0.98] transition-all duration-150",
1320
+ "disabled:cursor-not-allowed"
1321
+ ),
1322
+ style: {
1323
+ background: "var(--payman-modal-btn-reject-bg, color-mix(in srgb, var(--payman-destructive, #ef4444) 10%, transparent))",
1324
+ color: "var(--payman-modal-btn-reject-fg, var(--payman-destructive, #ef4444))"
1325
+ },
1326
+ children: [
1327
+ actionType === "reject" ? /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 animate-spin shrink-0" }) : /* @__PURE__ */ jsx(X, { className: "w-4 h-4 shrink-0", strokeWidth: 2.5 }),
1328
+ actionType === "reject" ? MODAL_CONTENT.LOADING_REJECT : BUTTON_LABELS.REJECT
1329
+ ]
1330
+ }
1331
+ ),
1332
+ /* @__PURE__ */ jsxs(
1333
+ "button",
1334
+ {
1335
+ type: "button",
1336
+ onClick: handleApprove,
1337
+ disabled: !isOtpValid || isSubmitting,
1338
+ className: cn(
1339
+ "flex-1 flex items-center justify-center gap-2",
1340
+ "min-h-[48px] py-3 px-4 rounded-xl text-sm font-medium border-0 cursor-pointer",
1341
+ "payman-modal-btn-approve",
1342
+ "active:scale-[0.98] transition-all duration-150",
1343
+ "disabled:cursor-not-allowed"
1344
+ ),
1345
+ style: {
1346
+ background: "var(--payman-modal-btn-approve-bg, var(--payman-primary, #18181b))",
1347
+ color: "var(--payman-modal-btn-approve-fg, var(--payman-primary-foreground, #fafafa))"
1348
+ },
1349
+ children: [
1350
+ actionType === "approve" ? /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 animate-spin shrink-0" }) : /* @__PURE__ */ jsx(Check, { className: "w-4 h-4 shrink-0", strokeWidth: 2.5 }),
1351
+ actionType === "approve" ? MODAL_CONTENT.LOADING_APPROVE : BUTTON_LABELS.APPROVE
1352
+ ]
1353
+ }
1354
+ )
1355
+ ] }),
1356
+ /* @__PURE__ */ jsxs(
1357
+ "button",
1358
+ {
1359
+ type: "button",
1360
+ onClick: handleResend,
1361
+ disabled: isSubmitting || resendCooldownRemaining > 0,
1362
+ className: cn(
1363
+ "w-full flex items-center justify-center gap-2",
1364
+ "min-h-[48px] py-3 px-4 rounded-xl text-sm font-medium cursor-pointer",
1365
+ "payman-modal-btn-resend",
1366
+ "transition-all duration-150 disabled:cursor-not-allowed"
1367
+ ),
1368
+ style: {
1369
+ border: "1px solid var(--payman-modal-btn-resend-border, var(--payman-border, #e4e4e7))",
1370
+ color: "var(--payman-modal-btn-resend-fg, var(--payman-muted-foreground, #71717a))"
1371
+ },
1372
+ children: [
1373
+ actionType === "resend" ? /* @__PURE__ */ jsx(Loader2, { className: "w-4 h-4 animate-spin shrink-0" }) : /* @__PURE__ */ jsx(RefreshCw, { className: "w-4 h-4 shrink-0", strokeWidth: 2.5 }),
1374
+ actionType === "resend" ? MODAL_CONTENT.LOADING_RESEND : resendCooldownRemaining > 0 ? `${MODAL_CONTENT.RESEND_AVAILABLE_IN} ${resendCooldownRemaining}s` : BUTTON_LABELS.RESEND
1375
+ ]
1376
+ }
1377
+ )
1378
+ ] })
1060
1379
  ]
1061
1380
  }
1062
1381
  )
1063
- ]
1064
- }
1065
- )
1382
+ }
1383
+ )
1384
+ ]
1066
1385
  }
1067
- ) });
1386
+ );
1068
1387
  }
1069
1388
  var DEFAULT_USER_ACTION_STATE = {
1070
1389
  request: null,
@@ -1193,7 +1512,7 @@ function PaymanChat({
1193
1512
  "div",
1194
1513
  {
1195
1514
  className: cn(
1196
- "bg-card border border-border rounded-lg overflow-hidden flex flex-col flex-[4]",
1515
+ "bg-card overflow-hidden flex flex-col flex-[4]",
1197
1516
  className
1198
1517
  ),
1199
1518
  style,
@@ -1208,7 +1527,7 @@ function PaymanChat({
1208
1527
  "div",
1209
1528
  {
1210
1529
  className: cn(
1211
- "bg-card border border-border rounded-lg overflow-hidden flex flex-col flex-[4]",
1530
+ "bg-card overflow-hidden flex flex-col flex-[4]",
1212
1531
  className
1213
1532
  ),
1214
1533
  style,
@@ -1223,7 +1542,7 @@ function PaymanChat({
1223
1542
  "div",
1224
1543
  {
1225
1544
  className: cn(
1226
- "bg-card border border-border rounded-lg overflow-hidden flex flex-col flex-[4]",
1545
+ "bg-card overflow-hidden flex flex-col flex-[4]",
1227
1546
  className
1228
1547
  ),
1229
1548
  style,