@paymanai/payman-ask-sdk 1.1.2 → 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.
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
  ) }) }) });
@@ -1134,6 +1137,7 @@ function PaymanChat({
1134
1137
  children
1135
1138
  }) {
1136
1139
  const [inputValue, setInputValue] = react.useState("");
1140
+ const prevInputValueRef = react.useRef(inputValue);
1137
1141
  const chat = paymanTypescriptAskSdk.useChat(config, callbacks);
1138
1142
  const {
1139
1143
  messages,
@@ -1150,6 +1154,29 @@ function PaymanChat({
1150
1154
  const rejectUserAction = chat.rejectUserAction ?? NOOP_ASYNC;
1151
1155
  const resendOtp = chat.resendOtp ?? NOOP_ASYNC;
1152
1156
  const isUserActionSupported = typeof chat.approveUserAction === "function" && typeof chat.rejectUserAction === "function" && typeof chat.resendOtp === "function";
1157
+ const {
1158
+ isAvailable: voiceAvailable,
1159
+ isRecording,
1160
+ startRecording,
1161
+ stopRecording,
1162
+ clearTranscript
1163
+ } = paymanTypescriptAskSdk.useVoice(
1164
+ {
1165
+ lang: config.voiceLang || "en-US",
1166
+ interimResults: config.voiceInterimResults !== false,
1167
+ continuous: config.voiceContinuous !== false
1168
+ },
1169
+ {
1170
+ onResult: (text) => {
1171
+ setInputValue(text);
1172
+ },
1173
+ onEnd: () => {
1174
+ },
1175
+ onError: (error) => {
1176
+ console.error("Voice error:", error);
1177
+ }
1178
+ }
1179
+ );
1153
1180
  const contextValue = react.useMemo(
1154
1181
  () => ({
1155
1182
  resetSession,
@@ -1196,7 +1223,21 @@ function PaymanChat({
1196
1223
  if (!sessionParams) return false;
1197
1224
  return !!(sessionParams.id?.trim() && sessionParams.name?.trim());
1198
1225
  }, [sessionParams?.id, sessionParams?.name]);
1226
+ react.useEffect(() => {
1227
+ const wasEmpty = prevInputValueRef.current.trim() === "";
1228
+ const isEmpty = inputValue.trim() === "";
1229
+ prevInputValueRef.current = inputValue;
1230
+ if (!wasEmpty && isEmpty) {
1231
+ clearTranscript();
1232
+ if (isRecording) {
1233
+ stopRecording();
1234
+ }
1235
+ }
1236
+ }, [inputValue, clearTranscript, isRecording, stopRecording]);
1199
1237
  const handleSend = () => {
1238
+ if (isRecording) {
1239
+ stopRecording();
1240
+ }
1200
1241
  if (inputValue.trim() && !disableInput && isSessionParamsConfigured) {
1201
1242
  sendMessage(inputValue.trim());
1202
1243
  setInputValue("");
@@ -1276,10 +1317,14 @@ function PaymanChat({
1276
1317
  onSend: handleSend,
1277
1318
  onPause: cancelStream,
1278
1319
  disabled: isInputDisabled,
1279
- placeholder,
1320
+ placeholder: isRecording ? "Listening..." : placeholder,
1280
1321
  isWaitingForResponse,
1281
1322
  hasSelectedSession: true,
1282
1323
  isSessionParamsConfigured,
1324
+ enableVoice: config.enableVoice === true,
1325
+ onVoicePress: isRecording ? stopRecording : startRecording,
1326
+ voiceAvailable: config.enableVoice === true && voiceAvailable,
1327
+ isRecording,
1283
1328
  inputStyle,
1284
1329
  layout
1285
1330
  }
@@ -1324,6 +1369,10 @@ Object.defineProperty(exports, "useChat", {
1324
1369
  enumerable: true,
1325
1370
  get: function () { return paymanTypescriptAskSdk.useChat; }
1326
1371
  });
1372
+ Object.defineProperty(exports, "useVoice", {
1373
+ enumerable: true,
1374
+ get: function () { return paymanTypescriptAskSdk.useVoice; }
1375
+ });
1327
1376
  exports.PaymanChat = PaymanChat;
1328
1377
  exports.PaymanChatContext = PaymanChatContext;
1329
1378
  exports.cn = cn;