@paymanai/payman-ask-sdk 1.2.21 → 1.2.23

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.
@@ -9,12 +9,32 @@ var Animated2 = require('react-native-reanimated');
9
9
  var Markdown = require('react-native-markdown-display');
10
10
  var clsx = require('clsx');
11
11
  var tailwindMerge = require('tailwind-merge');
12
+ var Sentry = require('@sentry/react');
12
13
 
13
14
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
14
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
+
15
34
  var React__default = /*#__PURE__*/_interopDefault(React);
16
35
  var Animated2__default = /*#__PURE__*/_interopDefault(Animated2);
17
36
  var Markdown__default = /*#__PURE__*/_interopDefault(Markdown);
37
+ var Sentry__namespace = /*#__PURE__*/_interopNamespace(Sentry);
18
38
 
19
39
  // src/components/PaymanChat/index.native.tsx
20
40
  var PaymanChatContext = React.createContext(void 0);
@@ -116,7 +136,8 @@ function ChatInput({
116
136
  recordingDurationSeconds = 0,
117
137
  onConfirmRecording,
118
138
  onCancelRecording,
119
- transcribedText
139
+ transcribedText,
140
+ onInputFocus
120
141
  }) {
121
142
  const isInputDisabled = disabled || isWaitingForResponse;
122
143
  const showVoiceButton = enableVoice && onVoicePress != null;
@@ -223,6 +244,7 @@ function ChatInput({
223
244
  ref: inputRef,
224
245
  value,
225
246
  onChangeText: onChange,
247
+ onFocus: onInputFocus,
226
248
  onPress: onClick,
227
249
  editable: !isInputDisabled,
228
250
  placeholder: getPlaceholder(),
@@ -1124,6 +1146,32 @@ function formatDate(timestamp) {
1124
1146
  year: date.getFullYear() !== now.getFullYear() ? "numeric" : void 0
1125
1147
  });
1126
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
+ workflowName: context.workflowName ?? 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
+ }
1127
1175
  function UserMessage({
1128
1176
  message,
1129
1177
  animated = false,
@@ -1296,16 +1344,57 @@ function MessageList({
1296
1344
  streamingStepsText,
1297
1345
  completedStepsText,
1298
1346
  onExecutionTraceClick,
1299
- className
1347
+ className,
1348
+ isWaitingForResponse = false,
1349
+ scrollToEndHandleRef
1300
1350
  }) {
1301
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]);
1302
1377
  React.useEffect(() => {
1303
1378
  if (messages.length > 0) {
1304
1379
  setTimeout(() => {
1305
- flatListRef.current?.scrollToEnd({ animated: true });
1380
+ scrollToEnd();
1306
1381
  }, 100);
1307
1382
  }
1308
- }, [messages.length]);
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]);
1309
1398
  if (isLoading) {
1310
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(
1311
1400
  MessageRowSkeleton,
@@ -1332,8 +1421,11 @@ function MessageList({
1332
1421
  reactNative.FlatList,
1333
1422
  {
1334
1423
  ref: flatListRef,
1424
+ style: s5.list,
1335
1425
  data: messages,
1336
1426
  keyExtractor: (item) => item.id,
1427
+ keyboardShouldPersistTaps: "handled",
1428
+ keyboardDismissMode: "interactive",
1337
1429
  renderItem: ({ item }) => /* @__PURE__ */ jsxRuntime.jsx(
1338
1430
  MessageRow,
1339
1431
  {
@@ -1356,11 +1448,15 @@ function MessageList({
1356
1448
  s5.listContent,
1357
1449
  layout === "centered" && s5.listContentCentered
1358
1450
  ],
1451
+ ListFooterComponent: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s5.listFooterSpacer }),
1452
+ onContentSizeChange: scrollToEndOnAssistantLayout,
1359
1453
  showsVerticalScrollIndicator: false
1360
1454
  }
1361
1455
  );
1362
1456
  }
1363
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 },
1364
1460
  container: { flex: 1 },
1365
1461
  skeletonContent: { padding: 16, gap: 16 },
1366
1462
  emptyContainer: {
@@ -1393,8 +1489,17 @@ var s5 = reactNative.StyleSheet.create({
1393
1489
  textAlign: "center",
1394
1490
  lineHeight: 20
1395
1491
  },
1396
- listContent: { padding: 16, gap: 16 },
1397
- listContentCentered: { maxWidth: 672, alignSelf: "center", width: "100%" }
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
+ }
1398
1503
  });
1399
1504
 
1400
1505
  // src/assets/payman-mono-crop-blue.png
@@ -2057,6 +2162,7 @@ function PaymanChat({
2057
2162
  const recordingStartRef = React.useRef(null);
2058
2163
  const recordingIntervalRef = React.useRef(null);
2059
2164
  const prevInputValueRef = React.useRef(inputValue);
2165
+ const scrollMessagesToEndRef = React.useRef(null);
2060
2166
  const chat = paymanTypescriptAskSdk.useChat(config, callbacks);
2061
2167
  const {
2062
2168
  messages,
@@ -2201,6 +2307,13 @@ function PaymanChat({
2201
2307
  const handleCancelRecording = React.useCallback(() => {
2202
2308
  stopRecording();
2203
2309
  }, [stopRecording]);
2310
+ const handleInputFocus = React.useCallback(() => {
2311
+ const run = () => scrollMessagesToEndRef.current?.();
2312
+ run();
2313
+ requestAnimationFrame(run);
2314
+ setTimeout(run, 100);
2315
+ setTimeout(run, 280);
2316
+ }, []);
2204
2317
  if (isChatDisabled) {
2205
2318
  if (disabledComponent) {
2206
2319
  return /* @__PURE__ */ jsxRuntime.jsx(PaymanChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [s8.container, style], children: [
@@ -2217,14 +2330,15 @@ function PaymanChat({
2217
2330
  reactNative.KeyboardAvoidingView,
2218
2331
  {
2219
2332
  style: [s8.container, style],
2220
- behavior: reactNative.Platform.OS === "ios" ? "padding" : "height",
2333
+ behavior: "padding",
2221
2334
  keyboardVerticalOffset: reactNative.Platform.OS === "ios" ? 90 : 0,
2222
2335
  children: [
2223
2336
  children,
2224
- /* @__PURE__ */ jsxRuntime.jsx(
2337
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s8.messageListWrap, children: /* @__PURE__ */ jsxRuntime.jsx(
2225
2338
  MessageList,
2226
2339
  {
2227
2340
  messages,
2341
+ isWaitingForResponse,
2228
2342
  isLoading: false,
2229
2343
  emptyStateText,
2230
2344
  showEmptyStateIcon,
@@ -2241,10 +2355,11 @@ function PaymanChat({
2241
2355
  showStreamingDot,
2242
2356
  streamingStepsText,
2243
2357
  completedStepsText,
2244
- onExecutionTraceClick
2358
+ onExecutionTraceClick,
2359
+ scrollToEndHandleRef: scrollMessagesToEndRef
2245
2360
  }
2246
- ),
2247
- hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(
2361
+ ) }),
2362
+ hasAskPermission && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s8.inputBarWrap, children: /* @__PURE__ */ jsxRuntime.jsx(
2248
2363
  ChatInput,
2249
2364
  {
2250
2365
  value: inputValue,
@@ -2265,9 +2380,10 @@ function PaymanChat({
2265
2380
  recordingDurationSeconds: recordingElapsedSeconds,
2266
2381
  onConfirmRecording: enableVoice ? handleConfirmRecording : void 0,
2267
2382
  onCancelRecording: enableVoice ? handleCancelRecording : void 0,
2268
- transcribedText: enableVoice && isRecording ? transcribedText : void 0
2383
+ transcribedText: enableVoice && isRecording ? transcribedText : void 0,
2384
+ onInputFocus: handleInputFocus
2269
2385
  }
2270
- ),
2386
+ ) }),
2271
2387
  /* @__PURE__ */ jsxRuntime.jsx(
2272
2388
  UserActionModal,
2273
2389
  {
@@ -2284,6 +2400,20 @@ function PaymanChat({
2284
2400
  ) });
2285
2401
  }
2286
2402
  var s8 = reactNative.StyleSheet.create({
2403
+ /** Lets the message list shrink when the keyboard opens; pairs with FlatList flex:1. */
2404
+ messageListWrap: { flex: 1, minHeight: 0, zIndex: 0 },
2405
+ /**
2406
+ * Keeps the composer above the message list when scrolling so rows (e.g. stream step toggles)
2407
+ * do not paint over the input. Elevation applies on Android only.
2408
+ */
2409
+ inputBarWrap: {
2410
+ width: "100%",
2411
+ zIndex: 2,
2412
+ ...reactNative.Platform.select({
2413
+ android: { elevation: 8 },
2414
+ default: {}
2415
+ })
2416
+ },
2287
2417
  container: {
2288
2418
  flex: 1,
2289
2419
  backgroundColor: "#FFFFFF",
@@ -2331,6 +2461,7 @@ Object.defineProperty(exports, "useVoice", {
2331
2461
  });
2332
2462
  exports.PaymanChat = PaymanChat;
2333
2463
  exports.PaymanChatContext = PaymanChatContext;
2464
+ exports.captureSentryError = captureSentryError;
2334
2465
  exports.cn = cn;
2335
2466
  exports.formatDate = formatDate;
2336
2467
  exports.usePaymanChat = usePaymanChat;