@paymanai/payman-ask-sdk 1.1.1 → 1.2.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,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var React = require('react');
4
- var reactNative = require('react-native');
5
3
  var paymanTypescriptAskSdk = require('@paymanai/payman-typescript-ask-sdk');
4
+ var React4 = require('react');
5
+ var reactNative = require('react-native');
6
6
  var lucideReactNative = require('lucide-react-native');
7
7
  var jsxRuntime = require('react/jsx-runtime');
8
8
  var Animated = require('react-native-reanimated');
@@ -12,14 +12,14 @@ var tailwindMerge = require('tailwind-merge');
12
12
 
13
13
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
14
14
 
15
- var React__default = /*#__PURE__*/_interopDefault(React);
15
+ var React4__default = /*#__PURE__*/_interopDefault(React4);
16
16
  var Animated__default = /*#__PURE__*/_interopDefault(Animated);
17
17
  var Markdown__default = /*#__PURE__*/_interopDefault(Markdown);
18
18
 
19
19
  // src/components/PaymanChat/index.native.tsx
20
- var PaymanChatContext = React.createContext(void 0);
20
+ var PaymanChatContext = React4.createContext(void 0);
21
21
  function usePaymanChat() {
22
- const context = React.useContext(PaymanChatContext);
22
+ const context = React4.useContext(PaymanChatContext);
23
23
  if (!context) {
24
24
  throw new Error("usePaymanChat must be used within a PaymanChat component");
25
25
  }
@@ -38,10 +38,15 @@ function ChatInput({
38
38
  onClick,
39
39
  inputStyle = "rounded",
40
40
  layout = "full-width",
41
- className
41
+ className,
42
+ enableVoice = false,
43
+ onVoicePress,
44
+ voiceAvailable = false
42
45
  }) {
43
46
  const isInputDisabled = disabled || isWaitingForResponse;
44
47
  const showPauseButton = isWaitingForResponse && onPause;
48
+ const showVoiceButton = enableVoice && onVoicePress != null;
49
+ const isVoiceButtonDisabled = isWaitingForResponse || !voiceAvailable || !isSessionParamsConfigured;
45
50
  const getPlaceholder = () => {
46
51
  if (!hasSelectedSession) {
47
52
  return "Select a version to start chatting";
@@ -51,49 +56,87 @@ function ChatInput({
51
56
  }
52
57
  return placeholder;
53
58
  };
54
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.container, layout === "centered" && styles.containerCentered], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.wrapper, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.inputContainer, inputStyle === "rounded" && styles.inputRounded], children: [
55
- /* @__PURE__ */ jsxRuntime.jsx(
56
- reactNative.TextInput,
57
- {
58
- value,
59
- onChangeText: onChange,
60
- onPress: onClick,
61
- editable: !isInputDisabled,
62
- placeholder: getPlaceholder(),
63
- placeholderTextColor: "#999",
64
- multiline: true,
65
- style: [
66
- styles.input,
67
- isInputDisabled && styles.inputDisabled
68
- ],
69
- returnKeyType: "default",
70
- blurOnSubmit: false
71
- }
72
- ),
73
- showPauseButton ? /* @__PURE__ */ jsxRuntime.jsx(
74
- reactNative.Pressable,
75
- {
76
- onPress: onPause,
77
- style: ({ pressed }) => [
78
- styles.button,
79
- pressed && styles.buttonPressed
80
- ],
81
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Pause, { size: 16, color: "#FFFFFF" })
82
- }
83
- ) : /* @__PURE__ */ jsxRuntime.jsx(
84
- reactNative.Pressable,
85
- {
86
- onPress: onSend,
87
- disabled: isInputDisabled || !value.trim(),
88
- style: ({ pressed }) => [
89
- styles.button,
90
- (isInputDisabled || !value.trim()) && styles.buttonDisabled,
91
- pressed && styles.buttonPressed
92
- ],
93
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Send, { size: 16, color: "#FFFFFF" })
94
- }
95
- )
96
- ] }) }) });
59
+ return /* @__PURE__ */ jsxRuntime.jsx(
60
+ reactNative.View,
61
+ {
62
+ style: [
63
+ styles.container,
64
+ layout === "centered" && styles.containerCentered
65
+ ],
66
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.wrapper, children: /* @__PURE__ */ jsxRuntime.jsxs(
67
+ reactNative.View,
68
+ {
69
+ style: [
70
+ styles.inputContainer,
71
+ inputStyle === "rounded" && styles.inputRounded
72
+ ],
73
+ children: [
74
+ /* @__PURE__ */ jsxRuntime.jsx(
75
+ reactNative.TextInput,
76
+ {
77
+ value,
78
+ onChangeText: onChange,
79
+ onPress: onClick,
80
+ editable: !isInputDisabled,
81
+ placeholder: getPlaceholder(),
82
+ placeholderTextColor: "#999",
83
+ multiline: true,
84
+ style: [styles.input, isInputDisabled && styles.inputDisabled],
85
+ returnKeyType: "default",
86
+ blurOnSubmit: false
87
+ }
88
+ ),
89
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.actionsRow, children: [
90
+ showVoiceButton && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.voiceButton, children: /* @__PURE__ */ jsxRuntime.jsx(
91
+ reactNative.Pressable,
92
+ {
93
+ onPress: onVoicePress,
94
+ disabled: isVoiceButtonDisabled,
95
+ style: ({ pressed }) => [
96
+ styles.iconButton,
97
+ pressed && styles.buttonPressed,
98
+ isVoiceButtonDisabled && styles.buttonDisabled
99
+ ],
100
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Mic, { size: 20, color: "#000000" })
101
+ }
102
+ ) }),
103
+ showPauseButton ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.button, children: /* @__PURE__ */ jsxRuntime.jsx(
104
+ reactNative.Pressable,
105
+ {
106
+ onPress: onPause,
107
+ style: ({ pressed }) => [
108
+ styles.iconButton,
109
+ pressed && styles.buttonPressed
110
+ ],
111
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Pause, { size: 16, color: "#FFFFFF" })
112
+ }
113
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx(
114
+ reactNative.View,
115
+ {
116
+ style: [
117
+ styles.button,
118
+ (isInputDisabled || !value.trim()) && styles.buttonDisabled
119
+ ],
120
+ children: /* @__PURE__ */ jsxRuntime.jsx(
121
+ reactNative.Pressable,
122
+ {
123
+ onPress: onSend,
124
+ disabled: isInputDisabled || !value.trim(),
125
+ style: ({ pressed }) => [
126
+ styles.iconButton,
127
+ pressed && styles.buttonPressed
128
+ ],
129
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Send, { size: 16, color: "#FFFFFF" })
130
+ }
131
+ )
132
+ }
133
+ )
134
+ ] })
135
+ ]
136
+ }
137
+ ) })
138
+ }
139
+ );
97
140
  }
98
141
  var styles = reactNative.StyleSheet.create({
99
142
  container: {
@@ -127,7 +170,7 @@ var styles = reactNative.StyleSheet.create({
127
170
  paddingTop: 12,
128
171
  paddingBottom: 12,
129
172
  paddingLeft: 16,
130
- paddingRight: 48,
173
+ paddingRight: 8,
131
174
  fontSize: 14,
132
175
  color: "#000",
133
176
  lineHeight: 20
@@ -135,10 +178,14 @@ var styles = reactNative.StyleSheet.create({
135
178
  inputDisabled: {
136
179
  opacity: 0.5
137
180
  },
181
+ actionsRow: {
182
+ flexDirection: "row",
183
+ alignItems: "center",
184
+ marginRight: 8,
185
+ marginBottom: 8,
186
+ gap: 4
187
+ },
138
188
  button: {
139
- position: "absolute",
140
- bottom: 8,
141
- right: 8,
142
189
  width: 32,
143
190
  height: 32,
144
191
  borderRadius: 16,
@@ -146,16 +193,28 @@ var styles = reactNative.StyleSheet.create({
146
193
  justifyContent: "center",
147
194
  alignItems: "center"
148
195
  },
196
+ iconButton: {
197
+ width: "100%",
198
+ height: "100%",
199
+ justifyContent: "center",
200
+ alignItems: "center"
201
+ },
149
202
  buttonDisabled: {
150
203
  opacity: 0.5
151
204
  },
152
205
  buttonPressed: {
153
206
  opacity: 0.8
207
+ },
208
+ voiceButton: {
209
+ width: 32,
210
+ height: 32,
211
+ justifyContent: "center",
212
+ alignItems: "center"
154
213
  }
155
214
  });
156
215
  function AnimatedLoader({ size = 16, color = "#007AFF" }) {
157
216
  const rotation = Animated.useSharedValue(0);
158
- React__default.default.useEffect(() => {
217
+ React4__default.default.useEffect(() => {
159
218
  rotation.value = Animated.withRepeat(
160
219
  Animated.withTiming(360, {
161
220
  duration: 1e3,
@@ -191,10 +250,10 @@ function AgentMessage({
191
250
  const isError = message.isError ?? (message.streamProgress === "error" || !!message.errorDetails);
192
251
  const isCancelled = message.isCancelled ?? false;
193
252
  const currentExecutingStepId = message.currentExecutingStepId;
194
- const [isStepsExpanded, setIsStepsExpanded] = React.useState(
253
+ const [isStepsExpanded, setIsStepsExpanded] = React4.useState(
195
254
  isStreaming && hasSteps
196
255
  );
197
- React.useEffect(() => {
256
+ React4.useEffect(() => {
198
257
  if (isStreaming && hasSteps) {
199
258
  setIsStepsExpanded(true);
200
259
  }
@@ -822,7 +881,7 @@ function MessageRowSkeleton({
822
881
  isRightAligned = false
823
882
  }) {
824
883
  const opacity = Animated.useSharedValue(0.3);
825
- React.useEffect(() => {
884
+ React4.useEffect(() => {
826
885
  opacity.value = Animated.withRepeat(
827
886
  Animated.withTiming(1, { duration: 1e3 }),
828
887
  -1,
@@ -937,8 +996,8 @@ function MessageList({
937
996
  onExecutionTraceClick,
938
997
  className
939
998
  }) {
940
- const flatListRef = React.useRef(null);
941
- React.useEffect(() => {
999
+ const flatListRef = React4.useRef(null);
1000
+ React4.useEffect(() => {
942
1001
  if (messages.length > 0) {
943
1002
  setTimeout(() => {
944
1003
  flatListRef.current?.scrollToEnd({ animated: true });
@@ -1059,13 +1118,283 @@ var styles5 = reactNative.StyleSheet.create({
1059
1118
  width: "100%"
1060
1119
  }
1061
1120
  });
1121
+ var WaveformBar = React4.memo(
1122
+ ({
1123
+ delay,
1124
+ isActive,
1125
+ color
1126
+ }) => {
1127
+ const height = React4.useRef(new reactNative.Animated.Value(6)).current;
1128
+ React4.useEffect(() => {
1129
+ if (isActive) {
1130
+ const animation = reactNative.Animated.loop(
1131
+ reactNative.Animated.sequence([
1132
+ reactNative.Animated.timing(height, {
1133
+ toValue: 14 + Math.random() * 10,
1134
+ duration: 300 + Math.random() * 50,
1135
+ delay,
1136
+ easing: reactNative.Easing.inOut(reactNative.Easing.ease),
1137
+ useNativeDriver: false
1138
+ }),
1139
+ reactNative.Animated.timing(height, {
1140
+ toValue: 6 + Math.random() * 4,
1141
+ duration: 300 + Math.random() * 50,
1142
+ easing: reactNative.Easing.inOut(reactNative.Easing.ease),
1143
+ useNativeDriver: false
1144
+ })
1145
+ ])
1146
+ );
1147
+ animation.start();
1148
+ return () => animation.stop();
1149
+ } else {
1150
+ reactNative.Animated.timing(height, {
1151
+ toValue: 6,
1152
+ duration: 300,
1153
+ useNativeDriver: false
1154
+ }).start();
1155
+ }
1156
+ }, [isActive, delay, height]);
1157
+ return /* @__PURE__ */ jsxRuntime.jsx(
1158
+ reactNative.Animated.View,
1159
+ {
1160
+ style: [styles6.waveformBar, { height, backgroundColor: color }]
1161
+ }
1162
+ );
1163
+ }
1164
+ );
1165
+ WaveformBar.displayName = "WaveformBar";
1166
+ var RollingText = React4.memo(({ text, color }) => {
1167
+ const [lines, setLines] = React4__default.default.useState([]);
1168
+ const [currentLineIndex, setCurrentLineIndex] = React4__default.default.useState(0);
1169
+ const translateY = React4.useRef(new reactNative.Animated.Value(0)).current;
1170
+ const opacity = React4.useRef(new reactNative.Animated.Value(1)).current;
1171
+ React4.useEffect(() => {
1172
+ if (!text) {
1173
+ setLines([]);
1174
+ setCurrentLineIndex(0);
1175
+ return;
1176
+ }
1177
+ const words = text.split(" ");
1178
+ const newLines = [];
1179
+ let currentLine = "";
1180
+ words.forEach((word) => {
1181
+ const testLine = currentLine ? `${currentLine} ${word}` : word;
1182
+ if (testLine.length > 35 && currentLine) {
1183
+ newLines.push(currentLine);
1184
+ currentLine = word;
1185
+ } else {
1186
+ currentLine = testLine;
1187
+ }
1188
+ });
1189
+ if (currentLine) {
1190
+ newLines.push(currentLine);
1191
+ }
1192
+ setLines(newLines);
1193
+ if (newLines.length > 0) {
1194
+ const newIndex = newLines.length - 1;
1195
+ if (newIndex > currentLineIndex) {
1196
+ reactNative.Animated.sequence([
1197
+ reactNative.Animated.parallel([
1198
+ reactNative.Animated.timing(translateY, {
1199
+ toValue: -20,
1200
+ duration: 150,
1201
+ useNativeDriver: true
1202
+ }),
1203
+ reactNative.Animated.timing(opacity, {
1204
+ toValue: 0,
1205
+ duration: 150,
1206
+ useNativeDriver: true
1207
+ })
1208
+ ]),
1209
+ reactNative.Animated.timing(translateY, {
1210
+ toValue: 20,
1211
+ duration: 0,
1212
+ useNativeDriver: true
1213
+ }),
1214
+ reactNative.Animated.parallel([
1215
+ reactNative.Animated.timing(translateY, {
1216
+ toValue: 0,
1217
+ duration: 200,
1218
+ easing: reactNative.Easing.out(reactNative.Easing.ease),
1219
+ useNativeDriver: true
1220
+ }),
1221
+ reactNative.Animated.timing(opacity, {
1222
+ toValue: 1,
1223
+ duration: 200,
1224
+ useNativeDriver: true
1225
+ })
1226
+ ])
1227
+ ]).start();
1228
+ setCurrentLineIndex(newIndex);
1229
+ }
1230
+ }
1231
+ }, [text, currentLineIndex, translateY, opacity]);
1232
+ const currentText = lines[currentLineIndex] || text;
1233
+ return /* @__PURE__ */ jsxRuntime.jsx(
1234
+ reactNative.Animated.Text,
1235
+ {
1236
+ style: [
1237
+ styles6.transcribedText,
1238
+ {
1239
+ color,
1240
+ opacity,
1241
+ transform: [{ translateY }]
1242
+ }
1243
+ ],
1244
+ numberOfLines: 1,
1245
+ children: currentText
1246
+ }
1247
+ );
1248
+ });
1249
+ RollingText.displayName = "RollingText";
1250
+ var VoiceOverlay = React4.memo(
1251
+ ({
1252
+ visible,
1253
+ voiceState,
1254
+ transcribedText,
1255
+ onStopRecording
1256
+ }) => {
1257
+ const panelHeight = React4.useRef(new reactNative.Animated.Value(0)).current;
1258
+ const contentOpacity = React4.useRef(new reactNative.Animated.Value(0)).current;
1259
+ const isListening = voiceState === "listening";
1260
+ const PANEL_CONTENT_HEIGHT = 180;
1261
+ const bgColor = "rgba(250, 250, 252, 0.98)";
1262
+ const textColor = "#000000";
1263
+ const mutedColor = "#007AFF";
1264
+ const primaryColor = "#007AFF";
1265
+ React4.useEffect(() => {
1266
+ if (visible) {
1267
+ reactNative.Animated.sequence([
1268
+ reactNative.Animated.timing(panelHeight, {
1269
+ toValue: PANEL_CONTENT_HEIGHT,
1270
+ duration: 300,
1271
+ easing: reactNative.Easing.out(reactNative.Easing.cubic),
1272
+ useNativeDriver: false
1273
+ }),
1274
+ reactNative.Animated.timing(contentOpacity, {
1275
+ toValue: 1,
1276
+ duration: 150,
1277
+ useNativeDriver: true
1278
+ })
1279
+ ]).start();
1280
+ } else {
1281
+ reactNative.Animated.parallel([
1282
+ reactNative.Animated.timing(contentOpacity, {
1283
+ toValue: 0,
1284
+ duration: 100,
1285
+ useNativeDriver: true
1286
+ }),
1287
+ reactNative.Animated.timing(panelHeight, {
1288
+ toValue: 0,
1289
+ duration: 220,
1290
+ easing: reactNative.Easing.in(reactNative.Easing.ease),
1291
+ useNativeDriver: false
1292
+ })
1293
+ ]).start();
1294
+ }
1295
+ }, [visible, panelHeight, contentOpacity]);
1296
+ if (!visible && panelHeight._value === 0) {
1297
+ return null;
1298
+ }
1299
+ return /* @__PURE__ */ jsxRuntime.jsx(
1300
+ reactNative.Animated.View,
1301
+ {
1302
+ style: [
1303
+ styles6.panel,
1304
+ {
1305
+ height: panelHeight,
1306
+ backgroundColor: bgColor
1307
+ }
1308
+ ],
1309
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Animated.View, { style: [styles6.content, { opacity: contentOpacity }], children: [
1310
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles6.textContainer, children: transcribedText.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(RollingText, { text: transcribedText, color: textColor }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.placeholderText, { color: mutedColor }], children: "Listening..." }) }),
1311
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.controlsContainer, children: [
1312
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles6.waveformContainer, children: [0, 1, 2, 3, 4].map((index) => /* @__PURE__ */ jsxRuntime.jsx(
1313
+ WaveformBar,
1314
+ {
1315
+ delay: index * 20,
1316
+ isActive: isListening,
1317
+ color: primaryColor
1318
+ },
1319
+ index
1320
+ )) }),
1321
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: onStopRecording, style: styles6.micButton, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Mic, { size: 24, color: primaryColor, strokeWidth: 2 }) }),
1322
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.hintText, { color: mutedColor }], children: "Tap to stop" })
1323
+ ] })
1324
+ ] })
1325
+ }
1326
+ );
1327
+ }
1328
+ );
1329
+ VoiceOverlay.displayName = "VoiceOverlay";
1330
+ var styles6 = reactNative.StyleSheet.create({
1331
+ panel: {
1332
+ overflow: "hidden",
1333
+ borderTopLeftRadius: 20,
1334
+ borderTopRightRadius: 20,
1335
+ borderWidth: 1,
1336
+ borderColor: "#E5E5E5"
1337
+ },
1338
+ content: {
1339
+ flex: 1,
1340
+ paddingHorizontal: 24,
1341
+ paddingTop: 20
1342
+ },
1343
+ textContainer: {
1344
+ height: 32,
1345
+ justifyContent: "center",
1346
+ marginBottom: 16,
1347
+ overflow: "hidden"
1348
+ },
1349
+ transcribedText: {
1350
+ fontSize: 18,
1351
+ fontWeight: "500",
1352
+ textAlign: "left"
1353
+ },
1354
+ placeholderText: {
1355
+ fontSize: 15,
1356
+ textAlign: "left",
1357
+ fontWeight: "800"
1358
+ },
1359
+ controlsContainer: {
1360
+ flex: 1,
1361
+ alignItems: "center",
1362
+ justifyContent: "flex-start"
1363
+ },
1364
+ waveformContainer: {
1365
+ flexDirection: "row",
1366
+ alignItems: "center",
1367
+ justifyContent: "center",
1368
+ gap: 5,
1369
+ height: 28,
1370
+ marginBottom: 16
1371
+ },
1372
+ waveformBar: {
1373
+ width: 3,
1374
+ borderRadius: 2
1375
+ },
1376
+ micButton: {
1377
+ width: 56,
1378
+ height: 56,
1379
+ borderRadius: 28,
1380
+ alignItems: "center",
1381
+ justifyContent: "center",
1382
+ backgroundColor: "#FFFFFF",
1383
+ borderWidth: 1,
1384
+ borderColor: "#E5E5E5"
1385
+ },
1386
+ hintText: {
1387
+ fontSize: 12,
1388
+ marginTop: 12
1389
+ }
1390
+ });
1062
1391
  var DEFAULT_MAX_LENGTH = 6;
1063
1392
  var MAX_SUPPORTED_LENGTH = 12;
1064
1393
  function OtpInput({ value, onChange, maxLength, disabled = false }) {
1065
- const inputRefs = React.useRef([]);
1394
+ const inputRefs = React4.useRef([]);
1066
1395
  const safeMaxLength = Number.isInteger(maxLength) && maxLength > 0 ? Math.min(maxLength, MAX_SUPPORTED_LENGTH) : DEFAULT_MAX_LENGTH;
1067
1396
  const digits = value.split("").concat(Array(safeMaxLength).fill("")).slice(0, safeMaxLength);
1068
- React.useEffect(() => {
1397
+ React4.useEffect(() => {
1069
1398
  if (!disabled) {
1070
1399
  inputRefs.current[0]?.focus();
1071
1400
  }
@@ -1109,7 +1438,7 @@ function OtpInput({ value, onChange, maxLength, disabled = false }) {
1109
1438
  }
1110
1439
  }
1111
1440
  };
1112
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles6.container, children: digits.map((digit, i) => /* @__PURE__ */ jsxRuntime.jsx(
1441
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles7.container, children: digits.map((digit, i) => /* @__PURE__ */ jsxRuntime.jsx(
1113
1442
  reactNative.TextInput,
1114
1443
  {
1115
1444
  ref: (el) => {
@@ -1122,13 +1451,13 @@ function OtpInput({ value, onChange, maxLength, disabled = false }) {
1122
1451
  onChangeText: (text) => handleChange(i, text),
1123
1452
  onKeyPress: ({ nativeEvent }) => handleKeyPress(i, nativeEvent.key),
1124
1453
  onFocus: () => inputRefs.current[i]?.setNativeProps({ selection: { start: 0, end: 1 } }),
1125
- style: [styles6.input, disabled && styles6.inputDisabled],
1454
+ style: [styles7.input, disabled && styles7.inputDisabled],
1126
1455
  accessibilityLabel: `Digit ${i + 1}`
1127
1456
  },
1128
1457
  i
1129
1458
  )) });
1130
1459
  }
1131
- var styles6 = reactNative.StyleSheet.create({
1460
+ var styles7 = reactNative.StyleSheet.create({
1132
1461
  container: {
1133
1462
  flexDirection: "row",
1134
1463
  gap: 8,
@@ -1192,16 +1521,16 @@ function UserActionModal({
1192
1521
  onResend,
1193
1522
  clearOtpTrigger
1194
1523
  }) {
1195
- const [otp, setOtp] = React.useState("");
1196
- const [actionType, setActionType] = React.useState(null);
1197
- const [isSubmitting, setIsSubmitting] = React.useState(false);
1198
- const [resendCooldownRemaining, setResendCooldownRemaining] = React.useState(0);
1524
+ const [otp, setOtp] = React4.useState("");
1525
+ const [actionType, setActionType] = React4.useState(null);
1526
+ const [isSubmitting, setIsSubmitting] = React4.useState(false);
1527
+ const [resendCooldownRemaining, setResendCooldownRemaining] = React4.useState(0);
1199
1528
  const schema = getOtpSchemaFromRequest(userActionRequest?.requestedSchema);
1200
- const resetActionState = React.useCallback(() => {
1529
+ const resetActionState = React4.useCallback(() => {
1201
1530
  setIsSubmitting(false);
1202
1531
  setActionType(null);
1203
1532
  }, []);
1204
- React.useEffect(() => {
1533
+ React4.useEffect(() => {
1205
1534
  if (isOpen) {
1206
1535
  setResendCooldownRemaining(RESEND_OTP_COOLDOWN_SECONDS);
1207
1536
  } else {
@@ -1210,20 +1539,20 @@ function UserActionModal({
1210
1539
  setResendCooldownRemaining(0);
1211
1540
  }
1212
1541
  }, [isOpen, resetActionState]);
1213
- React.useEffect(() => {
1542
+ React4.useEffect(() => {
1214
1543
  if (resendCooldownRemaining <= 0) return;
1215
1544
  const timer = setTimeout(() => {
1216
1545
  setResendCooldownRemaining((prev) => prev - 1);
1217
1546
  }, 1e3);
1218
1547
  return () => clearTimeout(timer);
1219
1548
  }, [resendCooldownRemaining]);
1220
- React.useEffect(() => {
1549
+ React4.useEffect(() => {
1221
1550
  if (clearOtpTrigger > 0) {
1222
1551
  setOtp("");
1223
1552
  resetActionState();
1224
1553
  }
1225
1554
  }, [clearOtpTrigger, resetActionState]);
1226
- React.useEffect(() => {
1555
+ React4.useEffect(() => {
1227
1556
  if (!isOpen || !isSubmitting) return;
1228
1557
  if (actionType !== "approve" && actionType !== "reject") return;
1229
1558
  const timeout = setTimeout(() => {
@@ -1231,7 +1560,7 @@ function UserActionModal({
1231
1560
  }, ACTION_PENDING_TIMEOUT_MS);
1232
1561
  return () => clearTimeout(timeout);
1233
1562
  }, [isOpen, isSubmitting, actionType, resetActionState]);
1234
- const handleApprove = React.useCallback(async () => {
1563
+ const handleApprove = React4.useCallback(async () => {
1235
1564
  if (otp.length !== schema.maxLength || !/^\d+$/.test(otp)) return;
1236
1565
  setIsSubmitting(true);
1237
1566
  setActionType("approve");
@@ -1241,7 +1570,7 @@ function UserActionModal({
1241
1570
  resetActionState();
1242
1571
  }
1243
1572
  }, [otp, schema.maxLength, onApprove, resetActionState]);
1244
- const handleReject = React.useCallback(async () => {
1573
+ const handleReject = React4.useCallback(async () => {
1245
1574
  setIsSubmitting(true);
1246
1575
  setActionType("reject");
1247
1576
  try {
@@ -1250,7 +1579,7 @@ function UserActionModal({
1250
1579
  resetActionState();
1251
1580
  }
1252
1581
  }, [onReject, resetActionState]);
1253
- const handleResend = React.useCallback(async () => {
1582
+ const handleResend = React4.useCallback(async () => {
1254
1583
  if (resendCooldownRemaining > 0) return;
1255
1584
  setIsSubmitting(true);
1256
1585
  setActionType("resend");
@@ -1279,10 +1608,10 @@ function UserActionModal({
1279
1608
  animationType: "fade",
1280
1609
  onRequestClose: () => {
1281
1610
  },
1282
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles7.backdrop, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.dialog, children: [
1283
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles7.titleContainer, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles7.title, children: MODAL_CONTENT.TITLE }) }),
1284
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles7.description, children: userActionRequest.message }),
1285
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles7.otpContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
1611
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles8.backdrop, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles8.dialog, children: [
1612
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles8.titleContainer, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles8.title, children: MODAL_CONTENT.TITLE }) }),
1613
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles8.description, children: userActionRequest.message }),
1614
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles8.otpContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
1286
1615
  OtpInput,
1287
1616
  {
1288
1617
  value: otp,
@@ -1291,21 +1620,21 @@ function UserActionModal({
1291
1620
  disabled: isSubmitting
1292
1621
  }
1293
1622
  ) }),
1294
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.buttonsColumn, children: [
1295
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.primaryButtonsRow, children: [
1623
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles8.buttonsColumn, children: [
1624
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles8.primaryButtonsRow, children: [
1296
1625
  /* @__PURE__ */ jsxRuntime.jsxs(
1297
1626
  reactNative.Pressable,
1298
1627
  {
1299
1628
  onPress: handleReject,
1300
1629
  disabled: isSubmitting,
1301
1630
  style: ({ pressed }) => [
1302
- styles7.rejectButton,
1303
- isSubmitting && styles7.buttonDisabled,
1304
- pressed && styles7.rejectButtonPressed
1631
+ styles8.rejectButton,
1632
+ isSubmitting && styles8.buttonDisabled,
1633
+ pressed && styles8.rejectButtonPressed
1305
1634
  ],
1306
1635
  children: [
1307
- actionType === "reject" && /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { size: "small", color: "#DC2626", style: styles7.spinner }),
1308
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles7.rejectButtonText, children: actionType === "reject" ? MODAL_CONTENT.LOADING_REJECT : BUTTON_LABELS.REJECT })
1636
+ actionType === "reject" && /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { size: "small", color: "#DC2626", style: styles8.spinner }),
1637
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles8.rejectButtonText, children: actionType === "reject" ? MODAL_CONTENT.LOADING_REJECT : BUTTON_LABELS.REJECT })
1309
1638
  ]
1310
1639
  }
1311
1640
  ),
@@ -1315,13 +1644,13 @@ function UserActionModal({
1315
1644
  onPress: handleApprove,
1316
1645
  disabled: !isOtpValid || isSubmitting,
1317
1646
  style: ({ pressed }) => [
1318
- styles7.approveButton,
1319
- (!isOtpValid || isSubmitting) && styles7.buttonDisabled,
1320
- pressed && styles7.approveButtonPressed
1647
+ styles8.approveButton,
1648
+ (!isOtpValid || isSubmitting) && styles8.buttonDisabled,
1649
+ pressed && styles8.approveButtonPressed
1321
1650
  ],
1322
1651
  children: [
1323
- actionType === "approve" && /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { size: "small", color: "#FFF", style: styles7.spinner }),
1324
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles7.approveButtonText, children: actionType === "approve" ? MODAL_CONTENT.LOADING_APPROVE : BUTTON_LABELS.APPROVE })
1652
+ actionType === "approve" && /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { size: "small", color: "#FFF", style: styles8.spinner }),
1653
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles8.approveButtonText, children: actionType === "approve" ? MODAL_CONTENT.LOADING_APPROVE : BUTTON_LABELS.APPROVE })
1325
1654
  ]
1326
1655
  }
1327
1656
  )
@@ -1332,13 +1661,13 @@ function UserActionModal({
1332
1661
  onPress: handleResend,
1333
1662
  disabled: isSubmitting || resendCooldownRemaining > 0,
1334
1663
  style: ({ pressed }) => [
1335
- styles7.resendButton,
1336
- (isSubmitting || resendCooldownRemaining > 0) && styles7.buttonDisabled,
1337
- pressed && styles7.resendButtonPressed
1664
+ styles8.resendButton,
1665
+ (isSubmitting || resendCooldownRemaining > 0) && styles8.buttonDisabled,
1666
+ pressed && styles8.resendButtonPressed
1338
1667
  ],
1339
1668
  children: [
1340
- actionType === "resend" && /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { size: "small", color: "#666", style: styles7.spinner }),
1341
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles7.resendButtonText, children: getResendLabel() })
1669
+ actionType === "resend" && /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { size: "small", color: "#666", style: styles8.spinner }),
1670
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles8.resendButtonText, children: getResendLabel() })
1342
1671
  ]
1343
1672
  }
1344
1673
  )
@@ -1347,7 +1676,7 @@ function UserActionModal({
1347
1676
  }
1348
1677
  );
1349
1678
  }
1350
- var styles7 = reactNative.StyleSheet.create({
1679
+ var styles8 = reactNative.StyleSheet.create({
1351
1680
  backdrop: {
1352
1681
  flex: 1,
1353
1682
  backgroundColor: "rgba(0, 0, 0, 0.5)",
@@ -1460,7 +1789,9 @@ function PaymanChat({
1460
1789
  style,
1461
1790
  children
1462
1791
  }) {
1463
- const [inputValue, setInputValue] = React.useState("");
1792
+ const [inputValue, setInputValue] = React4.useState("");
1793
+ const [voiceOverlayVisible, setVoiceOverlayVisible] = React4.useState(false);
1794
+ const prevInputValueRef = React4.useRef(inputValue);
1464
1795
  const chat = paymanTypescriptAskSdk.useChat(config, callbacks);
1465
1796
  const {
1466
1797
  messages,
@@ -1477,7 +1808,27 @@ function PaymanChat({
1477
1808
  const rejectUserAction = chat.rejectUserAction ?? NOOP_ASYNC;
1478
1809
  const resendOtp = chat.resendOtp ?? NOOP_ASYNC;
1479
1810
  const isUserActionSupported = typeof chat.approveUserAction === "function" && typeof chat.rejectUserAction === "function" && typeof chat.resendOtp === "function";
1480
- const contextValue = React.useMemo(
1811
+ const {
1812
+ voiceState,
1813
+ transcribedText,
1814
+ isAvailable: voiceAvailable,
1815
+ startRecording,
1816
+ stopRecording,
1817
+ clearTranscript
1818
+ } = paymanTypescriptAskSdk.useVoice(
1819
+ {
1820
+ lang: config.voiceLang || "en-US",
1821
+ interimResults: config.voiceInterimResults !== false,
1822
+ continuous: config.voiceContinuous !== false
1823
+ },
1824
+ {
1825
+ onError: (error) => {
1826
+ console.error("Voice error:", error);
1827
+ setVoiceOverlayVisible(false);
1828
+ }
1829
+ }
1830
+ );
1831
+ const contextValue = React4.useMemo(
1481
1832
  () => ({
1482
1833
  resetSession,
1483
1834
  clearMessages,
@@ -1517,35 +1868,67 @@ function PaymanChat({
1517
1868
  isChatDisabled = false,
1518
1869
  disabledComponent,
1519
1870
  showEmptyStateIcon = true,
1520
- inputStyle = "rounded"
1871
+ inputStyle = "rounded",
1872
+ enableVoice = false
1521
1873
  } = config;
1522
- const isSessionParamsConfigured = React.useMemo(() => {
1874
+ const isSessionParamsConfigured = React4.useMemo(() => {
1523
1875
  if (!sessionParams) return false;
1524
1876
  return !!(sessionParams.id?.trim() && sessionParams.name?.trim());
1525
1877
  }, [sessionParams?.id, sessionParams?.name]);
1526
- const handleSend = () => {
1878
+ React4.useEffect(() => {
1879
+ const wasEmpty = prevInputValueRef.current.trim() === "";
1880
+ const isEmpty = inputValue.trim() === "";
1881
+ prevInputValueRef.current = inputValue;
1882
+ if (!wasEmpty && isEmpty) {
1883
+ clearTranscript();
1884
+ stopRecording();
1885
+ setVoiceOverlayVisible(false);
1886
+ }
1887
+ }, [inputValue, clearTranscript, stopRecording]);
1888
+ const handleSend = React4.useCallback(() => {
1889
+ stopRecording();
1890
+ setVoiceOverlayVisible(false);
1527
1891
  if (inputValue.trim() && !disableInput && isSessionParamsConfigured) {
1528
1892
  sendMessage(inputValue.trim());
1529
1893
  setInputValue("");
1530
1894
  }
1531
- };
1895
+ }, [
1896
+ inputValue,
1897
+ disableInput,
1898
+ isSessionParamsConfigured,
1899
+ sendMessage,
1900
+ stopRecording
1901
+ ]);
1532
1902
  const isInputDisabled = isWaitingForResponse || !isSessionParamsConfigured || disableInput;
1903
+ const handleVoicePress = React4.useCallback(async () => {
1904
+ if (!voiceAvailable) return;
1905
+ setVoiceOverlayVisible(true);
1906
+ clearTranscript();
1907
+ await startRecording();
1908
+ }, [voiceAvailable, startRecording, clearTranscript]);
1909
+ const handleStopRecording = React4.useCallback(() => {
1910
+ stopRecording();
1911
+ setVoiceOverlayVisible(false);
1912
+ if (transcribedText.trim()) {
1913
+ setInputValue(transcribedText);
1914
+ }
1915
+ }, [stopRecording, transcribedText]);
1533
1916
  if (isChatDisabled) {
1534
1917
  if (disabledComponent) {
1535
- return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles8.container, style], children: [
1918
+ return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles9.container, style], children: [
1536
1919
  children,
1537
1920
  disabledComponent
1538
1921
  ] }) });
1539
1922
  }
1540
- return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles8.container, style], children: [
1923
+ return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles9.container, style], children: [
1541
1924
  children,
1542
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles8.disabledContainer, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles8.disabledText, children: "Chat is currently disabled" }) })
1925
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles9.disabledContainer, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles9.disabledText, children: "Chat is currently disabled" }) })
1543
1926
  ] }) });
1544
1927
  }
1545
1928
  return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
1546
1929
  reactNative.KeyboardAvoidingView,
1547
1930
  {
1548
- style: [styles8.container, style],
1931
+ style: [styles9.container, style],
1549
1932
  behavior: reactNative.Platform.OS === "ios" ? "padding" : "height",
1550
1933
  keyboardVerticalOffset: reactNative.Platform.OS === "ios" ? 90 : 0,
1551
1934
  children: [
@@ -1573,22 +1956,36 @@ function PaymanChat({
1573
1956
  onExecutionTraceClick
1574
1957
  }
1575
1958
  ),
1576
- hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
1577
- ChatInput,
1578
- {
1579
- value: inputValue,
1580
- onChange: setInputValue,
1581
- onSend: handleSend,
1582
- onPause: cancelStream,
1583
- disabled: isInputDisabled,
1584
- placeholder,
1585
- isWaitingForResponse,
1586
- hasSelectedSession: true,
1587
- isSessionParamsConfigured,
1588
- inputStyle,
1589
- layout
1590
- }
1591
- ),
1959
+ hasAskPermission && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1960
+ /* @__PURE__ */ jsxRuntime.jsx(
1961
+ ChatInput,
1962
+ {
1963
+ value: inputValue,
1964
+ onChange: setInputValue,
1965
+ onSend: handleSend,
1966
+ onPause: cancelStream,
1967
+ disabled: isInputDisabled,
1968
+ placeholder,
1969
+ isWaitingForResponse,
1970
+ hasSelectedSession: true,
1971
+ isSessionParamsConfigured,
1972
+ inputStyle,
1973
+ layout,
1974
+ enableVoice,
1975
+ onVoicePress: enableVoice ? handleVoicePress : void 0,
1976
+ voiceAvailable: enableVoice && voiceAvailable
1977
+ }
1978
+ ),
1979
+ enableVoice && /* @__PURE__ */ jsxRuntime.jsx(
1980
+ VoiceOverlay,
1981
+ {
1982
+ visible: voiceOverlayVisible,
1983
+ voiceState,
1984
+ transcribedText,
1985
+ onStopRecording: handleStopRecording
1986
+ }
1987
+ )
1988
+ ] }),
1592
1989
  /* @__PURE__ */ jsxRuntime.jsx(
1593
1990
  UserActionModal,
1594
1991
  {
@@ -1604,7 +2001,7 @@ function PaymanChat({
1604
2001
  }
1605
2002
  ) });
1606
2003
  }
1607
- var styles8 = reactNative.StyleSheet.create({
2004
+ var styles9 = reactNative.StyleSheet.create({
1608
2005
  container: {
1609
2006
  flex: 1,
1610
2007
  backgroundColor: "#FFF",
@@ -1650,6 +2047,10 @@ Object.defineProperty(exports, "useChat", {
1650
2047
  enumerable: true,
1651
2048
  get: function () { return paymanTypescriptAskSdk.useChat; }
1652
2049
  });
2050
+ Object.defineProperty(exports, "useVoice", {
2051
+ enumerable: true,
2052
+ get: function () { return paymanTypescriptAskSdk.useVoice; }
2053
+ });
1653
2054
  exports.PaymanChat = PaymanChat;
1654
2055
  exports.PaymanChatContext = PaymanChatContext;
1655
2056
  exports.cn = cn;