@sampleapp.ai/sdk 1.0.25 → 1.0.26
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 +84 -50
- 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 +2 -5
- package/dist/index.es.js +1045 -6082
- package/dist/index.js +1 -0
- package/dist/index.standalone.js +87 -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, hasPendingEnv, 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
|
+
}) => {
|
|
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
|
|
@@ -362,8 +393,26 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
362
393
|
}
|
|
363
394
|
}
|
|
364
395
|
};
|
|
396
|
+
const handleSubmit = (e) => {
|
|
397
|
+
e.preventDefault();
|
|
398
|
+
if (!query.trim()) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
if (playgroundUid) {
|
|
403
|
+
const encodedQuery = encodeURIComponent(query.trim());
|
|
404
|
+
const url = `https://${playgroundUid}.sampleapp.ai?q=${encodedQuery}`;
|
|
405
|
+
window.open(url, "_blank");
|
|
406
|
+
}
|
|
407
|
+
if (onSubmit) {
|
|
408
|
+
onSubmit(e);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
console.error("Submit error:", error);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
365
415
|
return (React.createElement("div", null,
|
|
366
|
-
React.createElement(VoiceOverlay, { isRecording: isRecording, isTranscribing: isTranscribing, onDismiss: handleVoiceOverlayDismiss }),
|
|
367
416
|
React.createElement("div", { style: {
|
|
368
417
|
position: "relative",
|
|
369
418
|
padding: "2px",
|
|
@@ -373,7 +422,7 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
373
422
|
marginRight: "auto",
|
|
374
423
|
} },
|
|
375
424
|
React.createElement("div", { style: {
|
|
376
|
-
backgroundColor: colorScheme.shadow.dark,
|
|
425
|
+
backgroundColor: ((_a = colorScheme.shadow) === null || _a === void 0 ? void 0 : _a.dark) || "rgba(0, 0, 0, 0.2)",
|
|
377
426
|
position: "absolute",
|
|
378
427
|
top: "0",
|
|
379
428
|
right: "0",
|
|
@@ -386,7 +435,7 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
386
435
|
pointerEvents: "none",
|
|
387
436
|
} }),
|
|
388
437
|
React.createElement("div", { style: {
|
|
389
|
-
background: colorScheme.gradient,
|
|
438
|
+
background: colorScheme.gradient || DEFAULT_COLOR_SCHEME.gradient,
|
|
390
439
|
backgroundSize: "400% 400%",
|
|
391
440
|
position: "absolute",
|
|
392
441
|
top: "0",
|
|
@@ -402,7 +451,7 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
402
451
|
animation: "gradient-bg 5s ease infinite",
|
|
403
452
|
} }),
|
|
404
453
|
React.createElement("div", { style: {
|
|
405
|
-
background: colorScheme.gradient,
|
|
454
|
+
background: colorScheme.gradient || DEFAULT_COLOR_SCHEME.gradient,
|
|
406
455
|
backgroundSize: "400% 400%",
|
|
407
456
|
position: "absolute",
|
|
408
457
|
top: "0",
|
|
@@ -437,7 +486,7 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
437
486
|
paddingTop: "4px",
|
|
438
487
|
paddingBottom: "4px",
|
|
439
488
|
} },
|
|
440
|
-
|
|
489
|
+
typingTexts.length > 0 ? (React.createElement(TypingTextarea, { texts: typingTexts, ref: textareaRef, style: {
|
|
441
490
|
flex: 1,
|
|
442
491
|
width: "100%",
|
|
443
492
|
border: "none",
|
|
@@ -464,8 +513,8 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
464
513
|
e.preventDefault();
|
|
465
514
|
return;
|
|
466
515
|
}
|
|
467
|
-
|
|
468
|
-
},
|
|
516
|
+
handleSubmit(e);
|
|
517
|
+
}, rows: 1 })) : (React.createElement(Textarea, { ref: textareaRef, placeholder: placeholder, style: {
|
|
469
518
|
flex: 1,
|
|
470
519
|
width: "100%",
|
|
471
520
|
border: "none",
|
|
@@ -492,30 +541,15 @@ export const ChatBar = ({ query, placeholder = "Ask anything...", setQuery, onSu
|
|
|
492
541
|
return;
|
|
493
542
|
}
|
|
494
543
|
e.preventDefault();
|
|
495
|
-
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
onSubmit(e);
|
|
544
|
+
handleSubmit(e);
|
|
499
545
|
}
|
|
500
546
|
}, rows: 1 })),
|
|
501
|
-
React.createElement("div",
|
|
502
|
-
React.createElement(VoiceButton, { isRecording: isRecording, isTranscribing: isTranscribing, onVoiceRecording: handleVoiceRecording
|
|
547
|
+
React.createElement("div", { style: { display: "flex", flexDirection: "row", gap: "4px" } },
|
|
548
|
+
React.createElement(VoiceButton, { isRecording: isRecording, isTranscribing: isTranscribing, onVoiceRecording: handleVoiceRecording }),
|
|
503
549
|
React.createElement(Button, { style: {
|
|
504
550
|
height: "32px",
|
|
505
551
|
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" } }))))),
|
|
552
|
+
}, disabled: query.length === 0 || isSubmitting || hasPendingEnv, 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
553
|
React.createElement("div", { style: {
|
|
520
554
|
display: "flex",
|
|
521
555
|
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
|
+
};
|