goji-search 1.1.0 → 1.1.2
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/goji-search/components/elements/action-buttons.js +10 -14
- package/dist/goji-search/components/elements/auto-expanding-textarea.js +11 -1
- package/dist/goji-search/components/elements/inspiration-menu.d.ts +2 -1
- package/dist/goji-search/components/elements/inspiration-menu.js +41 -33
- package/dist/goji-search/components/elements/message-list.js +134 -104
- package/dist/goji-search/components/elements/search-input.d.ts +3 -1
- package/dist/goji-search/components/elements/search-input.js +51 -9
- package/dist/goji-search/components/elements/suggested-questions.js +34 -31
- package/dist/goji-search/components/goji-search-component.js +71 -87
- package/dist/goji-search/config/company.d.ts +1 -0
- package/dist/goji-search/config/company.js +5 -0
- package/dist/goji-search.css +1 -1
- package/package.json +1 -1
|
@@ -51,28 +51,24 @@ export function ActionButtons({ size, isHovered, isStreaming, searchQuery, isRec
|
|
|
51
51
|
zIndex: 30,
|
|
52
52
|
}, children: "Send message" })] }), _jsxs("div", { style: { position: "relative", display: "inline-block", marginLeft: "0.25rem" }, onMouseEnter: () => setHoverVoice(true), onMouseLeave: () => setHoverVoice(false), children: [_jsxs("button", { type: "button", onClick: onVoiceSearch, "aria-describedby": hoverVoice ? voiceTooltipId : undefined, onFocus: () => setHoverVoice(true), onBlur: () => setHoverVoice(false), style: {
|
|
53
53
|
borderRadius: "1.3rem",
|
|
54
|
-
border:
|
|
55
|
-
backgroundColor:
|
|
54
|
+
border: "1px solid rgba(255, 255, 255, 0.3)",
|
|
55
|
+
backgroundColor: "rgba(255, 255, 255, 0.2)",
|
|
56
56
|
padding: size === "l" || size === "xl" ? "0.5rem" : "0.3rem",
|
|
57
57
|
display: "flex",
|
|
58
58
|
alignItems: "center",
|
|
59
59
|
justifyContent: "center",
|
|
60
|
-
color:
|
|
60
|
+
color: "rgba(0, 0, 0, 0.75)",
|
|
61
61
|
backdropFilter: "blur(20px)",
|
|
62
62
|
WebkitBackdropFilter: "blur(20px)",
|
|
63
63
|
transition: "all 0.2s",
|
|
64
64
|
cursor: "pointer",
|
|
65
65
|
outline: "none",
|
|
66
66
|
opacity: size === "m" && !isHovered ? 0.7 : 1,
|
|
67
|
-
boxShadow:
|
|
67
|
+
boxShadow: "inset 0 1px 0 rgba(255, 255, 255, 0.3)",
|
|
68
68
|
}, onMouseEnter: (e) => {
|
|
69
|
-
|
|
70
|
-
e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.3)";
|
|
71
|
-
}
|
|
69
|
+
e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.3)";
|
|
72
70
|
}, onMouseLeave: (e) => {
|
|
73
|
-
|
|
74
|
-
e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
75
|
-
}
|
|
71
|
+
e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
76
72
|
}, children: [_jsx(Mic, { style: {
|
|
77
73
|
width: size === "l" || size === "xl" ? "1rem" : "0.875rem",
|
|
78
74
|
height: size === "l" || size === "xl" ? "1rem" : "0.875rem",
|
|
@@ -85,28 +81,28 @@ export function ActionButtons({ size, isHovered, isStreaming, searchQuery, isRec
|
|
|
85
81
|
}, children: [_jsx("span", { style: {
|
|
86
82
|
width: "2px",
|
|
87
83
|
height: "55%",
|
|
88
|
-
backgroundColor: "rgba(
|
|
84
|
+
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
|
89
85
|
borderRadius: "1px",
|
|
90
86
|
transformOrigin: "bottom",
|
|
91
87
|
animation: "eqPulse 900ms ease-in-out 0ms infinite",
|
|
92
88
|
} }), _jsx("span", { style: {
|
|
93
89
|
width: "2px",
|
|
94
90
|
height: "85%",
|
|
95
|
-
backgroundColor: "rgba(
|
|
91
|
+
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
|
96
92
|
borderRadius: "1px",
|
|
97
93
|
transformOrigin: "bottom",
|
|
98
94
|
animation: "eqPulse 900ms ease-in-out 100ms infinite",
|
|
99
95
|
} }), _jsx("span", { style: {
|
|
100
96
|
width: "2px",
|
|
101
97
|
height: "65%",
|
|
102
|
-
backgroundColor: "rgba(
|
|
98
|
+
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
|
103
99
|
borderRadius: "1px",
|
|
104
100
|
transformOrigin: "bottom",
|
|
105
101
|
animation: "eqPulse 900ms ease-in-out 200ms infinite",
|
|
106
102
|
} }), _jsx("span", { style: {
|
|
107
103
|
width: "2px",
|
|
108
104
|
height: "78%",
|
|
109
|
-
backgroundColor: "rgba(
|
|
105
|
+
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
|
110
106
|
borderRadius: "1px",
|
|
111
107
|
transformOrigin: "bottom",
|
|
112
108
|
animation: "eqPulse 900ms ease-in-out 300ms infinite",
|
|
@@ -5,16 +5,26 @@ const AutoExpandingTextarea = forwardRef(({ value, onChange, onKeyDown, classNam
|
|
|
5
5
|
useEffect(() => {
|
|
6
6
|
const el = ref?.current || textareaRef.current;
|
|
7
7
|
if (el) {
|
|
8
|
+
// Reset to minimum height when value is empty
|
|
9
|
+
if (!value || value.trim() === '') {
|
|
10
|
+
el.style.height = "24px";
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
8
13
|
el.style.height = "auto";
|
|
9
14
|
const scrollHeight = el.scrollHeight;
|
|
10
15
|
const minHeight = 24; // one line height
|
|
11
16
|
const maxHeight = 160; // max ~5 lines
|
|
12
17
|
el.style.height = `${Math.min(Math.max(scrollHeight, minHeight), maxHeight)}px`;
|
|
13
18
|
}
|
|
14
|
-
}, [value]);
|
|
19
|
+
}, [value, ref]);
|
|
15
20
|
return (_jsx("textarea", { ref: ref || textareaRef, value: value, onChange: onChange, onKeyDown: (e) => {
|
|
16
21
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
17
22
|
e.preventDefault();
|
|
23
|
+
// Trigger form submission by finding parent form and calling submit
|
|
24
|
+
const form = e.currentTarget.closest('form');
|
|
25
|
+
if (form) {
|
|
26
|
+
form.requestSubmit(); // This triggers onSubmit handler
|
|
27
|
+
}
|
|
18
28
|
onKeyDown?.(e);
|
|
19
29
|
}
|
|
20
30
|
else {
|
|
@@ -2,6 +2,7 @@ interface InspirationMenuProps {
|
|
|
2
2
|
questions: string[];
|
|
3
3
|
onQuestionClick: (question: string) => void;
|
|
4
4
|
onClose: () => void;
|
|
5
|
+
suggestedLabel?: string;
|
|
5
6
|
}
|
|
6
|
-
export declare function InspirationMenu({ questions, onQuestionClick, onClose }: InspirationMenuProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function InspirationMenu({ questions, onQuestionClick, onClose, suggestedLabel }: InspirationMenuProps): import("react/jsx-runtime").JSX.Element;
|
|
7
8
|
export {};
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { X } from "lucide-react";
|
|
3
|
-
export function InspirationMenu({ questions, onQuestionClick, onClose }) {
|
|
4
|
-
return (_jsxs("div", { style: {
|
|
3
|
+
export function InspirationMenu({ questions, onQuestionClick, onClose, suggestedLabel = "Suggested" }) {
|
|
4
|
+
return (_jsxs("div", { "data-inspiration-menu": true, style: {
|
|
5
5
|
position: "absolute",
|
|
6
6
|
bottom: "calc(100% + 0.5rem)",
|
|
7
7
|
left: 0,
|
|
8
8
|
minWidth: "16rem",
|
|
9
9
|
maxWidth: "20rem",
|
|
10
10
|
borderRadius: "0.75rem",
|
|
11
|
-
border: "1px solid rgba(
|
|
12
|
-
//
|
|
13
|
-
backgroundColor: "
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
border: "1px solid rgba(255, 255, 255, 0.35)",
|
|
12
|
+
// Glass-like background with transparency
|
|
13
|
+
backgroundColor: "rgba(255, 255, 255, 0.75)",
|
|
14
|
+
backdropFilter: "blur(24px) saturate(180%)",
|
|
15
|
+
WebkitBackdropFilter: "blur(24px) saturate(180%)",
|
|
16
|
+
boxShadow: "0 8px 28px rgba(0, 0, 0, 0.14), inset 0 1px 0 rgba(255, 255, 255, 0.8)",
|
|
16
17
|
padding: "0.625rem",
|
|
17
18
|
animation: "slideUpMenu 0.25s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
18
19
|
// Ensure the popover sits above chat bubbles and other content
|
|
19
20
|
zIndex: 1000,
|
|
20
|
-
}, onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { style: {
|
|
21
|
+
}, onClick: (e) => e.stopPropagation(), onMouseEnter: (e) => e.stopPropagation(), onMouseLeave: onClose, children: [_jsxs("div", { style: {
|
|
21
22
|
display: "flex",
|
|
22
23
|
justifyContent: "space-between",
|
|
23
24
|
alignItems: "center",
|
|
@@ -30,7 +31,7 @@ export function InspirationMenu({ questions, onQuestionClick, onClose }) {
|
|
|
30
31
|
color: "rgba(0, 0, 0, 0.5)",
|
|
31
32
|
letterSpacing: "0.05em",
|
|
32
33
|
textTransform: "capitalize",
|
|
33
|
-
}, children:
|
|
34
|
+
}, children: suggestedLabel }), _jsx("button", { type: "button", onClick: onClose, style: {
|
|
34
35
|
padding: "0.25rem",
|
|
35
36
|
borderRadius: "0.375rem",
|
|
36
37
|
border: "1px solid rgba(255, 255, 255, 0.2)",
|
|
@@ -52,28 +53,35 @@ export function InspirationMenu({ questions, onQuestionClick, onClose }) {
|
|
|
52
53
|
e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.15)";
|
|
53
54
|
e.currentTarget.style.color = "rgba(0, 0, 0, 0.5)";
|
|
54
55
|
e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.2)";
|
|
55
|
-
}, children: _jsx(X, { style: { width: "0.875rem", height: "0.875rem" } }) })] }), _jsx("div", { style: { display: "flex", flexDirection: "column", gap: "0.375rem" }, children: questions.map((question, index) =>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
e
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
56
|
+
}, children: _jsx(X, { style: { width: "0.875rem", height: "0.875rem" } }) })] }), _jsx("div", { style: { display: "flex", flexDirection: "column", gap: "0.375rem" }, children: questions.map((question, index) => {
|
|
57
|
+
const truncatedQuestion = question.length > 80 ? question.slice(0, 80) + '...' : question;
|
|
58
|
+
return (_jsx("button", { type: "button", onClick: () => onQuestionClick(question), title: question.length > 80 ? question : undefined, style: {
|
|
59
|
+
padding: "0.5rem 0.75rem",
|
|
60
|
+
borderRadius: "1.3rem",
|
|
61
|
+
border: "1px solid rgba(0, 0, 0, 0.06)",
|
|
62
|
+
backgroundColor: "#ffffff",
|
|
63
|
+
color: "rgba(0, 0, 0, 0.85)",
|
|
64
|
+
fontSize: "0.75rem",
|
|
65
|
+
fontWeight: 500,
|
|
66
|
+
textAlign: "left",
|
|
67
|
+
cursor: "pointer",
|
|
68
|
+
transition: "all 0.2s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
69
|
+
boxShadow: "0 1px 2px rgba(0, 0, 0, 0.06)",
|
|
70
|
+
outline: "none",
|
|
71
|
+
lineHeight: "1.3",
|
|
72
|
+
overflow: "hidden",
|
|
73
|
+
textOverflow: "ellipsis",
|
|
74
|
+
whiteSpace: "nowrap",
|
|
75
|
+
}, onMouseEnter: (e) => {
|
|
76
|
+
e.currentTarget.style.backgroundColor = "rgba(140, 110, 230, 0.12)";
|
|
77
|
+
e.currentTarget.style.borderColor = "rgba(140, 110, 230, 0.3)";
|
|
78
|
+
e.currentTarget.style.transform = "translateY(-1px)";
|
|
79
|
+
e.currentTarget.style.boxShadow = "0 3px 8px rgba(140, 110, 230, 0.15)";
|
|
80
|
+
}, onMouseLeave: (e) => {
|
|
81
|
+
e.currentTarget.style.backgroundColor = "#ffffff";
|
|
82
|
+
e.currentTarget.style.borderColor = "rgba(0, 0, 0, 0.06)";
|
|
83
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
84
|
+
e.currentTarget.style.boxShadow = "0 1px 2px rgba(0, 0, 0, 0.06)";
|
|
85
|
+
}, children: truncatedQuestion }, index));
|
|
86
|
+
}) })] }));
|
|
79
87
|
}
|
|
@@ -43,18 +43,116 @@ export function MessageList({ messages, isStreaming, aiAvatarSrc = '', size = 'l
|
|
|
43
43
|
borderRadius: "0.75rem",
|
|
44
44
|
backgroundColor: "rgba(255, 255, 255, 0.08)",
|
|
45
45
|
border: "1px solid rgba(255, 255, 255, 0.15)",
|
|
46
|
-
}, className: "custom-scrollbar", children: [messages.map((message, index) =>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
}, className: "custom-scrollbar", children: [messages.map((message, index) => {
|
|
47
|
+
// Skip rendering empty assistant messages during streaming (thinking dots will show instead)
|
|
48
|
+
const isEmptyAssistantWhileStreaming = message.role === "assistant" && !message.content && isStreaming;
|
|
49
|
+
if (isEmptyAssistantWhileStreaming)
|
|
50
|
+
return null;
|
|
51
|
+
return (_jsxs("div", { style: {
|
|
52
|
+
display: "flex",
|
|
53
|
+
justifyContent: message.role === "user" ? "flex-end" : "flex-start",
|
|
54
|
+
animation: "fadeIn 0.3s ease-in",
|
|
55
|
+
gap: "0.4rem",
|
|
56
|
+
}, children: [_jsx("div", { style: {
|
|
57
|
+
display: "flex",
|
|
58
|
+
gap: "0.4rem",
|
|
59
|
+
alignItems: message.role === "user" ? "flex-end" : "flex-start"
|
|
60
|
+
}, children: _jsx("div", { style: {
|
|
61
|
+
width: message.role === "user" ? "0" : "1.4rem",
|
|
62
|
+
height: message.role === "user" ? "0" : "auto",
|
|
63
|
+
borderRadius: "none",
|
|
64
|
+
backgroundColor: "transparent",
|
|
65
|
+
display: "flex",
|
|
66
|
+
alignItems: "center",
|
|
67
|
+
justifyContent: "center",
|
|
68
|
+
fontSize: "0.75rem",
|
|
69
|
+
border: "none",
|
|
70
|
+
boxShadow: "none",
|
|
71
|
+
}, children: message.role === "user" ? (
|
|
72
|
+
// <User size={16} color="rgba(0, 0, 0, 1)" />
|
|
73
|
+
_jsx("div", {})) : (aiAvatarSrc ? (_jsx("img", { src: aiAvatarSrc, alt: "AI Avatar", style: {
|
|
74
|
+
width: "100%",
|
|
75
|
+
height: "100%",
|
|
76
|
+
objectFit: "cover"
|
|
77
|
+
} })) : (_jsx("div", { style: {
|
|
78
|
+
width: "100%",
|
|
79
|
+
height: "100%",
|
|
80
|
+
display: "flex",
|
|
81
|
+
alignItems: "center",
|
|
82
|
+
justifyContent: "center",
|
|
83
|
+
fontSize: "0.75rem",
|
|
84
|
+
color: "rgba(217, 219, 234, 1)"
|
|
85
|
+
}, children: "AI" }))) }) }), _jsxs("div", { style: {
|
|
86
|
+
maxWidth: "85%",
|
|
87
|
+
padding: message.role === "user" ? "0.5rem 0.75rem" : "0.5rem 0",
|
|
88
|
+
borderRadius: message.role === "user" ? "1.2rem" : "0",
|
|
89
|
+
fontSize: "0.875rem",
|
|
90
|
+
marginLeft: message.role === "user" ? "0" : "1rem",
|
|
91
|
+
marginRight: message.role === "user" ? "1rem" : "0",
|
|
92
|
+
lineHeight: "1.4",
|
|
93
|
+
backgroundColor: message.role === "user" ? "rgb(114, 126, 237)" : "transparent",
|
|
94
|
+
color: message.role === "user" ? "white" : "rgba(0, 0, 0, 0.85)",
|
|
95
|
+
border: message.role === "user" ? "1px solid rgba(255, 255, 255, 0.35)" : "none",
|
|
96
|
+
boxShadow: message.role === "user" ? "0 2px 8px rgba(114, 126, 237, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.25)" : "none",
|
|
97
|
+
}, children: [message.role === "user" ? (_jsx("p", { style: { margin: 0, whiteSpace: "pre-wrap" }, children: message.content })) : (_jsx("div", { className: "markdown-content", style: {
|
|
98
|
+
fontSize: "0.875rem",
|
|
99
|
+
lineHeight: "1.4",
|
|
100
|
+
color: "rgba(0, 0, 0, 0.85)",
|
|
101
|
+
}, children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: {
|
|
102
|
+
a: ({ href, children, ...props }) => {
|
|
103
|
+
const hasSources = Array.isArray(message.sources) && message.sources.length > 0;
|
|
104
|
+
if (!hasSources) {
|
|
105
|
+
const anyProps = props;
|
|
106
|
+
return (_jsx("span", { className: anyProps?.className, style: anyProps?.style, children: children }));
|
|
107
|
+
}
|
|
108
|
+
return (_jsx("a", { href: href || '', target: "_blank", rel: "noopener noreferrer", ...props, children: children }));
|
|
109
|
+
},
|
|
110
|
+
}, children: convertCitationLinksToUrls(message.content, message.sources) }) })), message.role === "assistant" && message.sources && message.sources.length > 0 && (_jsxs("div", { style: {
|
|
111
|
+
marginTop: "0.25rem",
|
|
112
|
+
paddingTop: "0.25rem",
|
|
113
|
+
borderTop: "1px solid rgba(0, 0, 0, 0.1)",
|
|
114
|
+
}, children: [_jsx("div", { style: {
|
|
115
|
+
display: "flex",
|
|
116
|
+
alignItems: "center",
|
|
117
|
+
justifyContent: "flex-end",
|
|
118
|
+
gap: "0.5rem",
|
|
119
|
+
marginBottom: "0.25rem",
|
|
120
|
+
}, children: _jsx("button", { type: "button", onClick: () => toggleSources(index), style: {
|
|
121
|
+
borderRadius: "0.5rem",
|
|
122
|
+
border: "1px solid rgba(255, 255, 255, 0.3)",
|
|
123
|
+
backgroundColor: "rgba(255, 255, 255, 0.2)",
|
|
124
|
+
padding: "0.2rem 0.4rem",
|
|
125
|
+
fontSize: "0.65rem",
|
|
126
|
+
color: "rgba(0, 0, 0, 0.75)",
|
|
127
|
+
cursor: "pointer",
|
|
128
|
+
outline: "none",
|
|
129
|
+
textTransform: "none",
|
|
130
|
+
}, children: expandedSourcesByMessageIndex[index] ? "Hide sources" : "Show sources" }) }), expandedSourcesByMessageIndex[index] && (_jsx("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: message.sources.map((source) => (_jsxs("a", { href: source.url, target: "_blank", rel: "noopener noreferrer", style: {
|
|
131
|
+
display: "flex",
|
|
132
|
+
alignItems: "center",
|
|
133
|
+
gap: "0.5rem",
|
|
134
|
+
fontSize: "0.75rem",
|
|
135
|
+
color: "rgba(0, 0, 0, 0.7)",
|
|
136
|
+
textDecoration: "none",
|
|
137
|
+
transition: "color 0.2s",
|
|
138
|
+
}, onMouseEnter: (e) => {
|
|
139
|
+
e.currentTarget.style.color = "rgba(0, 0, 0, 0.9)";
|
|
140
|
+
}, onMouseLeave: (e) => {
|
|
141
|
+
e.currentTarget.style.color = "rgba(0, 0, 0, 0.7)";
|
|
142
|
+
}, children: [_jsxs("span", { style: {
|
|
143
|
+
backgroundColor: "rgba(0, 0, 0, 0.1)",
|
|
144
|
+
padding: "0.125rem 0.375rem",
|
|
145
|
+
borderRadius: "0.375rem",
|
|
146
|
+
fontSize: "0.65rem",
|
|
147
|
+
fontWeight: 600,
|
|
148
|
+
}, children: ["#", source.index] }), _jsx("span", { style: { flex: 1, wordBreak: "break-all" }, children: source.url }), _jsx(ExternalLink, { style: { width: "0.875rem", height: "0.875rem", opacity: 0.6 } })] }, source.index))) }))] }))] })] }, index));
|
|
149
|
+
}), showThinkingDots && (_jsxs("div", { style: { display: "flex", justifyContent: "flex-start", gap: "0.4rem", alignItems: "flex-start" }, children: [_jsx("div", { style: {
|
|
52
150
|
display: "flex",
|
|
53
151
|
gap: "0.4rem",
|
|
54
|
-
alignItems:
|
|
152
|
+
alignItems: "flex-start"
|
|
55
153
|
}, children: _jsx("div", { style: {
|
|
56
|
-
width:
|
|
57
|
-
height:
|
|
154
|
+
width: "1.4rem",
|
|
155
|
+
height: "auto",
|
|
58
156
|
borderRadius: "none",
|
|
59
157
|
backgroundColor: "transparent",
|
|
60
158
|
display: "flex",
|
|
@@ -63,9 +161,7 @@ export function MessageList({ messages, isStreaming, aiAvatarSrc = '', size = 'l
|
|
|
63
161
|
fontSize: "0.75rem",
|
|
64
162
|
border: "none",
|
|
65
163
|
boxShadow: "none",
|
|
66
|
-
}, children:
|
|
67
|
-
// <User size={16} color="rgba(0, 0, 0, 1)" />
|
|
68
|
-
_jsx("div", {})) : (aiAvatarSrc ? (_jsx("img", { src: aiAvatarSrc, alt: "AI Avatar", style: {
|
|
164
|
+
}, children: aiAvatarSrc ? (_jsx("img", { src: aiAvatarSrc, alt: "AI Avatar", style: {
|
|
69
165
|
width: "100%",
|
|
70
166
|
height: "100%",
|
|
71
167
|
objectFit: "cover"
|
|
@@ -77,98 +173,32 @@ export function MessageList({ messages, isStreaming, aiAvatarSrc = '', size = 'l
|
|
|
77
173
|
justifyContent: "center",
|
|
78
174
|
fontSize: "0.75rem",
|
|
79
175
|
color: "rgba(217, 219, 234, 1)"
|
|
80
|
-
}, children: "AI" }))
|
|
81
|
-
|
|
82
|
-
padding:
|
|
83
|
-
borderRadius:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
paddingTop: "0.25rem",
|
|
107
|
-
borderTop: "1px solid rgba(0, 0, 0, 0.1)",
|
|
108
|
-
}, children: [_jsxs("div", { style: {
|
|
109
|
-
display: "flex",
|
|
110
|
-
alignItems: "center",
|
|
111
|
-
justifyContent: "space-between",
|
|
112
|
-
gap: "0.5rem",
|
|
113
|
-
marginBottom: "0.25rem",
|
|
114
|
-
}, children: [_jsx("p", { style: {
|
|
115
|
-
fontSize: "0.65rem",
|
|
116
|
-
fontWeight: 600,
|
|
117
|
-
textTransform: "uppercase",
|
|
118
|
-
letterSpacing: "0.05em",
|
|
119
|
-
color: "rgba(0, 0, 0, 0.5)",
|
|
120
|
-
margin: 0,
|
|
121
|
-
}, children: "Sources" }), _jsx("button", { type: "button", onClick: () => toggleSources(index), style: {
|
|
122
|
-
borderRadius: "0.5rem",
|
|
123
|
-
border: "1px solid rgba(255, 255, 255, 0.3)",
|
|
124
|
-
backgroundColor: "rgba(255, 255, 255, 0.2)",
|
|
125
|
-
padding: "0.2rem 0.4rem",
|
|
126
|
-
fontSize: "0.7rem",
|
|
127
|
-
color: "rgba(0, 0, 0, 0.75)",
|
|
128
|
-
cursor: "pointer",
|
|
129
|
-
outline: "none",
|
|
130
|
-
}, children: expandedSourcesByMessageIndex[index] ? "Hide" : "Show" })] }), expandedSourcesByMessageIndex[index] && (_jsx("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: message.sources.map((source) => (_jsxs("a", { href: source.url, target: "_blank", rel: "noopener noreferrer", style: {
|
|
131
|
-
display: "flex",
|
|
132
|
-
alignItems: "center",
|
|
133
|
-
gap: "0.5rem",
|
|
134
|
-
fontSize: "0.75rem",
|
|
135
|
-
color: "rgba(0, 0, 0, 0.7)",
|
|
136
|
-
textDecoration: "none",
|
|
137
|
-
transition: "color 0.2s",
|
|
138
|
-
}, onMouseEnter: (e) => {
|
|
139
|
-
e.currentTarget.style.color = "rgba(0, 0, 0, 0.9)";
|
|
140
|
-
}, onMouseLeave: (e) => {
|
|
141
|
-
e.currentTarget.style.color = "rgba(0, 0, 0, 0.7)";
|
|
142
|
-
}, children: [_jsxs("span", { style: {
|
|
143
|
-
backgroundColor: "rgba(0, 0, 0, 0.1)",
|
|
144
|
-
padding: "0.125rem 0.375rem",
|
|
145
|
-
borderRadius: "0.375rem",
|
|
146
|
-
fontSize: "0.65rem",
|
|
147
|
-
fontWeight: 600,
|
|
148
|
-
}, children: ["#", source.index] }), _jsx("span", { style: { flex: 1, wordBreak: "break-all" }, children: source.url }), _jsx(ExternalLink, { style: { width: "0.875rem", height: "0.875rem", opacity: 0.6 } })] }, source.index))) }))] }))] })] }, index))), showThinkingDots && (_jsx("div", { style: { display: "flex", justifyContent: "flex-start" }, children: _jsxs("div", { style: {
|
|
149
|
-
padding: "0.25rem 0.6rem",
|
|
150
|
-
borderRadius: "1rem",
|
|
151
|
-
display: "flex",
|
|
152
|
-
gap: "0.375rem",
|
|
153
|
-
}, children: [_jsx("span", { style: {
|
|
154
|
-
width: "0.5rem",
|
|
155
|
-
height: "0.5rem",
|
|
156
|
-
borderRadius: "50%",
|
|
157
|
-
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
158
|
-
animation: "pulse 1.4s ease-in-out infinite",
|
|
159
|
-
} }), _jsx("span", { style: {
|
|
160
|
-
width: "0.5rem",
|
|
161
|
-
height: "0.5rem",
|
|
162
|
-
borderRadius: "50%",
|
|
163
|
-
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
164
|
-
animation: "pulse 1.4s ease-in-out 0.2s infinite",
|
|
165
|
-
} }), _jsx("span", { style: {
|
|
166
|
-
width: "0.5rem",
|
|
167
|
-
height: "0.5rem",
|
|
168
|
-
borderRadius: "50%",
|
|
169
|
-
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
170
|
-
animation: "pulse 1.4s ease-in-out 0.4s infinite",
|
|
171
|
-
} })] }) })), _jsx("div", { ref: messagesEndRef }), _jsx("style", { children: `
|
|
176
|
+
}, children: "AI" })) }) }), _jsxs("div", { style: {
|
|
177
|
+
marginLeft: "1rem",
|
|
178
|
+
padding: "0.25rem 0.5rem",
|
|
179
|
+
borderRadius: "1rem",
|
|
180
|
+
display: "flex",
|
|
181
|
+
gap: "0.25rem",
|
|
182
|
+
alignItems: "center",
|
|
183
|
+
}, children: [_jsx("span", { style: {
|
|
184
|
+
width: "0.35rem",
|
|
185
|
+
height: "0.35rem",
|
|
186
|
+
borderRadius: "50%",
|
|
187
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
188
|
+
animation: "pulse 1.4s ease-in-out infinite",
|
|
189
|
+
} }), _jsx("span", { style: {
|
|
190
|
+
width: "0.35rem",
|
|
191
|
+
height: "0.35rem",
|
|
192
|
+
borderRadius: "50%",
|
|
193
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
194
|
+
animation: "pulse 1.4s ease-in-out 0.2s infinite",
|
|
195
|
+
} }), _jsx("span", { style: {
|
|
196
|
+
width: "0.35rem",
|
|
197
|
+
height: "0.35rem",
|
|
198
|
+
borderRadius: "50%",
|
|
199
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
200
|
+
animation: "pulse 1.4s ease-in-out 0.4s infinite",
|
|
201
|
+
} })] })] })), _jsx("div", { ref: messagesEndRef }), _jsx("style", { children: `
|
|
172
202
|
.markdown-content p {
|
|
173
203
|
margin: 0 0 0.75rem 0;
|
|
174
204
|
}
|
|
@@ -6,10 +6,12 @@ interface SearchInputProps {
|
|
|
6
6
|
onBlur: () => void;
|
|
7
7
|
placeholder: string;
|
|
8
8
|
size: "xs" | "s" | "m" | "l" | "xl";
|
|
9
|
+
setSize: (size: "xs" | "s" | "m" | "l" | "xl") => void;
|
|
9
10
|
inputRef: React.RefObject<HTMLTextAreaElement>;
|
|
10
11
|
inspirationQuestions: string[];
|
|
11
12
|
onInspirationClick: (question: string) => void;
|
|
12
13
|
sparkleRef: React.RefObject<HTMLDivElement>;
|
|
14
|
+
suggestedLabel?: string;
|
|
13
15
|
}
|
|
14
|
-
export declare function SearchInput({ value, onChange, onFocus, onBlur, placeholder, size, inputRef, inspirationQuestions, onInspirationClick, sparkleRef, }: SearchInputProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare function SearchInput({ value, onChange, onFocus, onBlur, placeholder, size, setSize, inputRef, inspirationQuestions, onInspirationClick, sparkleRef, suggestedLabel, }: SearchInputProps): import("react/jsx-runtime").JSX.Element;
|
|
15
17
|
export {};
|
|
@@ -3,13 +3,21 @@ import { Sparkles } from "lucide-react";
|
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
4
|
import { InspirationMenu } from "./inspiration-menu";
|
|
5
5
|
import AutoExpandingTextarea from "./auto-expanding-textarea"; // make sure path matches
|
|
6
|
-
export function SearchInput({ value, onChange, onFocus, onBlur, placeholder, size, inputRef, inspirationQuestions, onInspirationClick, sparkleRef, }) {
|
|
6
|
+
export function SearchInput({ value, onChange, onFocus, onBlur, placeholder, size, setSize, inputRef, inspirationQuestions, onInspirationClick, sparkleRef, suggestedLabel = "Suggested", }) {
|
|
7
7
|
const [showInspirationMenu, setShowInspirationMenu] = useState(false);
|
|
8
8
|
const [isSparkleHovered, setIsSparkleHovered] = useState(false);
|
|
9
9
|
const [typewriterText, setTypewriterText] = useState("");
|
|
10
10
|
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
|
11
11
|
const [isTyping, setIsTyping] = useState(true);
|
|
12
12
|
const [charIndex, setCharIndex] = useState(0);
|
|
13
|
+
const [isSingleLine, setIsSingleLine] = useState(true);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (inputRef.current) {
|
|
16
|
+
const textarea = inputRef.current;
|
|
17
|
+
const lineHeight = 24; // match your `line-height` in CSS (1.5rem ≈ 24px)
|
|
18
|
+
setIsSingleLine(textarea.scrollHeight <= lineHeight * 1.5);
|
|
19
|
+
}
|
|
20
|
+
}, [value]);
|
|
13
21
|
// Typewriter animation for placeholder
|
|
14
22
|
useEffect(() => {
|
|
15
23
|
if (size !== "s" || value || inspirationQuestions.length === 0)
|
|
@@ -48,33 +56,52 @@ export function SearchInput({ value, onChange, onFocus, onBlur, placeholder, siz
|
|
|
48
56
|
setShowInspirationMenu(!showInspirationMenu);
|
|
49
57
|
};
|
|
50
58
|
const handleQuestionClick = (question) => {
|
|
59
|
+
if (size !== "l" && size !== "xl") {
|
|
60
|
+
setSize("l");
|
|
61
|
+
}
|
|
51
62
|
onInspirationClick(question);
|
|
52
63
|
setShowInspirationMenu(false);
|
|
53
64
|
};
|
|
54
65
|
const displayPlaceholder = size === "s" && !value ? typewriterText : placeholder;
|
|
55
|
-
return (_jsxs("div", { style: { position: "relative", zIndex: 1 }, children: [_jsxs("div", { ref: sparkleRef, onClick: handleSparkleClick, onMouseEnter: () =>
|
|
66
|
+
return (_jsxs("div", { style: { position: "relative", zIndex: 1 }, children: [size !== "s" && (_jsxs("div", { ref: sparkleRef, onClick: handleSparkleClick, onMouseEnter: () => {
|
|
67
|
+
setIsSparkleHovered(true);
|
|
68
|
+
setShowInspirationMenu(true);
|
|
69
|
+
}, onMouseLeave: () => {
|
|
70
|
+
setIsSparkleHovered(false);
|
|
71
|
+
// Don't immediately close, wait for user to move to menu
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
if (!document.querySelector('[data-inspiration-menu]:hover')) {
|
|
74
|
+
setShowInspirationMenu(false);
|
|
75
|
+
}
|
|
76
|
+
}, 150);
|
|
77
|
+
}, style: {
|
|
56
78
|
position: "absolute",
|
|
57
79
|
left: "0.75rem",
|
|
58
|
-
top: "0.
|
|
80
|
+
top: "0.42rem",
|
|
59
81
|
cursor: "pointer",
|
|
60
82
|
// Keep the trigger above the input; menu has an even higher z-index
|
|
61
83
|
zIndex: 2,
|
|
62
|
-
}, children: [_jsx(Sparkles, { style: {
|
|
63
|
-
width:
|
|
64
|
-
height:
|
|
84
|
+
}, children: [_jsx(Sparkles, { className: "sparkle-icon-animated", style: {
|
|
85
|
+
width: "1rem",
|
|
86
|
+
height: "1rem",
|
|
65
87
|
color: "rgba(0, 0, 0, 0.6)",
|
|
66
88
|
transition: "all 0.3s ease",
|
|
67
|
-
} }), showInspirationMenu && (_jsx(InspirationMenu, { questions: inspirationQuestions, onQuestionClick: handleQuestionClick, onClose: () => setShowInspirationMenu(false) }))] }), _jsx(AutoExpandingTextarea, { ref: inputRef, value: value, onChange: onChange, onFocus: onFocus,
|
|
68
|
-
|
|
89
|
+
} }), showInspirationMenu && (_jsx(InspirationMenu, { questions: inspirationQuestions, onQuestionClick: handleQuestionClick, onClose: () => setShowInspirationMenu(false), suggestedLabel: suggestedLabel }))] })), _jsx(AutoExpandingTextarea, { ref: inputRef, value: value, onChange: onChange, onFocus: onFocus, onMouseEnter: () => {
|
|
90
|
+
if (size === "m") {
|
|
91
|
+
setShowInspirationMenu(true);
|
|
92
|
+
}
|
|
93
|
+
}, onBlur: onBlur, placeholder: displayPlaceholder, className: `sparkle-input ${isSparkleHovered ? "sparkle-hover" : ""}`, style: {
|
|
94
|
+
padding: size === "s" ? "0 0.75rem" : "0 0.75rem 0 2rem",
|
|
69
95
|
outline: "none",
|
|
70
96
|
transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
97
|
+
borderRadius: isSingleLine ? "9999px" : "15px",
|
|
71
98
|
} }), _jsx("style", { children: `
|
|
72
99
|
.sparkle-input {
|
|
73
100
|
width: 100%;
|
|
74
101
|
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
75
102
|
background-color: transparent;
|
|
76
103
|
border-radius: 9999px; /* full rounded */
|
|
77
|
-
font-size: 0.
|
|
104
|
+
font-size: 0.85rem; /* ~14px */
|
|
78
105
|
color: rgba(0, 0, 0, 0.85);
|
|
79
106
|
backdrop-filter: blur(20px) saturate(180%);
|
|
80
107
|
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
|
@@ -90,5 +117,20 @@ export function SearchInput({ value, onChange, onFocus, onBlur, placeholder, siz
|
|
|
90
117
|
.sparkle-input::placeholder {
|
|
91
118
|
color: rgba(0, 0, 0, 0.75);
|
|
92
119
|
}
|
|
120
|
+
|
|
121
|
+
.sparkle-icon-animated {
|
|
122
|
+
animation: sparkleGlow 2s ease-in-out infinite;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@keyframes sparkleGlow {
|
|
126
|
+
0%, 100% {
|
|
127
|
+
opacity: 0.6;
|
|
128
|
+
transform: scale(1);
|
|
129
|
+
}
|
|
130
|
+
50% {
|
|
131
|
+
opacity: 1;
|
|
132
|
+
transform: scale(1.1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
93
135
|
` })] }));
|
|
94
136
|
}
|
|
@@ -20,35 +20,38 @@ export function SuggestedQuestions({ questions, onQuestionClick }) {
|
|
|
20
20
|
display: "flex",
|
|
21
21
|
flexWrap: "wrap",
|
|
22
22
|
gap: "0.375rem",
|
|
23
|
-
}, children: questions.map((question, index) =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
23
|
+
}, children: questions.map((question, index) => {
|
|
24
|
+
const truncatedQuestion = question.length > 80 ? question.slice(0, 80) + '...' : question;
|
|
25
|
+
return (_jsx("button", { type: "button", onClick: () => onQuestionClick(question), title: question.length > 80 ? question : undefined, style: {
|
|
26
|
+
padding: "0.5rem 0.75rem",
|
|
27
|
+
borderRadius: "1.3rem",
|
|
28
|
+
border: "1px solid rgba(255, 255, 255, 0.25)",
|
|
29
|
+
backgroundColor: "rgba(255, 255, 255, 0.2)",
|
|
30
|
+
color: "rgba(0, 0, 0, 0.75)",
|
|
31
|
+
fontSize: "0.75rem",
|
|
32
|
+
textAlign: "left",
|
|
33
|
+
cursor: "pointer",
|
|
34
|
+
transition: "all 0.2s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
35
|
+
backdropFilter: "blur(20px) saturate(180%)",
|
|
36
|
+
WebkitBackdropFilter: "blur(20px) saturate(180%)",
|
|
37
|
+
boxShadow: "0 1px 2px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.3)",
|
|
38
|
+
outline: "none",
|
|
39
|
+
lineHeight: "1.3",
|
|
40
|
+
}, onMouseEnter: (e) => {
|
|
41
|
+
e.currentTarget.style.backgroundColor = "rgba(140, 110, 230, 0.15)";
|
|
42
|
+
e.currentTarget.style.borderColor = "rgba(140, 110, 230, 0.3)";
|
|
43
|
+
e.currentTarget.style.transform = "translateY(-1px)";
|
|
44
|
+
e.currentTarget.style.boxShadow =
|
|
45
|
+
"0 3px 8px rgba(140, 110, 230, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.4)";
|
|
46
|
+
}, onMouseLeave: (e) => {
|
|
47
|
+
e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
48
|
+
e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.25)";
|
|
49
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
50
|
+
e.currentTarget.style.boxShadow = "0 1px 2px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.3)";
|
|
51
|
+
}, onFocus: (e) => {
|
|
52
|
+
e.currentTarget.style.boxShadow = "0 0 0 2px rgba(140, 110, 230, 0.3)";
|
|
53
|
+
}, onBlur: (e) => {
|
|
54
|
+
e.currentTarget.style.boxShadow = "0 1px 2px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.3)";
|
|
55
|
+
}, children: truncatedQuestion }, index));
|
|
56
|
+
}) })] }));
|
|
54
57
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
3
|
import "./goji-search.css";
|
|
3
4
|
import { ArrowLeft, X, Maximize2, Minimize2 } from "lucide-react";
|
|
4
5
|
import { useState, useEffect, useRef, useId, useMemo } from "react";
|
|
5
|
-
import { SuggestedQuestions } from "./elements/suggested-questions";
|
|
6
6
|
import { MessageList } from "./elements/message-list";
|
|
7
7
|
import chatLogo from "../assets/chat-logo.png";
|
|
8
8
|
import { SearchInput } from "./elements/search-input";
|
|
@@ -18,22 +18,22 @@ export function GojiSearchComponent({ apiUrl } = {}) {
|
|
|
18
18
|
const [isHovered, setIsHovered] = useState(false);
|
|
19
19
|
const [messages, setMessages] = useState([]);
|
|
20
20
|
const [isStreaming, setIsStreaming] = useState(false);
|
|
21
|
-
const [showSuggestions, setShowSuggestions] = useState(false);
|
|
22
21
|
const [showCalendar, setShowCalendar] = useState(false);
|
|
23
22
|
const [sessionId, setSessionId] = useState();
|
|
24
|
-
const [hasInteracted, setHasInteracted] = useState(false);
|
|
25
23
|
const inputRef = useRef(null);
|
|
26
24
|
const sparkleRef = useRef(null);
|
|
27
25
|
const streamingCancelRef = useRef(null);
|
|
28
26
|
const mediaRecorderRef = useRef(null);
|
|
29
27
|
const audioChunksRef = useRef([]);
|
|
30
|
-
const
|
|
28
|
+
const componentRef = useRef(null);
|
|
31
29
|
const [hoverTopExpand, setHoverTopExpand] = useState(false);
|
|
32
30
|
const [hoverTopClose, setHoverTopClose] = useState(false);
|
|
33
31
|
const [isRecording, setIsRecording] = useState(false);
|
|
34
32
|
const [backendSuggestedQuestions, setBackendSuggestedQuestions] = useState(null);
|
|
33
|
+
const mouseLeaveTimeoutRef = useRef(null);
|
|
34
|
+
const mouseEnterTimeoutRef = useRef(null);
|
|
35
35
|
// Language selection state - will be set from backend default
|
|
36
|
-
const [selectedLanguage, setSelectedLanguage] = useState(
|
|
36
|
+
const [selectedLanguage, setSelectedLanguage] = useState("en");
|
|
37
37
|
const [showLanguageMenu, setShowLanguageMenu] = useState(false);
|
|
38
38
|
const currentLanguage = selectedLanguage;
|
|
39
39
|
const languageConfig = companyConfig.languages[currentLanguage];
|
|
@@ -54,45 +54,47 @@ export function GojiSearchComponent({ apiUrl } = {}) {
|
|
|
54
54
|
return;
|
|
55
55
|
const handleClickOutside = (e) => {
|
|
56
56
|
const target = e.target;
|
|
57
|
-
if (!target.closest(
|
|
57
|
+
if (!target.closest("[data-language-menu]")) {
|
|
58
58
|
setShowLanguageMenu(false);
|
|
59
59
|
}
|
|
60
60
|
};
|
|
61
|
-
document.addEventListener(
|
|
62
|
-
return () => document.removeEventListener(
|
|
61
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
62
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
63
63
|
}, [showLanguageMenu]);
|
|
64
64
|
// Language change handler with persistence
|
|
65
65
|
const handleLanguageChange = (lang) => {
|
|
66
66
|
setSelectedLanguage(lang);
|
|
67
67
|
setShowLanguageMenu(false);
|
|
68
68
|
// Persist to localStorage
|
|
69
|
-
if (typeof window !==
|
|
70
|
-
localStorage.setItem(
|
|
69
|
+
if (typeof window !== "undefined") {
|
|
70
|
+
localStorage.setItem("goji-language", lang);
|
|
71
71
|
}
|
|
72
72
|
// Reload suggestions for new language
|
|
73
|
-
client
|
|
74
|
-
.
|
|
73
|
+
client
|
|
74
|
+
.getSuggestions({ language: lang })
|
|
75
|
+
.then((res) => {
|
|
75
76
|
if (Array.isArray(res?.suggestions) && res.suggestions.length > 0) {
|
|
76
77
|
setBackendSuggestedQuestions(res.suggestions);
|
|
77
78
|
}
|
|
78
79
|
})
|
|
79
|
-
.catch(e => console.error("Failed to fetch suggestions", e));
|
|
80
|
+
.catch((e) => console.error("Failed to fetch suggestions", e));
|
|
80
81
|
};
|
|
81
82
|
useEffect(() => {
|
|
82
83
|
// Load initial suggestions and default language from backend on mount (ONLY ONCE)
|
|
84
|
+
;
|
|
83
85
|
(async () => {
|
|
84
86
|
try {
|
|
85
87
|
// Fetch suggestions with initial language (en)
|
|
86
|
-
const res = await client.getSuggestions({ language:
|
|
88
|
+
const res = await client.getSuggestions({ language: "en" });
|
|
87
89
|
// Check if backend provides a different default language
|
|
88
|
-
if (res?.default_language && typeof res.default_language ===
|
|
90
|
+
if (res?.default_language && typeof res.default_language === "string") {
|
|
89
91
|
const backendDefaultLang = res.default_language.toLowerCase().trim();
|
|
90
92
|
// Validate it's a supported language
|
|
91
93
|
if (companyConfig.languages[backendDefaultLang]) {
|
|
92
94
|
// Set the language FIRST
|
|
93
95
|
setSelectedLanguage(backendDefaultLang);
|
|
94
96
|
// Then fetch suggestions for the correct language
|
|
95
|
-
if (backendDefaultLang !==
|
|
97
|
+
if (backendDefaultLang !== "en") {
|
|
96
98
|
const langRes = await client.getSuggestions({ language: backendDefaultLang });
|
|
97
99
|
if (Array.isArray(langRes?.suggestions) && langRes.suggestions.length > 0) {
|
|
98
100
|
setBackendSuggestedQuestions(langRes.suggestions);
|
|
@@ -125,6 +127,18 @@ export function GojiSearchComponent({ apiUrl } = {}) {
|
|
|
125
127
|
client.closeWebSocket();
|
|
126
128
|
};
|
|
127
129
|
}, [client]);
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (size === "xs")
|
|
132
|
+
return;
|
|
133
|
+
const handleClickOutside = (e) => {
|
|
134
|
+
const target = e.target;
|
|
135
|
+
if (componentRef.current && !componentRef.current.contains(target)) {
|
|
136
|
+
handleClose();
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
140
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
141
|
+
}, [size]);
|
|
128
142
|
const handleSearch = async (e) => {
|
|
129
143
|
e.preventDefault();
|
|
130
144
|
if (!searchQuery.trim() || isStreaming)
|
|
@@ -200,7 +214,6 @@ export function GojiSearchComponent({ apiUrl } = {}) {
|
|
|
200
214
|
}
|
|
201
215
|
};
|
|
202
216
|
const handleSuggestionClick = async (question) => {
|
|
203
|
-
setShowSuggestions(false);
|
|
204
217
|
const userMessage = {
|
|
205
218
|
role: "user",
|
|
206
219
|
content: question,
|
|
@@ -273,6 +286,7 @@ export function GojiSearchComponent({ apiUrl } = {}) {
|
|
|
273
286
|
setSearchQuery("");
|
|
274
287
|
setSize("xs");
|
|
275
288
|
setShowCalendar(false);
|
|
289
|
+
setHoverTopClose(false);
|
|
276
290
|
};
|
|
277
291
|
const handleVoiceSearch = async () => {
|
|
278
292
|
try {
|
|
@@ -400,7 +414,6 @@ export function GojiSearchComponent({ apiUrl } = {}) {
|
|
|
400
414
|
}
|
|
401
415
|
};
|
|
402
416
|
const handleToggle = () => {
|
|
403
|
-
setHasInteracted(true);
|
|
404
417
|
if (size === "xs") {
|
|
405
418
|
setSize(messages.length > 0 ? "l" : "s");
|
|
406
419
|
}
|
|
@@ -416,53 +429,52 @@ export function GojiSearchComponent({ apiUrl } = {}) {
|
|
|
416
429
|
else {
|
|
417
430
|
setSize("xl");
|
|
418
431
|
}
|
|
432
|
+
setHoverTopExpand(false);
|
|
419
433
|
};
|
|
420
434
|
const handleInputFocus = () => {
|
|
421
|
-
setHasInteracted(true);
|
|
422
435
|
if (messages.length > 0) {
|
|
423
436
|
// if currently xl, keep xl; otherwise go to l
|
|
424
437
|
setSize((prev) => (prev === "xl" ? "xl" : "l"));
|
|
425
438
|
}
|
|
426
439
|
else {
|
|
427
440
|
setSize("m");
|
|
428
|
-
setShowSuggestions(true);
|
|
429
441
|
}
|
|
430
442
|
};
|
|
431
|
-
const handleInputAreaMouseEnter = () => {
|
|
432
|
-
setHasInteracted(true);
|
|
433
|
-
setIsInputHovered(true);
|
|
434
|
-
if (messages.length === 0 && !searchQuery) {
|
|
435
|
-
setShowSuggestions(true);
|
|
436
|
-
}
|
|
437
|
-
};
|
|
438
|
-
const handleInputAreaMouseLeave = () => {
|
|
439
|
-
setIsInputHovered(false);
|
|
440
|
-
setShowSuggestions(false);
|
|
441
|
-
};
|
|
442
443
|
const handleInputChange = (e) => {
|
|
443
444
|
setSearchQuery(e.target.value);
|
|
444
445
|
if (e.target.value && size === "s") {
|
|
445
446
|
setSize("m");
|
|
446
447
|
}
|
|
447
|
-
if (e.target.value.length > 0 && messages.length === 0) {
|
|
448
|
-
setShowSuggestions(true);
|
|
449
|
-
}
|
|
450
|
-
else {
|
|
451
|
-
setShowSuggestions(false);
|
|
452
|
-
}
|
|
453
448
|
};
|
|
454
449
|
const handleMouseEnter = () => {
|
|
455
|
-
setHasInteracted(true);
|
|
456
450
|
setIsHovered(true);
|
|
457
|
-
if (
|
|
458
|
-
|
|
451
|
+
if (mouseLeaveTimeoutRef.current) {
|
|
452
|
+
clearTimeout(mouseLeaveTimeoutRef.current);
|
|
453
|
+
mouseLeaveTimeoutRef.current = null;
|
|
454
|
+
}
|
|
455
|
+
if (mouseEnterTimeoutRef.current) {
|
|
456
|
+
clearTimeout(mouseEnterTimeoutRef.current);
|
|
457
|
+
mouseEnterTimeoutRef.current = null;
|
|
459
458
|
}
|
|
459
|
+
mouseEnterTimeoutRef.current = setTimeout(() => {
|
|
460
|
+
if (size === "s") {
|
|
461
|
+
setSize("m");
|
|
462
|
+
}
|
|
463
|
+
mouseEnterTimeoutRef.current = null;
|
|
464
|
+
}, 100);
|
|
460
465
|
};
|
|
461
466
|
const handleMouseLeave = () => {
|
|
462
467
|
setIsHovered(false);
|
|
463
|
-
if (
|
|
464
|
-
|
|
468
|
+
if (mouseEnterTimeoutRef.current) {
|
|
469
|
+
clearTimeout(mouseEnterTimeoutRef.current);
|
|
470
|
+
mouseEnterTimeoutRef.current = null;
|
|
465
471
|
}
|
|
472
|
+
mouseLeaveTimeoutRef.current = setTimeout(() => {
|
|
473
|
+
if (size === "m" && !searchQuery && messages.length === 0) {
|
|
474
|
+
setSize("s");
|
|
475
|
+
}
|
|
476
|
+
mouseLeaveTimeoutRef.current = null;
|
|
477
|
+
}, 150);
|
|
466
478
|
};
|
|
467
479
|
const handleInputBlur = () => {
|
|
468
480
|
if (!searchQuery && messages.length === 0) {
|
|
@@ -492,14 +504,14 @@ export function GojiSearchComponent({ apiUrl } = {}) {
|
|
|
492
504
|
zIndex: 50,
|
|
493
505
|
padding: "0.7rem",
|
|
494
506
|
transition: "bottom 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
495
|
-
},
|
|
507
|
+
}, children: _jsx("div", { style: {
|
|
496
508
|
maxWidth: currentConfig.maxWidth,
|
|
497
509
|
margin: "0 auto",
|
|
498
510
|
transition: "max-width 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
499
|
-
}, children: _jsxs("div", { style: {
|
|
511
|
+
}, children: _jsxs("div", { ref: componentRef, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: {
|
|
500
512
|
borderRadius: size === "xs" ? "0.75rem" : "1rem",
|
|
501
513
|
border: size === "xs" || size === "s" ? "none" : "1px solid rgba(85, 85, 85, 0.18)",
|
|
502
|
-
backgroundColor: size === "xs" || size === "s" ? "transparent" : "rgba(
|
|
514
|
+
backgroundColor: size === "xs" || size === "s" ? "transparent" : "rgba(245, 245, 250, 0.12)",
|
|
503
515
|
padding: size === "s" ? "0" : currentConfig.padding,
|
|
504
516
|
// remove top padding so inner message list / calendar sit flush with the component border
|
|
505
517
|
paddingTop: 0,
|
|
@@ -523,10 +535,10 @@ export function GojiSearchComponent({ apiUrl } = {}) {
|
|
|
523
535
|
paddingBottom: "0.5rem",
|
|
524
536
|
}, children: _jsx("div", { style: {
|
|
525
537
|
position: "relative",
|
|
526
|
-
display:
|
|
527
|
-
gap:
|
|
528
|
-
alignItems:
|
|
529
|
-
justifyContent:
|
|
538
|
+
display: ((size === "l" || size === "xl") && messages.length > 0) || showCalendar ? "flex" : "none",
|
|
539
|
+
gap: "0.5rem",
|
|
540
|
+
alignItems: "center",
|
|
541
|
+
justifyContent: "flex-end",
|
|
530
542
|
zIndex: 20,
|
|
531
543
|
paddingRight: "0.3rem",
|
|
532
544
|
backgroundColor: "transparent",
|
|
@@ -600,7 +612,7 @@ export function GojiSearchComponent({ apiUrl } = {}) {
|
|
|
600
612
|
display: "flex",
|
|
601
613
|
flexDirection: "column",
|
|
602
614
|
gap: "0.75rem",
|
|
603
|
-
height: "100%"
|
|
615
|
+
height: "100%",
|
|
604
616
|
}, children: showCalendar ? (_jsxs("div", { style: { flex: 1, display: "flex", flexDirection: "column", gap: "0.75rem", minHeight: 0 }, children: [_jsx("button", { type: "button", onClick: handleBackToChat, style: {
|
|
605
617
|
display: "flex",
|
|
606
618
|
alignItems: "center",
|
|
@@ -634,7 +646,7 @@ export function GojiSearchComponent({ apiUrl } = {}) {
|
|
|
634
646
|
cursor: "pointer",
|
|
635
647
|
outline: "none",
|
|
636
648
|
flexShrink: 0,
|
|
637
|
-
}, children: _jsxs("div", { className: "ai", children: [_jsxs("div", { className: "container-ai", children: [_jsx("div", { className: "c c4" }), _jsx("div", { className: "c c1" }), _jsx("div", { className: "c c2" }), _jsx("div", { className: "c c3" }), _jsx("div", { className: "rings-ai", children: _jsx("div", { className: "rings-ai" }) })] }), _jsx("div", { className: "glass-ai" })] }) }), _jsx("div", {
|
|
649
|
+
}, children: _jsxs("div", { className: "ai", children: [_jsxs("div", { className: "container-ai", children: [_jsx("div", { className: "c c4" }), _jsx("div", { className: "c c1" }), _jsx("div", { className: "c c2" }), _jsx("div", { className: "c c3" }), _jsx("div", { className: "rings-ai", children: _jsx("div", { className: "rings-ai" }) })] }), _jsx("div", { className: "glass-ai" })] }) }), _jsx("div", { style: {
|
|
638
650
|
display: "flex",
|
|
639
651
|
flexDirection: "column",
|
|
640
652
|
gap: "0.35rem",
|
|
@@ -652,44 +664,16 @@ export function GojiSearchComponent({ apiUrl } = {}) {
|
|
|
652
664
|
gap: "0.375rem",
|
|
653
665
|
transition: "gap 0.3s ease",
|
|
654
666
|
flexShrink: 0,
|
|
655
|
-
}, children: [_jsx("div", { style: { flex: 1 }, children: _jsx(SearchInput, { value: searchQuery, onChange: handleInputChange, onFocus: handleInputFocus, onBlur: handleInputBlur, placeholder: "Search...", size: size, inputRef: inputRef, inspirationQuestions: inspirationQuestions, onInspirationClick: handleSuggestionClick, sparkleRef: sparkleRef }) }), _jsx(ActionButtons, { size: size, isHovered: isHovered, isStreaming: isStreaming, searchQuery: searchQuery, isRecording: isRecording, onSubmit: () => { }, onVoiceSearch: handleVoiceSearch, onClose: handleClose, onCalendarClick: handleCalendarClick })] }) })] })) : (
|
|
667
|
+
}, children: [_jsx("div", { style: { flex: 1 }, children: _jsx(SearchInput, { value: searchQuery, onChange: handleInputChange, onFocus: handleInputFocus, onBlur: handleInputBlur, placeholder: "Search...", size: size, setSize: setSize, inputRef: inputRef, inspirationQuestions: inspirationQuestions, onInspirationClick: handleSuggestionClick, sparkleRef: sparkleRef, suggestedLabel: languageConfig.suggested }) }), _jsx(ActionButtons, { size: size, isHovered: isHovered, isStreaming: isStreaming, searchQuery: searchQuery, isRecording: isRecording, onSubmit: () => { }, onVoiceSearch: handleVoiceSearch, onClose: handleClose, onCalendarClick: handleCalendarClick })] }) })] })) : (_jsx("div", { style: {
|
|
656
668
|
display: "flex",
|
|
657
669
|
flexDirection: "column",
|
|
658
670
|
gap: "0.5rem",
|
|
659
671
|
paddingTop: 0,
|
|
660
|
-
}, children:
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
width: "fit-content",
|
|
668
|
-
color: "rgba(0, 0, 0, 0.75)",
|
|
669
|
-
transition: "all 0.2s",
|
|
670
|
-
cursor: "pointer",
|
|
671
|
-
outline: "none"
|
|
672
|
-
}, children: _jsx(X, { style: { width: "0.875rem", height: "0.875rem" } }) }), _jsx("span", { role: "tooltip", style: {
|
|
673
|
-
position: "absolute",
|
|
674
|
-
top: "calc(100% + 0.4rem)",
|
|
675
|
-
left: "50%",
|
|
676
|
-
transform: "translateX(-50%)",
|
|
677
|
-
whiteSpace: "nowrap",
|
|
678
|
-
background: "rgba(0,0,0,0.9)",
|
|
679
|
-
color: "#fff",
|
|
680
|
-
padding: "4px 6px",
|
|
681
|
-
borderRadius: "1.3rem",
|
|
682
|
-
fontSize: "0.55rem",
|
|
683
|
-
boxShadow: "0 6px 18px rgba(0,0,0,0.35)",
|
|
684
|
-
opacity: 0,
|
|
685
|
-
pointerEvents: "none",
|
|
686
|
-
transition: "opacity 120ms ease-in-out",
|
|
687
|
-
zIndex: 30,
|
|
688
|
-
}, children: "Close" }), showSuggestions && messages.length === 0 && !searchQuery && (_jsx(SuggestedQuestions, { questions: inspirationQuestions, onQuestionClick: handleSuggestionClick })), _jsxs("form", { onSubmit: handleSearch, style: {
|
|
689
|
-
display: "flex",
|
|
690
|
-
alignItems: "center",
|
|
691
|
-
gap: "0.375rem",
|
|
692
|
-
transition: "gap 0.3s ease",
|
|
693
|
-
flexShrink: 0,
|
|
694
|
-
}, children: [_jsx("div", { style: { flex: 1 }, children: _jsx(SearchInput, { value: searchQuery, onChange: handleInputChange, onFocus: handleInputFocus, onBlur: handleInputBlur, placeholder: "Ask me anything...", size: size, inputRef: inputRef, inspirationQuestions: inspirationQuestions, onInspirationClick: handleSuggestionClick, sparkleRef: sparkleRef }) }), _jsx(ActionButtons, { size: size, isHovered: isHovered, isStreaming: isStreaming, searchQuery: searchQuery, isRecording: isRecording, onSubmit: () => { }, onVoiceSearch: handleVoiceSearch, onClose: handleClose, onCalendarClick: handleCalendarClick })] })] }))] })) }))] }) }) }));
|
|
672
|
+
}, children: _jsxs("form", { onSubmit: handleSearch, style: {
|
|
673
|
+
display: "flex",
|
|
674
|
+
alignItems: "center",
|
|
675
|
+
gap: "0.375rem",
|
|
676
|
+
transition: "gap 0.3s ease",
|
|
677
|
+
flexShrink: 0,
|
|
678
|
+
}, children: [_jsx("div", { style: { flex: 1 }, children: _jsx(SearchInput, { value: searchQuery, onChange: handleInputChange, onFocus: handleInputFocus, onBlur: handleInputBlur, placeholder: "Ask me anything", size: size, setSize: setSize, inputRef: inputRef, inspirationQuestions: inspirationQuestions, onInspirationClick: handleSuggestionClick, sparkleRef: sparkleRef, suggestedLabel: languageConfig.suggested }) }), _jsx(ActionButtons, { size: size, isHovered: isHovered, isStreaming: isStreaming, searchQuery: searchQuery, isRecording: isRecording, onSubmit: () => { }, onVoiceSearch: handleVoiceSearch, onClose: handleClose, onCalendarClick: handleCalendarClick })] }) }))] })) }))] }) }) }));
|
|
695
679
|
}
|
|
@@ -22,6 +22,7 @@ export const companyConfig = {
|
|
|
22
22
|
bookDemoDialogTitle: "Book a Demo with Chaimaa",
|
|
23
23
|
bookDemoDialogDescription: "Schedule a 30-minute call to discuss your needs and see how we can help.",
|
|
24
24
|
errorMessage: "I'm sorry, I encountered an error. Please try again.",
|
|
25
|
+
suggested: "Suggested",
|
|
25
26
|
},
|
|
26
27
|
it: {
|
|
27
28
|
label: "Italiano",
|
|
@@ -39,6 +40,7 @@ export const companyConfig = {
|
|
|
39
40
|
bookDemoDialogTitle: "Prenota una demo con Chaimaa",
|
|
40
41
|
bookDemoDialogDescription: "Pianifica una chiamata di 30 minuti per discutere le tue esigenze e vedere come possiamo aiutarti.",
|
|
41
42
|
errorMessage: "Si è verificato un errore. Riprova più tardi.",
|
|
43
|
+
suggested: "Suggerito",
|
|
42
44
|
},
|
|
43
45
|
fr: {
|
|
44
46
|
label: "Français",
|
|
@@ -56,6 +58,7 @@ export const companyConfig = {
|
|
|
56
58
|
bookDemoDialogTitle: "Réserver une démo avec Chaimaa",
|
|
57
59
|
bookDemoDialogDescription: "Planifiez un appel de 30 minutes pour discuter de vos besoins et découvrir nos solutions.",
|
|
58
60
|
errorMessage: "Une erreur s'est produite. Merci de réessayer.",
|
|
61
|
+
suggested: "Suggéré",
|
|
59
62
|
},
|
|
60
63
|
es: {
|
|
61
64
|
label: "Español",
|
|
@@ -73,6 +76,7 @@ export const companyConfig = {
|
|
|
73
76
|
bookDemoDialogTitle: "Reservar una demo con Chaimaa",
|
|
74
77
|
bookDemoDialogDescription: "Programa una llamada de 30 minutos para discutir tus necesidades y ver cómo podemos ayudarte.",
|
|
75
78
|
errorMessage: "Lo siento, ocurrió un error. Por favor, inténtalo de nuevo.",
|
|
79
|
+
suggested: "Sugerido",
|
|
76
80
|
},
|
|
77
81
|
de: {
|
|
78
82
|
label: "Deutsch",
|
|
@@ -90,6 +94,7 @@ export const companyConfig = {
|
|
|
90
94
|
bookDemoDialogTitle: "Demo mit Chaimaa buchen",
|
|
91
95
|
bookDemoDialogDescription: "Plane einen 30-minütigen Anruf, um deine Bedürfnisse zu besprechen und zu sehen, wie wir helfen können.",
|
|
92
96
|
errorMessage: "Entschuldigung, es ist ein Fehler aufgetreten. Bitte versuche es erneut.",
|
|
97
|
+
suggested: "Vorgeschlagen",
|
|
93
98
|
},
|
|
94
99
|
},
|
|
95
100
|
};
|
package/dist/goji-search.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@property --a{syntax: "<angle>"; inherits: true; initial-value: 0deg;}@property --l{syntax: "<number>"; inherits: true; initial-value: 0;}@property --x{syntax: "<length>"; inherits: false; initial-value: 0;}@property --y{syntax: "<length>"; inherits: false; initial-value: 0;}@property --o{syntax: "<number>"; inherits: false; initial-value: 0;}@property --value{syntax: "<angle>"; inherits: true; initial-value: 0deg;}@property --width-ratio{syntax: "<number>"; inherits: true; initial-value: 0;}@property --scale{syntax: "<number>"; inherits: true; initial-value: 0;}:root{--count: 4;--radius: .75rem;--width: .125rem;--duration: 8s}.ai{--s: 3.3rem;--p: calc(var(--s) / 4);width:var(--s);aspect-ratio:1;--bg-color: color-mix(in srgb, #
|
|
1
|
+
@property --a{syntax: "<angle>"; inherits: true; initial-value: 0deg;}@property --l{syntax: "<number>"; inherits: true; initial-value: 0;}@property --x{syntax: "<length>"; inherits: false; initial-value: 0;}@property --y{syntax: "<length>"; inherits: false; initial-value: 0;}@property --o{syntax: "<number>"; inherits: false; initial-value: 0;}@property --value{syntax: "<angle>"; inherits: true; initial-value: 0deg;}@property --width-ratio{syntax: "<number>"; inherits: true; initial-value: 0;}@property --scale{syntax: "<number>"; inherits: true; initial-value: 0;}:root{--count: 4;--radius: .75rem;--width: .125rem;--duration: 8s}.ai{--s: 3.3rem;--p: calc(var(--s) / 4);width:var(--s);aspect-ratio:1;--bg-color: color-mix(in srgb, #4654F7, transparent 90%);background:radial-gradient(60% 75% at center,var(--bg-color) 50%,transparent 50%),radial-gradient(75% 60% at center,var(--bg-color) 50%,transparent 50%);padding:var(--p);display:grid;place-items:center;position:relative;border-radius:50%}@keyframes ai{0%{--a: 360deg;--l: .35;--o: 1}30%{--l: 1.5}70%{--o: .4;--l: .05}98%{--o: .7}to{--a: 0deg;--l: .35;--o: 1}}.c{opacity:.9;position:absolute;width:1.25rem;aspect-ratio:1;border-radius:50%;--offset-per-item: calc(360deg / var(--count));--current-angle-offset: calc(var(--offset-per-item) * var(--i) + var(--a));translate:calc(cos(var(--current-angle-offset)) * var(--radius) + var(--x, 0)) calc(sin(var(--current-angle-offset)) * var(--radius) * -1);scale:calc(.6 + var(--l));animation:ai 5.5s cubic-bezier(.45,-.35,.68,.24) infinite;transition:opacity .3s linear;opacity:var(--o, 1)}.c:nth-child(1){--i: 0}.c:nth-child(2){--i: 1}.c:nth-child(3){--i: 2}.c:nth-child(4){--i: 3}.c1{background:radial-gradient(50% 50% at center,#6d8cfb,#5bb2e0);--x: .125rem;width:2rem;animation-timing-function:cubic-bezier(.12,.32,.68,.24)}.c2{background:radial-gradient(50% 50% at center,#6ea2f8,#e7eaff);width:1.875rem}.c3{background:radial-gradient(50% 50% at center,#688ef7,transparent);width:.625rem;opacity:.6;--x: -.125rem}.c4{background:#3c48d1;animation-timing-function:cubic-bezier(.39,-.03,.75,.47)}.container-ai{overflow:hidden;background:#3c48d1;width:100%;border-radius:50%;aspect-ratio:1;position:relative;display:grid;place-items:center}.glass-ai{overflow:hidden;position:absolute;--w: .0625rem;inset:calc(var(--p) - var(--w));border-radius:50%;-webkit-backdrop-filter:blur(.1625rem);backdrop-filter:blur(.1625rem);box-shadow:0 0 1rem color-mix(in srgb,black,transparent 70%);background:radial-gradient(1.25rem at 70% 30%,rgba(255,255,255,.7),transparent)}.glass-ai:after{content:"";position:absolute;inset:0;--c: rgba(255, 255, 255, .03);--w: .0625rem;--g: .1875rem;background:repeating-linear-gradient(var(--c),var(--c),var(--w),transparent var(--w),transparent calc(var(--w) + var(--g)));border-radius:inherit;border:.125rem rgba(255,255,255,.1) solid}.rings-ai{aspect-ratio:1;border-radius:50%;position:absolute;inset:0;perspective:11rem;opacity:.9}.rings-ai:before,.rings-ai:after{content:"";position:absolute;inset:0;background:red;border-radius:50%;--width-ratio: 1;border:calc(var(--width) * var(--width-ratio)) solid transparent;mask:linear-gradient(#fff 0 0) padding-box,linear-gradient(#fff 0 0);background:linear-gradient(#fff,#00f,#f0f,violet,#ffffe0) border-box;mask-composite:exclude;-webkit-mask-composite:xor;animation:ring-ai var(--duration) ease-in-out infinite;--start: 180deg;--value: var(--start);--scale: 1;transform:rotateY(var(--value)) rotateX(var(--value)) rotate(var(--value)) scale(var(--scale))}.rings-ai:before{--start: 180deg}.rings-ai:after{--start: 90deg}.rings-ai>.rings-ai:before{--start: 360deg}.rings-ai>.rings-ai:after{--start: 270deg}@keyframes ring-ai{0%{--value: var(--start);--scale: 1}50%{--scale: 1.2;--width-ratio: 1.5}70%{--scale: 1;--value: calc(var(--start) + 180deg);--width-ratio: 1}80%{--scale: 1.2;--width-ratio: 1.5}to{--value: calc(var(--start) + 360deg);--scale: 1;--width-ratio: 1}}@keyframes fadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes pulse{0%,to{opacity:.4}50%{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes slideUpMenu{0%{opacity:0;transform:translateY(12px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes eqPulse{0%{transform:scaleY(.4);opacity:.7}20%{transform:scaleY(1);opacity:1}40%{transform:scaleY(.6);opacity:.85}60%{transform:scaleY(.9);opacity:.95}80%{transform:scaleY(.5);opacity:.8}to{transform:scaleY(.4);opacity:.7}}
|