@paymanai/payman-ask-sdk 1.1.2 → 1.2.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.
package/dist/index.d.mts CHANGED
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import React__default from 'react';
4
4
  import { UserActionResult, UserActionRequest } from '@paymanai/payman-typescript-ask-sdk';
5
- export { StreamEvent, StreamOptions, UserActionRequest, UserActionResult, UserActionState, cancelUserAction, generateId, resendUserAction, streamWorkflowEvents, submitUserAction, useChat } from '@paymanai/payman-typescript-ask-sdk';
5
+ export { StreamEvent, StreamOptions, UseVoiceReturn, UserActionRequest, UserActionResult, UserActionState, VoiceCallbacks, VoiceConfig, VoicePermissions, VoiceState, cancelUserAction, generateId, resendUserAction, streamWorkflowEvents, submitUserAction, useChat, useVoice } from '@paymanai/payman-typescript-ask-sdk';
6
6
  import { ClassValue } from 'clsx';
7
7
 
8
8
  type MessageRole = "user" | "assistant" | "system";
@@ -114,6 +114,14 @@ type ChatConfig = {
114
114
  isChatDisabled?: boolean;
115
115
  /** Custom component to render when chat is disabled. If not provided, default disabled UI will be shown */
116
116
  disabledComponent?: React__default.ReactNode;
117
+ /** Enable voice input (default: true) */
118
+ enableVoice?: boolean;
119
+ /** Voice language (default: "en-US") */
120
+ voiceLang?: string;
121
+ /** Voice interim results (default: true) */
122
+ voiceInterimResults?: boolean;
123
+ /** Voice continuous mode (default: true) */
124
+ voiceContinuous?: boolean;
117
125
  };
118
126
  type ChatCallbacks = {
119
127
  /** Called when a message is sent (before API call) */
@@ -176,6 +184,14 @@ type ChatInputProps = {
176
184
  layout?: "centered" | "full-width";
177
185
  /** Custom class name */
178
186
  className?: string;
187
+ /** Enable voice input in the chat input. When false, voice button is hidden. */
188
+ enableVoice?: boolean;
189
+ /** Voice button handler */
190
+ onVoicePress?: () => void;
191
+ /** Is voice available (e.g. speech recognition ready) */
192
+ voiceAvailable?: boolean;
193
+ /** Is currently recording voice */
194
+ isRecording?: boolean;
179
195
  };
180
196
  type MessageListProps = {
181
197
  /** Messages to display */
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import React__default from 'react';
4
4
  import { UserActionResult, UserActionRequest } from '@paymanai/payman-typescript-ask-sdk';
5
- export { StreamEvent, StreamOptions, UserActionRequest, UserActionResult, UserActionState, cancelUserAction, generateId, resendUserAction, streamWorkflowEvents, submitUserAction, useChat } from '@paymanai/payman-typescript-ask-sdk';
5
+ export { StreamEvent, StreamOptions, UseVoiceReturn, UserActionRequest, UserActionResult, UserActionState, VoiceCallbacks, VoiceConfig, VoicePermissions, VoiceState, cancelUserAction, generateId, resendUserAction, streamWorkflowEvents, submitUserAction, useChat, useVoice } from '@paymanai/payman-typescript-ask-sdk';
6
6
  import { ClassValue } from 'clsx';
7
7
 
8
8
  type MessageRole = "user" | "assistant" | "system";
@@ -114,6 +114,14 @@ type ChatConfig = {
114
114
  isChatDisabled?: boolean;
115
115
  /** Custom component to render when chat is disabled. If not provided, default disabled UI will be shown */
116
116
  disabledComponent?: React__default.ReactNode;
117
+ /** Enable voice input (default: true) */
118
+ enableVoice?: boolean;
119
+ /** Voice language (default: "en-US") */
120
+ voiceLang?: string;
121
+ /** Voice interim results (default: true) */
122
+ voiceInterimResults?: boolean;
123
+ /** Voice continuous mode (default: true) */
124
+ voiceContinuous?: boolean;
117
125
  };
118
126
  type ChatCallbacks = {
119
127
  /** Called when a message is sent (before API call) */
@@ -176,6 +184,14 @@ type ChatInputProps = {
176
184
  layout?: "centered" | "full-width";
177
185
  /** Custom class name */
178
186
  className?: string;
187
+ /** Enable voice input in the chat input. When false, voice button is hidden. */
188
+ enableVoice?: boolean;
189
+ /** Voice button handler */
190
+ onVoicePress?: () => void;
191
+ /** Is voice available (e.g. speech recognition ready) */
192
+ voiceAvailable?: boolean;
193
+ /** Is currently recording voice */
194
+ isRecording?: boolean;
179
195
  };
180
196
  type MessageListProps = {
181
197
  /** Messages to display */
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var react = require('react');
4
3
  var paymanTypescriptAskSdk = require('@paymanai/payman-typescript-ask-sdk');
4
+ var react = require('react');
5
5
  var clsx = require('clsx');
6
6
  var tailwindMerge = require('tailwind-merge');
7
7
  var framerMotion = require('framer-motion');
@@ -64,7 +64,11 @@ function ChatInput({
64
64
  hasSelectedSession = true,
65
65
  isSessionParamsConfigured = true,
66
66
  onClick,
67
- className
67
+ className,
68
+ enableVoice = false,
69
+ onVoicePress,
70
+ voiceAvailable = false,
71
+ isRecording = false
68
72
  }) {
69
73
  const textareaRef = react.useRef(null);
70
74
  react.useEffect(() => {
@@ -83,6 +87,8 @@ function ChatInput({
83
87
  };
84
88
  const isInputDisabled = disabled || isWaitingForResponse;
85
89
  const showPauseButton = isWaitingForResponse && onPause;
90
+ const showVoiceButton = enableVoice && onVoicePress != null;
91
+ const isVoiceButtonDisabled = isWaitingForResponse || !voiceAvailable || !isSessionParamsConfigured;
86
92
  const getPlaceholder = () => {
87
93
  if (!hasSelectedSession) {
88
94
  return "Select a version to start chatting";
@@ -96,7 +102,7 @@ function ChatInput({
96
102
  framerMotion.motion.div,
97
103
  {
98
104
  initial: false,
99
- className: "relative flex items-end overflow-hidden transition-colors bg-input border border-border shadow-sm",
105
+ className: "flex items-end overflow-hidden transition-colors bg-input border border-border shadow-sm",
100
106
  style: { borderRadius: "24px" },
101
107
  children: [
102
108
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -109,51 +115,48 @@ function ChatInput({
109
115
  onClick,
110
116
  disabled: isInputDisabled,
111
117
  placeholder: getPlaceholder(),
112
- className: "bg-transparent text-foreground focus:outline-none resize-none overflow-y-auto disabled:opacity-50 disabled:cursor-not-allowed w-full text-sm placeholder:text-muted-foreground",
118
+ className: "bg-transparent text-foreground focus:outline-none resize-none overflow-y-auto disabled:opacity-50 disabled:cursor-not-allowed flex-1 min-w-0 text-sm placeholder:text-muted-foreground",
113
119
  style: {
114
120
  minHeight: "48px",
115
121
  maxHeight: "200px",
116
- padding: "12px 48px 12px 16px"
122
+ padding: "12px 12px 12px 16px"
117
123
  },
118
124
  rows: 1
119
125
  }
120
126
  ),
121
- showPauseButton ? /* @__PURE__ */ jsxRuntime.jsx(
122
- "button",
123
- {
124
- onClick: onPause,
125
- className: "flex items-center justify-center flex-shrink-0 transition-all bg-primary text-primary-foreground hover:bg-primary/90",
126
- style: {
127
- position: "absolute",
128
- bottom: "8px",
129
- right: "8px",
130
- padding: "8px",
131
- borderRadius: "9999px",
132
- width: "32px",
133
- height: "32px"
134
- },
135
- "aria-label": "Pause request",
136
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pause, { className: "w-4 h-4" })
137
- }
138
- ) : /* @__PURE__ */ jsxRuntime.jsx(
139
- "button",
140
- {
141
- onClick: onSend,
142
- disabled: isInputDisabled || !value.trim(),
143
- className: "flex items-center justify-center flex-shrink-0 transition-all bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed",
144
- style: {
145
- position: "absolute",
146
- bottom: "8px",
147
- right: "8px",
148
- padding: "8px",
149
- borderRadius: "9999px",
150
- width: "32px",
151
- height: "32px"
152
- },
153
- "aria-label": "Send message",
154
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "w-4 h-4" })
155
- }
156
- )
127
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-row items-center gap-1 shrink-0 mb-2 mr-2", children: [
128
+ showVoiceButton && /* @__PURE__ */ jsxRuntime.jsx(
129
+ "button",
130
+ {
131
+ type: "button",
132
+ onClick: onVoicePress,
133
+ disabled: isVoiceButtonDisabled,
134
+ className: `flex items-center justify-center flex-shrink-0 transition-all rounded-full w-8 h-8 min-w-[32px] ${isRecording ? "animate-pulse" : "hover:opacity-80"} disabled:opacity-50 disabled:cursor-not-allowed`,
135
+ "aria-label": isRecording ? "Stop recording" : "Voice input",
136
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, { className: `w-5 h-5 shrink-0 ${isRecording ? "text-red-500" : "text-foreground"}` })
137
+ }
138
+ ),
139
+ showPauseButton ? /* @__PURE__ */ jsxRuntime.jsx(
140
+ "button",
141
+ {
142
+ type: "button",
143
+ onClick: onPause,
144
+ className: "flex items-center justify-center flex-shrink-0 transition-all rounded-full w-8 h-8 bg-primary text-primary-foreground hover:bg-primary/90",
145
+ "aria-label": "Pause request",
146
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pause, { className: "w-4 h-4" })
147
+ }
148
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
149
+ "button",
150
+ {
151
+ type: "button",
152
+ onClick: onSend,
153
+ disabled: isInputDisabled || !value.trim(),
154
+ className: "flex items-center justify-center flex-shrink-0 transition-all rounded-full w-8 h-8 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed",
155
+ "aria-label": "Send message",
156
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "w-4 h-4" })
157
+ }
158
+ )
159
+ ] })
157
160
  ]
158
161
  }
159
162
  ) }) }) });
@@ -748,19 +751,7 @@ function OtpInput({ value, onChange, maxLength, disabled = false }) {
748
751
  onPaste: handlePaste,
749
752
  onFocus: (e) => e.target.select(),
750
753
  "aria-label": `Digit ${i + 1}`,
751
- style: {
752
- width: "40px",
753
- height: "48px",
754
- textAlign: "center",
755
- fontSize: "20px",
756
- fontWeight: 600,
757
- border: "1px solid hsl(var(--border, 214.3 31.8% 91.4%))",
758
- borderRadius: "8px",
759
- outline: "none",
760
- backgroundColor: disabled ? "hsl(var(--muted, 210 40% 96.1%))" : "transparent",
761
- color: "hsl(var(--foreground, 222.2 84% 4.9%))",
762
- opacity: disabled ? 0.5 : 1
763
- }
754
+ className: `w-10 h-12 text-center text-xl font-semibold border border-border rounded-lg outline-none text-foreground ${disabled ? "bg-muted opacity-50" : "bg-transparent"}`
764
755
  },
765
756
  i
766
757
  )) });
@@ -949,67 +940,16 @@ function UserActionModal({
949
940
  ref: dialogRef,
950
941
  role: "dialog",
951
942
  "aria-modal": "true",
952
- style: {
953
- backgroundColor: "hsl(var(--card, 0 0% 100%))",
954
- borderRadius: "12px",
955
- padding: "24px",
956
- width: "100%",
957
- maxWidth: "420px",
958
- margin: "0 16px",
959
- boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)"
960
- },
943
+ className: "bg-card rounded-xl p-6 shadow-2xl",
944
+ style: { width: "100%", maxWidth: "420px", margin: "0 16px" },
961
945
  tabIndex: -1,
962
946
  children: [
963
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", marginBottom: "16px" }, children: [
964
- /* @__PURE__ */ jsxRuntime.jsx(
965
- "div",
966
- {
967
- style: {
968
- display: "inline-flex",
969
- alignItems: "center",
970
- justifyContent: "center",
971
- width: "48px",
972
- height: "48px",
973
- borderRadius: "50%",
974
- backgroundColor: "hsl(var(--muted, 210 40% 96.1%))",
975
- marginBottom: "12px"
976
- },
977
- children: /* @__PURE__ */ jsxRuntime.jsx(
978
- lucideReact.ShieldCheck,
979
- {
980
- className: "text-primary",
981
- style: { width: "24px", height: "24px" }
982
- }
983
- )
984
- }
985
- ),
986
- /* @__PURE__ */ jsxRuntime.jsx(
987
- "h2",
988
- {
989
- style: {
990
- fontSize: "18px",
991
- fontWeight: 600,
992
- color: "hsl(var(--foreground, 222.2 84% 4.9%))",
993
- margin: 0
994
- },
995
- children: MODAL_CONTENT.TITLE
996
- }
997
- )
947
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-4", children: [
948
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center justify-center w-12 h-12 rounded-full bg-muted mb-3", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ShieldCheck, { className: "w-6 h-6 text-primary" }) }),
949
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-foreground m-0", children: MODAL_CONTENT.TITLE })
998
950
  ] }),
999
- /* @__PURE__ */ jsxRuntime.jsx(
1000
- "p",
1001
- {
1002
- style: {
1003
- fontSize: "14px",
1004
- color: "hsl(var(--muted-foreground, 215.4 16.3% 46.9%))",
1005
- textAlign: "center",
1006
- margin: "0 0 24px 0",
1007
- lineHeight: 1.5
1008
- },
1009
- children: userActionRequest.message
1010
- }
1011
- ),
1012
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "24px" }, children: /* @__PURE__ */ jsxRuntime.jsx(
951
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground text-center mb-6 leading-relaxed", children: userActionRequest.message }),
952
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6", children: /* @__PURE__ */ jsxRuntime.jsx(
1013
953
  OtpInput,
1014
954
  {
1015
955
  value: otp,
@@ -1134,6 +1074,7 @@ function PaymanChat({
1134
1074
  children
1135
1075
  }) {
1136
1076
  const [inputValue, setInputValue] = react.useState("");
1077
+ const prevInputValueRef = react.useRef(inputValue);
1137
1078
  const chat = paymanTypescriptAskSdk.useChat(config, callbacks);
1138
1079
  const {
1139
1080
  messages,
@@ -1150,6 +1091,29 @@ function PaymanChat({
1150
1091
  const rejectUserAction = chat.rejectUserAction ?? NOOP_ASYNC;
1151
1092
  const resendOtp = chat.resendOtp ?? NOOP_ASYNC;
1152
1093
  const isUserActionSupported = typeof chat.approveUserAction === "function" && typeof chat.rejectUserAction === "function" && typeof chat.resendOtp === "function";
1094
+ const {
1095
+ isAvailable: voiceAvailable,
1096
+ isRecording,
1097
+ startRecording,
1098
+ stopRecording,
1099
+ clearTranscript
1100
+ } = paymanTypescriptAskSdk.useVoice(
1101
+ {
1102
+ lang: config.voiceLang || "en-US",
1103
+ interimResults: config.voiceInterimResults !== false,
1104
+ continuous: config.voiceContinuous !== false
1105
+ },
1106
+ {
1107
+ onResult: (text) => {
1108
+ setInputValue(text);
1109
+ },
1110
+ onEnd: () => {
1111
+ },
1112
+ onError: (error) => {
1113
+ console.error("Voice error:", error);
1114
+ }
1115
+ }
1116
+ );
1153
1117
  const contextValue = react.useMemo(
1154
1118
  () => ({
1155
1119
  resetSession,
@@ -1196,7 +1160,21 @@ function PaymanChat({
1196
1160
  if (!sessionParams) return false;
1197
1161
  return !!(sessionParams.id?.trim() && sessionParams.name?.trim());
1198
1162
  }, [sessionParams?.id, sessionParams?.name]);
1163
+ react.useEffect(() => {
1164
+ const wasEmpty = prevInputValueRef.current.trim() === "";
1165
+ const isEmpty = inputValue.trim() === "";
1166
+ prevInputValueRef.current = inputValue;
1167
+ if (!wasEmpty && isEmpty) {
1168
+ clearTranscript();
1169
+ if (isRecording) {
1170
+ stopRecording();
1171
+ }
1172
+ }
1173
+ }, [inputValue, clearTranscript, isRecording, stopRecording]);
1199
1174
  const handleSend = () => {
1175
+ if (isRecording) {
1176
+ stopRecording();
1177
+ }
1200
1178
  if (inputValue.trim() && !disableInput && isSessionParamsConfigured) {
1201
1179
  sendMessage(inputValue.trim());
1202
1180
  setInputValue("");
@@ -1276,10 +1254,14 @@ function PaymanChat({
1276
1254
  onSend: handleSend,
1277
1255
  onPause: cancelStream,
1278
1256
  disabled: isInputDisabled,
1279
- placeholder,
1257
+ placeholder: isRecording ? "Listening..." : placeholder,
1280
1258
  isWaitingForResponse,
1281
1259
  hasSelectedSession: true,
1282
1260
  isSessionParamsConfigured,
1261
+ enableVoice: config.enableVoice === true,
1262
+ onVoicePress: isRecording ? stopRecording : startRecording,
1263
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
1264
+ isRecording,
1283
1265
  inputStyle,
1284
1266
  layout
1285
1267
  }
@@ -1324,6 +1306,10 @@ Object.defineProperty(exports, "useChat", {
1324
1306
  enumerable: true,
1325
1307
  get: function () { return paymanTypescriptAskSdk.useChat; }
1326
1308
  });
1309
+ Object.defineProperty(exports, "useVoice", {
1310
+ enumerable: true,
1311
+ get: function () { return paymanTypescriptAskSdk.useVoice; }
1312
+ });
1327
1313
  exports.PaymanChat = PaymanChat;
1328
1314
  exports.PaymanChatContext = PaymanChatContext;
1329
1315
  exports.cn = cn;