@paymanai/payman-ask-sdk 1.2.2 → 1.2.4

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