@paymanai/payman-ask-sdk 4.0.0 → 4.0.1

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,2476 +0,0 @@
1
- 'use strict';
2
-
3
- var paymanTypescriptAskSdk = require('@paymanai/payman-typescript-ask-sdk');
4
- var React = require('react');
5
- var reactNative = require('react-native');
6
- var lucideReactNative = require('lucide-react-native');
7
- var jsxRuntime = require('react/jsx-runtime');
8
- var Animated2 = require('react-native-reanimated');
9
- var Markdown = require('react-native-markdown-display');
10
- var clsx = require('clsx');
11
- var tailwindMerge = require('tailwind-merge');
12
- var Sentry = require('@sentry/react');
13
-
14
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
-
16
- function _interopNamespace(e) {
17
- if (e && e.__esModule) return e;
18
- var n = Object.create(null);
19
- if (e) {
20
- Object.keys(e).forEach(function (k) {
21
- if (k !== 'default') {
22
- var d = Object.getOwnPropertyDescriptor(e, k);
23
- Object.defineProperty(n, k, d.get ? d : {
24
- enumerable: true,
25
- get: function () { return e[k]; }
26
- });
27
- }
28
- });
29
- }
30
- n.default = e;
31
- return Object.freeze(n);
32
- }
33
-
34
- var React__default = /*#__PURE__*/_interopDefault(React);
35
- var Animated2__default = /*#__PURE__*/_interopDefault(Animated2);
36
- var Markdown__default = /*#__PURE__*/_interopDefault(Markdown);
37
- var Sentry__namespace = /*#__PURE__*/_interopNamespace(Sentry);
38
-
39
- // 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",
392
- alignItems: "center",
393
- borderRadius: 18,
394
- overflow: "hidden"
395
- },
396
- iconBtnInner: {
397
- width: 36,
398
- height: 36,
399
- 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
420
- },
421
- input: {
422
- minHeight: 56,
423
- maxHeight: 140,
424
- paddingVertical: 16,
425
- paddingHorizontal: 16,
426
- 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
- fontWeight: "600",
520
- color: VOICE_THEME_COLOR,
521
- minWidth: 36,
522
- textAlign: "right"
523
- },
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"
533
- }
534
- });
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);
543
- }
544
- function parseErrorPayload(payload) {
545
- 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
- };
556
- }
557
- } catch {
558
- }
559
- return {};
560
- }
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;
574
- }
575
- return rawPayload || void 0;
576
- }
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 };
612
- });
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
674
- );
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]
696
- );
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));
708
- }
709
- return `${count} ${stepWord} completed`;
710
- },
711
- [message.steps, streamingStepsText, completedStepsText, totalElapsedMs]
712
- );
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 }) });
732
- };
733
- const showSteps = showExecutionSteps && hasSteps && !isStreaming && !isError;
734
- const handleStepsToggle = React.useCallback(() => {
735
- setIsStepsExpanded((prev) => !prev);
736
- }, []);
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,
744
- {
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
- ]
790
- }
791
- ),
792
- hasTraceData && onExecutionTraceClick && /* @__PURE__ */ jsxRuntime.jsx(
793
- reactNative.Pressable,
794
- {
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" })
802
- }
803
- )
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: [
807
- /* @__PURE__ */ jsxRuntime.jsx(
808
- reactNative.View,
809
- {
810
- style: {
811
- transform: [{ rotate: isStepsExpanded ? "90deg" : "0deg" }]
812
- },
813
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.ChevronRight, { size: 12, color: "rgba(156,163,175,0.6)" })
814
- }
815
- ),
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 });
861
- }
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));
1121
- }
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
- });
1148
- }
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
- agentId: context.agentId ?? null,
1164
- cfRay: context.cfRay ?? null,
1165
- customerId: context.customerId ?? null,
1166
- customerEmail: context.customerEmail ?? null
1167
- }
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
- ] }) });
1189
- }
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 = "DEVELOPMENT",
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
1236
- }) {
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,
1242
- {
1243
- message,
1244
- animated,
1245
- showAvatar: actualShowUserAvatar
1246
- }
1247
- );
1248
- }
1249
- return /* @__PURE__ */ jsxRuntime.jsx(
1250
- AgentMessage,
1251
- {
1252
- message,
1253
- stage,
1254
- animated,
1255
- showAgentName,
1256
- agentName,
1257
- showAvatar: actualShowAssistantAvatar,
1258
- showExecutionSteps,
1259
- showStreamingDot,
1260
- streamingStepsText,
1261
- completedStepsText,
1262
- onExecutionTraceClick
1263
- }
1264
- );
1265
- }
1266
- function MessageRowSkeleton({
1267
- isRightAligned = false
1268
- }) {
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
- return /* @__PURE__ */ jsxRuntime.jsx(
1275
- reactNative.View,
1276
- {
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
- )
1302
- }
1303
- );
1304
- }
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 = "DEVELOPMENT",
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);
1389
- }
1390
- prevLastStreamingRef.current = streaming;
1391
- }, [messages, scrollToEnd]);
1392
- React.useEffect(() => {
1393
- if (prevWaitingRef.current === true && isWaitingForResponse === false) {
1394
- setTimeout(() => scrollToEnd(), 50);
1395
- }
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
- )) }) });
1406
- }
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
- ] }) });
1419
- }
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
- }
1455
- );
1456
- }
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",
1465
- alignItems: "center",
1466
- padding: 24
1467
- },
1468
- emptyContent: { alignItems: "center", gap: 10, maxWidth: 280 },
1469
- iconContainer: {
1470
- width: 56,
1471
- 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",
1477
- 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
1491
- },
1492
- listContent: {
1493
- paddingTop: 16,
1494
- paddingHorizontal: 16,
1495
- paddingBottom: 16,
1496
- gap: 16
1497
- },
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
- }
1503
- });
1504
-
1505
- // src/assets/payman-mono-crop-blue.png
1506
- var payman_mono_crop_blue_default = "data:image/png;base64,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";
1507
- var DEFAULT_MAX_LENGTH = 6;
1508
- var MAX_SUPPORTED_LENGTH = 12;
1509
- var OTP_ERROR_BORDER = "#ef4444";
1510
- var COMPLETE_PULSE_MS = 350;
1511
- var SHAKE_MS = 400;
1512
- function OtpInput({
1513
- value,
1514
- onChange,
1515
- maxLength,
1516
- disabled = false,
1517
- error = false
1518
- }) {
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);
1524
- const safeMaxLength = Number.isInteger(maxLength) && maxLength > 0 ? Math.min(maxLength, MAX_SUPPORTED_LENGTH) : DEFAULT_MAX_LENGTH;
1525
- const digits = value.split("").concat(Array(safeMaxLength).fill("")).slice(0, safeMaxLength);
1526
- const isFull = value.length === safeMaxLength && /^\d+$/.test(value);
1527
- React.useEffect(() => {
1528
- if (!disabled) {
1529
- inputRefs.current[0]?.focus();
1530
- }
1531
- }, [disabled]);
1532
- React.useEffect(() => {
1533
- if (!error) {
1534
- setInternalError(false);
1535
- return;
1536
- }
1537
- setInternalError(true);
1538
- reactNative.Animated.sequence([
1539
- reactNative.Animated.timing(shakeAnim, { toValue: 1, duration: SHAKE_MS, useNativeDriver: true }),
1540
- reactNative.Animated.timing(shakeAnim, { toValue: 0, duration: 0, useNativeDriver: true })
1541
- ]).start();
1542
- }, [error, shakeAnim]);
1543
- React.useEffect(() => {
1544
- if (isFull && prevLenRef.current < safeMaxLength) {
1545
- reactNative.Animated.sequence([
1546
- reactNative.Animated.timing(scaleAnim, { toValue: 1.04, duration: COMPLETE_PULSE_MS * 0.4, useNativeDriver: true }),
1547
- reactNative.Animated.timing(scaleAnim, { toValue: 1, duration: COMPLETE_PULSE_MS * 0.6, useNativeDriver: true })
1548
- ]).start();
1549
- }
1550
- prevLenRef.current = value.length;
1551
- }, [isFull, value.length, safeMaxLength, scaleAnim]);
1552
- const shakeTranslate = shakeAnim.interpolate({
1553
- inputRange: [0, 0.15, 0.3, 0.45, 0.6, 0.75, 0.9, 1],
1554
- outputRange: [0, -6, 5, -4, 3, -2, 1, 0]
1555
- });
1556
- const focusInput = (index) => {
1557
- if (index >= 0 && index < safeMaxLength) {
1558
- inputRefs.current[index]?.focus();
1559
- }
1560
- };
1561
- const updateValue = (newDigits) => {
1562
- onChange(newDigits.join("").slice(0, safeMaxLength));
1563
- };
1564
- const handleChange = (index, text) => {
1565
- const cleaned = text.replace(/\D/g, "");
1566
- if (cleaned.length > 1) {
1567
- const pasted = cleaned.slice(0, safeMaxLength);
1568
- const newDigits2 = pasted.split("").concat(Array(safeMaxLength).fill("")).slice(0, safeMaxLength);
1569
- updateValue(newDigits2);
1570
- focusInput(Math.min(pasted.length, safeMaxLength - 1));
1571
- return;
1572
- }
1573
- const char = cleaned.slice(-1);
1574
- const newDigits = [...digits];
1575
- newDigits[index] = char;
1576
- updateValue(newDigits);
1577
- if (char && index < safeMaxLength - 1) {
1578
- focusInput(index + 1);
1579
- }
1580
- };
1581
- const handleKeyPress = (index, key) => {
1582
- if (key === "Backspace") {
1583
- if (digits[index]) {
1584
- const newDigits = [...digits];
1585
- newDigits[index] = "";
1586
- updateValue(newDigits);
1587
- } else if (index > 0) {
1588
- const newDigits = [...digits];
1589
- newDigits[index - 1] = "";
1590
- updateValue(newDigits);
1591
- focusInput(index - 1);
1592
- }
1593
- }
1594
- };
1595
- return /* @__PURE__ */ jsxRuntime.jsx(
1596
- reactNative.Animated.View,
1597
- {
1598
- style: [
1599
- s6.container,
1600
- { transform: [{ translateX: shakeTranslate }, { scale: scaleAnim }] }
1601
- ],
1602
- children: digits.map((digit, i) => /* @__PURE__ */ jsxRuntime.jsx(
1603
- reactNative.TextInput,
1604
- {
1605
- ref: (el) => {
1606
- inputRefs.current[i] = el;
1607
- },
1608
- value: digit,
1609
- editable: !disabled,
1610
- keyboardType: "number-pad",
1611
- maxLength: 1,
1612
- onChangeText: (text) => handleChange(i, text),
1613
- onKeyPress: ({ nativeEvent }) => handleKeyPress(i, nativeEvent.key),
1614
- onFocus: () => inputRefs.current[i]?.setNativeProps({ selection: { start: 0, end: 1 } }),
1615
- style: [
1616
- s6.input,
1617
- disabled && s6.inputDisabled,
1618
- internalError && s6.inputError
1619
- ],
1620
- accessibilityLabel: `Digit ${i + 1}`
1621
- },
1622
- i
1623
- ))
1624
- }
1625
- );
1626
- }
1627
- var PAYMAN_OTP = {
1628
- bg: "#FFFFFF",
1629
- border: "rgba(0,0,0,0.1)",
1630
- fg: "#18181b",
1631
- disabledBg: "rgba(0,0,0,0.03)"
1632
- };
1633
- var s6 = reactNative.StyleSheet.create({
1634
- container: { flexDirection: "row", gap: 8, justifyContent: "center" },
1635
- input: {
1636
- width: 44,
1637
- height: 50,
1638
- textAlign: "center",
1639
- fontSize: 20,
1640
- fontWeight: "600",
1641
- borderWidth: 1,
1642
- borderColor: PAYMAN_OTP.border,
1643
- borderRadius: 10,
1644
- color: PAYMAN_OTP.fg,
1645
- backgroundColor: PAYMAN_OTP.bg
1646
- },
1647
- inputDisabled: { backgroundColor: PAYMAN_OTP.disabledBg, opacity: 0.5 },
1648
- inputError: {
1649
- borderColor: OTP_ERROR_BORDER,
1650
- borderWidth: 1.5
1651
- }
1652
- });
1653
-
1654
- // src/components/UserActionModal/constants.ts
1655
- var BUTTON_LABELS = {
1656
- /** Link-style actions (new layout) */
1657
- RESEND_CODE: "Resend OTP",
1658
- CANCEL_TRANSFER: "Cancel Payment",
1659
- /** Short cancel label for payee approval flows */
1660
- CANCEL: "Cancel"
1661
- };
1662
- var RESEND_OTP_COOLDOWN_SECONDS = 30;
1663
- var DEFAULT_OTP_MAX_LENGTH = 6;
1664
- var MIN_OTP_MAX_LENGTH = 1;
1665
- var MAX_OTP_MAX_LENGTH = 12;
1666
- var ACTION_PENDING_TIMEOUT_MS = 15e3;
1667
- var MODAL_CONTENT = {
1668
- LOADING_APPROVE: "Verifying...",
1669
- LOADING_REJECT: "Rejecting...",
1670
- LOADING_RESEND: "Resending...",
1671
- RESEND_AVAILABLE_IN: "Resend OTP in",
1672
- SECURED_BY_PREFIX: "Secured by",
1673
- SECURED_BY_BRAND: "Payman"
1674
- };
1675
-
1676
- // src/components/UserActionModal/utils.ts
1677
- function getOtpSchemaFromRequest(schema) {
1678
- const properties = schema?.properties;
1679
- const otp = properties?.otp;
1680
- const maxLengthRaw = otp?.maxLength;
1681
- const parsedMaxLength = Number.isInteger(maxLengthRaw) ? Number(maxLengthRaw) : DEFAULT_OTP_MAX_LENGTH;
1682
- const clampedMaxLength = Math.min(
1683
- MAX_OTP_MAX_LENGTH,
1684
- Math.max(MIN_OTP_MAX_LENGTH, parsedMaxLength)
1685
- );
1686
- return {
1687
- maxLength: clampedMaxLength
1688
- };
1689
- }
1690
- function formatAmountForDisplay(amount) {
1691
- const normalized = amount.replace(/,/g, "").trim();
1692
- const n = Number(normalized);
1693
- if (!Number.isFinite(n)) {
1694
- return amount.startsWith("$") ? amount : `$${amount}`;
1695
- }
1696
- return new Intl.NumberFormat("en-US", {
1697
- style: "currency",
1698
- currency: "USD"
1699
- }).format(n);
1700
- }
1701
- var PAYMAN_BRAND_GREEN = "#0A3B44";
1702
- var PAYMAN = {
1703
- mutedForeground: "#71717a",
1704
- border: "#e4e4e7",
1705
- card: "#ffffff",
1706
- modalBackdrop: "rgba(0,0,0,0.4)"
1707
- };
1708
- var { width: SCREEN_WIDTH } = reactNative.Dimensions.get("window");
1709
- var DIALOG_MAX_WIDTH = Math.min(440, SCREEN_WIDTH * 0.94);
1710
- var DIALOG_PADDING = 28;
1711
- var OTP_ERROR_FLASH_MS = 600;
1712
- function UserActionModal({
1713
- isOpen,
1714
- userActionRequest,
1715
- onApprove,
1716
- onReject,
1717
- onResend,
1718
- clearOtpTrigger
1719
- }) {
1720
- const [otp, setOtp] = React.useState("");
1721
- const [actionType, setActionType] = React.useState(null);
1722
- const [isSubmitting, setIsSubmitting] = React.useState(false);
1723
- const [resendCooldownRemaining, setResendCooldownRemaining] = React.useState(0);
1724
- const [keyboardVisible, setKeyboardVisible] = React.useState(false);
1725
- const [otpError, setOtpError] = React.useState(false);
1726
- const lastAutoSubmittedRef = React.useRef("");
1727
- const submitInFlightRef = React.useRef(false);
1728
- const submitGenerationRef = React.useRef(0);
1729
- const schema = getOtpSchemaFromRequest(userActionRequest?.requestedSchema);
1730
- React.useEffect(() => {
1731
- const show = reactNative.Keyboard.addListener(
1732
- reactNative.Platform.OS === "ios" ? "keyboardWillShow" : "keyboardDidShow",
1733
- () => setKeyboardVisible(true)
1734
- );
1735
- const hide = reactNative.Keyboard.addListener(
1736
- reactNative.Platform.OS === "ios" ? "keyboardWillHide" : "keyboardDidHide",
1737
- () => setKeyboardVisible(false)
1738
- );
1739
- return () => {
1740
- show.remove();
1741
- hide.remove();
1742
- };
1743
- }, []);
1744
- const resetActionState = React.useCallback(() => {
1745
- setIsSubmitting(false);
1746
- setActionType(null);
1747
- }, []);
1748
- React.useEffect(() => {
1749
- if (isOpen) {
1750
- setResendCooldownRemaining(RESEND_OTP_COOLDOWN_SECONDS);
1751
- } else {
1752
- setOtp("");
1753
- resetActionState();
1754
- setResendCooldownRemaining(0);
1755
- setOtpError(false);
1756
- lastAutoSubmittedRef.current = "";
1757
- submitInFlightRef.current = false;
1758
- submitGenerationRef.current += 1;
1759
- }
1760
- }, [isOpen, resetActionState]);
1761
- React.useEffect(() => {
1762
- if (resendCooldownRemaining <= 0) return;
1763
- const timer = setTimeout(
1764
- () => setResendCooldownRemaining((prev) => prev - 1),
1765
- 1e3
1766
- );
1767
- return () => clearTimeout(timer);
1768
- }, [resendCooldownRemaining]);
1769
- React.useEffect(() => {
1770
- if (clearOtpTrigger > 0) {
1771
- setOtpError(true);
1772
- const t = setTimeout(() => {
1773
- setOtpError(false);
1774
- setOtp("");
1775
- resetActionState();
1776
- }, OTP_ERROR_FLASH_MS);
1777
- return () => clearTimeout(t);
1778
- }
1779
- }, [clearOtpTrigger, resetActionState]);
1780
- React.useEffect(() => {
1781
- if (!isOpen || !isSubmitting) return;
1782
- if (actionType !== "approve" && actionType !== "reject") return;
1783
- const timeout = setTimeout(
1784
- () => resetActionState(),
1785
- ACTION_PENDING_TIMEOUT_MS
1786
- );
1787
- return () => clearTimeout(timeout);
1788
- }, [isOpen, isSubmitting, actionType, resetActionState]);
1789
- React.useEffect(() => {
1790
- if (!isOpen || !userActionRequest) return;
1791
- if (otp.length !== schema.maxLength || !/^\d+$/.test(otp)) {
1792
- return;
1793
- }
1794
- if (isSubmitting || submitInFlightRef.current) return;
1795
- if (lastAutoSubmittedRef.current === otp) return;
1796
- lastAutoSubmittedRef.current = otp;
1797
- submitInFlightRef.current = true;
1798
- const submitGeneration = submitGenerationRef.current;
1799
- void (async () => {
1800
- setIsSubmitting(true);
1801
- setActionType("approve");
1802
- try {
1803
- await onApprove(otp);
1804
- } catch {
1805
- if (submitGenerationRef.current !== submitGeneration) return;
1806
- resetActionState();
1807
- lastAutoSubmittedRef.current = otp;
1808
- } finally {
1809
- if (submitGenerationRef.current !== submitGeneration) return;
1810
- submitInFlightRef.current = false;
1811
- }
1812
- })();
1813
- }, [
1814
- otp,
1815
- isOpen,
1816
- isSubmitting,
1817
- userActionRequest,
1818
- schema.maxLength,
1819
- onApprove,
1820
- resetActionState
1821
- ]);
1822
- const handleReject = React.useCallback(async () => {
1823
- setIsSubmitting(true);
1824
- setActionType("reject");
1825
- try {
1826
- await onReject();
1827
- } catch {
1828
- resetActionState();
1829
- }
1830
- }, [onReject, resetActionState]);
1831
- const handleResend = React.useCallback(async () => {
1832
- if (resendCooldownRemaining > 0) return;
1833
- setIsSubmitting(true);
1834
- setActionType("resend");
1835
- try {
1836
- await onResend();
1837
- setResendCooldownRemaining(RESEND_OTP_COOLDOWN_SECONDS);
1838
- } catch {
1839
- } finally {
1840
- setActionType(null);
1841
- setIsSubmitting(false);
1842
- }
1843
- }, [resendCooldownRemaining, onResend]);
1844
- if (!userActionRequest) return null;
1845
- const isPayment = userActionRequest.userActionType === "PAYMENT_APPROVAL" && Boolean(userActionRequest.metadata?.amount);
1846
- const isPayee = userActionRequest.userActionType === "PAYEE_APPROVAL" && Boolean(userActionRequest.metadata?.payeeName);
1847
- const isVerifying = actionType === "approve" && isSubmitting;
1848
- const isCancelling = actionType === "reject" && isSubmitting;
1849
- const modalPosition = keyboardVisible ? "flex-end" : "center";
1850
- return /* @__PURE__ */ jsxRuntime.jsx(
1851
- reactNative.Modal,
1852
- {
1853
- visible: isOpen,
1854
- transparent: true,
1855
- animationType: "fade",
1856
- onRequestClose: () => {
1857
- },
1858
- statusBarTranslucent: reactNative.Platform.OS === "android",
1859
- children: /* @__PURE__ */ jsxRuntime.jsx(
1860
- reactNative.KeyboardAvoidingView,
1861
- {
1862
- style: [s7.keyboardAvoid, { justifyContent: modalPosition }],
1863
- behavior: reactNative.Platform.OS === "ios" ? "padding" : "height",
1864
- keyboardVerticalOffset: reactNative.Platform.OS === "ios" ? 0 : 20,
1865
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [s7.modalOverlay, { justifyContent: modalPosition }], children: /* @__PURE__ */ jsxRuntime.jsx(
1866
- reactNative.ScrollView,
1867
- {
1868
- contentContainerStyle: [
1869
- s7.scrollContent,
1870
- { justifyContent: modalPosition }
1871
- ],
1872
- showsVerticalScrollIndicator: false,
1873
- keyboardShouldPersistTaps: "handled",
1874
- children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [s7.dialog, { width: DIALOG_MAX_WIDTH }], children: [
1875
- /* @__PURE__ */ jsxRuntime.jsx(
1876
- reactNative.Pressable,
1877
- {
1878
- onPress: handleReject,
1879
- disabled: isSubmitting,
1880
- style: s7.closeBtn,
1881
- accessibilityLabel: "Close",
1882
- hitSlop: 8,
1883
- children: /* @__PURE__ */ jsxRuntime.jsx(
1884
- lucideReactNative.X,
1885
- {
1886
- size: 16,
1887
- color: PAYMAN.mutedForeground,
1888
- strokeWidth: 2.5
1889
- }
1890
- )
1891
- }
1892
- ),
1893
- /* @__PURE__ */ jsxRuntime.jsx(
1894
- reactNative.Text,
1895
- {
1896
- style: s7.description,
1897
- accessibilityLabel: "payman-modal-desc",
1898
- children: userActionRequest.message
1899
- }
1900
- ),
1901
- isPayment ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.transferLabel, children: "Pay" }) : null,
1902
- isPayment && userActionRequest.metadata?.amount ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.heroAmount, children: formatAmountForDisplay(userActionRequest.metadata.amount) }) : null,
1903
- isPayee ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.transferLabel, children: "Create Payee" }) : null,
1904
- isPayee && userActionRequest.metadata?.payeeName ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s7.heroPayeeWrap, children: [
1905
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.heroPayeeName, children: userActionRequest.metadata.payeeName }),
1906
- userActionRequest.metadata.payeeType ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.heroPayeeType, children: userActionRequest.metadata.payeeType }) : null
1907
- ] }) : null,
1908
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s7.otpWrap, accessibilityLabel: "payman-otp-wrap", children: [
1909
- /* @__PURE__ */ jsxRuntime.jsx(
1910
- OtpInput,
1911
- {
1912
- value: otp,
1913
- onChange: setOtp,
1914
- maxLength: schema.maxLength,
1915
- disabled: isSubmitting,
1916
- error: otpError
1917
- }
1918
- ),
1919
- isVerifying ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s7.verifyingRow, children: [
1920
- /* @__PURE__ */ jsxRuntime.jsx(
1921
- reactNative.ActivityIndicator,
1922
- {
1923
- size: "small",
1924
- color: PAYMAN_BRAND_GREEN
1925
- }
1926
- ),
1927
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.verifyingText, children: MODAL_CONTENT.LOADING_APPROVE })
1928
- ] }) : null
1929
- ] }),
1930
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s7.linksCol, children: [
1931
- /* @__PURE__ */ jsxRuntime.jsx(
1932
- reactNative.Pressable,
1933
- {
1934
- onPress: handleResend,
1935
- disabled: isSubmitting || resendCooldownRemaining > 0,
1936
- accessibilityLabel: "payman-modal-btn-resend",
1937
- testID: "payman-modal-btn-resend",
1938
- children: /* @__PURE__ */ jsxRuntime.jsx(
1939
- reactNative.Text,
1940
- {
1941
- style: [
1942
- s7.linkText,
1943
- (isSubmitting || resendCooldownRemaining > 0) && s7.linkDisabled
1944
- ],
1945
- children: actionType === "resend" ? MODAL_CONTENT.LOADING_RESEND : resendCooldownRemaining > 0 ? `${MODAL_CONTENT.RESEND_AVAILABLE_IN} ${resendCooldownRemaining}s` : BUTTON_LABELS.RESEND_CODE
1946
- }
1947
- )
1948
- }
1949
- ),
1950
- /* @__PURE__ */ jsxRuntime.jsx(
1951
- reactNative.Pressable,
1952
- {
1953
- onPress: handleReject,
1954
- disabled: isSubmitting,
1955
- accessibilityLabel: "payman-modal-btn-reject",
1956
- testID: "payman-modal-btn-reject",
1957
- children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 6 }, children: [
1958
- isCancelling ? /* @__PURE__ */ jsxRuntime.jsx(
1959
- reactNative.ActivityIndicator,
1960
- {
1961
- size: "small",
1962
- color: PAYMAN.mutedForeground
1963
- }
1964
- ) : null,
1965
- /* @__PURE__ */ jsxRuntime.jsx(
1966
- reactNative.Text,
1967
- {
1968
- style: [
1969
- s7.linkText,
1970
- isSubmitting && !isCancelling && s7.linkDisabled
1971
- ],
1972
- children: isCancelling ? MODAL_CONTENT.LOADING_REJECT : isPayee ? BUTTON_LABELS.CANCEL : BUTTON_LABELS.CANCEL_TRANSFER
1973
- }
1974
- )
1975
- ] })
1976
- }
1977
- )
1978
- ] }),
1979
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: s7.footer, children: [
1980
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.footerPrefix, children: MODAL_CONTENT.SECURED_BY_PREFIX }),
1981
- /* @__PURE__ */ jsxRuntime.jsx(
1982
- reactNative.Image,
1983
- {
1984
- source: { uri: payman_mono_crop_blue_default },
1985
- style: s7.footerLogo,
1986
- resizeMode: "contain",
1987
- accessibilityElementsHidden: true,
1988
- importantForAccessibility: "no-hide-descendants"
1989
- }
1990
- ),
1991
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s7.footerBrand, children: MODAL_CONTENT.SECURED_BY_BRAND })
1992
- ] })
1993
- ] })
1994
- }
1995
- ) })
1996
- }
1997
- )
1998
- }
1999
- );
2000
- }
2001
- var s7 = reactNative.StyleSheet.create({
2002
- keyboardAvoid: {
2003
- flex: 1,
2004
- width: "100%"
2005
- },
2006
- modalOverlay: {
2007
- flex: 1,
2008
- width: "100%",
2009
- backgroundColor: PAYMAN.modalBackdrop,
2010
- alignItems: "center",
2011
- paddingHorizontal: 16,
2012
- paddingBottom: 24
2013
- },
2014
- scrollContent: {
2015
- flexGrow: 1,
2016
- width: "100%",
2017
- alignItems: "center",
2018
- paddingBottom: 8
2019
- },
2020
- dialog: {
2021
- position: "relative",
2022
- backgroundColor: PAYMAN.card,
2023
- borderRadius: 16,
2024
- paddingTop: 36,
2025
- paddingHorizontal: DIALOG_PADDING,
2026
- paddingBottom: 28,
2027
- borderWidth: 1,
2028
- borderColor: PAYMAN.border
2029
- },
2030
- closeBtn: {
2031
- position: "absolute",
2032
- right: 10,
2033
- top: 10,
2034
- width: 28,
2035
- height: 28,
2036
- alignItems: "center",
2037
- justifyContent: "center",
2038
- zIndex: 2
2039
- },
2040
- description: {
2041
- fontSize: 14,
2042
- lineHeight: 20,
2043
- color: PAYMAN.mutedForeground,
2044
- textAlign: "center",
2045
- marginBottom: 20,
2046
- paddingHorizontal: 24
2047
- },
2048
- transferLabel: {
2049
- fontSize: 12,
2050
- fontWeight: "600",
2051
- letterSpacing: 0.8,
2052
- textTransform: "uppercase",
2053
- color: PAYMAN.mutedForeground,
2054
- textAlign: "center",
2055
- marginBottom: 4,
2056
- opacity: 0.7
2057
- },
2058
- heroAmount: {
2059
- fontSize: 42,
2060
- fontWeight: "700",
2061
- color: PAYMAN_BRAND_GREEN,
2062
- textAlign: "center",
2063
- marginBottom: 32,
2064
- letterSpacing: -0.5
2065
- },
2066
- heroPayeeWrap: {
2067
- alignItems: "center",
2068
- marginBottom: 32,
2069
- gap: 4
2070
- },
2071
- heroPayeeName: {
2072
- fontSize: 28,
2073
- fontWeight: "700",
2074
- color: PAYMAN_BRAND_GREEN,
2075
- textAlign: "center"
2076
- },
2077
- heroPayeeType: {
2078
- fontSize: 10,
2079
- fontWeight: "600",
2080
- letterSpacing: 0.8,
2081
- textTransform: "uppercase",
2082
- color: PAYMAN_BRAND_GREEN,
2083
- opacity: 0.85
2084
- },
2085
- otpWrap: {
2086
- marginBottom: 30
2087
- },
2088
- verifyingRow: {
2089
- flexDirection: "row",
2090
- alignItems: "center",
2091
- justifyContent: "center",
2092
- gap: 6,
2093
- marginTop: 10
2094
- },
2095
- verifyingText: {
2096
- fontSize: 12,
2097
- fontWeight: "500",
2098
- color: PAYMAN_BRAND_GREEN
2099
- },
2100
- linksCol: {
2101
- alignItems: "center",
2102
- gap: 12,
2103
- marginBottom: 32
2104
- },
2105
- linkText: {
2106
- fontSize: 11,
2107
- fontWeight: "600",
2108
- letterSpacing: 0.02,
2109
- color: PAYMAN.mutedForeground,
2110
- textAlign: "center"
2111
- },
2112
- linkDisabled: {
2113
- opacity: 0.4
2114
- },
2115
- footer: {
2116
- flexDirection: "row",
2117
- alignItems: "center",
2118
- justifyContent: "center",
2119
- flexWrap: "nowrap",
2120
- gap: 6,
2121
- borderTopWidth: 1,
2122
- borderTopColor: PAYMAN.border,
2123
- paddingTop: 20
2124
- },
2125
- footerPrefix: {
2126
- fontSize: 11,
2127
- fontWeight: "500",
2128
- color: PAYMAN.mutedForeground,
2129
- opacity: 0.9
2130
- },
2131
- footerLogo: {
2132
- height: 18,
2133
- width: 18
2134
- },
2135
- footerBrand: {
2136
- fontSize: 12,
2137
- fontWeight: "700",
2138
- letterSpacing: 0.3,
2139
- color: "#0A3B44"
2140
- }
2141
- });
2142
- var DEFAULT_USER_ACTION_STATE = {
2143
- request: null,
2144
- clearOtpTrigger: 0
2145
- };
2146
- var NOOP_ASYNC = async () => {
2147
- };
2148
- function PaymanChat({
2149
- config,
2150
- callbacks = {},
2151
- className,
2152
- style,
2153
- children
2154
- }) {
2155
- const [inputValue, setInputValue] = React.useState("");
2156
- const [recordingElapsedSeconds, setRecordingElapsedSeconds] = React.useState(0);
2157
- const recordingStartRef = React.useRef(null);
2158
- const recordingIntervalRef = React.useRef(null);
2159
- const prevInputValueRef = React.useRef(inputValue);
2160
- const scrollMessagesToEndRef = React.useRef(null);
2161
- const chat = paymanTypescriptAskSdk.useChatV2(config, callbacks);
2162
- const {
2163
- messages,
2164
- sendMessage,
2165
- isWaitingForResponse,
2166
- resetSession,
2167
- clearMessages,
2168
- cancelStream,
2169
- getSessionId,
2170
- getMessages
2171
- } = chat;
2172
- const userActionState = chat.userActionState ?? DEFAULT_USER_ACTION_STATE;
2173
- const approveUserAction = chat.approveUserAction ?? NOOP_ASYNC;
2174
- const rejectUserAction = chat.rejectUserAction ?? NOOP_ASYNC;
2175
- const resendOtp = chat.resendOtp ?? NOOP_ASYNC;
2176
- const isUserActionSupported = typeof chat.approveUserAction === "function" && typeof chat.rejectUserAction === "function" && typeof chat.resendOtp === "function";
2177
- const {
2178
- voiceState,
2179
- transcribedText,
2180
- isAvailable: voiceAvailable,
2181
- startRecording,
2182
- stopRecording,
2183
- clearTranscript
2184
- } = paymanTypescriptAskSdk.useVoice(
2185
- {
2186
- lang: config.voiceLang || "en-US",
2187
- interimResults: config.voiceInterimResults !== false,
2188
- continuous: config.voiceContinuous !== false
2189
- },
2190
- {
2191
- onError: (error) => {
2192
- console.error("Voice error:", error);
2193
- }
2194
- }
2195
- );
2196
- const isRecording = voiceState === "listening";
2197
- const contextValue = React.useMemo(
2198
- () => ({
2199
- resetSession,
2200
- clearMessages,
2201
- cancelStream,
2202
- getSessionId,
2203
- getMessages,
2204
- isWaitingForResponse
2205
- }),
2206
- [
2207
- resetSession,
2208
- clearMessages,
2209
- cancelStream,
2210
- getSessionId,
2211
- getMessages,
2212
- isWaitingForResponse
2213
- ]
2214
- );
2215
- const { onExecutionTraceClick } = callbacks;
2216
- const {
2217
- placeholder = "Type your message...",
2218
- emptyStateText = "What can I help with?",
2219
- sessionParams,
2220
- disableInput = false,
2221
- hasAskPermission = true,
2222
- streamingStepsText,
2223
- completedStepsText,
2224
- showAvatars = false,
2225
- showUserAvatar,
2226
- showAssistantAvatar,
2227
- showAgentName = true,
2228
- agentName = "Assistant",
2229
- showExecutionSteps = true,
2230
- showStreamingDot = true,
2231
- layout = "full-width",
2232
- showTimestamps = false,
2233
- animated = true,
2234
- isChatDisabled = false,
2235
- disabledComponent,
2236
- showEmptyStateIcon = true,
2237
- inputStyle = "rounded",
2238
- enableVoice = false
2239
- } = config;
2240
- const isSessionParamsConfigured = React.useMemo(() => {
2241
- if (!sessionParams) return false;
2242
- return !!(sessionParams.id?.trim() && sessionParams.name?.trim());
2243
- }, [sessionParams?.id, sessionParams?.name]);
2244
- React.useEffect(() => {
2245
- if (isRecording) {
2246
- recordingStartRef.current = Date.now();
2247
- setRecordingElapsedSeconds(0);
2248
- recordingIntervalRef.current = setInterval(() => {
2249
- if (recordingStartRef.current != null) {
2250
- setRecordingElapsedSeconds(
2251
- Math.floor((Date.now() - recordingStartRef.current) / 1e3)
2252
- );
2253
- }
2254
- }, 1e3);
2255
- } else {
2256
- recordingStartRef.current = null;
2257
- if (recordingIntervalRef.current) {
2258
- clearInterval(recordingIntervalRef.current);
2259
- recordingIntervalRef.current = null;
2260
- }
2261
- setRecordingElapsedSeconds(0);
2262
- }
2263
- return () => {
2264
- if (recordingIntervalRef.current) {
2265
- clearInterval(recordingIntervalRef.current);
2266
- recordingIntervalRef.current = null;
2267
- }
2268
- };
2269
- }, [isRecording]);
2270
- React.useEffect(() => {
2271
- const wasEmpty = prevInputValueRef.current.trim() === "";
2272
- const isEmpty = inputValue.trim() === "";
2273
- prevInputValueRef.current = inputValue;
2274
- if (!wasEmpty && isEmpty) {
2275
- clearTranscript();
2276
- stopRecording();
2277
- }
2278
- }, [inputValue, clearTranscript, stopRecording]);
2279
- const handleSend = React.useCallback(() => {
2280
- stopRecording();
2281
- if (inputValue.trim() && !disableInput && isSessionParamsConfigured) {
2282
- sendMessage(inputValue.trim());
2283
- setInputValue("");
2284
- }
2285
- }, [
2286
- inputValue,
2287
- disableInput,
2288
- isSessionParamsConfigured,
2289
- sendMessage,
2290
- stopRecording
2291
- ]);
2292
- const isInputDisabled = isWaitingForResponse || !isSessionParamsConfigured || disableInput;
2293
- const handleVoicePress = React.useCallback(async () => {
2294
- if (!voiceAvailable) return;
2295
- clearTranscript();
2296
- await startRecording();
2297
- }, [voiceAvailable, startRecording, clearTranscript]);
2298
- const handleConfirmRecording = React.useCallback(() => {
2299
- stopRecording();
2300
- if (transcribedText.trim()) setInputValue(transcribedText);
2301
- }, [stopRecording, transcribedText]);
2302
- const handleCancelRecording = React.useCallback(() => {
2303
- stopRecording();
2304
- }, [stopRecording]);
2305
- const handleInputFocus = React.useCallback(() => {
2306
- const run = () => scrollMessagesToEndRef.current?.();
2307
- run();
2308
- requestAnimationFrame(run);
2309
- setTimeout(run, 100);
2310
- setTimeout(run, 280);
2311
- }, []);
2312
- if (isChatDisabled) {
2313
- if (disabledComponent) {
2314
- return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [s8.container, style], children: [
2315
- children,
2316
- disabledComponent
2317
- ] }) });
2318
- }
2319
- return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [s8.container, style], children: [
2320
- children,
2321
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s8.disabledWrap, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: s8.disabledText, children: "Chat is currently disabled" }) })
2322
- ] }) });
2323
- }
2324
- return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
2325
- reactNative.KeyboardAvoidingView,
2326
- {
2327
- style: [s8.container, style],
2328
- behavior: "padding",
2329
- keyboardVerticalOffset: reactNative.Platform.OS === "ios" ? 90 : 0,
2330
- children: [
2331
- children,
2332
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s8.messageListWrap, children: /* @__PURE__ */ jsxRuntime.jsx(
2333
- MessageList,
2334
- {
2335
- messages,
2336
- isWaitingForResponse,
2337
- isLoading: false,
2338
- emptyStateText,
2339
- showEmptyStateIcon,
2340
- layout,
2341
- showTimestamps,
2342
- stage: config.stage || "DEVELOPMENT",
2343
- animated,
2344
- showAgentName,
2345
- agentName,
2346
- showAvatars,
2347
- showUserAvatar,
2348
- showAssistantAvatar,
2349
- showExecutionSteps,
2350
- showStreamingDot,
2351
- streamingStepsText,
2352
- completedStepsText,
2353
- onExecutionTraceClick,
2354
- scrollToEndHandleRef: scrollMessagesToEndRef
2355
- }
2356
- ) }),
2357
- hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s8.inputBarWrap, children: /* @__PURE__ */ jsxRuntime.jsx(
2358
- ChatInput,
2359
- {
2360
- value: inputValue,
2361
- onChange: setInputValue,
2362
- onSend: handleSend,
2363
- onPause: cancelStream,
2364
- disabled: isInputDisabled,
2365
- placeholder,
2366
- isWaitingForResponse,
2367
- hasSelectedSession: true,
2368
- isSessionParamsConfigured,
2369
- inputStyle,
2370
- layout,
2371
- enableVoice,
2372
- onVoicePress: enableVoice ? handleVoicePress : void 0,
2373
- voiceAvailable: enableVoice && voiceAvailable,
2374
- isRecording: enableVoice && isRecording,
2375
- recordingDurationSeconds: recordingElapsedSeconds,
2376
- onConfirmRecording: enableVoice ? handleConfirmRecording : void 0,
2377
- onCancelRecording: enableVoice ? handleCancelRecording : void 0,
2378
- transcribedText: enableVoice && isRecording ? transcribedText : void 0,
2379
- onInputFocus: handleInputFocus
2380
- }
2381
- ) }),
2382
- /* @__PURE__ */ jsxRuntime.jsx(
2383
- UserActionModal,
2384
- {
2385
- isOpen: isUserActionSupported && userActionState.request !== null,
2386
- userActionRequest: userActionState.request,
2387
- onApprove: approveUserAction,
2388
- onReject: rejectUserAction,
2389
- onResend: resendOtp,
2390
- clearOtpTrigger: userActionState.clearOtpTrigger
2391
- }
2392
- )
2393
- ]
2394
- }
2395
- ) });
2396
- }
2397
- var s8 = reactNative.StyleSheet.create({
2398
- /** Lets the message list shrink when the keyboard opens; pairs with FlatList flex:1. */
2399
- messageListWrap: { flex: 1, minHeight: 0, zIndex: 0 },
2400
- /**
2401
- * Keeps the composer above the message list when scrolling so rows (e.g. stream step toggles)
2402
- * do not paint over the input. Elevation applies on Android only.
2403
- */
2404
- inputBarWrap: {
2405
- width: "100%",
2406
- zIndex: 2,
2407
- ...reactNative.Platform.select({
2408
- android: { elevation: 8 },
2409
- default: {}
2410
- })
2411
- },
2412
- container: {
2413
- flex: 1,
2414
- backgroundColor: "#FFFFFF",
2415
- borderWidth: reactNative.StyleSheet.hairlineWidth,
2416
- borderColor: "rgba(0,0,0,0.08)",
2417
- borderRadius: 16,
2418
- overflow: "hidden"
2419
- },
2420
- disabledWrap: {
2421
- flex: 1,
2422
- justifyContent: "center",
2423
- alignItems: "center",
2424
- padding: 16
2425
- },
2426
- disabledText: { fontSize: 14, color: "#9CA3AF", textAlign: "center" }
2427
- });
2428
-
2429
- Object.defineProperty(exports, "buildFormattedThinking", {
2430
- enumerable: true,
2431
- get: function () { return paymanTypescriptAskSdk.buildFormattedThinking; }
2432
- });
2433
- Object.defineProperty(exports, "cancelUserAction", {
2434
- enumerable: true,
2435
- get: function () { return paymanTypescriptAskSdk.cancelUserAction; }
2436
- });
2437
- Object.defineProperty(exports, "createInitialV2State", {
2438
- enumerable: true,
2439
- get: function () { return paymanTypescriptAskSdk.createInitialV2State; }
2440
- });
2441
- Object.defineProperty(exports, "generateId", {
2442
- enumerable: true,
2443
- get: function () { return paymanTypescriptAskSdk.generateId; }
2444
- });
2445
- Object.defineProperty(exports, "processStreamEventV2", {
2446
- enumerable: true,
2447
- get: function () { return paymanTypescriptAskSdk.processStreamEventV2; }
2448
- });
2449
- Object.defineProperty(exports, "resendUserAction", {
2450
- enumerable: true,
2451
- get: function () { return paymanTypescriptAskSdk.resendUserAction; }
2452
- });
2453
- Object.defineProperty(exports, "streamWorkflowEvents", {
2454
- enumerable: true,
2455
- get: function () { return paymanTypescriptAskSdk.streamWorkflowEvents; }
2456
- });
2457
- Object.defineProperty(exports, "submitUserAction", {
2458
- enumerable: true,
2459
- get: function () { return paymanTypescriptAskSdk.submitUserAction; }
2460
- });
2461
- Object.defineProperty(exports, "useChatV2", {
2462
- enumerable: true,
2463
- get: function () { return paymanTypescriptAskSdk.useChatV2; }
2464
- });
2465
- Object.defineProperty(exports, "useVoice", {
2466
- enumerable: true,
2467
- get: function () { return paymanTypescriptAskSdk.useVoice; }
2468
- });
2469
- exports.PaymanChat = PaymanChat;
2470
- exports.PaymanChatContext = PaymanChatContext;
2471
- exports.captureSentryError = captureSentryError;
2472
- exports.cn = cn;
2473
- exports.formatDate = formatDate;
2474
- exports.usePaymanChat = usePaymanChat;
2475
- //# sourceMappingURL=index.native.js.map
2476
- //# sourceMappingURL=index.native.js.map