@sampleapp.ai/sdk 1.0.25 → 1.0.27
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/components/chat-bar/typing-textarea.js +17 -11
- package/dist/components/chat-bar/voice-button.js +4 -22
- package/dist/components/chat-bar.js +100 -61
- package/dist/components/icons.js +155 -0
- package/dist/components/minimal-chat-bar.js +51 -0
- package/dist/components/ui/button.js +78 -30
- package/dist/index.d.ts +5 -8
- package/dist/index.es.js +1050 -6085
- package/dist/index.js +1 -0
- package/dist/index.standalone.js +85 -0
- package/dist/index.standalone.umd.js +147 -0
- package/dist/index.umd.js +300 -96
- package/dist/sdk.css +1 -0
- package/package.json +5 -3
|
@@ -12,7 +12,6 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
};
|
|
13
13
|
import React from "react";
|
|
14
14
|
import { useState, useEffect, useRef, forwardRef } from "react";
|
|
15
|
-
import { motion, AnimatePresence } from "framer-motion";
|
|
16
15
|
import { Textarea } from "../ui/textarea";
|
|
17
16
|
export const TypingTextarea = forwardRef((_a, ref) => {
|
|
18
17
|
var { texts, typingSpeed = 30, deletingSpeed = 15, delayAfterText = 800, onSubmit, isStreaming = false, className = "", placeholderClassName = "", value, onChange, onFocus, onBlur, onKeyDown, onPaste, placeholder: originalPlaceholder, rows = 1 } = _a, props = __rest(_a, ["texts", "typingSpeed", "deletingSpeed", "delayAfterText", "onSubmit", "isStreaming", "className", "placeholderClassName", "value", "onChange", "onFocus", "onBlur", "onKeyDown", "onPaste", "placeholder", "rows"]);
|
|
@@ -22,11 +21,11 @@ export const TypingTextarea = forwardRef((_a, ref) => {
|
|
|
22
21
|
const [isWaiting, setIsWaiting] = useState(false);
|
|
23
22
|
const [showTabHint, setShowTabHint] = useState(false);
|
|
24
23
|
const [isPausing, setIsPausing] = useState(false);
|
|
24
|
+
const [isAnimating, setIsAnimating] = useState(false);
|
|
25
25
|
const localRef = useRef(null);
|
|
26
26
|
const textareaRef = ref || localRef;
|
|
27
27
|
const currentText = texts[textIndex];
|
|
28
28
|
const isEmpty = value === "" || value === undefined;
|
|
29
|
-
// const isFullTextDisplayed = displayText === currentText && !isDeleting;
|
|
30
29
|
// Handle animation only when textarea is empty
|
|
31
30
|
useEffect(() => {
|
|
32
31
|
if (!isEmpty)
|
|
@@ -37,14 +36,16 @@ export const TypingTextarea = forwardRef((_a, ref) => {
|
|
|
37
36
|
setIsWaiting(false);
|
|
38
37
|
setIsPausing(true);
|
|
39
38
|
setShowTabHint(true);
|
|
39
|
+
setIsAnimating(true);
|
|
40
40
|
}, delayAfterText);
|
|
41
41
|
return () => clearTimeout(timeout);
|
|
42
42
|
}
|
|
43
43
|
if (isPausing) {
|
|
44
|
-
// Show tab hint for 3 seconds
|
|
44
|
+
// Show tab hint for 3 seconds
|
|
45
45
|
timeout = setTimeout(() => {
|
|
46
|
-
|
|
46
|
+
setIsAnimating(false);
|
|
47
47
|
setTimeout(() => {
|
|
48
|
+
setShowTabHint(false);
|
|
48
49
|
setIsPausing(false);
|
|
49
50
|
setIsDeleting(true);
|
|
50
51
|
}, 300); // Wait for fade out animation
|
|
@@ -120,25 +121,30 @@ export const TypingTextarea = forwardRef((_a, ref) => {
|
|
|
120
121
|
paddingRight: "4px",
|
|
121
122
|
paddingTop: "0",
|
|
122
123
|
paddingBottom: "0",
|
|
123
|
-
marginTop: "
|
|
124
|
+
marginTop: "5px",
|
|
124
125
|
} },
|
|
125
|
-
React.createElement("p", { style: Object.assign({ color: "#a1a1aa", fontWeight: "500", lineHeight: "1.
|
|
126
|
+
React.createElement("p", { style: Object.assign({ color: "#a1a1aa", fontWeight: "500", lineHeight: "1.25" }, (placeholderClassName ? {} : {})) },
|
|
126
127
|
displayText,
|
|
127
|
-
React.createElement(
|
|
128
|
+
React.createElement("span", { style: {
|
|
128
129
|
fontSize: "12px",
|
|
129
|
-
color: "#d1d5db",
|
|
130
|
-
backgroundColor: "#27272a",
|
|
130
|
+
color: "#d1d5db",
|
|
131
|
+
backgroundColor: "#27272a",
|
|
131
132
|
paddingLeft: "6px",
|
|
132
133
|
paddingRight: "6px",
|
|
133
134
|
paddingTop: "2px",
|
|
134
135
|
paddingBottom: "2px",
|
|
135
136
|
borderRadius: "8px",
|
|
136
|
-
border: "1px solid #3f3f46",
|
|
137
|
+
border: "1px solid #3f3f46",
|
|
137
138
|
alignItems: "center",
|
|
138
139
|
marginLeft: "8px",
|
|
139
140
|
display: "inline-flex",
|
|
140
141
|
whiteSpace: "nowrap",
|
|
141
|
-
|
|
142
|
+
opacity: isAnimating ? 1 : 0,
|
|
143
|
+
transform: isAnimating ? "scale(1)" : "scale(0.8)",
|
|
144
|
+
transition: "opacity 0.3s ease, transform 0.3s ease",
|
|
145
|
+
visibility: showTabHint ? "visible" : "hidden",
|
|
146
|
+
verticalAlign: "middle",
|
|
147
|
+
} }, "Tab to select")))),
|
|
142
148
|
React.createElement(Textarea, Object.assign({ ref: textareaRef, placeholder: originalPlaceholder, style: className ? {} : {}, value: value, onFocus: onFocus, onBlur: onBlur, onChange: onChange, onKeyDown: handleKeyDown, onPaste: onPaste, rows: rows }, props))));
|
|
143
149
|
});
|
|
144
150
|
TypingTextarea.displayName = "TypingTextarea";
|
|
@@ -1,26 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { Button } from "../ui/button";
|
|
4
|
-
import { VoiceIcon } from "
|
|
5
|
-
export const VoiceButton = ({ isRecording, isTranscribing, onVoiceRecording,
|
|
6
|
-
isLoggedIn = false, }) => {
|
|
4
|
+
import { VoiceIcon } from "../icons";
|
|
5
|
+
export const VoiceButton = ({ isRecording, isTranscribing, onVoiceRecording, }) => {
|
|
7
6
|
// Don't show voice button for statsig playground
|
|
8
7
|
// if (playgroundUid === "statsig") {
|
|
9
8
|
// return null;
|
|
10
9
|
// }
|
|
11
|
-
|
|
12
|
-
if (isTranscribing) {
|
|
13
|
-
return "Transcribing your voice...";
|
|
14
|
-
}
|
|
15
|
-
if (isRecording) {
|
|
16
|
-
return "Click to stop recording";
|
|
17
|
-
}
|
|
18
|
-
if (isLoggedIn) {
|
|
19
|
-
return "Voice Mode – Build with voice (Ctrl/Cmd + /)";
|
|
20
|
-
}
|
|
21
|
-
return "Sign up to use voice mode";
|
|
22
|
-
};
|
|
23
|
-
return (React.createElement(Button, { size: "sm", variant: "ghostOutline", style: Object.assign(Object.assign({ height: "32px", paddingLeft: "8px", paddingRight: "8px", fontSize: "12px", fontWeight: "500", transition: "all 0.2s ease-in-out" }, (isRecording && {
|
|
10
|
+
return (React.createElement(Button, { size: "icon", variant: "outline", style: Object.assign(Object.assign({ height: "32px", paddingLeft: "8px", paddingRight: "8px", fontSize: "12px", fontWeight: "500", transition: "all 0.2s ease-in-out" }, (isRecording && {
|
|
24
11
|
backgroundColor: "#ef4444", // red-500
|
|
25
12
|
borderColor: "#ef4444",
|
|
26
13
|
boxShadow: "0 10px 15px -3px rgba(239, 68, 68, 0.25)",
|
|
@@ -31,12 +18,7 @@ isLoggedIn = false, }) => {
|
|
|
31
18
|
boxShadow: "0 10px 15px -3px rgba(59, 130, 246, 0.25)",
|
|
32
19
|
color: "white",
|
|
33
20
|
})), onClick: () => {
|
|
34
|
-
|
|
35
|
-
onVoiceRecording();
|
|
36
|
-
}
|
|
37
|
-
else if (!isLoggedIn) {
|
|
38
|
-
window.location.href = "/sign-up";
|
|
39
|
-
}
|
|
21
|
+
onVoiceRecording();
|
|
40
22
|
}, disabled: isTranscribing },
|
|
41
23
|
React.createElement(VoiceIcon, { isRecording: isRecording, isTranscribing: isTranscribing })));
|
|
42
24
|
};
|
|
@@ -2,22 +2,36 @@
|
|
|
2
2
|
/* eslint-disable @next/next/no-img-element */
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { useState, useRef, useEffect, useMemo } from "react";
|
|
5
|
-
import { ArrowUp, Loader2, CircleStop } from "lucide-react";
|
|
6
|
-
import { TypingTextarea } from "./chat-bar/typing-textarea";
|
|
7
5
|
import { Textarea } from "./ui/textarea";
|
|
8
|
-
import { VoiceButton } from "./chat-bar/voice-button";
|
|
9
|
-
import { VoiceOverlay } from "./chat-bar/voice-overlay";
|
|
10
6
|
import { Button } from "./ui/button";
|
|
11
7
|
import { getTheme } from "../themes";
|
|
8
|
+
import { LoaderIcon, ArrowUpIcon } from "./icons";
|
|
9
|
+
import { TypingTextarea } from "./chat-bar/typing-textarea";
|
|
10
|
+
import { VoiceButton } from "./chat-bar/voice-button";
|
|
11
|
+
// Default fallback color scheme
|
|
12
|
+
const DEFAULT_COLOR_SCHEME = {
|
|
13
|
+
gradient: "linear-gradient(45deg, #3b82f6, #8b5cf6, #ec4899)",
|
|
14
|
+
shadow: {
|
|
15
|
+
dark: "rgba(0, 0, 0, 0.2)",
|
|
16
|
+
},
|
|
17
|
+
};
|
|
12
18
|
const getColorScheme = (themeName, playgroundUid) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
try {
|
|
20
|
+
// If theme is provided, use it directly
|
|
21
|
+
if (themeName) {
|
|
22
|
+
const theme = getTheme(themeName);
|
|
23
|
+
return theme || DEFAULT_COLOR_SCHEME;
|
|
24
|
+
}
|
|
25
|
+
// Use default theme
|
|
26
|
+
const defaultTheme = getTheme();
|
|
27
|
+
return defaultTheme || DEFAULT_COLOR_SCHEME;
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
console.warn("Theme system error, using fallback:", error);
|
|
31
|
+
return DEFAULT_COLOR_SCHEME;
|
|
16
32
|
}
|
|
17
|
-
// Use default theme
|
|
18
|
-
return getTheme();
|
|
19
33
|
};
|
|
20
|
-
export const ChatBar = ({
|
|
34
|
+
export const ChatBar = ({ placeholder = "Ask anything...", onSubmit, playgroundUid, isSubmitting, typingTexts = [
|
|
21
35
|
"Build me an AI chatbot application using OpenAI API",
|
|
22
36
|
"Build me an ecommerce website using Stripe API",
|
|
23
37
|
"Build me a price tracking tool using AgentQL API",
|
|
@@ -25,31 +39,43 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
25
39
|
"Build me a weather forecasting app using OpenWeather API",
|
|
26
40
|
"Build me a SMS notification system using Twilio API",
|
|
27
41
|
"Build me a travel booking platform using Skyscanner API",
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
], shouldFocusOnMount = true, showModelSelector = true, projectUid, theme, }) => {
|
|
42
|
+
], shouldFocusOnMount = true, showModelSelector = true, projectUid, theme, height = "auto", // Add default for required height prop
|
|
43
|
+
deepgramApiKey, }) => {
|
|
44
|
+
var _a;
|
|
32
45
|
const [isFocused, setIsFocused] = useState(true);
|
|
33
46
|
const [isRecording, setIsRecording] = useState(false);
|
|
34
47
|
const [isTranscribing, setIsTranscribing] = useState(false);
|
|
35
48
|
const [, setShowVoiceOverlay] = useState(false);
|
|
36
49
|
const [recordingTimeout, setRecordingTimeout] = useState(null);
|
|
37
50
|
const [recordingStartTime, setRecordingStartTime] = useState(null);
|
|
51
|
+
const [query, setQuery] = useState("");
|
|
38
52
|
const mediaRecorderRef = useRef(null);
|
|
39
53
|
const audioChunksRef = useRef([]);
|
|
40
|
-
const fileInputRef = useRef(null);
|
|
41
54
|
const textareaRef = useRef(null);
|
|
42
55
|
const [isMounted, setIsMounted] = useState(false);
|
|
43
|
-
// Get color scheme for gradient border
|
|
44
|
-
const colorScheme = useMemo(() =>
|
|
56
|
+
// Get color scheme for gradient border with error handling
|
|
57
|
+
const colorScheme = useMemo(() => {
|
|
58
|
+
try {
|
|
59
|
+
return getColorScheme(theme, playgroundUid);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.warn("Error getting color scheme:", error);
|
|
63
|
+
return DEFAULT_COLOR_SCHEME;
|
|
64
|
+
}
|
|
65
|
+
}, [theme, playgroundUid]);
|
|
45
66
|
useEffect(() => {
|
|
46
67
|
setIsMounted(true);
|
|
47
68
|
}, []);
|
|
48
69
|
useEffect(() => {
|
|
49
70
|
// Focus and select all text in the textarea when component mounts
|
|
50
71
|
if (shouldFocusOnMount && textareaRef.current && isMounted) {
|
|
51
|
-
|
|
52
|
-
|
|
72
|
+
try {
|
|
73
|
+
textareaRef.current.focus();
|
|
74
|
+
textareaRef.current.select();
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.warn("Focus error:", error);
|
|
78
|
+
}
|
|
53
79
|
}
|
|
54
80
|
}, [isMounted, shouldFocusOnMount]);
|
|
55
81
|
const handleFocus = () => {
|
|
@@ -62,11 +88,16 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
62
88
|
const adjustTextareaHeight = () => {
|
|
63
89
|
const textarea = textareaRef.current;
|
|
64
90
|
if (textarea) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
91
|
+
try {
|
|
92
|
+
// Reset height to auto to get the correct scrollHeight
|
|
93
|
+
textarea.style.height = "auto";
|
|
94
|
+
// Set new height based on scrollHeight, with a maximum of 200px
|
|
95
|
+
const newHeight = Math.min(textarea.scrollHeight, 200);
|
|
96
|
+
textarea.style.height = `${newHeight}px`;
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.warn("Textarea height adjustment error:", error);
|
|
100
|
+
}
|
|
70
101
|
}
|
|
71
102
|
};
|
|
72
103
|
// Update height whenever query changes
|
|
@@ -113,16 +144,15 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
113
144
|
setShowVoiceOverlay(false);
|
|
114
145
|
};
|
|
115
146
|
}, [recordingTimeout, isRecording]);
|
|
116
|
-
const handleVoiceOverlayDismiss = () => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
};
|
|
147
|
+
// const handleVoiceOverlayDismiss = () => {
|
|
148
|
+
// // Only allow dismissing during recording, not transcribing
|
|
149
|
+
// if (isTranscribing) return;
|
|
150
|
+
// if (isRecording) {
|
|
151
|
+
// // Stop recording if currently recording
|
|
152
|
+
// stopRecording();
|
|
153
|
+
// }
|
|
154
|
+
// setShowVoiceOverlay(false);
|
|
155
|
+
// };
|
|
126
156
|
const stopRecording = async () => {
|
|
127
157
|
if (mediaRecorderRef.current && isRecording) {
|
|
128
158
|
// Check minimum recording duration (1 second)
|
|
@@ -178,7 +208,11 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
178
208
|
// Send to backend for transcription
|
|
179
209
|
const formData = new FormData();
|
|
180
210
|
formData.append("audio", audioBlob, "recording.wav");
|
|
181
|
-
|
|
211
|
+
// Add Deepgram API key if provided
|
|
212
|
+
if (deepgramApiKey) {
|
|
213
|
+
formData.append("deepgramApiKey", deepgramApiKey);
|
|
214
|
+
}
|
|
215
|
+
const response = await fetch(`/api/voice/transcribe`, {
|
|
182
216
|
method: "POST",
|
|
183
217
|
body: formData,
|
|
184
218
|
});
|
|
@@ -362,8 +396,26 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
362
396
|
}
|
|
363
397
|
}
|
|
364
398
|
};
|
|
399
|
+
const handleSubmit = (e) => {
|
|
400
|
+
e.preventDefault();
|
|
401
|
+
if (!query.trim()) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
if (playgroundUid) {
|
|
406
|
+
const encodedQuery = encodeURIComponent(query.trim());
|
|
407
|
+
const url = `https://${playgroundUid}.sampleapp.ai?q=${encodedQuery}`;
|
|
408
|
+
window.open(url, "_blank");
|
|
409
|
+
}
|
|
410
|
+
if (onSubmit) {
|
|
411
|
+
onSubmit(e);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
console.error("Submit error:", error);
|
|
416
|
+
}
|
|
417
|
+
};
|
|
365
418
|
return (React.createElement("div", null,
|
|
366
|
-
React.createElement(VoiceOverlay, { isRecording: isRecording, isTranscribing: isTranscribing, onDismiss: handleVoiceOverlayDismiss }),
|
|
367
419
|
React.createElement("div", { style: {
|
|
368
420
|
position: "relative",
|
|
369
421
|
padding: "2px",
|
|
@@ -373,7 +425,7 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
373
425
|
marginRight: "auto",
|
|
374
426
|
} },
|
|
375
427
|
React.createElement("div", { style: {
|
|
376
|
-
backgroundColor: colorScheme.shadow.dark,
|
|
428
|
+
backgroundColor: ((_a = colorScheme.shadow) === null || _a === void 0 ? void 0 : _a.dark) || "rgba(0, 0, 0, 0.2)",
|
|
377
429
|
position: "absolute",
|
|
378
430
|
top: "0",
|
|
379
431
|
right: "0",
|
|
@@ -386,7 +438,7 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
386
438
|
pointerEvents: "none",
|
|
387
439
|
} }),
|
|
388
440
|
React.createElement("div", { style: {
|
|
389
|
-
background: colorScheme.gradient,
|
|
441
|
+
background: colorScheme.gradient || DEFAULT_COLOR_SCHEME.gradient,
|
|
390
442
|
backgroundSize: "400% 400%",
|
|
391
443
|
position: "absolute",
|
|
392
444
|
top: "0",
|
|
@@ -402,7 +454,7 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
402
454
|
animation: "gradient-bg 5s ease infinite",
|
|
403
455
|
} }),
|
|
404
456
|
React.createElement("div", { style: {
|
|
405
|
-
background: colorScheme.gradient,
|
|
457
|
+
background: colorScheme.gradient || DEFAULT_COLOR_SCHEME.gradient,
|
|
406
458
|
backgroundSize: "400% 400%",
|
|
407
459
|
position: "absolute",
|
|
408
460
|
top: "0",
|
|
@@ -437,7 +489,7 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
437
489
|
paddingTop: "4px",
|
|
438
490
|
paddingBottom: "4px",
|
|
439
491
|
} },
|
|
440
|
-
|
|
492
|
+
typingTexts.length > 0 ? (React.createElement(TypingTextarea, { texts: typingTexts, ref: textareaRef, style: {
|
|
441
493
|
flex: 1,
|
|
442
494
|
width: "100%",
|
|
443
495
|
border: "none",
|
|
@@ -458,14 +510,15 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
458
510
|
marginTop: "4px",
|
|
459
511
|
color: "#f4f4f5",
|
|
460
512
|
fontFamily: "inherit",
|
|
513
|
+
height: height,
|
|
461
514
|
}, value: query, onFocus: handleFocus, onBlur: handleBlur, onChange: (e) => setQuery(e.target.value), onSubmit: (e) => {
|
|
462
515
|
// Prevent submission if recording
|
|
463
516
|
if (isRecording || isTranscribing) {
|
|
464
517
|
e.preventDefault();
|
|
465
518
|
return;
|
|
466
519
|
}
|
|
467
|
-
|
|
468
|
-
},
|
|
520
|
+
handleSubmit(e);
|
|
521
|
+
}, rows: 1 })) : (React.createElement(Textarea, { ref: textareaRef, placeholder: placeholder, style: {
|
|
469
522
|
flex: 1,
|
|
470
523
|
width: "100%",
|
|
471
524
|
border: "none",
|
|
@@ -486,36 +539,22 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
486
539
|
marginTop: "4px",
|
|
487
540
|
color: "#f4f4f5",
|
|
488
541
|
fontFamily: "inherit",
|
|
542
|
+
height: height,
|
|
489
543
|
}, value: query, onFocus: handleFocus, onBlur: handleBlur, onChange: (e) => setQuery(e.target.value), onKeyDown: (e) => {
|
|
490
544
|
if (e.key === "Enter") {
|
|
491
545
|
if (e.shiftKey) {
|
|
492
546
|
return;
|
|
493
547
|
}
|
|
494
548
|
e.preventDefault();
|
|
495
|
-
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
onSubmit(e);
|
|
549
|
+
handleSubmit(e);
|
|
499
550
|
}
|
|
500
551
|
}, rows: 1 })),
|
|
501
|
-
React.createElement("div",
|
|
502
|
-
React.createElement(VoiceButton, { isRecording: isRecording, isTranscribing: isTranscribing, onVoiceRecording: handleVoiceRecording
|
|
552
|
+
React.createElement("div", { style: { display: "flex", flexDirection: "row", gap: "4px" } },
|
|
553
|
+
React.createElement(VoiceButton, { isRecording: isRecording, isTranscribing: isTranscribing, onVoiceRecording: handleVoiceRecording }),
|
|
503
554
|
React.createElement(Button, { style: {
|
|
504
555
|
height: "32px",
|
|
505
556
|
width: "32px",
|
|
506
|
-
|
|
507
|
-
}, disabled: query.length === 0 || isSubmitting || hasPendingEnv, variant: isStreaming ? "outline" : "default", onClick: (e) => {
|
|
508
|
-
if (isStreaming && onCancel) {
|
|
509
|
-
onCancel();
|
|
510
|
-
}
|
|
511
|
-
else {
|
|
512
|
-
onSubmit(e);
|
|
513
|
-
}
|
|
514
|
-
} }, isSubmitting ? (React.createElement(Loader2, { style: {
|
|
515
|
-
height: "20px",
|
|
516
|
-
width: "20px",
|
|
517
|
-
animation: "spin 1s linear infinite",
|
|
518
|
-
} })) : isStreaming ? (React.createElement(CircleStop, { style: { height: "24px", width: "24px" } })) : (React.createElement(ArrowUp, { style: { height: "20px", width: "20px" } }))))),
|
|
557
|
+
}, disabled: query.length === 0 || isSubmitting, variant: "secondary", onClick: handleSubmit, size: "icon" }, isSubmitting ? (React.createElement(LoaderIcon, { size: 20, style: { animation: "spin 1s linear infinite" } })) : (React.createElement(ArrowUpIcon, { style: { width: "20px", height: "20px" } }))))),
|
|
519
558
|
React.createElement("div", { style: {
|
|
520
559
|
display: "flex",
|
|
521
560
|
alignItems: "center",
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
// Loader/Spinner Icon Component
|
|
4
|
+
export const LoaderIcon = ({ size = 20, className = "", style = {}, }) => {
|
|
5
|
+
return (React.createElement(React.Fragment, null,
|
|
6
|
+
React.createElement("style", null, `
|
|
7
|
+
@keyframes icon-spin {
|
|
8
|
+
from { transform: rotate(0deg); }
|
|
9
|
+
to { transform: rotate(360deg); }
|
|
10
|
+
}
|
|
11
|
+
`),
|
|
12
|
+
React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: `lucide lucide-loader-circle ${className}`, style: Object.assign({ animation: "icon-spin 1s linear infinite" }, style) },
|
|
13
|
+
React.createElement("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }))));
|
|
14
|
+
};
|
|
15
|
+
// Arrow Up Icon Component
|
|
16
|
+
export const ArrowUpIcon = ({ size = 20, className = "", style = {}, }) => {
|
|
17
|
+
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: `lucide lucide-arrow-up ${className}`, style: Object.assign({}, style) },
|
|
18
|
+
React.createElement("path", { d: "m5 12 7-7 7 7" }),
|
|
19
|
+
React.createElement("path", { d: "M12 19V5" })));
|
|
20
|
+
};
|
|
21
|
+
export const VoiceIcon = ({ isRecording = false, isTranscribing = false, size = 20, style = {}, }) => {
|
|
22
|
+
const containerStyle = Object.assign({ position: "relative", width: `${size}px`, height: `${Math.floor(size * 0.8)}px`, display: "flex", alignItems: "center", justifyContent: "center" }, style);
|
|
23
|
+
if (isTranscribing) {
|
|
24
|
+
return (React.createElement(React.Fragment, null,
|
|
25
|
+
React.createElement("style", null, `
|
|
26
|
+
@keyframes icon-spin {
|
|
27
|
+
from { transform: rotate(0deg); }
|
|
28
|
+
to { transform: rotate(360deg); }
|
|
29
|
+
}
|
|
30
|
+
`),
|
|
31
|
+
React.createElement("div", { style: containerStyle },
|
|
32
|
+
React.createElement("div", { style: {
|
|
33
|
+
width: `${Math.floor(size * 0.6)}px`,
|
|
34
|
+
height: `${Math.floor(size * 0.6)}px`,
|
|
35
|
+
border: "2px solid currentColor",
|
|
36
|
+
borderTop: "2px solid transparent",
|
|
37
|
+
borderRadius: "50%",
|
|
38
|
+
animation: "icon-spin 1s linear infinite",
|
|
39
|
+
} }))));
|
|
40
|
+
}
|
|
41
|
+
if (isRecording) {
|
|
42
|
+
return (React.createElement(React.Fragment, null,
|
|
43
|
+
React.createElement("style", null, `
|
|
44
|
+
@keyframes icon-pulse {
|
|
45
|
+
0%, 100% { transform: scale(1); }
|
|
46
|
+
50% { transform: scale(0.95); }
|
|
47
|
+
}
|
|
48
|
+
`),
|
|
49
|
+
React.createElement("div", { style: containerStyle },
|
|
50
|
+
React.createElement("div", { style: {
|
|
51
|
+
width: `${Math.floor(size * 0.6)}px`,
|
|
52
|
+
height: `${Math.floor(size * 0.6)}px`,
|
|
53
|
+
backgroundColor: "currentColor",
|
|
54
|
+
borderRadius: "2px",
|
|
55
|
+
animation: "icon-pulse 1s ease-in-out infinite",
|
|
56
|
+
} }))));
|
|
57
|
+
}
|
|
58
|
+
// Default voice icon with sound wave animation
|
|
59
|
+
const barWidth = Math.max(1, Math.floor(size * 0.075));
|
|
60
|
+
const gap = Math.max(1, Math.floor(size * 0.1));
|
|
61
|
+
return (React.createElement(React.Fragment, null,
|
|
62
|
+
React.createElement("style", null, `
|
|
63
|
+
@keyframes wave1 {
|
|
64
|
+
0%, 100% {
|
|
65
|
+
transform: scaleY(0.4);
|
|
66
|
+
opacity: 0.6;
|
|
67
|
+
}
|
|
68
|
+
50% {
|
|
69
|
+
transform: scaleY(0.8);
|
|
70
|
+
opacity: 0.9;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@keyframes wave2 {
|
|
75
|
+
0%, 100% {
|
|
76
|
+
transform: scaleY(0.5);
|
|
77
|
+
opacity: 0.7;
|
|
78
|
+
}
|
|
79
|
+
50% {
|
|
80
|
+
transform: scaleY(1);
|
|
81
|
+
opacity: 1;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@keyframes wave3 {
|
|
86
|
+
0%, 100% {
|
|
87
|
+
transform: scaleY(0.6);
|
|
88
|
+
opacity: 0.8;
|
|
89
|
+
}
|
|
90
|
+
50% {
|
|
91
|
+
transform: scaleY(1);
|
|
92
|
+
opacity: 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@keyframes wave4 {
|
|
97
|
+
0%, 100% {
|
|
98
|
+
transform: scaleY(0.5);
|
|
99
|
+
opacity: 0.7;
|
|
100
|
+
}
|
|
101
|
+
50% {
|
|
102
|
+
transform: scaleY(0.9);
|
|
103
|
+
opacity: 1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@keyframes wave5 {
|
|
108
|
+
0%, 100% {
|
|
109
|
+
transform: scaleY(0.4);
|
|
110
|
+
opacity: 0.6;
|
|
111
|
+
}
|
|
112
|
+
50% {
|
|
113
|
+
transform: scaleY(0.7);
|
|
114
|
+
opacity: 0.9;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
`),
|
|
118
|
+
React.createElement("div", { style: containerStyle },
|
|
119
|
+
React.createElement("div", { style: { display: "flex", alignItems: "center", gap: `${gap}px` } },
|
|
120
|
+
React.createElement("div", { style: {
|
|
121
|
+
width: `${barWidth}px`,
|
|
122
|
+
height: `${Math.floor(size * 0.4)}px`,
|
|
123
|
+
backgroundColor: "currentColor",
|
|
124
|
+
borderRadius: "999px",
|
|
125
|
+
animation: "wave1 1.8s ease-in-out infinite",
|
|
126
|
+
} }),
|
|
127
|
+
React.createElement("div", { style: {
|
|
128
|
+
width: `${barWidth}px`,
|
|
129
|
+
height: `${Math.floor(size * 0.6)}px`,
|
|
130
|
+
backgroundColor: "currentColor",
|
|
131
|
+
borderRadius: "999px",
|
|
132
|
+
animation: "wave2 2.1s ease-in-out infinite",
|
|
133
|
+
} }),
|
|
134
|
+
React.createElement("div", { style: {
|
|
135
|
+
width: `${barWidth}px`,
|
|
136
|
+
height: `${Math.floor(size * 0.8)}px`,
|
|
137
|
+
backgroundColor: "currentColor",
|
|
138
|
+
borderRadius: "999px",
|
|
139
|
+
animation: "wave3 1.6s ease-in-out infinite",
|
|
140
|
+
} }),
|
|
141
|
+
React.createElement("div", { style: {
|
|
142
|
+
width: `${barWidth}px`,
|
|
143
|
+
height: `${Math.floor(size * 0.6)}px`,
|
|
144
|
+
backgroundColor: "currentColor",
|
|
145
|
+
borderRadius: "999px",
|
|
146
|
+
animation: "wave4 1.9s ease-in-out infinite",
|
|
147
|
+
} }),
|
|
148
|
+
React.createElement("div", { style: {
|
|
149
|
+
width: `${barWidth}px`,
|
|
150
|
+
height: `${Math.floor(size * 0.4)}px`,
|
|
151
|
+
backgroundColor: "currentColor",
|
|
152
|
+
borderRadius: "999px",
|
|
153
|
+
animation: "wave5 2.2s ease-in-out infinite",
|
|
154
|
+
} })))));
|
|
155
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// src/components/MinimalChatBar.tsx
|
|
2
|
+
import React, { useState } from "react";
|
|
3
|
+
export const MinimalChatBar = ({ placeholder = "Ask anything...", onSubmit, theme = "light", }) => {
|
|
4
|
+
const [message, setMessage] = useState("");
|
|
5
|
+
const handleSubmit = (e) => {
|
|
6
|
+
e.preventDefault();
|
|
7
|
+
if (message.trim()) {
|
|
8
|
+
onSubmit === null || onSubmit === void 0 ? void 0 : onSubmit(message.trim());
|
|
9
|
+
setMessage("");
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
return (React.createElement("div", { style: {
|
|
13
|
+
width: "100%",
|
|
14
|
+
maxWidth: "600px",
|
|
15
|
+
margin: "0 auto",
|
|
16
|
+
} },
|
|
17
|
+
React.createElement("form", { onSubmit: handleSubmit, style: {
|
|
18
|
+
display: "flex",
|
|
19
|
+
gap: "8px",
|
|
20
|
+
padding: "12px",
|
|
21
|
+
border: theme === "dark" ? "1px solid #333" : "1px solid #ccc",
|
|
22
|
+
borderRadius: "8px",
|
|
23
|
+
backgroundColor: theme === "dark" ? "#1a1a1a" : "#ffffff",
|
|
24
|
+
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
|
25
|
+
} },
|
|
26
|
+
React.createElement("input", { type: "text", value: message, onChange: (e) => setMessage(e.target.value), placeholder: placeholder, style: {
|
|
27
|
+
flex: 1,
|
|
28
|
+
padding: "8px 12px",
|
|
29
|
+
border: "1px solid #ddd",
|
|
30
|
+
borderRadius: "4px",
|
|
31
|
+
fontSize: "14px",
|
|
32
|
+
outline: "none",
|
|
33
|
+
backgroundColor: theme === "dark" ? "#333" : "#fff",
|
|
34
|
+
color: theme === "dark" ? "#fff" : "#000",
|
|
35
|
+
} }),
|
|
36
|
+
React.createElement("button", { type: "submit", disabled: !message.trim(), style: {
|
|
37
|
+
padding: "8px 16px",
|
|
38
|
+
backgroundColor: message.trim() ? "#007bff" : "#ccc",
|
|
39
|
+
color: "white",
|
|
40
|
+
border: "none",
|
|
41
|
+
borderRadius: "4px",
|
|
42
|
+
cursor: message.trim() ? "pointer" : "not-allowed",
|
|
43
|
+
fontSize: "14px",
|
|
44
|
+
} }, "Send")),
|
|
45
|
+
React.createElement("p", { style: {
|
|
46
|
+
fontSize: "12px",
|
|
47
|
+
color: "#666",
|
|
48
|
+
textAlign: "center",
|
|
49
|
+
marginTop: "8px",
|
|
50
|
+
} }, "SDK Version: @sampleapp.ai/sdk v1.0.25")));
|
|
51
|
+
};
|