@paymanai/payman-ask-sdk 1.2.26 → 2.0.0

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.
@@ -1,18 +1,17 @@
1
1
  'use strict';
2
2
 
3
- var paymanTypescriptAskSdk = require('@paymanai/payman-typescript-ask-sdk');
4
- var React = require('react');
3
+ var react = require('react');
5
4
  var reactNative = require('react-native');
6
- var lucideReactNative = require('lucide-react-native');
7
5
  var jsxRuntime = require('react/jsx-runtime');
8
- var Animated2 = require('react-native-reanimated');
9
- var Markdown = require('react-native-markdown-display');
6
+ var lucideReact = require('lucide-react');
7
+ var framerMotion = require('framer-motion');
8
+ var reactDom = require('react-dom');
10
9
  var clsx = require('clsx');
11
10
  var tailwindMerge = require('tailwind-merge');
11
+ var paymanTypescriptAskSdk = require('@paymanai/payman-typescript-ask-sdk');
12
+ var lucideReactNative = require('lucide-react-native');
12
13
  var Sentry = require('@sentry/react');
13
14
 
14
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
-
16
15
  function _interopNamespace(e) {
17
16
  if (e && e.__esModule) return e;
18
17
  var n = Object.create(null);
@@ -31,1475 +30,919 @@ function _interopNamespace(e) {
31
30
  return Object.freeze(n);
32
31
  }
33
32
 
34
- var React__default = /*#__PURE__*/_interopDefault(React);
35
- var Animated2__default = /*#__PURE__*/_interopDefault(Animated2);
36
- var Markdown__default = /*#__PURE__*/_interopDefault(Markdown);
37
33
  var Sentry__namespace = /*#__PURE__*/_interopNamespace(Sentry);
38
34
 
39
35
  // src/components/PaymanChat/index.native.tsx
40
- var PaymanChatContext = React.createContext(void 0);
41
- function usePaymanChat() {
42
- const context = React.useContext(PaymanChatContext);
43
- if (!context) {
44
- throw new Error("usePaymanChat must be used within a PaymanChat component");
45
- }
46
- return context;
47
- }
48
- if (reactNative.Platform.OS === "android" && reactNative.UIManager.setLayoutAnimationEnabledExperimental) {
49
- reactNative.UIManager.setLayoutAnimationEnabledExperimental(true);
50
- }
51
- var VOICE_DOT_COUNT = 24;
52
- var VOICE_DOT_MIN_H = 5;
53
- var VOICE_DOT_MAX_H = 14;
54
- var SPEECH_ACTIVITY_MS = 1400;
55
- var VOICE_THEME_COLOR = "#00858d";
56
- var VOICE_THEME_RGBA = "rgba(0, 133, 141, 0.6)";
57
- var VOICE_THEME_RGBA_LIGHT = "rgba(0, 133, 141, 0.2)";
58
- var VOICE_BAR_BG = "#f0f9f9";
59
- var VOICE_BAR_BORDER = "rgba(0, 133, 141, 0.35)";
60
- var AI_DISCLAIMER_TEXT = "AI can make mistakes. Please double-check responses.";
61
- var VoiceWaveformBar = React.memo(
62
- ({
63
- index,
64
- isActive,
65
- barColor
66
- }) => {
67
- const height = React.useRef(new reactNative.Animated.Value(VOICE_DOT_MIN_H)).current;
68
- React.useEffect(() => {
69
- if (isActive) {
70
- const delay = index * 25;
71
- const duration = 180 + index % 3 * 40;
72
- const animation = reactNative.Animated.loop(
73
- reactNative.Animated.sequence([
74
- reactNative.Animated.timing(height, {
75
- toValue: VOICE_DOT_MAX_H,
76
- duration,
77
- delay,
78
- easing: reactNative.Easing.inOut(reactNative.Easing.ease),
79
- useNativeDriver: false
80
- }),
81
- reactNative.Animated.timing(height, {
82
- toValue: VOICE_DOT_MIN_H,
83
- duration,
84
- easing: reactNative.Easing.inOut(reactNative.Easing.ease),
85
- useNativeDriver: false
86
- })
87
- ])
88
- );
89
- animation.start();
90
- return () => animation.stop();
91
- } else {
92
- reactNative.Animated.timing(height, {
93
- toValue: VOICE_DOT_MIN_H,
94
- duration: 200,
95
- useNativeDriver: false
96
- }).start();
97
- }
98
- }, [isActive, index, height]);
99
- return /* @__PURE__ */ jsxRuntime.jsx(
100
- reactNative.Animated.View,
101
- {
102
- style: [
103
- s.voiceDotBase,
104
- {
105
- height,
106
- backgroundColor: barColor
107
- }
108
- ]
109
- }
110
- );
111
- }
112
- );
113
- VoiceWaveformBar.displayName = "VoiceWaveformBar";
114
- function formatDuration(seconds) {
115
- const m = Math.floor(seconds / 60);
116
- const s9 = seconds % 60;
117
- return `${m}:${s9.toString().padStart(2, "0")}`;
118
- }
119
- function ChatInput({
120
- value,
121
- onChange,
122
- onSend,
123
- onPause,
124
- disabled = false,
125
- placeholder = "Chat with Claude",
126
- isWaitingForResponse = false,
127
- hasSelectedSession = true,
128
- isSessionParamsConfigured = true,
129
- onClick,
130
- inputStyle = "rounded",
131
- layout = "full-width",
132
- enableVoice = false,
133
- onVoicePress,
134
- voiceAvailable = false,
135
- isRecording = false,
136
- recordingDurationSeconds = 0,
137
- onConfirmRecording,
138
- onCancelRecording,
139
- transcribedText,
140
- onInputFocus
141
- }) {
142
- const isInputDisabled = disabled || isWaitingForResponse;
143
- const showVoiceButton = enableVoice && onVoicePress != null;
144
- const isVoiceButtonDisabled = isWaitingForResponse || !voiceAvailable || !isSessionParamsConfigured;
145
- const canSend = !isInputDisabled && value.trim().length > 0;
146
- const showVoiceBar = enableVoice && isRecording && onConfirmRecording != null && onCancelRecording != null;
147
- const [isUserSpeaking, setIsUserSpeaking] = React.useState(false);
148
- const speechTimeoutRef = React.useRef(null);
149
- const prevTranscriptRef = React.useRef("");
150
- React.useEffect(() => {
151
- if (!showVoiceBar) {
152
- setIsUserSpeaking(false);
153
- prevTranscriptRef.current = "";
154
- if (speechTimeoutRef.current) {
155
- clearTimeout(speechTimeoutRef.current);
156
- speechTimeoutRef.current = null;
157
- }
158
- return;
159
- }
160
- const current = transcribedText ?? "";
161
- const changed = current !== prevTranscriptRef.current;
162
- prevTranscriptRef.current = current;
163
- if (changed) {
164
- setIsUserSpeaking(true);
165
- if (speechTimeoutRef.current) clearTimeout(speechTimeoutRef.current);
166
- speechTimeoutRef.current = setTimeout(() => {
167
- speechTimeoutRef.current = null;
168
- setIsUserSpeaking(false);
169
- }, SPEECH_ACTIVITY_MS);
170
- }
171
- return () => {
172
- if (speechTimeoutRef.current) {
173
- clearTimeout(speechTimeoutRef.current);
174
- speechTimeoutRef.current = null;
175
- }
176
- };
177
- }, [showVoiceBar, transcribedText]);
178
- const animOpacity = React.useRef(new reactNative.Animated.Value(1)).current;
179
- const animScale = React.useRef(new reactNative.Animated.Value(1)).current;
180
- const inputRef = React.useRef(null);
181
- React.useEffect(() => {
182
- if (showVoiceBar) {
183
- reactNative.LayoutAnimation.configureNext(
184
- reactNative.LayoutAnimation.create(200, "easeInEaseOut", "opacity")
185
- );
186
- animOpacity.setValue(0.92);
187
- animScale.setValue(0.98);
188
- reactNative.Animated.parallel([
189
- reactNative.Animated.timing(animOpacity, {
190
- toValue: 1,
191
- duration: 200,
192
- useNativeDriver: true
193
- }),
194
- reactNative.Animated.spring(animScale, {
195
- toValue: 1,
196
- useNativeDriver: true,
197
- speed: 12,
198
- bounciness: 4
199
- })
200
- ]).start();
201
- } else {
202
- reactNative.LayoutAnimation.configureNext(
203
- reactNative.LayoutAnimation.create(220, "easeInEaseOut", "opacity")
204
- );
205
- animOpacity.setValue(1);
206
- animScale.setValue(1);
207
- }
208
- }, [showVoiceBar, animOpacity, animScale]);
209
- const getPlaceholder = () => {
210
- if (!hasSelectedSession) return "Select a version to start chatting";
211
- if (!isSessionParamsConfigured)
212
- return "Configure User ID and User Label in Session Params";
213
- return placeholder;
214
- };
215
- const handleSubmit = () => {
216
- if (canSend) onSend();
217
- };
218
- const handleConfirmRecording = () => {
219
- reactNative.LayoutAnimation.configureNext(
220
- reactNative.LayoutAnimation.create(220, "easeOut", "opacity")
221
- );
222
- onConfirmRecording?.();
223
- };
224
- const handleCancelRecording = () => {
225
- reactNative.LayoutAnimation.configureNext(
226
- reactNative.LayoutAnimation.create(200, "easeIn", "opacity")
227
- );
228
- onCancelRecording?.();
229
- };
230
- return /* @__PURE__ */ jsxRuntime.jsxs(
231
- reactNative.View,
232
- {
233
- style: [
234
- s.outerBar,
235
- layout === "centered" && s.outerBarCentered,
236
- showVoiceBar && s.outerBarVoiceMargin
237
- ],
238
- children: [
239
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s.column, children: [
240
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [s.inputWrap, showVoiceBar && s.inputWrapRelative], children: [
241
- /* @__PURE__ */ jsxRuntime.jsx(
242
- reactNative.TextInput,
243
- {
244
- ref: inputRef,
245
- value,
246
- onChangeText: onChange,
247
- onFocus: onInputFocus,
248
- onPress: onClick,
249
- editable: !isInputDisabled,
250
- placeholder: getPlaceholder(),
251
- placeholderTextColor: "#9CA3AF",
252
- multiline: true,
253
- style: [
254
- s.input,
255
- isInputDisabled && s.inputDisabled,
256
- showVoiceBar && s.inputHiddenKeepFocus
257
- ],
258
- returnKeyType: "default",
259
- blurOnSubmit: false,
260
- onSubmitEditing: handleSubmit
261
- }
262
- ),
263
- showVoiceBar && /* @__PURE__ */ jsxRuntime.jsx(
264
- reactNative.Animated.View,
265
- {
266
- pointerEvents: "box-none",
267
- style: [
268
- s.voiceBarContainer,
269
- {
270
- opacity: animOpacity,
271
- transform: [{ scale: animScale }]
272
- }
273
- ],
274
- children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s.voiceBar, children: [
275
- /* @__PURE__ */ jsxRuntime.jsx(
276
- reactNative.Pressable,
277
- {
278
- onPress: handleCancelRecording,
279
- style: ({ pressed }) => [
280
- s.voiceBarCancelBtn,
281
- pressed && s.btnPressed
282
- ],
283
- accessibilityLabel: "Cancel recording",
284
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.X, { size: 20, color: VOICE_THEME_COLOR, strokeWidth: 2.5 })
285
- }
286
- ),
287
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s.voiceBarCenter, children: [
288
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s.voiceBarDots, children: Array.from({ length: VOICE_DOT_COUNT }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
289
- VoiceWaveformBar,
290
- {
291
- index: i,
292
- isActive: isUserSpeaking,
293
- barColor: VOICE_THEME_RGBA
294
- },
295
- i
296
- )) }),
297
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s.voiceBarTimer, children: formatDuration(recordingDurationSeconds) })
298
- ] }),
299
- /* @__PURE__ */ jsxRuntime.jsx(
300
- reactNative.Pressable,
301
- {
302
- onPress: handleConfirmRecording,
303
- style: ({ pressed }) => [
304
- s.voiceBarConfirmBtn,
305
- pressed && s.btnPressed
306
- ],
307
- accessibilityLabel: "Confirm and send recording",
308
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Check, { size: 22, color: VOICE_THEME_COLOR, strokeWidth: 2.5 })
309
- }
310
- )
311
- ] })
312
- }
313
- )
314
- ] }),
315
- !showVoiceBar && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s.actionsRow, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s.rightActions, children: [
316
- showVoiceButton && /* @__PURE__ */ jsxRuntime.jsx(
317
- reactNative.Pressable,
318
- {
319
- onPress: onVoicePress,
320
- disabled: isVoiceButtonDisabled,
321
- style: ({ pressed }) => [
322
- s.iconBtn,
323
- pressed && s.btnPressed,
324
- isVoiceButtonDisabled && s.btnDisabled
325
- ],
326
- accessibilityLabel: "Voice input",
327
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s.iconBtnInner, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Mic, { size: 22, color: "#6B7280", strokeWidth: 2 }) })
328
- }
329
- ),
330
- /* @__PURE__ */ jsxRuntime.jsx(
331
- reactNative.Pressable,
332
- {
333
- onPress: onSend,
334
- disabled: !canSend,
335
- style: ({ pressed }) => [
336
- s.sendBtn,
337
- !canSend && s.btnDisabled,
338
- pressed && s.btnPressed
339
- ],
340
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s.sendBtnInner, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Send, { size: 18, color: "#FFFFFF" }) })
341
- }
342
- )
343
- ] }) })
344
- ] }),
345
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s.disclaimerWrap, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s.disclaimerText, children: AI_DISCLAIMER_TEXT }) })
346
- ]
347
- }
348
- );
349
- }
350
- var s = reactNative.StyleSheet.create({
351
- outerBar: {
352
- width: "100%",
353
- backgroundColor: "#f1f5f9",
354
- borderTopWidth: 1,
355
- borderTopColor: "rgba(0,0,0,0.08)",
356
- paddingTop: 12,
357
- paddingBottom: reactNative.Platform.OS === "ios" ? 28 : 12,
358
- paddingHorizontal: 12,
359
- borderTopLeftRadius: 20,
360
- borderTopRightRadius: 20
361
- },
362
- outerBarCentered: { alignItems: "center" },
363
- disclaimerWrap: {
364
- maxWidth: 672,
365
- width: "100%",
366
- alignSelf: "center",
367
- paddingHorizontal: 12,
368
- marginTop: 2
369
- },
370
- disclaimerText: {
371
- fontSize: 11,
372
- lineHeight: 15,
373
- color: "#6B7280",
374
- textAlign: "center"
375
- },
376
- column: {
377
- maxWidth: 672,
378
- width: "100%",
379
- alignSelf: "center",
380
- gap: 10
381
- },
382
- actionsRow: {
383
- flexDirection: "row",
384
- justifyContent: "flex-end",
385
- alignItems: "center",
386
- marginBottom: 10
387
- },
388
- iconBtn: {
389
- width: 36,
390
- height: 36,
391
- justifyContent: "center",
36
+ var PaymanChat = react.forwardRef(function PaymanChat2(_props, _ref) {
37
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.root, children: [
38
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.title, children: "PaymanChat v2" }),
39
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.body, children: "React Native support is coming in a follow-up release. Please use the web SDK for now." })
40
+ ] });
41
+ });
42
+ var styles = reactNative.StyleSheet.create({
43
+ root: {
44
+ flex: 1,
392
45
  alignItems: "center",
393
- borderRadius: 18,
394
- overflow: "hidden"
395
- },
396
- iconBtnInner: {
397
- width: 36,
398
- height: 36,
399
46
  justifyContent: "center",
400
- alignItems: "center",
401
- backgroundColor: "rgba(0,0,0,0.1)",
402
- borderRadius: 18
403
- },
404
- inputWrap: {
405
- width: "100%",
406
- minHeight: 56,
407
- justifyContent: "center",
408
- paddingHorizontal: 4
409
- },
410
- inputWrapRelative: {
411
- position: "relative"
412
- },
413
- inputHiddenKeepFocus: {
414
- position: "absolute",
415
- left: 0,
416
- right: 0,
417
- top: 0,
418
- opacity: 0,
419
- zIndex: 0
47
+ padding: 24
420
48
  },
421
- input: {
422
- minHeight: 56,
423
- maxHeight: 140,
424
- paddingVertical: 16,
425
- paddingHorizontal: 16,
49
+ title: {
426
50
  fontSize: 16,
427
- color: "#1F2937",
428
- lineHeight: 22
429
- },
430
- inputDisabled: { opacity: 0.5 },
431
- rightActions: {
432
- flexDirection: "row",
433
- alignItems: "center",
434
- gap: 6
435
- },
436
- sendBtn: {
437
- width: 36,
438
- height: 36,
439
- borderRadius: 18,
440
- overflow: "hidden",
441
- justifyContent: "center",
442
- alignItems: "center"
443
- },
444
- sendBtnInner: {
445
- width: 36,
446
- height: 36,
447
- borderRadius: 18,
448
- backgroundColor: "#00858d",
449
- justifyContent: "center",
450
- alignItems: "center"
451
- },
452
- btnDisabled: { opacity: 0.35 },
453
- btnPressed: { opacity: 0.7 },
454
- outerBarVoiceMargin: {
455
- paddingBottom: reactNative.Platform.OS === "ios" ? 36 : 20
456
- },
457
- // Voice bar (replaces input when recording) – theme #00858d
458
- voiceBarContainer: {
459
- position: "absolute",
460
- left: 0,
461
- right: 0,
462
- top: 0,
463
- bottom: 0,
464
- justifyContent: "center",
465
- zIndex: 1
466
- },
467
- voiceBar: {
468
- flexDirection: "row",
469
- alignItems: "center",
470
- justifyContent: "space-between",
471
- backgroundColor: VOICE_BAR_BG,
472
- borderWidth: 1,
473
- borderColor: VOICE_BAR_BORDER,
474
- borderRadius: 28,
475
- minHeight: 56,
476
- paddingHorizontal: 12,
477
- paddingVertical: 10,
478
- gap: 12
479
- },
480
- voiceBarCancelBtn: {
481
- width: 40,
482
- height: 40,
483
- borderRadius: 20,
484
- backgroundColor: VOICE_THEME_RGBA_LIGHT,
485
- alignItems: "center",
486
- justifyContent: "center"
487
- },
488
- voiceBarCenter: {
489
- flex: 1,
490
- flexDirection: "row",
491
- alignItems: "center",
492
- justifyContent: "space-between",
493
- gap: 10,
494
- marginLeft: 8
495
- },
496
- voiceBarDots: {
497
- flex: 1,
498
- flexDirection: "row",
499
- alignItems: "center",
500
- justifyContent: "space-between",
501
- gap: 4
502
- },
503
- voiceDotBase: {
504
- width: 4,
505
- borderRadius: 2
506
- },
507
- voiceDot: {
508
- width: 4,
509
- height: 6,
510
- borderRadius: 2,
511
- backgroundColor: VOICE_THEME_RGBA
512
- },
513
- voiceDotActive: {
514
- height: 12,
515
- backgroundColor: VOICE_THEME_COLOR
516
- },
517
- voiceBarTimer: {
518
- fontSize: 15,
519
51
  fontWeight: "600",
520
- color: VOICE_THEME_COLOR,
521
- minWidth: 36,
522
- textAlign: "right"
52
+ marginBottom: 8
523
53
  },
524
- voiceBarConfirmBtn: {
525
- width: 40,
526
- height: 40,
527
- borderRadius: 20,
528
- backgroundColor: "#FFFFFF",
529
- borderWidth: 1,
530
- borderColor: VOICE_THEME_COLOR,
531
- alignItems: "center",
532
- justifyContent: "center"
54
+ body: {
55
+ fontSize: 13,
56
+ color: "#666",
57
+ textAlign: "center"
533
58
  }
534
59
  });
535
-
536
- // src/utils/errorMessages.ts
537
- var WORKFLOW_FAILED = "WORKFLOW_FAILED";
538
- var STREAM_NOT_STARTED = "STREAM_NOT_STARTED";
539
- var HTTP_ERROR_PREFIX = /^HTTP\s+(\d+)\s*:\s*([\s\S]+)$/;
540
- function isFriendlyWorkflowError(errorDetails) {
541
- if (!errorDetails) return false;
542
- return errorDetails === WORKFLOW_FAILED || errorDetails === STREAM_NOT_STARTED || errorDetails.includes(WORKFLOW_FAILED);
60
+ function cn(...inputs) {
61
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
543
62
  }
544
- function parseErrorPayload(payload) {
63
+ var DEFAULT_WIDTH = 280;
64
+ var DEFAULT_MIN_WIDTH = 240;
65
+ var DEFAULT_MAX_WIDTH = 480;
66
+ var DEFAULT_PAGE_SIZE = 20;
67
+ function getPersistKey(config, opts) {
68
+ if (opts.persistKey) return opts.persistKey;
69
+ return `payman-chat-sidebar:${config.workflow.id ?? "unknown"}`;
70
+ }
71
+ function loadPersistedState(key) {
72
+ if (typeof window === "undefined") return null;
545
73
  try {
546
- const parsed = JSON.parse(payload);
547
- if (typeof parsed === "string") {
548
- return { message: parsed.trim() || void 0 };
549
- }
550
- if (typeof parsed === "object" && parsed !== null) {
551
- const record = parsed;
552
- return {
553
- status: typeof record.status === "number" ? record.status : void 0,
554
- message: typeof record.message === "string" && record.message.trim() ? record.message.trim() : void 0
555
- };
74
+ const raw = window.localStorage.getItem(key);
75
+ if (!raw) return null;
76
+ const parsed = JSON.parse(raw);
77
+ if (typeof parsed.width !== "number" || typeof parsed.collapsed !== "boolean") {
78
+ return null;
556
79
  }
80
+ return { width: parsed.width, collapsed: parsed.collapsed };
557
81
  } catch {
82
+ return null;
558
83
  }
559
- return {};
560
84
  }
561
- function getConflictErrorMessage(errorDetails) {
562
- if (!errorDetails) return void 0;
563
- const trimmedError = errorDetails.trim();
564
- const httpMatch = trimmedError.match(HTTP_ERROR_PREFIX);
565
- const httpStatus = httpMatch ? Number(httpMatch[1]) : void 0;
566
- const rawPayload = (httpMatch ? httpMatch[2] : trimmedError).trim();
567
- const payload = parseErrorPayload(rawPayload);
568
- const status = payload.status ?? httpStatus;
569
- if (status !== 409) {
570
- return void 0;
571
- }
572
- if (payload.message) {
573
- return payload.message;
85
+ function savePersistedState(key, state) {
86
+ if (typeof window === "undefined") return;
87
+ try {
88
+ window.localStorage.setItem(key, JSON.stringify(state));
89
+ } catch {
574
90
  }
575
- return rawPayload || void 0;
576
91
  }
577
- function AnimatedLoader({
578
- size = 14,
579
- color = "#00858d"
580
- }) {
581
- const rotation = Animated2.useSharedValue(0);
582
- React__default.default.useEffect(() => {
583
- rotation.value = Animated2.withRepeat(
584
- Animated2.withTiming(360, { duration: 1e3, easing: Animated2.Easing.linear }),
585
- -1
586
- );
587
- }, [rotation]);
588
- const style = Animated2.useAnimatedStyle(() => ({
589
- transform: [{ rotate: `${rotation.value}deg` }]
590
- }));
591
- return /* @__PURE__ */ jsxRuntime.jsx(Animated2__default.default.View, { style, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Loader2, { size, color }) });
592
- }
593
- function ShimmerText({
594
- children,
595
- style: textStyle
596
- }) {
597
- const progress = Animated2.useSharedValue(0);
598
- React__default.default.useEffect(() => {
599
- progress.value = Animated2.withRepeat(
600
- Animated2.withTiming(1, { duration: 3500, easing: Animated2.Easing.linear }),
601
- -1,
602
- false
603
- );
604
- }, [progress]);
605
- const animatedStyle = Animated2.useAnimatedStyle(() => {
606
- const color = Animated2.interpolateColor(
607
- progress.value,
608
- [0, 0.35, 0.5, 0.65, 1],
609
- ["#9CA3AF", "#9CA3AF", "#00858d", "#9CA3AF", "#9CA3AF"]
610
- );
611
- return { color };
92
+ function useSessionHistory(config, options = {}, optimisticActivity) {
93
+ const defaultWidth = options.defaultWidth ?? DEFAULT_WIDTH;
94
+ const minWidth = options.minWidth ?? DEFAULT_MIN_WIDTH;
95
+ const maxWidth = options.maxWidth ?? DEFAULT_MAX_WIDTH;
96
+ const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
97
+ const persistKey = react.useMemo(
98
+ () => getPersistKey(config, options),
99
+ [config.workflow.id, options.persistKey]
100
+ );
101
+ const [width, setWidthState] = react.useState(() => {
102
+ const persisted = loadPersistedState(persistKey);
103
+ return persisted?.width ?? defaultWidth;
612
104
  });
613
- return /* @__PURE__ */ jsxRuntime.jsx(Animated2__default.default.Text, { style: [textStyle, animatedStyle, { fontWeight: "600" }], children });
614
- }
615
- function ThoughtProcessLabel({ style }) {
616
- const opacity = Animated2.useSharedValue(1);
617
- React__default.default.useEffect(() => {
618
- opacity.value = Animated2.withRepeat(
619
- Animated2.withTiming(0.5, { duration: 800, easing: Animated2.Easing.inOut(Animated2.Easing.ease) }),
620
- -1,
621
- true
622
- );
623
- }, [opacity]);
624
- const animatedStyle = Animated2.useAnimatedStyle(() => ({ opacity: opacity.value }));
625
- return /* @__PURE__ */ jsxRuntime.jsx(Animated2__default.default.Text, { style: [style, animatedStyle], children: "Thought Process" });
626
- }
627
- function ThinkingBlock({ text }) {
628
- const [isOpen, setIsOpen] = React.useState(false);
629
- const hasContent = typeof text === "string" && text.trim().length > 0;
630
- if (!hasContent) return null;
631
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: ts.thinkingWrap, children: [
632
- /* @__PURE__ */ jsxRuntime.jsxs(
633
- reactNative.Pressable,
634
- {
635
- onPress: () => setIsOpen((prev) => !prev),
636
- style: ts.thinkingToggle,
637
- accessibilityRole: "button",
638
- accessibilityState: { expanded: isOpen },
639
- accessibilityLabel: isOpen ? "Collapse thought process" : "Expand thought process",
640
- children: [
641
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { transform: [{ rotate: isOpen ? "180deg" : "0deg" }] }, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.ChevronDown, { size: 10, color: "rgba(156,163,175,0.5)" }) }),
642
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: ts.thinkingToggleText, children: "Thought process" })
643
- ]
644
- }
645
- ),
646
- isOpen && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: ts.thinkingContent, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: ts.thinkingContentText, children: text }) })
647
- ] });
648
- }
649
- function looksLikeRawError(text) {
650
- if (!text || text.length < 10) return false;
651
- return text.includes("errorType=") || /failed:\s*\{/.test(text);
652
- }
653
- function AgentMessage({
654
- message,
655
- animated = false,
656
- showAgentName = false,
657
- agentName = "Assistant",
658
- showAvatar = false,
659
- layout = "full-width",
660
- showTimestamp: _showTimestamp = false,
661
- showExecutionSteps = false,
662
- showStreamingDot = false,
663
- streamingStepsText,
664
- completedStepsText,
665
- onExecutionTraceClick
666
- }) {
667
- const isStreaming = message.isStreaming ?? false;
668
- const hasSteps = !!message.steps && message.steps.length > 0;
669
- const hasTraceData = !!(message.tracingData || message.executionId) && !isStreaming;
670
- const isCancelled = message.isCancelled ?? false;
671
- const currentExecutingStepId = message.currentExecutingStepId;
672
- const [isStepsExpanded, setIsStepsExpanded] = React.useState(
673
- isStreaming && hasSteps
105
+ const [collapsed, setCollapsedState] = react.useState(() => {
106
+ const persisted = loadPersistedState(persistKey);
107
+ return persisted?.collapsed ?? options.defaultCollapsed ?? false;
108
+ });
109
+ react.useEffect(() => {
110
+ const persisted = loadPersistedState(persistKey);
111
+ setWidthState(persisted?.width ?? defaultWidth);
112
+ setCollapsedState(persisted?.collapsed ?? options.defaultCollapsed ?? false);
113
+ }, [persistKey]);
114
+ const setWidth = react.useCallback(
115
+ (px) => {
116
+ const clamped = Math.max(minWidth, Math.min(maxWidth, Math.round(px)));
117
+ setWidthState(clamped);
118
+ savePersistedState(persistKey, { width: clamped, collapsed });
119
+ },
120
+ [collapsed, maxWidth, minWidth, persistKey]
674
121
  );
675
- const wasStreamingRef = React.useRef(isStreaming);
676
- React.useEffect(() => {
677
- if (isStreaming && hasSteps) setIsStepsExpanded(true);
678
- if (wasStreamingRef.current && !isStreaming) setIsStepsExpanded(false);
679
- wasStreamingRef.current = isStreaming;
680
- }, [isStreaming, hasSteps]);
681
- const totalElapsedMs = hasSteps ? message.steps.reduce((sum, step) => sum + (step.elapsedMs || 0), 0) : 0;
682
- const currentMessage = message.currentMessage;
683
- const rawContent = message.streamingContent || message.content || "";
684
- const content = rawContent.replace(/\\n/g, "\n");
685
- const hasMeaningfulContent = content.length > 0 && !looksLikeRawError(content);
686
- const completedWithNoContent = !isStreaming && !isCancelled && content.length === 0 && (message.streamProgress === "completed" || message.streamProgress === "error");
687
- const conflictErrorMessage = getConflictErrorMessage(message.errorDetails);
688
- const isError = !!conflictErrorMessage || (isFriendlyWorkflowError(message.errorDetails) || looksLikeRawError(content)) && !hasMeaningfulContent || completedWithNoContent;
689
- const activeThinkingText = message.activeThinkingText;
690
- const allThinkingText = message.allThinkingText;
691
- const currentStep = React.useMemo(
692
- () => message.steps?.find(
693
- (s9) => s9.id === currentExecutingStepId && s9.status === "in_progress"
694
- ),
695
- [message.steps, currentExecutingStepId]
122
+ const setCollapsed = react.useCallback(
123
+ (value) => {
124
+ setCollapsedState((prev) => {
125
+ const next = typeof value === "function" ? value(prev) : value;
126
+ savePersistedState(persistKey, { width, collapsed: next });
127
+ return next;
128
+ });
129
+ },
130
+ [persistKey, width]
696
131
  );
697
- const getStepsLabel = React.useCallback(
698
- (streaming) => {
699
- const count = message.steps.length;
700
- const stepWord = count === 1 ? "step" : "steps";
701
- if (streaming) {
702
- if (streamingStepsText)
703
- return streamingStepsText.replace("{count}", count.toString());
704
- return `${count} ${stepWord} in progress`;
705
- }
706
- if (completedStepsText) {
707
- return completedStepsText.replace("{count}", count.toString()).replace("{time}", (totalElapsedMs / 1e3).toFixed(1));
132
+ const isReady = !!(config.session?.owner?.id && (config.workflow.id || config.workflow.name));
133
+ const [sessions, setSessions] = react.useState([]);
134
+ const [isLoading, setIsLoading] = react.useState(false);
135
+ const [error, setError] = react.useState(null);
136
+ const [page, setPage] = react.useState(0);
137
+ const [hasNext, setHasNext] = react.useState(false);
138
+ const inFlightRef = react.useRef(null);
139
+ const configRef = react.useRef(config);
140
+ configRef.current = config;
141
+ const fetchPage = react.useCallback(
142
+ async (pageIndex, replace) => {
143
+ if (!isReady) return;
144
+ inFlightRef.current?.abort();
145
+ const ac = new AbortController();
146
+ inFlightRef.current = ac;
147
+ setIsLoading(true);
148
+ setError(null);
149
+ try {
150
+ const resp = await paymanTypescriptAskSdk.listSessions(configRef.current, {
151
+ page: pageIndex,
152
+ size: pageSize,
153
+ signal: ac.signal
154
+ });
155
+ setSessions((prev) => replace ? resp.data : [...prev, ...resp.data]);
156
+ setPage(pageIndex);
157
+ setHasNext(resp.pageInfo.hasNext);
158
+ } catch (err) {
159
+ if (err.name !== "AbortError") {
160
+ setError(err);
161
+ }
162
+ } finally {
163
+ if (inFlightRef.current === ac) {
164
+ setIsLoading(false);
165
+ }
708
166
  }
709
- return `${count} ${stepWord} completed`;
710
167
  },
711
- [message.steps, streamingStepsText, completedStepsText, totalElapsedMs]
168
+ [isReady, pageSize]
712
169
  );
713
- const renderStepIcon = (step, isCurrentlyExecuting) => {
714
- const W = s2.iconWrap;
715
- if (isCurrentlyExecuting)
716
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: W, children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedLoader, { size: 13, color: "#00858d" }) });
717
- if (step.status === "pending" && isCancelled)
718
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: W, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s2.dotMuted }) });
719
- if (step.status === "pending" || step.status === "in_progress")
720
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: W, children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedLoader, { size: 13, color: "rgba(0,133,141,0.4)" }) });
721
- if (step.status === "completed")
722
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: W, children: /* @__PURE__ */ jsxRuntime.jsx(
723
- lucideReactNative.Check,
724
- {
725
- size: 13,
726
- color: step.eventType === "USER_ACTION_SUCCESS" ? "#10B981" : "rgba(0,133,141,0.7)"
727
- }
728
- ) });
729
- if (step.status === "error")
730
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: W, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.X, { size: 13, color: "#EF4444" }) });
731
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: W, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s2.dotMuted }) });
170
+ const refresh = react.useCallback(() => fetchPage(0, true), [fetchPage]);
171
+ const loadMore = react.useCallback(async () => {
172
+ if (!hasNext || isLoading) return;
173
+ await fetchPage(page + 1, false);
174
+ }, [fetchPage, hasNext, isLoading, page]);
175
+ react.useEffect(() => {
176
+ if (!optimisticActivity) return;
177
+ setSessions((prev) => {
178
+ const next = [...prev];
179
+ let existingIndex = next.findIndex(
180
+ (session) => session.sessionId === optimisticActivity.sessionId
181
+ );
182
+ if (existingIndex === -1 && optimisticActivity.previousSessionId) {
183
+ existingIndex = next.findIndex(
184
+ (session) => session.sessionId === optimisticActivity.previousSessionId
185
+ );
186
+ }
187
+ const existing = existingIndex >= 0 ? next.splice(existingIndex, 1)[0] : void 0;
188
+ const preferredTitle = existing?.sessionTitle && existing.sessionTitle.trim() && existing.sessionTitle !== "Untitled session" ? existing.sessionTitle : optimisticActivity.sessionTitle?.trim() || existing?.sessionTitle || "Untitled session";
189
+ const localSession = existing ?? {
190
+ userId: config.session?.owner?.id ?? "",
191
+ userLabel: config.session?.owner?.name ?? config.session?.owner?.id ?? "",
192
+ sessionId: optimisticActivity.sessionId,
193
+ workflowId: config.workflow.id ?? "",
194
+ workflowName: config.workflow.name,
195
+ workflowVersion: config.workflow.version ?? 0,
196
+ lastMessageAt: optimisticActivity.lastMessageAt,
197
+ messageCount: 0,
198
+ sessionTitle: preferredTitle
199
+ };
200
+ next.unshift({
201
+ ...localSession,
202
+ sessionId: optimisticActivity.sessionId,
203
+ lastMessageAt: optimisticActivity.lastMessageAt,
204
+ sessionTitle: preferredTitle
205
+ });
206
+ return next;
207
+ });
208
+ }, [
209
+ config.session?.owner?.id,
210
+ config.session?.owner?.name,
211
+ config.workflow.id,
212
+ config.workflow.name,
213
+ config.workflow.version,
214
+ optimisticActivity
215
+ ]);
216
+ react.useEffect(() => {
217
+ if (!isReady) return;
218
+ void fetchPage(0, true);
219
+ return () => {
220
+ inFlightRef.current?.abort();
221
+ };
222
+ }, [
223
+ isReady,
224
+ config.workflow.id,
225
+ config.workflow.stage,
226
+ config.session?.owner?.id
227
+ ]);
228
+ return {
229
+ sessions,
230
+ isLoading,
231
+ error,
232
+ hasNext,
233
+ loadMore,
234
+ refresh,
235
+ width,
236
+ setWidth,
237
+ collapsed,
238
+ setCollapsed,
239
+ minWidth,
240
+ maxWidth,
241
+ isReady
732
242
  };
733
- const showSteps = showExecutionSteps && hasSteps && !isStreaming && !isError;
734
- const handleStepsToggle = React.useCallback(() => {
735
- setIsStepsExpanded((prev) => !prev);
243
+ }
244
+ var MOBILE_BREAKPOINT = 640;
245
+ function useIsMobile() {
246
+ const [isMobile, setIsMobile] = react.useState(() => {
247
+ if (typeof window === "undefined") return false;
248
+ return window.innerWidth < MOBILE_BREAKPOINT;
249
+ });
250
+ react.useEffect(() => {
251
+ if (typeof window === "undefined") return;
252
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
253
+ const handler = (e) => setIsMobile(e.matches);
254
+ setIsMobile(mql.matches);
255
+ mql.addEventListener("change", handler);
256
+ return () => mql.removeEventListener("change", handler);
736
257
  }, []);
737
- const AnimatedView = animated ? Animated2__default.default.View : reactNative.View;
738
- const animatedProps = animated ? { entering: Animated2.FadeInDown.duration(200), layout: Animated2.Layout } : {};
739
- const messageContent = /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.outerWrap, children: [
740
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.row, children: [
741
- showAvatar && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s2.avatarWrap, children: isStreaming && showStreamingDot ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s2.dotContainer, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s2.streamDot }) }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s2.avatar, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Sparkles, { size: 14, color: "#9CA3AF" }) }) }),
742
- /* @__PURE__ */ jsxRuntime.jsxs(
743
- reactNative.View,
258
+ return isMobile;
259
+ }
260
+ function readPortalThemeStyle(source) {
261
+ if (typeof window === "undefined" || !source) return {};
262
+ const root = source.closest(".payman-v2-root");
263
+ if (!(root instanceof HTMLElement)) return {};
264
+ const computed = window.getComputedStyle(root);
265
+ const style = {
266
+ color: computed.color,
267
+ fontFamily: computed.fontFamily,
268
+ fontSize: computed.fontSize,
269
+ lineHeight: computed.lineHeight
270
+ };
271
+ for (let index = 0; index < computed.length; index += 1) {
272
+ const name = computed.item(index);
273
+ if (!name.startsWith("--payman-v2-")) continue;
274
+ const value = computed.getPropertyValue(name).trim();
275
+ if (value) style[name] = value;
276
+ }
277
+ return style;
278
+ }
279
+ function SessionHistorySidebar({
280
+ config,
281
+ options,
282
+ activeSessionId,
283
+ loadingSessionId,
284
+ streamingSessionIds,
285
+ recentlyCompletedSessionIds,
286
+ optimisticActivity,
287
+ onSelectSession,
288
+ mobileOpen,
289
+ onMobileOpenChange
290
+ }) {
291
+ const isMobile = useIsMobile();
292
+ const mobileThemeSourceRef = react.useRef(null);
293
+ const [mobilePortalThemeStyle, setMobilePortalThemeStyle] = react.useState({});
294
+ const history = useSessionHistory(config, options, optimisticActivity);
295
+ react.useEffect(() => {
296
+ if (!isMobile) return;
297
+ setMobilePortalThemeStyle(readPortalThemeStyle(mobileThemeSourceRef.current));
298
+ }, [isMobile, mobileOpen]);
299
+ if (isMobile) {
300
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: mobileThemeSourceRef, style: { display: "contents" }, children: [
301
+ !mobileOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-mobile-trigger", children: /* @__PURE__ */ jsxRuntime.jsx(
302
+ "button",
744
303
  {
745
- style: [s2.bubbleCol, layout === "centered" && s2.bubbleColCentered],
746
- children: [
747
- message.userActionResult && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s2.badgeWrap, children: message.userActionResult === "approved" ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [s2.badge, s2.badgeApproved], children: [
748
- /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Check, { size: 10, color: "#15803D" }),
749
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.badgeApprovedText, children: "Verified" })
750
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [s2.badge, s2.badgeRejected], children: [
751
- /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.X, { size: 10, color: "#B91C1C" }),
752
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.badgeRejectedText, children: "Rejected" })
753
- ] }) }),
754
- /* @__PURE__ */ jsxRuntime.jsxs(
755
- reactNative.View,
756
- {
757
- style: [
758
- s2.bubble,
759
- layout === "centered" ? s2.bubbleCentered : s2.bubbleDefault,
760
- isError && s2.bubbleError
761
- ],
762
- children: [
763
- showAgentName && (isStreaming && !content ? /* @__PURE__ */ jsxRuntime.jsx(ThoughtProcessLabel, { style: s2.agentName }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.agentName, children: agentName })),
764
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { children: isStreaming && !content ? (
765
- // One active item at a time, by event order: thinking stream OR current step with loading (same bullet UI as dropdown)
766
- activeThinkingText ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.stepRow, children: [
767
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { marginTop: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s2.iconWrap, children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedLoader, { size: 13, color: "rgba(0,133,141,0.7)" }) }) }),
768
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [s2.bubbleBodyText, s2.thinkingTextFlex], children: activeThinkingText })
769
- ] }) : currentStep ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.stepRow, children: [
770
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { marginTop: 1 }, children: renderStepIcon(currentStep, true) }),
771
- /* @__PURE__ */ jsxRuntime.jsx(ShimmerText, { style: s2.bubbleBodyText, children: currentStep.message })
772
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.thinkingRow, children: [
773
- !showAvatar && /* @__PURE__ */ jsxRuntime.jsx(AnimatedLoader, { size: 15, color: "rgba(0,133,141,0.7)" }),
774
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.bubbleBodyText, children: currentMessage || "Thinking..." })
775
- ] })
776
- ) : isCancelled && !content ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.thinkingRow, children: [
777
- /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.X, { size: 15, color: "#9CA3AF" }),
778
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [s2.bubbleBodyText, { fontStyle: "italic" }], children: currentMessage || "Request was stopped." })
779
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
780
- Markdown__default.default,
781
- {
782
- style: isError ? markdownErrorStyles : markdownStyles,
783
- children: isError ? conflictErrorMessage ?? "Something went wrong. Please try again." : content || (isStreaming ? "Thinking..." : isCancelled ? "Request was stopped." : "")
784
- }
785
- ) })
786
- ]
787
- }
788
- )
789
- ]
304
+ type: "button",
305
+ "aria-label": "Open recent sessions",
306
+ title: "Recent sessions",
307
+ onClick: () => onMobileOpenChange(true),
308
+ className: "payman-sidebar-collapsed-button payman-sidebar-mobile-trigger-button",
309
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelBottomOpen, { size: 17 })
790
310
  }
791
- ),
792
- hasTraceData && onExecutionTraceClick && /* @__PURE__ */ jsxRuntime.jsx(
793
- reactNative.Pressable,
311
+ ) }),
312
+ /* @__PURE__ */ jsxRuntime.jsx(
313
+ MobileDrawer,
794
314
  {
795
- onPress: () => onExecutionTraceClick({
796
- message,
797
- tracingData: message.tracingData,
798
- executionId: message.executionId
799
- }),
800
- style: [s2.traceBtn, message.userActionResult && s2.traceBtnOffset],
801
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Binoculars, { size: 15, color: "#9CA3AF" })
315
+ open: mobileOpen,
316
+ onOpenChange: onMobileOpenChange,
317
+ themeStyle: mobilePortalThemeStyle,
318
+ history,
319
+ activeSessionId,
320
+ loadingSessionId,
321
+ streamingSessionIds,
322
+ recentlyCompletedSessionIds,
323
+ onSelectSession: (s3) => {
324
+ onSelectSession(s3);
325
+ onMobileOpenChange(false);
326
+ }
802
327
  }
803
328
  )
804
- ] }),
805
- showSteps && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [s2.stepsOuter, showAvatar && s2.stepsIndented], children: [
806
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { onPress: handleStepsToggle, style: s2.stepsToggle, children: [
329
+ ] });
330
+ }
331
+ return /* @__PURE__ */ jsxRuntime.jsx(
332
+ DesktopSidebar,
333
+ {
334
+ history,
335
+ activeSessionId,
336
+ loadingSessionId,
337
+ streamingSessionIds,
338
+ recentlyCompletedSessionIds,
339
+ onSelectSession
340
+ }
341
+ );
342
+ }
343
+ function DesktopSidebar({
344
+ history,
345
+ activeSessionId,
346
+ loadingSessionId,
347
+ streamingSessionIds,
348
+ recentlyCompletedSessionIds,
349
+ onSelectSession
350
+ }) {
351
+ const { width, setWidth, collapsed, setCollapsed, minWidth, maxWidth } = history;
352
+ const dragStateRef = react.useRef(null);
353
+ const [isDragging, setIsDragging] = react.useState(false);
354
+ const [handleHover, setHandleHover] = react.useState(false);
355
+ const onResizeStart = react.useCallback(
356
+ (e) => {
357
+ e.preventDefault();
358
+ dragStateRef.current = { startX: e.clientX, startWidth: width };
359
+ setIsDragging(true);
360
+ },
361
+ [width]
362
+ );
363
+ react.useEffect(() => {
364
+ if (!isDragging) return;
365
+ const onMove = (e) => {
366
+ const st = dragStateRef.current;
367
+ if (!st) return;
368
+ setWidth(st.startWidth + (e.clientX - st.startX));
369
+ };
370
+ const onEnd = () => {
371
+ dragStateRef.current = null;
372
+ setIsDragging(false);
373
+ };
374
+ window.addEventListener("pointermove", onMove);
375
+ window.addEventListener("pointerup", onEnd);
376
+ window.addEventListener("pointercancel", onEnd);
377
+ return () => {
378
+ window.removeEventListener("pointermove", onMove);
379
+ window.removeEventListener("pointerup", onEnd);
380
+ window.removeEventListener("pointercancel", onEnd);
381
+ };
382
+ }, [isDragging, setWidth]);
383
+ if (collapsed) {
384
+ return /* @__PURE__ */ jsxRuntime.jsx(
385
+ CollapsedButton,
386
+ {
387
+ onExpand: () => setCollapsed(false),
388
+ history,
389
+ activeSessionId,
390
+ loadingSessionId,
391
+ streamingSessionIds,
392
+ recentlyCompletedSessionIds,
393
+ onSelectSession
394
+ }
395
+ );
396
+ }
397
+ const handleActive = handleHover || isDragging;
398
+ return /* @__PURE__ */ jsxRuntime.jsxs(
399
+ "aside",
400
+ {
401
+ className: "payman-sidebar payman-sidebar-desktop",
402
+ style: {
403
+ width,
404
+ minWidth,
405
+ maxWidth,
406
+ height: "100%",
407
+ minHeight: 0,
408
+ alignSelf: "stretch",
409
+ display: "flex",
410
+ flexDirection: "column",
411
+ overflow: "hidden",
412
+ transition: isDragging ? "none" : "width 140ms ease"
413
+ },
414
+ children: [
415
+ /* @__PURE__ */ jsxRuntime.jsx(SidebarHeader, { onCollapse: () => setCollapsed(true) }),
416
+ /* @__PURE__ */ jsxRuntime.jsx(
417
+ SessionList,
418
+ {
419
+ history,
420
+ activeSessionId,
421
+ loadingSessionId,
422
+ streamingSessionIds,
423
+ recentlyCompletedSessionIds,
424
+ onSelectSession
425
+ }
426
+ ),
807
427
  /* @__PURE__ */ jsxRuntime.jsx(
808
- reactNative.View,
428
+ "div",
809
429
  {
430
+ role: "separator",
431
+ "aria-orientation": "vertical",
432
+ onPointerDown: onResizeStart,
433
+ onMouseEnter: () => setHandleHover(true),
434
+ onMouseLeave: () => setHandleHover(false),
435
+ className: "payman-sidebar-resize",
810
436
  style: {
811
- transform: [{ rotate: isStepsExpanded ? "90deg" : "0deg" }]
437
+ position: "absolute",
438
+ top: 0,
439
+ right: -4,
440
+ width: 8,
441
+ height: "100%",
442
+ cursor: "col-resize",
443
+ userSelect: "none",
444
+ touchAction: "none",
445
+ zIndex: 2,
446
+ display: "flex",
447
+ alignItems: "center",
448
+ justifyContent: "center"
812
449
  },
813
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.ChevronRight, { size: 12, color: "rgba(156,163,175,0.6)" })
450
+ children: /* @__PURE__ */ jsxRuntime.jsx(
451
+ "span",
452
+ {
453
+ "aria-hidden": true,
454
+ style: {
455
+ width: 3,
456
+ height: 36,
457
+ borderRadius: 999,
458
+ background: handleActive ? "var(--payman-v2-text-3)" : "transparent",
459
+ opacity: handleActive ? 1 : 0,
460
+ transition: "opacity 140ms ease, background 140ms ease"
461
+ }
462
+ }
463
+ )
464
+ }
465
+ )
466
+ ]
467
+ }
468
+ );
469
+ }
470
+ var POPOVER_LEAVE_DELAY_MS = 200;
471
+ function CollapsedButton({
472
+ onExpand,
473
+ history,
474
+ activeSessionId,
475
+ loadingSessionId,
476
+ streamingSessionIds,
477
+ recentlyCompletedSessionIds,
478
+ onSelectSession
479
+ }) {
480
+ const [hoverOpen, setHoverOpen] = react.useState(false);
481
+ const leaveTimerRef = react.useRef(null);
482
+ const openPopover = () => {
483
+ if (leaveTimerRef.current) clearTimeout(leaveTimerRef.current);
484
+ setHoverOpen(true);
485
+ };
486
+ const schedulePopoverClose = () => {
487
+ if (leaveTimerRef.current) clearTimeout(leaveTimerRef.current);
488
+ leaveTimerRef.current = setTimeout(
489
+ () => setHoverOpen(false),
490
+ POPOVER_LEAVE_DELAY_MS
491
+ );
492
+ };
493
+ react.useEffect(
494
+ () => () => {
495
+ if (leaveTimerRef.current) clearTimeout(leaveTimerRef.current);
496
+ },
497
+ []
498
+ );
499
+ return /* @__PURE__ */ jsxRuntime.jsxs(
500
+ "div",
501
+ {
502
+ className: "payman-sidebar-collapsed",
503
+ onMouseEnter: openPopover,
504
+ onMouseLeave: schedulePopoverClose,
505
+ style: { position: "relative", alignSelf: "flex-start" },
506
+ children: [
507
+ /* @__PURE__ */ jsxRuntime.jsx(
508
+ "button",
509
+ {
510
+ type: "button",
511
+ "aria-label": "Expand sidebar",
512
+ title: "Expand sidebar",
513
+ onClick: onExpand,
514
+ className: "payman-sidebar-collapsed-button",
515
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelLeftOpen, { size: 16 })
814
516
  }
815
517
  ),
816
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.stepsToggleText, children: getStepsLabel(false) })
817
- ] }),
818
- isStepsExpanded && hasSteps && /* @__PURE__ */ jsxRuntime.jsxs(
819
- Animated2__default.default.View,
820
- {
821
- entering: animated ? Animated2.FadeIn.duration(200) : void 0,
822
- exiting: animated ? Animated2.FadeOut.duration(150) : void 0,
823
- children: [
824
- !isStreaming && allThinkingText?.trim() ? /* @__PURE__ */ jsxRuntime.jsx(ThinkingBlock, { text: allThinkingText }) : null,
825
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s2.stepsList, children: message.steps.map((step) => {
826
- const isExec = step.id === currentExecutingStepId && step.status === "in_progress" && isStreaming && !isCancelled;
827
- const hasTime = step.elapsedMs != null && step.elapsedMs > 0;
828
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.stepItem, children: [
829
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.stepRow, children: [
830
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { marginTop: 1 }, children: renderStepIcon(step, isExec) }),
831
- isExec ? /* @__PURE__ */ jsxRuntime.jsx(ShimmerText, { style: s2.stepText, children: step.message }) : /* @__PURE__ */ jsxRuntime.jsx(
832
- reactNative.Text,
833
- {
834
- style: [
835
- s2.stepText,
836
- step.status === "completed" && s2.stepTextMuted,
837
- step.status === "error" && s2.stepTextError,
838
- step.eventType === "USER_ACTION_SUCCESS" && s2.stepTextSuccess,
839
- step.status === "pending" && s2.stepTextPending,
840
- step.status === "in_progress" && !isExec && s2.stepTextInProgress
841
- ],
842
- children: step.message
843
- }
844
- )
845
- ] }),
846
- hasTime && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s2.timeBadgeWrap, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.timeBadge, children: [
847
- /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Clock, { size: 8, color: "rgba(156,163,175,0.6)" }),
848
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: s2.timeBadgeText, children: [
849
- (step.elapsedMs / 1e3).toFixed(1),
850
- "s"
851
- ] })
852
- ] }) })
853
- ] }, step.id);
854
- }) })
855
- ]
856
- }
857
- )
858
- ] })
859
- ] });
860
- return /* @__PURE__ */ jsxRuntime.jsx(AnimatedView, { ...animatedProps, children: messageContent });
518
+ hoverOpen && /* @__PURE__ */ jsxRuntime.jsxs(
519
+ "div",
520
+ {
521
+ className: "payman-sidebar-popover",
522
+ onMouseEnter: openPopover,
523
+ onMouseLeave: schedulePopoverClose,
524
+ style: {
525
+ position: "absolute",
526
+ left: "calc(100% + 6px)",
527
+ top: 6,
528
+ width: Math.min(history.width, 320),
529
+ maxHeight: 480,
530
+ zIndex: 50,
531
+ display: "flex",
532
+ flexDirection: "column",
533
+ overflow: "hidden"
534
+ },
535
+ children: [
536
+ /* @__PURE__ */ jsxRuntime.jsx(SidebarHeader, {}),
537
+ /* @__PURE__ */ jsxRuntime.jsx(
538
+ SessionList,
539
+ {
540
+ history,
541
+ activeSessionId,
542
+ loadingSessionId,
543
+ streamingSessionIds,
544
+ recentlyCompletedSessionIds,
545
+ onSelectSession: (s3) => {
546
+ onSelectSession(s3);
547
+ setHoverOpen(false);
548
+ }
549
+ }
550
+ )
551
+ ]
552
+ }
553
+ )
554
+ ]
555
+ }
556
+ );
861
557
  }
862
- var s2 = reactNative.StyleSheet.create({
863
- outerWrap: { width: "100%" },
864
- row: {
865
- flexDirection: "row",
866
- alignItems: "flex-start",
867
- gap: 10,
868
- width: "100%"
869
- },
870
- // Avatar
871
- avatarWrap: { width: 28, height: 28, marginTop: 2 },
872
- dotContainer: {
873
- width: 28,
874
- height: 28,
875
- justifyContent: "center",
876
- alignItems: "center"
877
- },
878
- streamDot: {
879
- width: 8,
880
- height: 8,
881
- borderRadius: 4,
882
- backgroundColor: "#00858d",
883
- opacity: 0.75
884
- },
885
- avatar: {
886
- width: 28,
887
- height: 28,
888
- borderRadius: 14,
889
- backgroundColor: "rgba(0,0,0,0.03)",
890
- borderWidth: reactNative.StyleSheet.hairlineWidth,
891
- borderColor: "rgba(0,0,0,0.08)",
892
- justifyContent: "center",
893
- alignItems: "center"
894
- },
895
- // Bubble column
896
- // Bubble column — flex: 1 so it fills available width on mobile (still capped by maxWidth)
897
- bubbleCol: { flex: 1, minWidth: 0, maxWidth: "92%", flexShrink: 1 },
898
- bubbleColCentered: { maxWidth: "95%" },
899
- // Badge
900
- badgeWrap: { marginBottom: 6 },
901
- badge: {
902
- flexDirection: "row",
903
- alignItems: "center",
904
- gap: 4,
905
- paddingHorizontal: 8,
906
- paddingVertical: 3,
907
- borderRadius: 10,
908
- alignSelf: "flex-start"
909
- },
910
- badgeApproved: {
911
- backgroundColor: "#DCFCE7",
912
- borderWidth: reactNative.StyleSheet.hairlineWidth,
913
- borderColor: "rgba(34,197,94,0.3)"
914
- },
915
- badgeApprovedText: { fontSize: 14, lineHeight: 21, fontWeight: "500", color: "#15803D" },
916
- badgeRejected: {
917
- backgroundColor: "#FEE2E2",
918
- borderWidth: reactNative.StyleSheet.hairlineWidth,
919
- borderColor: "rgba(239,68,68,0.3)"
920
- },
921
- badgeRejectedText: { fontSize: 14, lineHeight: 21, fontWeight: "500", color: "#B91C1C" },
922
- // Bubble
923
- bubble: {
924
- overflow: "hidden",
925
- width: "100%",
926
- paddingHorizontal: 14,
927
- paddingVertical: 10
928
- },
929
- bubbleDefault: {
930
- backgroundColor: "rgba(0,0,0,0.04)",
931
- borderRadius: 16,
932
- borderTopLeftRadius: 4
933
- },
934
- bubbleCentered: {
935
- backgroundColor: "#FFFFFF",
936
- borderWidth: 1,
937
- borderColor: "rgba(0,0,0,0.08)",
938
- borderRadius: 16,
939
- borderTopLeftRadius: 4
940
- },
941
- bubbleError: {
942
- backgroundColor: "rgba(239,68,68,0.05)",
943
- borderWidth: 1,
944
- borderColor: "rgba(239,68,68,0.15)"
945
- },
946
- // Agent name (text-sm to match query)
947
- agentName: {
948
- fontSize: 14,
949
- lineHeight: 21,
950
- fontWeight: "600",
951
- color: "rgba(0,0,0,0.35)",
952
- marginBottom: 2
953
- },
954
- // All body text inside assistant bubble: same size as query (text-sm = 14px)
955
- bubbleBodyText: {
956
- fontSize: 14,
957
- lineHeight: 21,
958
- color: "#9CA3AF",
959
- flex: 1,
960
- minWidth: 0
961
- },
962
- // Thinking state
963
- thinkingRow: { flexDirection: "row", alignItems: "center", gap: 8 },
964
- thinkingText: { fontSize: 14, lineHeight: 21, color: "#9CA3AF" },
965
- thinkingTextFlex: { flex: 1, minWidth: 0 },
966
- // Trace button
967
- traceBtn: {
968
- padding: 7,
969
- backgroundColor: "rgba(0,0,0,0.02)",
970
- borderWidth: reactNative.StyleSheet.hairlineWidth,
971
- borderColor: "rgba(0,0,0,0.08)",
972
- borderRadius: 8,
973
- marginTop: 2
974
- },
975
- traceBtnOffset: { marginTop: 30 },
976
- // Steps section
977
- stepsOuter: { marginTop: 6 },
978
- stepsIndented: { marginLeft: 38 },
979
- // 28 avatar + 10 gap
980
- // Toggle
981
- stepsToggle: { flexDirection: "row", alignItems: "center", gap: 5 },
982
- stepsToggleText: { fontSize: 11, color: "rgba(156,163,175,0.65)" },
983
- // Steps list
984
- stepsList: { marginTop: 6 },
985
- stepItem: { marginBottom: 6 },
986
- stepRow: { flexDirection: "row", alignItems: "flex-start", gap: 5 },
987
- iconWrap: {
988
- width: 16,
989
- height: 16,
990
- justifyContent: "center",
991
- alignItems: "center"
992
- },
993
- dotMuted: {
994
- width: 5,
995
- height: 5,
996
- borderRadius: 3,
997
- backgroundColor: "rgba(0,0,0,0.12)"
998
- },
999
- stepText: {
1000
- fontSize: 12,
1001
- lineHeight: 17,
1002
- flex: 1,
1003
- minWidth: 0,
1004
- color: "#374151"
1005
- },
1006
- stepTextMuted: { color: "#9CA3AF" },
1007
- stepTextError: { color: "#EF4444" },
1008
- stepTextSuccess: { color: "#10B981" },
1009
- stepTextPending: { color: "rgba(156,163,175,0.4)" },
1010
- stepTextInProgress: { color: "rgba(156,163,175,0.6)" },
1011
- // Time badge — aligned with text (pl = 16 icon + 5 gap = 21)
1012
- timeBadgeWrap: { paddingLeft: 21, marginTop: 3 },
1013
- timeBadge: {
1014
- flexDirection: "row",
1015
- alignItems: "center",
1016
- gap: 3,
1017
- backgroundColor: "rgba(0,0,0,0.03)",
1018
- borderWidth: reactNative.StyleSheet.hairlineWidth,
1019
- borderColor: "rgba(0,0,0,0.06)",
1020
- borderRadius: 4,
1021
- paddingHorizontal: 5,
1022
- paddingVertical: 2,
1023
- alignSelf: "flex-start"
1024
- },
1025
- timeBadgeText: {
1026
- fontSize: 9,
1027
- fontFamily: "monospace",
1028
- color: "rgba(156,163,175,0.6)"
1029
- }
1030
- });
1031
- var ts = reactNative.StyleSheet.create({
1032
- thinkingWrap: { marginTop: 4, marginBottom: 4 },
1033
- thinkingToggle: {
1034
- flexDirection: "row",
1035
- alignItems: "center",
1036
- gap: 3
1037
- },
1038
- thinkingToggleText: {
1039
- fontSize: 10,
1040
- color: "rgba(156,163,175,0.5)"
1041
- },
1042
- thinkingContent: {
1043
- marginTop: 4,
1044
- backgroundColor: "rgba(0,0,0,0.03)",
1045
- borderRadius: 6,
1046
- padding: 8
1047
- },
1048
- thinkingContentText: {
1049
- fontSize: 11,
1050
- lineHeight: 16,
1051
- color: "rgba(107,114,128,0.8)"
1052
- }
1053
- });
1054
- var markdownStyles = {
1055
- body: { fontSize: 14, lineHeight: 21, color: "#1F2937" },
1056
- paragraph: { marginBottom: 8, fontSize: 14, lineHeight: 21 },
1057
- strong: { fontWeight: "600" },
1058
- em: { fontStyle: "italic" },
1059
- code_inline: {
1060
- backgroundColor: "rgba(0,0,0,0.04)",
1061
- paddingHorizontal: 5,
1062
- paddingVertical: 2,
1063
- borderRadius: 4,
1064
- fontSize: 12,
1065
- fontFamily: "monospace"
1066
- },
1067
- code_block: {
1068
- backgroundColor: "rgba(0,0,0,0.04)",
1069
- padding: 12,
1070
- borderRadius: 8,
1071
- fontSize: 12,
1072
- fontFamily: "monospace",
1073
- marginVertical: 8
1074
- },
1075
- list_item: { fontSize: 14, lineHeight: 21 },
1076
- bullet_list: { marginBottom: 8 },
1077
- ordered_list: { marginBottom: 8 },
1078
- heading1: {
1079
- fontSize: 18,
1080
- fontWeight: "600",
1081
- marginBottom: 6,
1082
- marginTop: 12,
1083
- color: "#1F2937"
1084
- },
1085
- heading2: {
1086
- fontSize: 16,
1087
- fontWeight: "600",
1088
- marginBottom: 6,
1089
- marginTop: 10,
1090
- color: "#1F2937"
1091
- },
1092
- heading3: {
1093
- fontSize: 14,
1094
- fontWeight: "600",
1095
- marginBottom: 4,
1096
- marginTop: 8,
1097
- color: "#1F2937"
1098
- },
1099
- blockquote: {
1100
- borderLeftWidth: 3,
1101
- borderLeftColor: "rgba(0,0,0,0.1)",
1102
- paddingLeft: 12,
1103
- marginVertical: 8,
1104
- fontStyle: "italic",
1105
- color: "#6B7280"
1106
- },
1107
- hr: {
1108
- marginVertical: 16,
1109
- borderBottomWidth: reactNative.StyleSheet.hairlineWidth,
1110
- borderBottomColor: "rgba(0,0,0,0.1)"
1111
- },
1112
- link: { color: "#007AFF", textDecorationLine: "underline" }
1113
- };
1114
- var markdownErrorStyles = {
1115
- ...markdownStyles,
1116
- body: { ...markdownStyles.body, color: "#EF4444" },
1117
- paragraph: { ...markdownStyles.paragraph, color: "#EF4444" }
1118
- };
1119
- function cn(...inputs) {
1120
- return tailwindMerge.twMerge(clsx.clsx(inputs));
558
+ function MobileDrawer({
559
+ open,
560
+ onOpenChange,
561
+ themeStyle,
562
+ history,
563
+ activeSessionId,
564
+ loadingSessionId,
565
+ streamingSessionIds,
566
+ recentlyCompletedSessionIds,
567
+ onSelectSession
568
+ }) {
569
+ if (!open || typeof document === "undefined") return null;
570
+ return reactDom.createPortal(
571
+ /* @__PURE__ */ jsxRuntime.jsx(
572
+ framerMotion.motion.div,
573
+ {
574
+ className: "payman-sidebar-backdrop",
575
+ onClick: () => onOpenChange(false),
576
+ style: themeStyle,
577
+ initial: { opacity: 0 },
578
+ animate: { opacity: 1 },
579
+ transition: { duration: 0.18, ease: "easeOut" },
580
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
581
+ framerMotion.motion.aside,
582
+ {
583
+ className: "payman-sidebar payman-sidebar-sheet",
584
+ "data-open": "true",
585
+ onClick: (e) => e.stopPropagation(),
586
+ role: "dialog",
587
+ "aria-modal": "true",
588
+ "aria-label": "Recent sessions",
589
+ initial: { y: 28, opacity: 0.96 },
590
+ animate: { y: 0, opacity: 1 },
591
+ transition: {
592
+ type: "spring",
593
+ stiffness: 380,
594
+ damping: 32,
595
+ mass: 0.92
596
+ },
597
+ children: [
598
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-sheet-grabber", "aria-hidden": true }),
599
+ /* @__PURE__ */ jsxRuntime.jsx(SidebarHeader, { onCollapse: () => onOpenChange(false), mobile: true }),
600
+ /* @__PURE__ */ jsxRuntime.jsx(
601
+ SessionList,
602
+ {
603
+ history,
604
+ activeSessionId,
605
+ loadingSessionId,
606
+ streamingSessionIds,
607
+ recentlyCompletedSessionIds,
608
+ onSelectSession
609
+ }
610
+ )
611
+ ]
612
+ }
613
+ )
614
+ }
615
+ ),
616
+ document.body
617
+ );
1121
618
  }
1122
-
1123
- // src/utils/formatDate.ts
1124
- function formatDate(timestamp) {
1125
- const date = typeof timestamp === "string" ? new Date(timestamp) : timestamp;
1126
- const now = /* @__PURE__ */ new Date();
1127
- const diffMs = now.getTime() - date.getTime();
1128
- const diffMins = Math.floor(diffMs / 6e4);
1129
- const diffHours = Math.floor(diffMs / 36e5);
1130
- const diffDays = Math.floor(diffMs / 864e5);
1131
- if (diffMins < 1) {
1132
- return "Just now";
1133
- }
1134
- if (diffMins < 60) {
1135
- return `${diffMins}m ago`;
1136
- }
1137
- if (diffHours < 24) {
1138
- return `${diffHours}h ago`;
1139
- }
1140
- if (diffDays < 7) {
1141
- return `${diffDays}d ago`;
1142
- }
1143
- return date.toLocaleDateString("en-US", {
1144
- month: "short",
1145
- day: "numeric",
1146
- year: date.getFullYear() !== now.getFullYear() ? "numeric" : void 0
1147
- });
619
+ function SidebarHeader({
620
+ onCollapse,
621
+ mobile = false
622
+ }) {
623
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-sidebar-header", children: [
624
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Recent sessions" }),
625
+ onCollapse && /* @__PURE__ */ jsxRuntime.jsx(
626
+ "button",
627
+ {
628
+ type: "button",
629
+ "aria-label": mobile ? "Close recent sessions" : "Collapse sidebar",
630
+ title: mobile ? "Close recent sessions" : "Collapse sidebar",
631
+ onClick: onCollapse,
632
+ children: mobile ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 16 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PanelLeftClose, { size: 14 })
633
+ }
634
+ )
635
+ ] });
1148
636
  }
1149
- function captureSentryError(error, context) {
1150
- if (!Sentry__namespace.getClient()) return;
1151
- const tags = {};
1152
- if (context.executionId) tags.executionId = context.executionId;
1153
- if (context.sessionId) tags.sessionId = context.sessionId;
1154
- if (context.route) tags.route = context.route;
1155
- if (context.cfRay) tags.cfRay = context.cfRay;
1156
- if (context.customerId) tags.customerId = context.customerId;
1157
- if (context.customerEmail) tags.customerEmail = context.customerEmail;
1158
- const contexts = {
1159
- chat_session: {
1160
- sessionId: context.sessionId ?? null,
1161
- sessionOwnerId: context.sessionOwnerId ?? null,
1162
- executionId: context.executionId ?? null,
1163
- workflowName: context.workflowName ?? null,
1164
- cfRay: context.cfRay ?? null,
1165
- customerId: context.customerId ?? null,
1166
- customerEmail: context.customerEmail ?? null
637
+ function SessionList({
638
+ history,
639
+ activeSessionId,
640
+ loadingSessionId,
641
+ streamingSessionIds,
642
+ recentlyCompletedSessionIds,
643
+ onSelectSession
644
+ }) {
645
+ const listRef = react.useRef(null);
646
+ const { sessions, hasNext, isLoading, error, loadMore, isReady, refresh } = history;
647
+ const formattedNow = react.useMemo(() => /* @__PURE__ */ new Date(), []);
648
+ const sessionGroups = react.useMemo(
649
+ () => groupSessionsByDate(sessions, formattedNow),
650
+ [sessions, formattedNow]
651
+ );
652
+ if (!isReady) {
653
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-sidebar-notice", children: [
654
+ "Pass ",
655
+ /* @__PURE__ */ jsxRuntime.jsx(InlineCode, { children: "workflow.id" }),
656
+ " (or",
657
+ " ",
658
+ /* @__PURE__ */ jsxRuntime.jsx(InlineCode, { children: "workflow.name" }),
659
+ ") and",
660
+ " ",
661
+ /* @__PURE__ */ jsxRuntime.jsx(InlineCode, { children: "session.owner.id" }),
662
+ " in your chat config to view recent sessions."
663
+ ] });
664
+ }
665
+ return /* @__PURE__ */ jsxRuntime.jsxs(
666
+ "div",
667
+ {
668
+ ref: listRef,
669
+ className: "payman-sidebar-list",
670
+ style: { flex: "1 1 0%", minHeight: 0, overflowY: "auto" },
671
+ children: [
672
+ error && sessions.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { onRetry: () => void refresh() }),
673
+ !error && sessions.length === 0 && isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-loading", children: /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 18 }) }),
674
+ !error && sessions.length === 0 && !isLoading && /* @__PURE__ */ jsxRuntime.jsx(EmptyState, {}),
675
+ sessionGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(
676
+ "section",
677
+ {
678
+ className: "payman-sidebar-group",
679
+ "aria-label": group.label,
680
+ children: [
681
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-group-label", children: group.label }),
682
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-group-items", children: group.sessions.map((s3) => /* @__PURE__ */ jsxRuntime.jsx(
683
+ SessionRow,
684
+ {
685
+ session: s3,
686
+ isActive: s3.sessionId === activeSessionId,
687
+ isLoading: s3.sessionId === loadingSessionId,
688
+ isStreaming: streamingSessionIds?.has(s3.sessionId) ?? false,
689
+ isRecentlyCompleted: recentlyCompletedSessionIds?.has(s3.sessionId) ?? false,
690
+ onSelect: onSelectSession
691
+ },
692
+ s3.sessionId
693
+ )) })
694
+ ]
695
+ },
696
+ group.key
697
+ )),
698
+ sessions.length > 0 && hasNext && /* @__PURE__ */ jsxRuntime.jsx(
699
+ LoadMoreButton,
700
+ {
701
+ isLoading,
702
+ onClick: () => void loadMore()
703
+ }
704
+ ),
705
+ sessions.length > 0 && error && /* @__PURE__ */ jsxRuntime.jsx(
706
+ LoadMoreButton,
707
+ {
708
+ isLoading: false,
709
+ label: "Retry",
710
+ onClick: () => void refresh()
711
+ }
712
+ )
713
+ ]
1167
714
  }
1168
- };
1169
- if (typeof error === "string") {
1170
- Sentry__namespace.captureMessage(error, { level: "error", tags, contexts });
1171
- } else {
1172
- Sentry__namespace.captureException(error, { tags, contexts });
1173
- }
1174
- }
1175
- function UserMessage({
1176
- message,
1177
- animated = false,
1178
- showAvatar = false
1179
- }) {
1180
- const AnimatedView = animated ? Animated2__default.default.View : reactNative.View;
1181
- const animatedProps = animated ? { entering: Animated2.FadeInDown.duration(250) } : {};
1182
- return /* @__PURE__ */ jsxRuntime.jsx(AnimatedView, { ...animatedProps, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s3.container, children: [
1183
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s3.messageCol, children: [
1184
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s3.bubble, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s3.text, children: message.content }) }),
1185
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s3.timestamp, children: formatDate(message.timestamp) })
1186
- ] }),
1187
- showAvatar && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s3.avatarWrap, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s3.avatar, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.User, { size: 13, color: "#FFFFFF" }) }) })
1188
- ] }) });
715
+ );
1189
716
  }
1190
- var s3 = reactNative.StyleSheet.create({
1191
- container: {
1192
- flexDirection: "row",
1193
- justifyContent: "flex-end",
1194
- width: "100%",
1195
- gap: 10
1196
- },
1197
- messageCol: {
1198
- maxWidth: "80%",
1199
- minWidth: 0,
1200
- flexDirection: "column",
1201
- alignItems: "flex-end"
1202
- },
1203
- bubble: {
1204
- borderRadius: 16,
1205
- borderBottomRightRadius: 4,
1206
- paddingHorizontal: 14,
1207
- paddingVertical: 10,
1208
- backgroundColor: "#00858d"
1209
- },
1210
- text: { fontSize: 14, color: "#FFFFFF", lineHeight: 20 },
1211
- timestamp: { fontSize: 10, marginTop: 3, color: "rgba(0,0,0,0.25)" },
1212
- avatarWrap: { width: 28, height: 28, marginTop: 2 },
1213
- avatar: {
1214
- width: 28,
1215
- height: 28,
1216
- borderRadius: 14,
1217
- backgroundColor: "#00858d",
1218
- justifyContent: "center",
1219
- alignItems: "center"
1220
- }
1221
- });
1222
- function MessageRow({
1223
- message,
1224
- stage = "DEV",
1225
- animated = false,
1226
- showAgentName = false,
1227
- agentName = "Paygent",
1228
- showAvatars = false,
1229
- showUserAvatar,
1230
- showAssistantAvatar,
1231
- showExecutionSteps = false,
1232
- showStreamingDot = false,
1233
- streamingStepsText,
1234
- completedStepsText,
1235
- onExecutionTraceClick
717
+ function SessionRow({
718
+ session,
719
+ isActive,
720
+ isLoading,
721
+ isStreaming,
722
+ isRecentlyCompleted,
723
+ onSelect
1236
724
  }) {
1237
- const actualShowUserAvatar = showUserAvatar !== void 0 ? showUserAvatar : showAvatars;
1238
- const actualShowAssistantAvatar = showAssistantAvatar !== void 0 ? showAssistantAvatar : showAvatars;
1239
- if (message.role === "user") {
1240
- return /* @__PURE__ */ jsxRuntime.jsx(
1241
- UserMessage,
725
+ let statusNode = null;
726
+ let statusLabel;
727
+ if (isStreaming) {
728
+ statusNode = /* @__PURE__ */ jsxRuntime.jsxs(
729
+ "span",
730
+ {
731
+ className: "payman-sidebar-row-status payman-sidebar-row-status-live",
732
+ "aria-hidden": true,
733
+ children: [
734
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-sidebar-row-live-dot" }),
735
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-sidebar-row-live-track" })
736
+ ]
737
+ }
738
+ );
739
+ statusLabel = "Running";
740
+ } else if (isRecentlyCompleted) {
741
+ statusNode = /* @__PURE__ */ jsxRuntime.jsx(
742
+ "span",
1242
743
  {
1243
- message,
1244
- animated,
1245
- showAvatar: actualShowUserAvatar
744
+ className: "payman-sidebar-row-status payman-sidebar-row-status-done",
745
+ "aria-hidden": true,
746
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 11, strokeWidth: 2.5 })
1246
747
  }
1247
748
  );
749
+ statusLabel = "Just finished";
750
+ } else if (isLoading) {
751
+ statusNode = /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "payman-sidebar-row-spinner" });
752
+ statusLabel = "Loading";
1248
753
  }
1249
754
  return /* @__PURE__ */ jsxRuntime.jsx(
1250
- AgentMessage,
755
+ "button",
1251
756
  {
1252
- message,
1253
- stage,
1254
- animated,
1255
- showAgentName,
1256
- agentName,
1257
- showAvatar: actualShowAssistantAvatar,
1258
- showExecutionSteps,
1259
- showStreamingDot,
1260
- streamingStepsText,
1261
- completedStepsText,
1262
- onExecutionTraceClick
757
+ type: "button",
758
+ onClick: () => onSelect(session),
759
+ className: cn("payman-sidebar-row", isActive && "is-active"),
760
+ title: statusLabel ? `${session.sessionTitle || "Untitled session"} \u2014 ${statusLabel}` : session.sessionTitle,
761
+ "aria-busy": isLoading || isStreaming || void 0,
762
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "payman-sidebar-row-main", children: [
763
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "payman-sidebar-row-title", children: session.sessionTitle || "Untitled session" }),
764
+ statusNode
765
+ ] })
1263
766
  }
1264
767
  );
1265
768
  }
1266
- function MessageRowSkeleton({
1267
- isRightAligned = false
769
+ function EmptyState() {
770
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-sidebar-empty", children: [
771
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-empty-icon", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { size: 18 }) }),
772
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-empty-title", children: "No sessions yet" }),
773
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-empty-desc", children: "Your conversations will appear here once you start chatting." })
774
+ ] });
775
+ }
776
+ function ErrorState({ onRetry }) {
777
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payman-sidebar-error", children: [
778
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-error-icon", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { size: 18 }) }),
779
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-error-title", children: "Couldn't load sessions" }),
780
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payman-sidebar-error-desc", children: "Check your connection and try again." }),
781
+ /* @__PURE__ */ jsxRuntime.jsx(
782
+ LoadMoreButton,
783
+ {
784
+ isLoading: false,
785
+ label: "Try again",
786
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { size: 12 }),
787
+ onClick: onRetry
788
+ }
789
+ )
790
+ ] });
791
+ }
792
+ function LoadMoreButton({
793
+ isLoading,
794
+ onClick,
795
+ label,
796
+ icon
1268
797
  }) {
1269
- const opacity = Animated2.useSharedValue(0.3);
1270
- React.useEffect(() => {
1271
- opacity.value = Animated2.withRepeat(Animated2.withTiming(0.8, { duration: 1200 }), -1, true);
1272
- }, []);
1273
- const animatedStyle = Animated2.useAnimatedStyle(() => ({ opacity: opacity.value }));
1274
798
  return /* @__PURE__ */ jsxRuntime.jsx(
1275
- reactNative.View,
799
+ "button",
1276
800
  {
1277
- style: [s4.container, isRightAligned ? s4.containerRight : s4.containerLeft],
1278
- children: /* @__PURE__ */ jsxRuntime.jsxs(
1279
- reactNative.View,
1280
- {
1281
- style: [s4.content, isRightAligned ? s4.contentRight : s4.contentLeft],
1282
- children: [
1283
- /* @__PURE__ */ jsxRuntime.jsx(
1284
- Animated2__default.default.View,
1285
- {
1286
- style: [
1287
- s4.bubble,
1288
- isRightAligned ? s4.bubbleRight : s4.bubbleLeft,
1289
- animatedStyle
1290
- ],
1291
- children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s4.skeletonWrap, children: [
1292
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [s4.line, { width: "70%" }] }),
1293
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [s4.line, { width: "100%" }] }),
1294
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [s4.line, { width: "80%" }] })
1295
- ] })
1296
- }
1297
- ),
1298
- /* @__PURE__ */ jsxRuntime.jsx(Animated2__default.default.View, { style: [s4.timestamp, animatedStyle] })
1299
- ]
1300
- }
1301
- )
801
+ type: "button",
802
+ onClick,
803
+ disabled: isLoading,
804
+ className: "payman-sidebar-load-more",
805
+ children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Spinner, {}) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
806
+ icon,
807
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label ?? "Load more sessions" })
808
+ ] })
1302
809
  }
1303
810
  );
1304
811
  }
1305
- var s4 = reactNative.StyleSheet.create({
1306
- container: { flexDirection: "row", width: "100%" },
1307
- containerLeft: { justifyContent: "flex-start" },
1308
- containerRight: { justifyContent: "flex-end" },
1309
- content: { maxWidth: "75%", width: "100%" },
1310
- contentLeft: { alignItems: "flex-start" },
1311
- contentRight: { alignItems: "flex-end" },
1312
- bubble: { borderRadius: 16, padding: 14, width: "100%" },
1313
- bubbleLeft: { backgroundColor: "rgba(0,0,0,0.04)", borderTopLeftRadius: 4 },
1314
- bubbleRight: {
1315
- backgroundColor: "rgba(0,122,255,0.08)",
1316
- borderBottomRightRadius: 4
1317
- },
1318
- skeletonWrap: { gap: 8 },
1319
- line: { height: 10, backgroundColor: "rgba(0,0,0,0.08)", borderRadius: 5 },
1320
- timestamp: {
1321
- height: 6,
1322
- width: 50,
1323
- backgroundColor: "rgba(0,0,0,0.06)",
1324
- borderRadius: 3,
1325
- marginTop: 4
1326
- }
1327
- });
1328
- function MessageList({
1329
- messages,
1330
- isLoading = false,
1331
- emptyStateText = "What can I help with?",
1332
- showEmptyStateIcon = true,
1333
- layout = "full-width",
1334
- showTimestamps = false,
1335
- stage = "DEV",
1336
- animated = false,
1337
- showAgentName = false,
1338
- agentName = "Paygent",
1339
- showAvatars = false,
1340
- showUserAvatar,
1341
- showAssistantAvatar,
1342
- showExecutionSteps = false,
1343
- showStreamingDot = false,
1344
- streamingStepsText,
1345
- completedStepsText,
1346
- onExecutionTraceClick,
1347
- className,
1348
- isWaitingForResponse = false,
1349
- scrollToEndHandleRef
1350
- }) {
1351
- const flatListRef = React.useRef(null);
1352
- const messagesRef = React.useRef(messages);
1353
- messagesRef.current = messages;
1354
- const prevWaitingRef = React.useRef(isWaitingForResponse);
1355
- const prevLastStreamingRef = React.useRef(void 0);
1356
- const scrollToEnd = React.useCallback(() => {
1357
- flatListRef.current?.scrollToEnd({ animated: true });
1358
- }, []);
1359
- const scrollToEndOnAssistantLayout = React.useCallback(() => {
1360
- const list = messagesRef.current;
1361
- const last = list[list.length - 1];
1362
- const shouldFollow = isWaitingForResponse || last?.role === "assistant" && last.isStreaming;
1363
- if (!shouldFollow) return;
1364
- const run = () => flatListRef.current?.scrollToEnd({ animated: false });
1365
- run();
1366
- requestAnimationFrame(run);
1367
- setTimeout(run, 16);
1368
- setTimeout(run, 48);
1369
- }, [isWaitingForResponse]);
1370
- React.useEffect(() => {
1371
- if (!scrollToEndHandleRef) return;
1372
- scrollToEndHandleRef.current = scrollToEnd;
1373
- return () => {
1374
- scrollToEndHandleRef.current = null;
1375
- };
1376
- }, [scrollToEndHandleRef, scrollToEnd]);
1377
- React.useEffect(() => {
1378
- if (messages.length > 0) {
1379
- setTimeout(() => {
1380
- scrollToEnd();
1381
- }, 100);
1382
- }
1383
- }, [messages.length, scrollToEnd]);
1384
- React.useEffect(() => {
1385
- const last = messages[messages.length - 1];
1386
- const streaming = last?.isStreaming;
1387
- if (prevLastStreamingRef.current === true && streaming === false) {
1388
- setTimeout(() => scrollToEnd(), 0);
812
+ function Spinner({ size = 12 }) {
813
+ return /* @__PURE__ */ jsxRuntime.jsx(
814
+ "span",
815
+ {
816
+ "aria-hidden": true,
817
+ className: "payman-sidebar-spinner",
818
+ style: {
819
+ width: size,
820
+ height: size,
821
+ borderRadius: "50%",
822
+ border: `${Math.max(1.5, size / 8)}px solid var(--payman-v2-border-1)`,
823
+ borderTopColor: "var(--payman-v2-text-2)",
824
+ display: "inline-block",
825
+ animation: "payman-v2-spin 0.7s linear infinite"
826
+ }
1389
827
  }
1390
- prevLastStreamingRef.current = streaming;
1391
- }, [messages, scrollToEnd]);
1392
- React.useEffect(() => {
1393
- if (prevWaitingRef.current === true && isWaitingForResponse === false) {
1394
- setTimeout(() => scrollToEnd(), 50);
828
+ );
829
+ }
830
+ function InlineCode({ children }) {
831
+ return /* @__PURE__ */ jsxRuntime.jsx("code", { className: "payman-sidebar-inline-code", children });
832
+ }
833
+ function groupSessionsByDate(sessions, now) {
834
+ const groups = /* @__PURE__ */ new Map();
835
+ const orderedGroups = [];
836
+ for (const session of sessions) {
837
+ const bucket = getSessionDateBucket(session.lastMessageAt, now);
838
+ const existing = groups.get(bucket.key);
839
+ if (existing) {
840
+ existing.sessions.push(session);
841
+ continue;
1395
842
  }
1396
- prevWaitingRef.current = isWaitingForResponse;
1397
- }, [isWaitingForResponse, scrollToEnd]);
1398
- if (isLoading) {
1399
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s5.container, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s5.skeletonContent, children: Array.from({ length: 5 }).map((_, index) => /* @__PURE__ */ jsxRuntime.jsx(
1400
- MessageRowSkeleton,
1401
- {
1402
- isRightAligned: index % 3 === 0
1403
- },
1404
- `skeleton-${index}`
1405
- )) }) });
843
+ const group = {
844
+ key: bucket.key,
845
+ label: bucket.label,
846
+ sessions: [session]
847
+ };
848
+ groups.set(bucket.key, group);
849
+ orderedGroups.push(group);
1406
850
  }
1407
- if (messages.length === 0) {
1408
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s5.emptyContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(Animated2__default.default.View, { entering: Animated2.FadeIn.duration(500), style: s5.emptyContent, children: [
1409
- showEmptyStateIcon && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s5.iconContainer, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.MessageCircle, { size: 24, color: "rgba(156,163,175,0.7)" }) }),
1410
- emptyStateText.split("\n").map((line, index) => /* @__PURE__ */ jsxRuntime.jsx(
1411
- reactNative.Text,
1412
- {
1413
- style: index === 0 ? s5.emptyTitle : s5.emptySubtitle,
1414
- children: line
1415
- },
1416
- index
1417
- ))
1418
- ] }) });
851
+ return orderedGroups;
852
+ }
853
+ function getSessionDateBucket(iso, now) {
854
+ const date = new Date(iso);
855
+ if (Number.isNaN(date.getTime())) {
856
+ return { key: "older", label: "Older" };
1419
857
  }
1420
- return /* @__PURE__ */ jsxRuntime.jsx(
1421
- reactNative.FlatList,
1422
- {
1423
- ref: flatListRef,
1424
- style: s5.list,
1425
- data: messages,
1426
- keyExtractor: (item) => item.id,
1427
- keyboardShouldPersistTaps: "handled",
1428
- keyboardDismissMode: "interactive",
1429
- renderItem: ({ item }) => /* @__PURE__ */ jsxRuntime.jsx(
1430
- MessageRow,
1431
- {
1432
- message: item,
1433
- stage,
1434
- animated,
1435
- showAgentName,
1436
- agentName,
1437
- showAvatars,
1438
- showUserAvatar,
1439
- showAssistantAvatar,
1440
- showExecutionSteps,
1441
- showStreamingDot,
1442
- streamingStepsText,
1443
- completedStepsText,
1444
- onExecutionTraceClick
1445
- }
1446
- ),
1447
- contentContainerStyle: [
1448
- s5.listContent,
1449
- layout === "centered" && s5.listContentCentered
1450
- ],
1451
- ListFooterComponent: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s5.listFooterSpacer }),
1452
- onContentSizeChange: scrollToEndOnAssistantLayout,
1453
- showsVerticalScrollIndicator: false
1454
- }
858
+ const dayKey = [
859
+ date.getFullYear(),
860
+ String(date.getMonth() + 1).padStart(2, "0"),
861
+ String(date.getDate()).padStart(2, "0")
862
+ ].join("-");
863
+ const sessionDay = new Date(date.getFullYear(), date.getMonth(), date.getDate());
864
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
865
+ const dayDiff = Math.round(
866
+ (today.getTime() - sessionDay.getTime()) / 864e5
1455
867
  );
868
+ if (dayDiff <= 0) return { key: dayKey, label: "Today" };
869
+ if (dayDiff === 1) return { key: dayKey, label: "Yesterday" };
870
+ if (dayDiff < 7) {
871
+ return {
872
+ key: dayKey,
873
+ label: date.toLocaleDateString(void 0, { weekday: "long" })
874
+ };
875
+ }
876
+ const sameYear = date.getFullYear() === now.getFullYear();
877
+ return {
878
+ key: dayKey,
879
+ label: date.toLocaleDateString(void 0, {
880
+ month: "short",
881
+ day: "numeric",
882
+ year: sameYear ? void 0 : "numeric"
883
+ })
884
+ };
1456
885
  }
1457
- var s5 = reactNative.StyleSheet.create({
1458
- /** Fills space above the input so messages stay in the scroll region, not behind it. */
1459
- list: { flex: 1 },
1460
- container: { flex: 1 },
1461
- skeletonContent: { padding: 16, gap: 16 },
1462
- emptyContainer: {
1463
- flex: 1,
1464
- justifyContent: "center",
886
+ function ChatHeader({
887
+ sessionId,
888
+ onCopySessionId,
889
+ onNewSession,
890
+ showResetSession = false,
891
+ children
892
+ }) {
893
+ const [copiedSessionId, setCopiedSessionId] = react.useState(false);
894
+ const handleCopySessionId = () => {
895
+ if (sessionId && onCopySessionId) {
896
+ reactNative.Clipboard.setString(sessionId);
897
+ setCopiedSessionId(true);
898
+ setTimeout(() => setCopiedSessionId(false), 2e3);
899
+ onCopySessionId(sessionId);
900
+ }
901
+ };
902
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles2.container, children: [
903
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles2.left, children: [
904
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles2.title, children: "Chat" }),
905
+ sessionId && /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { onPress: handleCopySessionId, style: styles2.sessionChip, children: [
906
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles2.sessionText, children: sessionId.length > 20 ? `${sessionId.substring(0, 8)}\u2026${sessionId.slice(-8)}` : sessionId }),
907
+ copiedSessionId ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles2.copyHint, children: "copied" }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles2.copyHint, children: "copy" })
908
+ ] })
909
+ ] }),
910
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles2.right, children: [
911
+ showResetSession && onNewSession && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onNewSession, style: styles2.btn, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles2.btnText, children: "Reset" }) }),
912
+ children
913
+ ] })
914
+ ] });
915
+ }
916
+ var styles2 = reactNative.StyleSheet.create({
917
+ container: {
918
+ flexDirection: "row",
1465
919
  alignItems: "center",
1466
- padding: 24
1467
- },
1468
- emptyContent: { alignItems: "center", gap: 10, maxWidth: 280 },
1469
- iconContainer: {
1470
- width: 56,
920
+ justifyContent: "space-between",
921
+ paddingHorizontal: 16,
1471
922
  height: 56,
1472
- borderRadius: 16,
1473
- backgroundColor: "rgba(0,0,0,0.03)",
1474
- borderWidth: reactNative.StyleSheet.hairlineWidth,
1475
- borderColor: "rgba(0,0,0,0.06)",
1476
- justifyContent: "center",
923
+ borderBottomWidth: 1,
924
+ borderBottomColor: "rgba(0,0,0,0.08)",
925
+ backgroundColor: "rgba(255,255,255,0.85)"
926
+ },
927
+ left: { flex: 1, minWidth: 0 },
928
+ right: { flexDirection: "row", alignItems: "center", gap: 6 },
929
+ title: { fontSize: 14, fontWeight: "600", color: "#111" },
930
+ sessionChip: {
931
+ flexDirection: "row",
1477
932
  alignItems: "center",
1478
- marginBottom: 4
1479
- },
1480
- emptyTitle: {
1481
- fontSize: 20,
1482
- fontWeight: "600",
1483
- color: "#1F2937",
1484
- textAlign: "center"
1485
- },
1486
- emptySubtitle: {
1487
- fontSize: 14,
1488
- color: "#9CA3AF",
1489
- textAlign: "center",
1490
- lineHeight: 20
933
+ gap: 4,
934
+ marginTop: 2
1491
935
  },
1492
- listContent: {
1493
- paddingTop: 16,
1494
- paddingHorizontal: 16,
1495
- paddingBottom: 16,
1496
- gap: 16
936
+ sessionText: { fontSize: 10, color: "#666", fontFamily: "Menlo" },
937
+ copyHint: { fontSize: 9, color: "#999" },
938
+ btn: {
939
+ paddingHorizontal: 10,
940
+ paddingVertical: 6,
941
+ borderRadius: 8,
942
+ borderWidth: 1,
943
+ borderColor: "rgba(0,0,0,0.12)"
1497
944
  },
1498
- listContentCentered: { maxWidth: 672, alignSelf: "center", width: "100%" },
1499
- /** Extra space below last message — thinking/stream text can wrap to multiple lines before scroll runs. */
1500
- listFooterSpacer: {
1501
- height: reactNative.Platform.select({ ios: 52, android: 60, default: 52 })
1502
- }
945
+ btnText: { fontSize: 12, color: "#555" }
1503
946
  });
1504
947
 
1505
948
  // src/assets/payman-mono-crop-blue.png
@@ -1516,20 +959,20 @@ function OtpInput({
1516
959
  disabled = false,
1517
960
  error = false
1518
961
  }) {
1519
- const inputRefs = React.useRef([]);
1520
- const shakeAnim = React.useRef(new reactNative.Animated.Value(0)).current;
1521
- const scaleAnim = React.useRef(new reactNative.Animated.Value(1)).current;
1522
- const prevLenRef = React.useRef(0);
1523
- const [internalError, setInternalError] = React.useState(false);
962
+ const inputRefs = react.useRef([]);
963
+ const shakeAnim = react.useRef(new reactNative.Animated.Value(0)).current;
964
+ const scaleAnim = react.useRef(new reactNative.Animated.Value(1)).current;
965
+ const prevLenRef = react.useRef(0);
966
+ const [internalError, setInternalError] = react.useState(false);
1524
967
  const safeMaxLength = Number.isInteger(maxLength) && maxLength > 0 ? Math.min(maxLength, MAX_SUPPORTED_LENGTH) : DEFAULT_MAX_LENGTH;
1525
968
  const digits = value.split("").concat(Array(safeMaxLength).fill("")).slice(0, safeMaxLength);
1526
969
  const isFull = value.length === safeMaxLength && /^\d+$/.test(value);
1527
- React.useEffect(() => {
970
+ react.useEffect(() => {
1528
971
  if (!disabled) {
1529
972
  inputRefs.current[0]?.focus();
1530
973
  }
1531
974
  }, [disabled]);
1532
- React.useEffect(() => {
975
+ react.useEffect(() => {
1533
976
  if (!error) {
1534
977
  setInternalError(false);
1535
978
  return;
@@ -1540,7 +983,7 @@ function OtpInput({
1540
983
  reactNative.Animated.timing(shakeAnim, { toValue: 0, duration: 0, useNativeDriver: true })
1541
984
  ]).start();
1542
985
  }, [error, shakeAnim]);
1543
- React.useEffect(() => {
986
+ react.useEffect(() => {
1544
987
  if (isFull && prevLenRef.current < safeMaxLength) {
1545
988
  reactNative.Animated.sequence([
1546
989
  reactNative.Animated.timing(scaleAnim, { toValue: 1.04, duration: COMPLETE_PULSE_MS * 0.4, useNativeDriver: true }),
@@ -1596,7 +1039,7 @@ function OtpInput({
1596
1039
  reactNative.Animated.View,
1597
1040
  {
1598
1041
  style: [
1599
- s6.container,
1042
+ s.container,
1600
1043
  { transform: [{ translateX: shakeTranslate }, { scale: scaleAnim }] }
1601
1044
  ],
1602
1045
  children: digits.map((digit, i) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -1613,9 +1056,9 @@ function OtpInput({
1613
1056
  onKeyPress: ({ nativeEvent }) => handleKeyPress(i, nativeEvent.key),
1614
1057
  onFocus: () => inputRefs.current[i]?.setNativeProps({ selection: { start: 0, end: 1 } }),
1615
1058
  style: [
1616
- s6.input,
1617
- disabled && s6.inputDisabled,
1618
- internalError && s6.inputError
1059
+ s.input,
1060
+ disabled && s.inputDisabled,
1061
+ internalError && s.inputError
1619
1062
  ],
1620
1063
  accessibilityLabel: `Digit ${i + 1}`
1621
1064
  },
@@ -1630,7 +1073,7 @@ var PAYMAN_OTP = {
1630
1073
  fg: "#18181b",
1631
1074
  disabledBg: "rgba(0,0,0,0.03)"
1632
1075
  };
1633
- var s6 = reactNative.StyleSheet.create({
1076
+ var s = reactNative.StyleSheet.create({
1634
1077
  container: { flexDirection: "row", gap: 8, justifyContent: "center" },
1635
1078
  input: {
1636
1079
  width: 44,
@@ -1718,17 +1161,17 @@ function UserActionModal({
1718
1161
  onResend,
1719
1162
  clearOtpTrigger
1720
1163
  }) {
1721
- const [otp, setOtp] = React.useState("");
1722
- const [actionType, setActionType] = React.useState(null);
1723
- const [isSubmitting, setIsSubmitting] = React.useState(false);
1724
- const [resendCooldownRemaining, setResendCooldownRemaining] = React.useState(0);
1725
- const [keyboardVisible, setKeyboardVisible] = React.useState(false);
1726
- const [otpError, setOtpError] = React.useState(false);
1727
- const lastAutoSubmittedRef = React.useRef("");
1728
- const submitInFlightRef = React.useRef(false);
1729
- const submitGenerationRef = React.useRef(0);
1164
+ const [otp, setOtp] = react.useState("");
1165
+ const [actionType, setActionType] = react.useState(null);
1166
+ const [isSubmitting, setIsSubmitting] = react.useState(false);
1167
+ const [resendCooldownRemaining, setResendCooldownRemaining] = react.useState(0);
1168
+ const [keyboardVisible, setKeyboardVisible] = react.useState(false);
1169
+ const [otpError, setOtpError] = react.useState(false);
1170
+ const lastAutoSubmittedRef = react.useRef("");
1171
+ const submitInFlightRef = react.useRef(false);
1172
+ const submitGenerationRef = react.useRef(0);
1730
1173
  const schema = getOtpSchemaFromRequest(userActionRequest?.requestedSchema);
1731
- React.useEffect(() => {
1174
+ react.useEffect(() => {
1732
1175
  const show = reactNative.Keyboard.addListener(
1733
1176
  reactNative.Platform.OS === "ios" ? "keyboardWillShow" : "keyboardDidShow",
1734
1177
  () => setKeyboardVisible(true)
@@ -1742,11 +1185,11 @@ function UserActionModal({
1742
1185
  hide.remove();
1743
1186
  };
1744
1187
  }, []);
1745
- const resetActionState = React.useCallback(() => {
1188
+ const resetActionState = react.useCallback(() => {
1746
1189
  setIsSubmitting(false);
1747
1190
  setActionType(null);
1748
1191
  }, []);
1749
- React.useEffect(() => {
1192
+ react.useEffect(() => {
1750
1193
  if (isOpen) {
1751
1194
  setResendCooldownRemaining(RESEND_OTP_COOLDOWN_SECONDS);
1752
1195
  } else {
@@ -1759,7 +1202,7 @@ function UserActionModal({
1759
1202
  submitGenerationRef.current += 1;
1760
1203
  }
1761
1204
  }, [isOpen, resetActionState]);
1762
- React.useEffect(() => {
1205
+ react.useEffect(() => {
1763
1206
  if (resendCooldownRemaining <= 0) return;
1764
1207
  const timer = setTimeout(
1765
1208
  () => setResendCooldownRemaining((prev) => prev - 1),
@@ -1767,7 +1210,7 @@ function UserActionModal({
1767
1210
  );
1768
1211
  return () => clearTimeout(timer);
1769
1212
  }, [resendCooldownRemaining]);
1770
- React.useEffect(() => {
1213
+ react.useEffect(() => {
1771
1214
  if (clearOtpTrigger > 0) {
1772
1215
  setOtpError(true);
1773
1216
  const t = setTimeout(() => {
@@ -1778,7 +1221,7 @@ function UserActionModal({
1778
1221
  return () => clearTimeout(t);
1779
1222
  }
1780
1223
  }, [clearOtpTrigger, resetActionState]);
1781
- React.useEffect(() => {
1224
+ react.useEffect(() => {
1782
1225
  if (!isOpen || !isSubmitting) return;
1783
1226
  if (actionType !== "approve" && actionType !== "reject") return;
1784
1227
  const timeout = setTimeout(
@@ -1787,7 +1230,7 @@ function UserActionModal({
1787
1230
  );
1788
1231
  return () => clearTimeout(timeout);
1789
1232
  }, [isOpen, isSubmitting, actionType, resetActionState]);
1790
- React.useEffect(() => {
1233
+ react.useEffect(() => {
1791
1234
  if (!isOpen || !userActionRequest) return;
1792
1235
  if (otp.length !== schema.maxLength || !/^\d+$/.test(otp)) {
1793
1236
  return;
@@ -1820,7 +1263,7 @@ function UserActionModal({
1820
1263
  onApprove,
1821
1264
  resetActionState
1822
1265
  ]);
1823
- const handleReject = React.useCallback(async () => {
1266
+ const handleReject = react.useCallback(async () => {
1824
1267
  setIsSubmitting(true);
1825
1268
  setActionType("reject");
1826
1269
  try {
@@ -1829,7 +1272,7 @@ function UserActionModal({
1829
1272
  resetActionState();
1830
1273
  }
1831
1274
  }, [onReject, resetActionState]);
1832
- const handleResend = React.useCallback(async () => {
1275
+ const handleResend = react.useCallback(async () => {
1833
1276
  if (resendCooldownRemaining > 0) return;
1834
1277
  setIsSubmitting(true);
1835
1278
  setActionType("resend");
@@ -1860,25 +1303,25 @@ function UserActionModal({
1860
1303
  children: /* @__PURE__ */ jsxRuntime.jsx(
1861
1304
  reactNative.KeyboardAvoidingView,
1862
1305
  {
1863
- style: [s7.keyboardAvoid, { justifyContent: modalPosition }],
1306
+ style: [s2.keyboardAvoid, { justifyContent: modalPosition }],
1864
1307
  behavior: reactNative.Platform.OS === "ios" ? "padding" : "height",
1865
1308
  keyboardVerticalOffset: reactNative.Platform.OS === "ios" ? 0 : 20,
1866
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [s7.modalOverlay, { justifyContent: modalPosition }], children: /* @__PURE__ */ jsxRuntime.jsx(
1309
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [s2.modalOverlay, { justifyContent: modalPosition }], children: /* @__PURE__ */ jsxRuntime.jsx(
1867
1310
  reactNative.ScrollView,
1868
1311
  {
1869
1312
  contentContainerStyle: [
1870
- s7.scrollContent,
1313
+ s2.scrollContent,
1871
1314
  { justifyContent: modalPosition }
1872
1315
  ],
1873
1316
  showsVerticalScrollIndicator: false,
1874
1317
  keyboardShouldPersistTaps: "handled",
1875
- children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [s7.dialog, { width: DIALOG_MAX_WIDTH }], children: [
1318
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [s2.dialog, { width: DIALOG_MAX_WIDTH }], children: [
1876
1319
  /* @__PURE__ */ jsxRuntime.jsx(
1877
1320
  reactNative.Pressable,
1878
1321
  {
1879
1322
  onPress: handleReject,
1880
1323
  disabled: isSubmitting,
1881
- style: s7.closeBtn,
1324
+ style: s2.closeBtn,
1882
1325
  accessibilityLabel: "Close",
1883
1326
  hitSlop: 8,
1884
1327
  children: ({ pressed }) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -1894,19 +1337,19 @@ function UserActionModal({
1894
1337
  /* @__PURE__ */ jsxRuntime.jsx(
1895
1338
  reactNative.Text,
1896
1339
  {
1897
- style: s7.description,
1340
+ style: s2.description,
1898
1341
  accessibilityLabel: "payman-modal-desc",
1899
1342
  children: userActionRequest.message
1900
1343
  }
1901
1344
  ),
1902
- isPayment ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.transferLabel, children: "Pay" }) : null,
1903
- isPayment && userActionRequest.metadata?.amount ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.heroAmount, children: formatAmountForDisplay(userActionRequest.metadata.amount) }) : null,
1904
- isPayee ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.transferLabel, children: "Create Payee" }) : null,
1905
- isPayee && userActionRequest.metadata?.payeeName ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s7.heroPayeeWrap, children: [
1906
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.heroPayeeName, children: userActionRequest.metadata.payeeName }),
1907
- userActionRequest.metadata.payeeType ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.heroPayeeType, children: userActionRequest.metadata.payeeType }) : null
1345
+ isPayment ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.transferLabel, children: "Pay" }) : null,
1346
+ isPayment && userActionRequest.metadata?.amount ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.heroAmount, children: formatAmountForDisplay(userActionRequest.metadata.amount) }) : null,
1347
+ isPayee ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.transferLabel, children: "Create Payee" }) : null,
1348
+ isPayee && userActionRequest.metadata?.payeeName ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.heroPayeeWrap, children: [
1349
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.heroPayeeName, children: userActionRequest.metadata.payeeName }),
1350
+ userActionRequest.metadata.payeeType ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.heroPayeeType, children: userActionRequest.metadata.payeeType }) : null
1908
1351
  ] }) : null,
1909
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s7.otpWrap, accessibilityLabel: "payman-otp-wrap", children: [
1352
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.otpWrap, accessibilityLabel: "payman-otp-wrap", children: [
1910
1353
  /* @__PURE__ */ jsxRuntime.jsx(
1911
1354
  OtpInput,
1912
1355
  {
@@ -1917,7 +1360,7 @@ function UserActionModal({
1917
1360
  error: otpError
1918
1361
  }
1919
1362
  ),
1920
- isVerifying ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s7.verifyingRow, children: [
1363
+ isVerifying ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.verifyingRow, children: [
1921
1364
  /* @__PURE__ */ jsxRuntime.jsx(
1922
1365
  reactNative.ActivityIndicator,
1923
1366
  {
@@ -1925,10 +1368,10 @@ function UserActionModal({
1925
1368
  color: PAYMAN_BRAND_GREEN
1926
1369
  }
1927
1370
  ),
1928
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.verifyingText, children: MODAL_CONTENT.LOADING_APPROVE })
1371
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.verifyingText, children: MODAL_CONTENT.LOADING_APPROVE })
1929
1372
  ] }) : null
1930
1373
  ] }),
1931
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s7.linksCol, children: [
1374
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.linksCol, children: [
1932
1375
  /* @__PURE__ */ jsxRuntime.jsx(
1933
1376
  reactNative.Pressable,
1934
1377
  {
@@ -1940,9 +1383,9 @@ function UserActionModal({
1940
1383
  reactNative.Text,
1941
1384
  {
1942
1385
  style: [
1943
- s7.linkText,
1386
+ s2.linkText,
1944
1387
  pressed && { opacity: 0.6 },
1945
- (isSubmitting || resendCooldownRemaining > 0) && s7.linkDisabled
1388
+ (isSubmitting || resendCooldownRemaining > 0) && s2.linkDisabled
1946
1389
  ],
1947
1390
  children: actionType === "resend" ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: MODAL_CONTENT.LOADING_RESEND }) : resendCooldownRemaining > 0 ? `${MODAL_CONTENT.RESEND_AVAILABLE_IN} ${resendCooldownRemaining}s` : BUTTON_LABELS.RESEND_CODE
1948
1391
  }
@@ -1968,11 +1411,11 @@ function UserActionModal({
1968
1411
  reactNative.Text,
1969
1412
  {
1970
1413
  style: [
1971
- s7.linkText,
1414
+ s2.linkText,
1972
1415
  pressed && !isSubmitting && {
1973
1416
  color: PAYMAN.foreground
1974
1417
  },
1975
- isSubmitting && !isCancelling && s7.linkDisabled
1418
+ isSubmitting && !isCancelling && s2.linkDisabled
1976
1419
  ],
1977
1420
  children: isCancelling ? MODAL_CONTENT.LOADING_REJECT : isPayee ? BUTTON_LABELS.CANCEL : BUTTON_LABELS.CANCEL_TRANSFER
1978
1421
  }
@@ -1981,19 +1424,19 @@ function UserActionModal({
1981
1424
  }
1982
1425
  )
1983
1426
  ] }),
1984
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s7.footer, children: [
1985
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.footerPrefix, children: MODAL_CONTENT.SECURED_BY_PREFIX }),
1427
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s2.footer, children: [
1428
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.footerPrefix, children: MODAL_CONTENT.SECURED_BY_PREFIX }),
1986
1429
  /* @__PURE__ */ jsxRuntime.jsx(
1987
1430
  reactNative.Image,
1988
1431
  {
1989
1432
  source: { uri: payman_mono_crop_blue_default },
1990
- style: s7.footerLogo,
1433
+ style: s2.footerLogo,
1991
1434
  resizeMode: "contain",
1992
1435
  accessibilityElementsHidden: true,
1993
1436
  importantForAccessibility: "no-hide-descendants"
1994
1437
  }
1995
1438
  ),
1996
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.footerBrand, children: MODAL_CONTENT.SECURED_BY_BRAND })
1439
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s2.footerBrand, children: MODAL_CONTENT.SECURED_BY_BRAND })
1997
1440
  ] })
1998
1441
  ] })
1999
1442
  }
@@ -2003,7 +1446,7 @@ function UserActionModal({
2003
1446
  }
2004
1447
  );
2005
1448
  }
2006
- var s7 = reactNative.StyleSheet.create({
1449
+ var s2 = reactNative.StyleSheet.create({
2007
1450
  keyboardAvoid: {
2008
1451
  flex: 1,
2009
1452
  width: "100%"
@@ -2144,297 +1587,222 @@ var s7 = reactNative.StyleSheet.create({
2144
1587
  color: "#0A3B44"
2145
1588
  }
2146
1589
  });
2147
- var DEFAULT_USER_ACTION_STATE = {
2148
- request: null,
2149
- clearOtpTrigger: 0
2150
- };
2151
- var NOOP_ASYNC = async () => {
2152
- };
2153
- function PaymanChat({
1590
+
1591
+ // src/utils/formatDate.ts
1592
+ function formatDate(timestamp) {
1593
+ const date = typeof timestamp === "string" ? new Date(timestamp) : timestamp;
1594
+ const now = /* @__PURE__ */ new Date();
1595
+ const diffMs = now.getTime() - date.getTime();
1596
+ const diffMins = Math.floor(diffMs / 6e4);
1597
+ const diffHours = Math.floor(diffMs / 36e5);
1598
+ const diffDays = Math.floor(diffMs / 864e5);
1599
+ if (diffMins < 1) {
1600
+ return "Just now";
1601
+ }
1602
+ if (diffMins < 60) {
1603
+ return `${diffMins}m ago`;
1604
+ }
1605
+ if (diffHours < 24) {
1606
+ return `${diffHours}h ago`;
1607
+ }
1608
+ if (diffDays < 7) {
1609
+ return `${diffDays}d ago`;
1610
+ }
1611
+ return date.toLocaleDateString("en-US", {
1612
+ month: "short",
1613
+ day: "numeric",
1614
+ year: date.getFullYear() !== now.getFullYear() ? "numeric" : void 0
1615
+ });
1616
+ }
1617
+ function captureSentryError(error, context) {
1618
+ if (!Sentry__namespace.getClient()) return;
1619
+ const tags = {};
1620
+ if (context.executionId) tags.executionId = context.executionId;
1621
+ if (context.sessionId) tags.sessionId = context.sessionId;
1622
+ if (context.route) tags.route = context.route;
1623
+ if (context.cfRay) tags.cfRay = context.cfRay;
1624
+ if (context.customerId) tags.customerId = context.customerId;
1625
+ if (context.customerEmail) tags.customerEmail = context.customerEmail;
1626
+ const contexts = {
1627
+ chat_session: {
1628
+ sessionId: context.sessionId ?? null,
1629
+ sessionOwnerId: context.sessionOwnerId ?? null,
1630
+ executionId: context.executionId ?? null,
1631
+ workflowName: context.workflowName ?? null,
1632
+ cfRay: context.cfRay ?? null,
1633
+ customerId: context.customerId ?? null,
1634
+ customerEmail: context.customerEmail ?? null
1635
+ }
1636
+ };
1637
+ if (typeof error === "string") {
1638
+ Sentry__namespace.captureMessage(error, { level: "error", tags, contexts });
1639
+ } else {
1640
+ Sentry__namespace.captureException(error, { tags, contexts });
1641
+ }
1642
+ }
1643
+ function FloatingChat({
2154
1644
  config,
2155
1645
  callbacks = {},
2156
- className,
2157
- style,
2158
- children
1646
+ buttonPosition = "bottom-right",
1647
+ buttonSize = "md",
1648
+ buttonColor,
1649
+ buttonIcon,
1650
+ buttonIconUrl,
1651
+ windowWidth = 380,
1652
+ windowHeight = 560,
1653
+ headerTitle = "AI Assistant",
1654
+ headerSubtitle = "Online \u2022 Ready to help",
1655
+ headerAvatar = "\u{1F916}",
1656
+ headerColor,
1657
+ defaultOpen = false,
1658
+ showNotificationBadge = false
2159
1659
  }) {
2160
- const [inputValue, setInputValue] = React.useState("");
2161
- const [recordingElapsedSeconds, setRecordingElapsedSeconds] = React.useState(0);
2162
- const recordingStartRef = React.useRef(null);
2163
- const recordingIntervalRef = React.useRef(null);
2164
- const prevInputValueRef = React.useRef(inputValue);
2165
- const scrollMessagesToEndRef = React.useRef(null);
2166
- const chat = paymanTypescriptAskSdk.useChat(config, callbacks);
2167
- const {
2168
- messages,
2169
- sendMessage,
2170
- isWaitingForResponse,
2171
- resetSession,
2172
- clearMessages,
2173
- cancelStream,
2174
- getSessionId,
2175
- getMessages
2176
- } = chat;
2177
- const userActionState = chat.userActionState ?? DEFAULT_USER_ACTION_STATE;
2178
- const approveUserAction = chat.approveUserAction ?? NOOP_ASYNC;
2179
- const rejectUserAction = chat.rejectUserAction ?? NOOP_ASYNC;
2180
- const resendOtp = chat.resendOtp ?? NOOP_ASYNC;
2181
- const isUserActionSupported = typeof chat.approveUserAction === "function" && typeof chat.rejectUserAction === "function" && typeof chat.resendOtp === "function";
2182
- const {
2183
- voiceState,
2184
- transcribedText,
2185
- isAvailable: voiceAvailable,
2186
- startRecording,
2187
- stopRecording,
2188
- clearTranscript
2189
- } = paymanTypescriptAskSdk.useVoice(
2190
- {
2191
- lang: config.voiceLang || "en-US",
2192
- interimResults: config.voiceInterimResults !== false,
2193
- continuous: config.voiceContinuous !== false
2194
- },
2195
- {
2196
- onError: (error) => {
2197
- console.error("Voice error:", error);
2198
- }
2199
- }
2200
- );
2201
- const isRecording = voiceState === "listening";
2202
- const contextValue = React.useMemo(
2203
- () => ({
2204
- resetSession,
2205
- clearMessages,
2206
- cancelStream,
2207
- getSessionId,
2208
- getMessages,
2209
- isWaitingForResponse
2210
- }),
2211
- [
2212
- resetSession,
2213
- clearMessages,
2214
- cancelStream,
2215
- getSessionId,
2216
- getMessages,
2217
- isWaitingForResponse
2218
- ]
2219
- );
2220
- const { onExecutionTraceClick } = callbacks;
2221
- const {
2222
- placeholder = "Type your message...",
2223
- emptyStateText = "What can I help with?",
2224
- sessionParams,
2225
- disableInput = false,
2226
- hasAskPermission = true,
2227
- streamingStepsText,
2228
- completedStepsText,
2229
- showAvatars = false,
2230
- showUserAvatar,
2231
- showAssistantAvatar,
2232
- showAgentName = true,
2233
- agentName = "Assistant",
2234
- showExecutionSteps = true,
2235
- showStreamingDot = true,
2236
- layout = "full-width",
2237
- showTimestamps = false,
2238
- animated = true,
2239
- isChatDisabled = false,
2240
- disabledComponent,
2241
- showEmptyStateIcon = true,
2242
- inputStyle = "rounded",
2243
- enableVoice = false
2244
- } = config;
2245
- const isSessionParamsConfigured = React.useMemo(() => {
2246
- if (!sessionParams) return false;
2247
- return !!(sessionParams.id?.trim() && sessionParams.name?.trim());
2248
- }, [sessionParams?.id, sessionParams?.name]);
2249
- React.useEffect(() => {
2250
- if (isRecording) {
2251
- recordingStartRef.current = Date.now();
2252
- setRecordingElapsedSeconds(0);
2253
- recordingIntervalRef.current = setInterval(() => {
2254
- if (recordingStartRef.current != null) {
2255
- setRecordingElapsedSeconds(
2256
- Math.floor((Date.now() - recordingStartRef.current) / 1e3)
2257
- );
2258
- }
2259
- }, 1e3);
2260
- } else {
2261
- recordingStartRef.current = null;
2262
- if (recordingIntervalRef.current) {
2263
- clearInterval(recordingIntervalRef.current);
2264
- recordingIntervalRef.current = null;
1660
+ const [isOpen, setIsOpen] = react.useState(defaultOpen);
1661
+ const toggleChat = () => setIsOpen(!isOpen);
1662
+ const buttonSizes = { sm: "w-12 h-12", md: "w-14 h-14", lg: "w-16 h-16" };
1663
+ const iconSizes = { sm: "w-5 h-5", md: "w-6 h-6", lg: "w-7 h-7" };
1664
+ const positions = {
1665
+ "bottom-right": "bottom-5 right-5",
1666
+ "bottom-left": "bottom-5 left-5",
1667
+ "top-right": "top-5 right-5",
1668
+ "top-left": "top-5 left-5"
1669
+ };
1670
+ const windowPositions = {
1671
+ "bottom-right": "bottom-[5.5rem] right-5",
1672
+ "bottom-left": "bottom-[5.5rem] left-5",
1673
+ "top-right": "top-[5.5rem] right-5",
1674
+ "top-left": "top-[5.5rem] left-5"
1675
+ };
1676
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1677
+ /* @__PURE__ */ jsxRuntime.jsxs(
1678
+ "button",
1679
+ {
1680
+ onClick: toggleChat,
1681
+ className: cn(
1682
+ "fixed z-[9999] rounded-full shadow-lg",
1683
+ "flex items-center justify-center cursor-pointer border-0 outline-none",
1684
+ "hover:scale-105 active:scale-95 transition-transform duration-200",
1685
+ buttonSizes[buttonSize],
1686
+ positions[buttonPosition],
1687
+ !buttonColor && "bg-foreground text-background"
1688
+ ),
1689
+ style: buttonColor ? { background: buttonColor, color: "#fff" } : void 0,
1690
+ "aria-label": isOpen ? "Close chat" : "Open chat",
1691
+ children: [
1692
+ showNotificationBadge && !isOpen && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-0.5 -right-0.5 w-3 h-3 bg-red-500 rounded-full border-2 border-background" }),
1693
+ buttonIcon ? buttonIcon : buttonIconUrl ? /* @__PURE__ */ jsxRuntime.jsx(
1694
+ "img",
1695
+ {
1696
+ src: buttonIconUrl,
1697
+ alt: "Chat",
1698
+ className: cn("object-contain", iconSizes[buttonSize])
1699
+ }
1700
+ ) : isOpen ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: cn(iconSizes[buttonSize]) }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageCircle, { className: cn(iconSizes[buttonSize]) })
1701
+ ]
2265
1702
  }
2266
- setRecordingElapsedSeconds(0);
2267
- }
2268
- return () => {
2269
- if (recordingIntervalRef.current) {
2270
- clearInterval(recordingIntervalRef.current);
2271
- recordingIntervalRef.current = null;
1703
+ ),
1704
+ isOpen && /* @__PURE__ */ jsxRuntime.jsxs(
1705
+ "div",
1706
+ {
1707
+ className: cn(
1708
+ "fixed z-[9998] bg-card rounded-2xl shadow-2xl overflow-hidden flex flex-col",
1709
+ "border border-border/60",
1710
+ "animate-fade-in-up",
1711
+ windowPositions[buttonPosition]
1712
+ ),
1713
+ style: {
1714
+ width: typeof windowWidth === "number" ? `${windowWidth}px` : windowWidth,
1715
+ height: typeof windowHeight === "number" ? `${windowHeight}px` : windowHeight,
1716
+ maxWidth: "calc(100vw - 40px)",
1717
+ maxHeight: "calc(100vh - 120px)"
1718
+ },
1719
+ children: [
1720
+ /* @__PURE__ */ jsxRuntime.jsxs(
1721
+ "div",
1722
+ {
1723
+ className: cn(
1724
+ "px-4 py-3 flex items-center justify-between shrink-0",
1725
+ !headerColor && "bg-foreground text-background"
1726
+ ),
1727
+ style: headerColor ? { background: headerColor, color: "#fff" } : void 0,
1728
+ children: [
1729
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
1730
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-9 h-9 rounded-xl bg-white/15 flex items-center justify-center text-lg shrink-0", children: headerAvatar }),
1731
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
1732
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-sm leading-tight truncate", children: headerTitle }),
1733
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] opacity-70 leading-tight truncate", children: headerSubtitle })
1734
+ ] })
1735
+ ] }),
1736
+ /* @__PURE__ */ jsxRuntime.jsx(
1737
+ "button",
1738
+ {
1739
+ onClick: toggleChat,
1740
+ className: "w-8 h-8 rounded-lg hover:bg-white/10 flex items-center justify-center transition-colors shrink-0",
1741
+ "aria-label": "Minimize chat",
1742
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "w-4 h-4" })
1743
+ }
1744
+ )
1745
+ ]
1746
+ }
1747
+ ),
1748
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
1749
+ PaymanChat,
1750
+ {
1751
+ config,
1752
+ callbacks,
1753
+ className: "h-full border-0 rounded-none"
1754
+ }
1755
+ ) })
1756
+ ]
2272
1757
  }
2273
- };
2274
- }, [isRecording]);
2275
- React.useEffect(() => {
2276
- const wasEmpty = prevInputValueRef.current.trim() === "";
2277
- const isEmpty = inputValue.trim() === "";
2278
- prevInputValueRef.current = inputValue;
2279
- if (!wasEmpty && isEmpty) {
2280
- clearTranscript();
2281
- stopRecording();
2282
- }
2283
- }, [inputValue, clearTranscript, stopRecording]);
2284
- const handleSend = React.useCallback(() => {
2285
- stopRecording();
2286
- if (inputValue.trim() && !disableInput && isSessionParamsConfigured) {
2287
- sendMessage(inputValue.trim());
2288
- setInputValue("");
2289
- }
2290
- }, [
2291
- inputValue,
2292
- disableInput,
2293
- isSessionParamsConfigured,
2294
- sendMessage,
2295
- stopRecording
2296
- ]);
2297
- const isInputDisabled = isWaitingForResponse || !isSessionParamsConfigured || disableInput;
2298
- const handleVoicePress = React.useCallback(async () => {
2299
- if (!voiceAvailable) return;
2300
- clearTranscript();
2301
- await startRecording();
2302
- }, [voiceAvailable, startRecording, clearTranscript]);
2303
- const handleConfirmRecording = React.useCallback(() => {
2304
- stopRecording();
2305
- if (transcribedText.trim()) setInputValue(transcribedText);
2306
- }, [stopRecording, transcribedText]);
2307
- const handleCancelRecording = React.useCallback(() => {
2308
- stopRecording();
2309
- }, [stopRecording]);
2310
- const handleInputFocus = React.useCallback(() => {
2311
- const run = () => scrollMessagesToEndRef.current?.();
2312
- run();
2313
- requestAnimationFrame(run);
2314
- setTimeout(run, 100);
2315
- setTimeout(run, 280);
2316
- }, []);
2317
- if (isChatDisabled) {
2318
- if (disabledComponent) {
2319
- return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [s8.container, style], children: [
2320
- children,
2321
- disabledComponent
2322
- ] }) });
2323
- }
2324
- return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [s8.container, style], children: [
2325
- children,
2326
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s8.disabledWrap, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s8.disabledText, children: "Chat is currently disabled" }) })
2327
- ] }) });
1758
+ )
1759
+ ] });
1760
+ }
1761
+ var PaymanChatContext = react.createContext(void 0);
1762
+ function usePaymanChat() {
1763
+ const ctx = react.useContext(PaymanChatContext);
1764
+ if (!ctx) {
1765
+ throw new Error("usePaymanChat must be used within a PaymanChat component");
2328
1766
  }
2329
- return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
2330
- reactNative.KeyboardAvoidingView,
2331
- {
2332
- style: [s8.container, style],
2333
- behavior: "padding",
2334
- keyboardVerticalOffset: reactNative.Platform.OS === "ios" ? 90 : 0,
2335
- children: [
2336
- children,
2337
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s8.messageListWrap, children: /* @__PURE__ */ jsxRuntime.jsx(
2338
- MessageList,
2339
- {
2340
- messages,
2341
- isWaitingForResponse,
2342
- isLoading: false,
2343
- emptyStateText,
2344
- showEmptyStateIcon,
2345
- layout,
2346
- showTimestamps,
2347
- stage: config.stage || "DEV",
2348
- animated,
2349
- showAgentName,
2350
- agentName,
2351
- showAvatars,
2352
- showUserAvatar,
2353
- showAssistantAvatar,
2354
- showExecutionSteps,
2355
- showStreamingDot,
2356
- streamingStepsText,
2357
- completedStepsText,
2358
- onExecutionTraceClick,
2359
- scrollToEndHandleRef: scrollMessagesToEndRef
2360
- }
2361
- ) }),
2362
- hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s8.inputBarWrap, children: /* @__PURE__ */ jsxRuntime.jsx(
2363
- ChatInput,
2364
- {
2365
- value: inputValue,
2366
- onChange: setInputValue,
2367
- onSend: handleSend,
2368
- onPause: cancelStream,
2369
- disabled: isInputDisabled,
2370
- placeholder,
2371
- isWaitingForResponse,
2372
- hasSelectedSession: true,
2373
- isSessionParamsConfigured,
2374
- inputStyle,
2375
- layout,
2376
- enableVoice,
2377
- onVoicePress: enableVoice ? handleVoicePress : void 0,
2378
- voiceAvailable: enableVoice && voiceAvailable,
2379
- isRecording: enableVoice && isRecording,
2380
- recordingDurationSeconds: recordingElapsedSeconds,
2381
- onConfirmRecording: enableVoice ? handleConfirmRecording : void 0,
2382
- onCancelRecording: enableVoice ? handleCancelRecording : void 0,
2383
- transcribedText: enableVoice && isRecording ? transcribedText : void 0,
2384
- onInputFocus: handleInputFocus
2385
- }
2386
- ) }),
2387
- /* @__PURE__ */ jsxRuntime.jsx(
2388
- UserActionModal,
2389
- {
2390
- isOpen: isUserActionSupported && userActionState.request !== null,
2391
- userActionRequest: userActionState.request,
2392
- onApprove: approveUserAction,
2393
- onReject: rejectUserAction,
2394
- onResend: resendOtp,
2395
- clearOtpTrigger: userActionState.clearOtpTrigger
2396
- }
2397
- )
2398
- ]
2399
- }
2400
- ) });
1767
+ return ctx;
1768
+ }
1769
+
1770
+ // src/utils/relativeTime.ts
1771
+ function formatRelativeTime(iso, now = /* @__PURE__ */ new Date()) {
1772
+ const then = new Date(iso);
1773
+ if (Number.isNaN(then.getTime())) return "";
1774
+ const diffMs = now.getTime() - then.getTime();
1775
+ const diffSec = Math.round(diffMs / 1e3);
1776
+ const diffMin = Math.round(diffMs / 6e4);
1777
+ const diffHour = Math.round(diffMs / 36e5);
1778
+ if (diffSec < 45) return "just now";
1779
+ if (diffMin < 60) return `${diffMin}m ago`;
1780
+ if (diffHour < 24) return `${diffHour}h ago`;
1781
+ const thenDate = new Date(then.getFullYear(), then.getMonth(), then.getDate());
1782
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
1783
+ const dayDiff = Math.round(
1784
+ (today.getTime() - thenDate.getTime()) / 864e5
1785
+ );
1786
+ if (dayDiff === 1) return "Yesterday";
1787
+ if (dayDiff < 7) {
1788
+ return then.toLocaleDateString(void 0, { weekday: "short" });
1789
+ }
1790
+ const sameYear = then.getFullYear() === now.getFullYear();
1791
+ return then.toLocaleDateString(void 0, {
1792
+ month: "short",
1793
+ day: "numeric",
1794
+ year: sameYear ? void 0 : "numeric"
1795
+ });
2401
1796
  }
2402
- var s8 = reactNative.StyleSheet.create({
2403
- /** Lets the message list shrink when the keyboard opens; pairs with FlatList flex:1. */
2404
- messageListWrap: { flex: 1, minHeight: 0, zIndex: 0 },
2405
- /**
2406
- * Keeps the composer above the message list when scrolling so rows (e.g. stream step toggles)
2407
- * do not paint over the input. Elevation applies on Android only.
2408
- */
2409
- inputBarWrap: {
2410
- width: "100%",
2411
- zIndex: 2,
2412
- ...reactNative.Platform.select({
2413
- android: { elevation: 8 },
2414
- default: {}
2415
- })
2416
- },
2417
- container: {
2418
- flex: 1,
2419
- backgroundColor: "#FFFFFF",
2420
- borderWidth: reactNative.StyleSheet.hairlineWidth,
2421
- borderColor: "rgba(0,0,0,0.08)",
2422
- borderRadius: 16,
2423
- overflow: "hidden"
2424
- },
2425
- disabledWrap: {
2426
- flex: 1,
2427
- justifyContent: "center",
2428
- alignItems: "center",
2429
- padding: 16
2430
- },
2431
- disabledText: { fontSize: 14, color: "#9CA3AF", textAlign: "center" }
2432
- });
2433
1797
 
2434
1798
  Object.defineProperty(exports, "buildFormattedThinking", {
2435
1799
  enumerable: true,
2436
1800
  get: function () { return paymanTypescriptAskSdk.buildFormattedThinking; }
2437
1801
  });
1802
+ Object.defineProperty(exports, "buildScopeKey", {
1803
+ enumerable: true,
1804
+ get: function () { return paymanTypescriptAskSdk.buildScopeKey; }
1805
+ });
2438
1806
  Object.defineProperty(exports, "cancelUserAction", {
2439
1807
  enumerable: true,
2440
1808
  get: function () { return paymanTypescriptAskSdk.cancelUserAction; }
@@ -2447,6 +1815,14 @@ Object.defineProperty(exports, "generateId", {
2447
1815
  enumerable: true,
2448
1816
  get: function () { return paymanTypescriptAskSdk.generateId; }
2449
1817
  });
1818
+ Object.defineProperty(exports, "listConversations", {
1819
+ enumerable: true,
1820
+ get: function () { return paymanTypescriptAskSdk.listConversations; }
1821
+ });
1822
+ Object.defineProperty(exports, "listSessions", {
1823
+ enumerable: true,
1824
+ get: function () { return paymanTypescriptAskSdk.listSessions; }
1825
+ });
2450
1826
  Object.defineProperty(exports, "processStreamEventV2", {
2451
1827
  enumerable: true,
2452
1828
  get: function () { return paymanTypescriptAskSdk.processStreamEventV2; }
@@ -2463,10 +1839,6 @@ Object.defineProperty(exports, "submitUserAction", {
2463
1839
  enumerable: true,
2464
1840
  get: function () { return paymanTypescriptAskSdk.submitUserAction; }
2465
1841
  });
2466
- Object.defineProperty(exports, "useChat", {
2467
- enumerable: true,
2468
- get: function () { return paymanTypescriptAskSdk.useChat; }
2469
- });
2470
1842
  Object.defineProperty(exports, "useChatV2", {
2471
1843
  enumerable: true,
2472
1844
  get: function () { return paymanTypescriptAskSdk.useChatV2; }
@@ -2475,11 +1847,17 @@ Object.defineProperty(exports, "useVoice", {
2475
1847
  enumerable: true,
2476
1848
  get: function () { return paymanTypescriptAskSdk.useVoice; }
2477
1849
  });
1850
+ exports.ChatHeader = ChatHeader;
1851
+ exports.FloatingChat = FloatingChat;
2478
1852
  exports.PaymanChat = PaymanChat;
2479
1853
  exports.PaymanChatContext = PaymanChatContext;
1854
+ exports.SessionHistorySidebar = SessionHistorySidebar;
1855
+ exports.UserActionModal = UserActionModal;
2480
1856
  exports.captureSentryError = captureSentryError;
2481
1857
  exports.cn = cn;
2482
1858
  exports.formatDate = formatDate;
1859
+ exports.formatRelativeTime = formatRelativeTime;
2483
1860
  exports.usePaymanChat = usePaymanChat;
1861
+ exports.useSessionHistory = useSessionHistory;
2484
1862
  //# sourceMappingURL=index.native.js.map
2485
1863
  //# sourceMappingURL=index.native.js.map