goji-search 1.0.1 → 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.d.ts +2 -1
- package/dist/goji-search/components/elements/action-buttons.js +42 -13
- package/dist/goji-search/components/elements/auto-expanding-textarea.d.ts +8 -0
- package/dist/goji-search/components/elements/auto-expanding-textarea.js +46 -0
- package/dist/goji-search/components/elements/inspiration-menu.d.ts +2 -1
- package/dist/goji-search/components/elements/inspiration-menu.js +43 -35
- package/dist/goji-search/components/elements/message-list.d.ts +1 -3
- package/dist/goji-search/components/elements/message-list.js +166 -158
- package/dist/goji-search/components/elements/search-input.d.ts +5 -3
- package/dist/goji-search/components/elements/search-input.js +83 -37
- package/dist/goji-search/components/elements/suggested-questions.js +35 -32
- package/dist/goji-search/components/goji-search-component.d.ts +9 -1
- package/dist/goji-search/components/goji-search-component.js +347 -173
- package/dist/goji-search/config/company.d.ts +1 -1
- package/dist/goji-search/config/company.js +39 -7
- package/dist/goji-search/lib/goji-client.d.ts +39 -0
- package/dist/goji-search/lib/goji-client.js +55 -2
- package/dist/goji-search.css +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
|
@@ -1,34 +1,44 @@
|
|
|
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
|
-
import { useState, useEffect, useRef, useId } from "react";
|
|
5
|
-
import { SuggestedQuestions } from "./elements/suggested-questions";
|
|
5
|
+
import { useState, useEffect, useRef, useId, useMemo } from "react";
|
|
6
6
|
import { MessageList } from "./elements/message-list";
|
|
7
|
+
import chatLogo from "../assets/chat-logo.png";
|
|
7
8
|
import { SearchInput } from "./elements/search-input";
|
|
8
9
|
import { ActionButtons } from "./elements/action-buttons";
|
|
9
10
|
import CalendarIntegration from "./elements/calendar-integration";
|
|
10
|
-
import {
|
|
11
|
+
import { createGojiClient } from "../lib/goji-client";
|
|
11
12
|
import { companyConfig } from "../config/company";
|
|
12
|
-
export function GojiSearchComponent() {
|
|
13
|
+
export function GojiSearchComponent({ apiUrl } = {}) {
|
|
14
|
+
// Create client instance based on provided API URL
|
|
15
|
+
const client = useMemo(() => createGojiClient(apiUrl), [apiUrl]);
|
|
13
16
|
const [searchQuery, setSearchQuery] = useState("");
|
|
14
17
|
const [size, setSize] = useState("xs");
|
|
15
18
|
const [isHovered, setIsHovered] = useState(false);
|
|
16
19
|
const [messages, setMessages] = useState([]);
|
|
17
20
|
const [isStreaming, setIsStreaming] = useState(false);
|
|
18
|
-
const [showSuggestions, setShowSuggestions] = useState(false);
|
|
19
21
|
const [showCalendar, setShowCalendar] = useState(false);
|
|
20
22
|
const [sessionId, setSessionId] = useState();
|
|
21
|
-
const [hasInteracted, setHasInteracted] = useState(false);
|
|
22
23
|
const inputRef = useRef(null);
|
|
23
24
|
const sparkleRef = useRef(null);
|
|
24
25
|
const streamingCancelRef = useRef(null);
|
|
25
|
-
const
|
|
26
|
+
const mediaRecorderRef = useRef(null);
|
|
27
|
+
const audioChunksRef = useRef([]);
|
|
28
|
+
const componentRef = useRef(null);
|
|
26
29
|
const [hoverTopExpand, setHoverTopExpand] = useState(false);
|
|
27
30
|
const [hoverTopClose, setHoverTopClose] = useState(false);
|
|
28
|
-
const
|
|
31
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
32
|
+
const [backendSuggestedQuestions, setBackendSuggestedQuestions] = useState(null);
|
|
33
|
+
const mouseLeaveTimeoutRef = useRef(null);
|
|
34
|
+
const mouseEnterTimeoutRef = useRef(null);
|
|
35
|
+
// Language selection state - will be set from backend default
|
|
36
|
+
const [selectedLanguage, setSelectedLanguage] = useState("en");
|
|
37
|
+
const [showLanguageMenu, setShowLanguageMenu] = useState(false);
|
|
38
|
+
const currentLanguage = selectedLanguage;
|
|
29
39
|
const languageConfig = companyConfig.languages[currentLanguage];
|
|
30
|
-
|
|
31
|
-
const inspirationQuestions =
|
|
40
|
+
// Use backend-provided questions only (no hardcoded prompts)
|
|
41
|
+
const inspirationQuestions = backendSuggestedQuestions || [];
|
|
32
42
|
const idBase = useId();
|
|
33
43
|
const xTooltipId = `${idBase}-x-tip`;
|
|
34
44
|
const expandTooltipId = `${idBase}-expand-tip`;
|
|
@@ -38,22 +48,97 @@ export function GojiSearchComponent() {
|
|
|
38
48
|
}, 4000);
|
|
39
49
|
return () => clearTimeout(timer);
|
|
40
50
|
}, []);
|
|
51
|
+
// Close language menu when clicking outside
|
|
41
52
|
useEffect(() => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
if (!showLanguageMenu)
|
|
54
|
+
return;
|
|
55
|
+
const handleClickOutside = (e) => {
|
|
56
|
+
const target = e.target;
|
|
57
|
+
if (!target.closest("[data-language-menu]")) {
|
|
58
|
+
setShowLanguageMenu(false);
|
|
45
59
|
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
60
|
+
};
|
|
61
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
62
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
63
|
+
}, [showLanguageMenu]);
|
|
64
|
+
// Language change handler with persistence
|
|
65
|
+
const handleLanguageChange = (lang) => {
|
|
66
|
+
setSelectedLanguage(lang);
|
|
67
|
+
setShowLanguageMenu(false);
|
|
68
|
+
// Persist to localStorage
|
|
69
|
+
if (typeof window !== "undefined") {
|
|
70
|
+
localStorage.setItem("goji-language", lang);
|
|
71
|
+
}
|
|
72
|
+
// Reload suggestions for new language
|
|
73
|
+
client
|
|
74
|
+
.getSuggestions({ language: lang })
|
|
75
|
+
.then((res) => {
|
|
76
|
+
if (Array.isArray(res?.suggestions) && res.suggestions.length > 0) {
|
|
77
|
+
setBackendSuggestedQuestions(res.suggestions);
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
.catch((e) => console.error("Failed to fetch suggestions", e));
|
|
81
|
+
};
|
|
49
82
|
useEffect(() => {
|
|
83
|
+
// Load initial suggestions and default language from backend on mount (ONLY ONCE)
|
|
84
|
+
;
|
|
85
|
+
(async () => {
|
|
86
|
+
try {
|
|
87
|
+
// Fetch suggestions with initial language (en)
|
|
88
|
+
const res = await client.getSuggestions({ language: "en" });
|
|
89
|
+
// Check if backend provides a different default language
|
|
90
|
+
if (res?.default_language && typeof res.default_language === "string") {
|
|
91
|
+
const backendDefaultLang = res.default_language.toLowerCase().trim();
|
|
92
|
+
// Validate it's a supported language
|
|
93
|
+
if (companyConfig.languages[backendDefaultLang]) {
|
|
94
|
+
// Set the language FIRST
|
|
95
|
+
setSelectedLanguage(backendDefaultLang);
|
|
96
|
+
// Then fetch suggestions for the correct language
|
|
97
|
+
if (backendDefaultLang !== "en") {
|
|
98
|
+
const langRes = await client.getSuggestions({ language: backendDefaultLang });
|
|
99
|
+
if (Array.isArray(langRes?.suggestions) && langRes.suggestions.length > 0) {
|
|
100
|
+
setBackendSuggestedQuestions(langRes.suggestions);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Use English suggestions
|
|
105
|
+
if (Array.isArray(res?.suggestions) && res.suggestions.length > 0) {
|
|
106
|
+
setBackendSuggestedQuestions(res.suggestions);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// No default language override, use English suggestions
|
|
113
|
+
if (Array.isArray(res?.suggestions) && res.suggestions.length > 0) {
|
|
114
|
+
setBackendSuggestedQuestions(res.suggestions);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
// Silent fail; UI will simply show empty inspiration until chat flows populate
|
|
120
|
+
console.error("Failed to fetch suggestions", e);
|
|
121
|
+
}
|
|
122
|
+
})();
|
|
50
123
|
return () => {
|
|
51
124
|
if (streamingCancelRef.current) {
|
|
52
125
|
streamingCancelRef.current();
|
|
53
126
|
}
|
|
54
|
-
|
|
127
|
+
client.closeWebSocket();
|
|
55
128
|
};
|
|
56
|
-
}, []);
|
|
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]);
|
|
57
142
|
const handleSearch = async (e) => {
|
|
58
143
|
e.preventDefault();
|
|
59
144
|
if (!searchQuery.trim() || isStreaming)
|
|
@@ -73,11 +158,11 @@ export function GojiSearchComponent() {
|
|
|
73
158
|
setMessages((prev) => [...prev, { role: "assistant", content: "", timestamp: Date.now() }]);
|
|
74
159
|
let accumulatedContent = "";
|
|
75
160
|
try {
|
|
76
|
-
const cancelFn = await
|
|
161
|
+
const cancelFn = await client.streamChat({
|
|
77
162
|
message: userMessage.content,
|
|
78
163
|
sessionId,
|
|
79
164
|
limit: 5,
|
|
80
|
-
language:
|
|
165
|
+
language: currentLanguage,
|
|
81
166
|
onDelta: (delta) => {
|
|
82
167
|
accumulatedContent += delta;
|
|
83
168
|
setMessages((prev) => {
|
|
@@ -95,10 +180,15 @@ export function GojiSearchComponent() {
|
|
|
95
180
|
const updated = [...prev];
|
|
96
181
|
updated[assistantMessageIndex] = {
|
|
97
182
|
...updated[assistantMessageIndex],
|
|
183
|
+
content: response.answer,
|
|
98
184
|
sources: response.sources,
|
|
99
185
|
};
|
|
100
186
|
return updated;
|
|
101
187
|
});
|
|
188
|
+
// Update suggested questions from backend
|
|
189
|
+
if (response.suggested_questions && response.suggested_questions.length > 0) {
|
|
190
|
+
setBackendSuggestedQuestions(response.suggested_questions);
|
|
191
|
+
}
|
|
102
192
|
setIsStreaming(false);
|
|
103
193
|
streamingCancelRef.current = null;
|
|
104
194
|
},
|
|
@@ -124,7 +214,6 @@ export function GojiSearchComponent() {
|
|
|
124
214
|
}
|
|
125
215
|
};
|
|
126
216
|
const handleSuggestionClick = async (question) => {
|
|
127
|
-
setShowSuggestions(false);
|
|
128
217
|
const userMessage = {
|
|
129
218
|
role: "user",
|
|
130
219
|
content: question,
|
|
@@ -138,11 +227,11 @@ export function GojiSearchComponent() {
|
|
|
138
227
|
setMessages((prev) => [...prev, { role: "assistant", content: "", timestamp: Date.now() }]);
|
|
139
228
|
let accumulatedContent = "";
|
|
140
229
|
try {
|
|
141
|
-
const cancelFn = await
|
|
230
|
+
const cancelFn = await client.streamChat({
|
|
142
231
|
message: question,
|
|
143
232
|
sessionId,
|
|
144
233
|
limit: 5,
|
|
145
|
-
language:
|
|
234
|
+
language: currentLanguage,
|
|
146
235
|
onDelta: (delta) => {
|
|
147
236
|
accumulatedContent += delta;
|
|
148
237
|
setMessages((prev) => {
|
|
@@ -160,10 +249,15 @@ export function GojiSearchComponent() {
|
|
|
160
249
|
const updated = [...prev];
|
|
161
250
|
updated[assistantMessageIndex] = {
|
|
162
251
|
...updated[assistantMessageIndex],
|
|
252
|
+
content: response.answer,
|
|
163
253
|
sources: response.sources,
|
|
164
254
|
};
|
|
165
255
|
return updated;
|
|
166
256
|
});
|
|
257
|
+
// Update suggested questions from backend
|
|
258
|
+
if (response.suggested_questions && response.suggested_questions.length > 0) {
|
|
259
|
+
setBackendSuggestedQuestions(response.suggested_questions);
|
|
260
|
+
}
|
|
167
261
|
setIsStreaming(false);
|
|
168
262
|
streamingCancelRef.current = null;
|
|
169
263
|
},
|
|
@@ -192,12 +286,134 @@ export function GojiSearchComponent() {
|
|
|
192
286
|
setSearchQuery("");
|
|
193
287
|
setSize("xs");
|
|
194
288
|
setShowCalendar(false);
|
|
289
|
+
setHoverTopClose(false);
|
|
195
290
|
};
|
|
196
|
-
const handleVoiceSearch = () => {
|
|
197
|
-
|
|
291
|
+
const handleVoiceSearch = async () => {
|
|
292
|
+
try {
|
|
293
|
+
if (typeof window === "undefined")
|
|
294
|
+
return;
|
|
295
|
+
if (isRecording) {
|
|
296
|
+
try {
|
|
297
|
+
mediaRecorderRef.current?.stop();
|
|
298
|
+
}
|
|
299
|
+
catch (_) { }
|
|
300
|
+
setIsRecording(false);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
304
|
+
const recorder = new MediaRecorder(stream);
|
|
305
|
+
mediaRecorderRef.current = recorder;
|
|
306
|
+
audioChunksRef.current = [];
|
|
307
|
+
recorder.ondataavailable = (e) => {
|
|
308
|
+
if (e.data && e.data.size > 0) {
|
|
309
|
+
audioChunksRef.current.push(e.data);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
recorder.onstop = async () => {
|
|
313
|
+
try {
|
|
314
|
+
const blob = new Blob(audioChunksRef.current, { type: "audio/webm" });
|
|
315
|
+
// Send to backend for transcription
|
|
316
|
+
const language = companyConfig.defaultLanguage || "en";
|
|
317
|
+
const result = await client.transcribeAudio({
|
|
318
|
+
file: blob,
|
|
319
|
+
filename: "recording.webm",
|
|
320
|
+
model: "gpt-4o-transcribe",
|
|
321
|
+
language,
|
|
322
|
+
});
|
|
323
|
+
const text = (result?.text || "").trim();
|
|
324
|
+
if (!text)
|
|
325
|
+
return;
|
|
326
|
+
// Auto-submit the transcribed text
|
|
327
|
+
const userMessage = {
|
|
328
|
+
role: "user",
|
|
329
|
+
content: text,
|
|
330
|
+
timestamp: Date.now(),
|
|
331
|
+
};
|
|
332
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
333
|
+
setSearchQuery("");
|
|
334
|
+
setSize((prev) => (prev === "xl" ? "xl" : "l"));
|
|
335
|
+
setIsStreaming(true);
|
|
336
|
+
const assistantMessageIndex = messages.length + 1;
|
|
337
|
+
setMessages((prev) => [...prev, { role: "assistant", content: "", timestamp: Date.now() }]);
|
|
338
|
+
let accumulatedContent = "";
|
|
339
|
+
try {
|
|
340
|
+
const cancelFn = await client.streamChat({
|
|
341
|
+
message: userMessage.content,
|
|
342
|
+
sessionId,
|
|
343
|
+
limit: 5,
|
|
344
|
+
language: currentLanguage,
|
|
345
|
+
onDelta: (delta) => {
|
|
346
|
+
accumulatedContent += delta;
|
|
347
|
+
setMessages((prev) => {
|
|
348
|
+
const updated = [...prev];
|
|
349
|
+
updated[assistantMessageIndex] = {
|
|
350
|
+
...updated[assistantMessageIndex],
|
|
351
|
+
content: accumulatedContent,
|
|
352
|
+
};
|
|
353
|
+
return updated;
|
|
354
|
+
});
|
|
355
|
+
},
|
|
356
|
+
onDone: (response) => {
|
|
357
|
+
setSessionId(response.session_id);
|
|
358
|
+
setMessages((prev) => {
|
|
359
|
+
const updated = [...prev];
|
|
360
|
+
updated[assistantMessageIndex] = {
|
|
361
|
+
...updated[assistantMessageIndex],
|
|
362
|
+
content: response.answer,
|
|
363
|
+
sources: response.sources,
|
|
364
|
+
};
|
|
365
|
+
return updated;
|
|
366
|
+
});
|
|
367
|
+
// Update suggested questions from backend
|
|
368
|
+
if (response.suggested_questions && response.suggested_questions.length > 0) {
|
|
369
|
+
setBackendSuggestedQuestions(response.suggested_questions);
|
|
370
|
+
}
|
|
371
|
+
setIsStreaming(false);
|
|
372
|
+
streamingCancelRef.current = null;
|
|
373
|
+
},
|
|
374
|
+
onError: (error) => {
|
|
375
|
+
console.error(" Streaming error:", error);
|
|
376
|
+
setMessages((prev) => {
|
|
377
|
+
const updated = [...prev];
|
|
378
|
+
updated[assistantMessageIndex] = {
|
|
379
|
+
...updated[assistantMessageIndex],
|
|
380
|
+
content: "I'm sorry, I encountered an error. Please try again.",
|
|
381
|
+
};
|
|
382
|
+
return updated;
|
|
383
|
+
});
|
|
384
|
+
setIsStreaming(false);
|
|
385
|
+
streamingCancelRef.current = null;
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
streamingCancelRef.current = cancelFn;
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
console.error(" Failed to start streaming:", error);
|
|
392
|
+
setIsStreaming(false);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
console.error("Transcription failed:", err);
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
// Cleanup audio tracks
|
|
400
|
+
try {
|
|
401
|
+
mediaRecorderRef.current?.stream?.getTracks?.().forEach((t) => t.stop());
|
|
402
|
+
}
|
|
403
|
+
catch (_) { }
|
|
404
|
+
mediaRecorderRef.current = null;
|
|
405
|
+
audioChunksRef.current = [];
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
recorder.start(250);
|
|
409
|
+
setIsRecording(true);
|
|
410
|
+
}
|
|
411
|
+
catch (err) {
|
|
412
|
+
console.error("Voice search failed:", err);
|
|
413
|
+
setIsRecording(false);
|
|
414
|
+
}
|
|
198
415
|
};
|
|
199
416
|
const handleToggle = () => {
|
|
200
|
-
setHasInteracted(true);
|
|
201
417
|
if (size === "xs") {
|
|
202
418
|
setSize(messages.length > 0 ? "l" : "s");
|
|
203
419
|
}
|
|
@@ -213,53 +429,52 @@ export function GojiSearchComponent() {
|
|
|
213
429
|
else {
|
|
214
430
|
setSize("xl");
|
|
215
431
|
}
|
|
432
|
+
setHoverTopExpand(false);
|
|
216
433
|
};
|
|
217
434
|
const handleInputFocus = () => {
|
|
218
|
-
setHasInteracted(true);
|
|
219
435
|
if (messages.length > 0) {
|
|
220
436
|
// if currently xl, keep xl; otherwise go to l
|
|
221
437
|
setSize((prev) => (prev === "xl" ? "xl" : "l"));
|
|
222
438
|
}
|
|
223
439
|
else {
|
|
224
440
|
setSize("m");
|
|
225
|
-
setShowSuggestions(true);
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
const handleInputAreaMouseEnter = () => {
|
|
229
|
-
setHasInteracted(true);
|
|
230
|
-
setIsInputHovered(true);
|
|
231
|
-
if (messages.length === 0 && !searchQuery) {
|
|
232
|
-
setShowSuggestions(true);
|
|
233
441
|
}
|
|
234
442
|
};
|
|
235
|
-
const handleInputAreaMouseLeave = () => {
|
|
236
|
-
setIsInputHovered(false);
|
|
237
|
-
setShowSuggestions(false);
|
|
238
|
-
};
|
|
239
443
|
const handleInputChange = (e) => {
|
|
240
444
|
setSearchQuery(e.target.value);
|
|
241
445
|
if (e.target.value && size === "s") {
|
|
242
446
|
setSize("m");
|
|
243
447
|
}
|
|
244
|
-
if (e.target.value.length > 0 && messages.length === 0) {
|
|
245
|
-
setShowSuggestions(true);
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
setShowSuggestions(false);
|
|
249
|
-
}
|
|
250
448
|
};
|
|
251
449
|
const handleMouseEnter = () => {
|
|
252
|
-
setHasInteracted(true);
|
|
253
450
|
setIsHovered(true);
|
|
254
|
-
if (
|
|
255
|
-
|
|
451
|
+
if (mouseLeaveTimeoutRef.current) {
|
|
452
|
+
clearTimeout(mouseLeaveTimeoutRef.current);
|
|
453
|
+
mouseLeaveTimeoutRef.current = null;
|
|
256
454
|
}
|
|
455
|
+
if (mouseEnterTimeoutRef.current) {
|
|
456
|
+
clearTimeout(mouseEnterTimeoutRef.current);
|
|
457
|
+
mouseEnterTimeoutRef.current = null;
|
|
458
|
+
}
|
|
459
|
+
mouseEnterTimeoutRef.current = setTimeout(() => {
|
|
460
|
+
if (size === "s") {
|
|
461
|
+
setSize("m");
|
|
462
|
+
}
|
|
463
|
+
mouseEnterTimeoutRef.current = null;
|
|
464
|
+
}, 100);
|
|
257
465
|
};
|
|
258
466
|
const handleMouseLeave = () => {
|
|
259
467
|
setIsHovered(false);
|
|
260
|
-
if (
|
|
261
|
-
|
|
468
|
+
if (mouseEnterTimeoutRef.current) {
|
|
469
|
+
clearTimeout(mouseEnterTimeoutRef.current);
|
|
470
|
+
mouseEnterTimeoutRef.current = null;
|
|
262
471
|
}
|
|
472
|
+
mouseLeaveTimeoutRef.current = setTimeout(() => {
|
|
473
|
+
if (size === "m" && !searchQuery && messages.length === 0) {
|
|
474
|
+
setSize("s");
|
|
475
|
+
}
|
|
476
|
+
mouseLeaveTimeoutRef.current = null;
|
|
477
|
+
}, 150);
|
|
263
478
|
};
|
|
264
479
|
const handleInputBlur = () => {
|
|
265
480
|
if (!searchQuery && messages.length === 0) {
|
|
@@ -268,7 +483,7 @@ export function GojiSearchComponent() {
|
|
|
268
483
|
};
|
|
269
484
|
const handleCalendarClick = () => {
|
|
270
485
|
setShowCalendar(true);
|
|
271
|
-
setSize("
|
|
486
|
+
setSize("xl");
|
|
272
487
|
};
|
|
273
488
|
const handleBackToChat = () => {
|
|
274
489
|
setShowCalendar(false);
|
|
@@ -277,8 +492,8 @@ export function GojiSearchComponent() {
|
|
|
277
492
|
xs: { maxWidth: "5.5rem", bottom: "0.5rem", padding: "0" },
|
|
278
493
|
s: { maxWidth: "25.5rem", bottom: "0.5rem", padding: "0" },
|
|
279
494
|
m: { maxWidth: "30rem", bottom: "0.75rem", padding: "0.25rem" },
|
|
280
|
-
l: { maxWidth: "45vw", bottom: "1.25rem", padding: "
|
|
281
|
-
xl: { maxWidth: "70vw", bottom: "1.5rem", padding: "
|
|
495
|
+
l: { maxWidth: "45vw", bottom: "1.25rem", padding: "0.5rem" },
|
|
496
|
+
xl: { maxWidth: "70vw", bottom: "1.5rem", padding: "0.75rem" },
|
|
282
497
|
};
|
|
283
498
|
const currentConfig = sizeConfig[size];
|
|
284
499
|
return (_jsx("div", { style: {
|
|
@@ -289,15 +504,17 @@ export function GojiSearchComponent() {
|
|
|
289
504
|
zIndex: 50,
|
|
290
505
|
padding: "0.7rem",
|
|
291
506
|
transition: "bottom 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
292
|
-
},
|
|
507
|
+
}, children: _jsx("div", { style: {
|
|
293
508
|
maxWidth: currentConfig.maxWidth,
|
|
294
509
|
margin: "0 auto",
|
|
295
510
|
transition: "max-width 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
296
|
-
}, children: _jsxs("div", { style: {
|
|
511
|
+
}, children: _jsxs("div", { ref: componentRef, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: {
|
|
297
512
|
borderRadius: size === "xs" ? "0.75rem" : "1rem",
|
|
298
513
|
border: size === "xs" || size === "s" ? "none" : "1px solid rgba(85, 85, 85, 0.18)",
|
|
299
|
-
backgroundColor: size === "xs" || size === "s" ? "transparent" : "rgba(
|
|
514
|
+
backgroundColor: size === "xs" || size === "s" ? "transparent" : "rgba(245, 245, 250, 0.12)",
|
|
300
515
|
padding: size === "s" ? "0" : currentConfig.padding,
|
|
516
|
+
// remove top padding so inner message list / calendar sit flush with the component border
|
|
517
|
+
paddingTop: 0,
|
|
301
518
|
height: size === "l" ? "55vh" : size === "xl" ? "70vh" : "auto",
|
|
302
519
|
display: "flex",
|
|
303
520
|
flexDirection: "column",
|
|
@@ -310,77 +527,76 @@ export function GojiSearchComponent() {
|
|
|
310
527
|
backdropFilter: size === "xs" || size === "s" ? "none" : "blur(40px) saturate(180%) brightness(110%)",
|
|
311
528
|
WebkitBackdropFilter: size === "xs" || size === "s" ? "none" : "blur(40px) saturate(180%) brightness(110%)",
|
|
312
529
|
transition: "all 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
313
|
-
}, children: [
|
|
530
|
+
}, children: [["m", "l", "xl"].includes(size) && (_jsx("div", { style: {
|
|
314
531
|
position: "absolute",
|
|
315
|
-
top:
|
|
316
|
-
right:
|
|
532
|
+
top: 0,
|
|
533
|
+
right: "0.5rem",
|
|
317
534
|
zIndex: 50,
|
|
535
|
+
paddingBottom: "0.5rem",
|
|
318
536
|
}, children: _jsx("div", { style: {
|
|
319
537
|
position: "relative",
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
538
|
+
display: ((size === "l" || size === "xl") && messages.length > 0) || showCalendar ? "flex" : "none",
|
|
539
|
+
gap: "0.5rem",
|
|
540
|
+
alignItems: "center",
|
|
541
|
+
justifyContent: "flex-end",
|
|
542
|
+
zIndex: 20,
|
|
543
|
+
paddingRight: "0.3rem",
|
|
544
|
+
backgroundColor: "transparent",
|
|
545
|
+
borderRadius: "0.75rem",
|
|
546
|
+
}, children: _jsxs("div", { style: {
|
|
547
|
+
display: "flex",
|
|
548
|
+
gap: "0.375rem",
|
|
549
|
+
alignItems: "center",
|
|
550
|
+
padding: "0.3rem 0 0.5rem 0",
|
|
551
|
+
}, children: [_jsxs("div", { style: { position: "relative" }, children: [_jsx("button", { type: "button", onClick: handleExpandToggle, onMouseEnter: () => setHoverTopExpand(true), onMouseLeave: () => setHoverTopExpand(false), "aria-label": size === "xl" ? "Restore" : "Expand", "aria-describedby": hoverTopExpand ? expandTooltipId : undefined, style: {
|
|
552
|
+
padding: "0.375rem",
|
|
553
|
+
display: "flex",
|
|
554
|
+
alignItems: "center",
|
|
555
|
+
justifyContent: "center",
|
|
556
|
+
color: "rgba(0, 0, 0, 0.75)",
|
|
557
|
+
transition: "all 0.15s",
|
|
558
|
+
cursor: "pointer",
|
|
559
|
+
outline: "none",
|
|
560
|
+
}, children: size === "xl" ? (_jsx(Minimize2, { style: { width: "0.9rem", height: "0.9rem" } })) : (_jsx(Maximize2, { style: { width: "0.9rem", height: "0.9rem" } })) }), _jsx("span", { id: expandTooltipId, role: "tooltip", style: {
|
|
561
|
+
position: "absolute",
|
|
562
|
+
top: "calc(100% + 0.4rem)",
|
|
563
|
+
left: "50%",
|
|
564
|
+
transform: "translateX(-50%)",
|
|
565
|
+
whiteSpace: "nowrap",
|
|
566
|
+
background: "rgba(0,0,0,0.9)",
|
|
567
|
+
color: "#fff",
|
|
568
|
+
padding: "4px 6px",
|
|
569
|
+
borderRadius: "1.3rem",
|
|
570
|
+
fontSize: "0.55rem",
|
|
571
|
+
boxShadow: "0 6px 18px rgba(0,0,0,0.35)",
|
|
350
572
|
opacity: hoverTopExpand ? 1 : 0,
|
|
351
|
-
pointerEvents:
|
|
352
|
-
transition:
|
|
573
|
+
pointerEvents: "none",
|
|
574
|
+
transition: "opacity 120ms ease-in-out",
|
|
353
575
|
zIndex: 30,
|
|
354
|
-
}, children: size ===
|
|
355
|
-
|
|
356
|
-
border: "1px solid rgba(255, 255, 255, 0.3)",
|
|
357
|
-
backgroundColor: "rgba(255, 255, 255, 0.2)",
|
|
358
|
-
padding: "0.4rem",
|
|
576
|
+
}, children: size === "xl" ? "Restore" : "Expand" })] }), _jsxs("div", { style: { position: "relative" }, children: [_jsx("button", { type: "button", onClick: handleClose, onMouseEnter: () => setHoverTopClose(true), onMouseLeave: () => setHoverTopClose(false), "aria-label": "Close", "aria-describedby": hoverTopClose ? xTooltipId : undefined, style: {
|
|
577
|
+
padding: "0.375rem",
|
|
359
578
|
display: "flex",
|
|
360
579
|
alignItems: "center",
|
|
361
580
|
justifyContent: "center",
|
|
362
581
|
color: "rgba(0, 0, 0, 0.75)",
|
|
363
|
-
|
|
364
|
-
WebkitBackdropFilter: "blur(20px)",
|
|
365
|
-
transition: "all 0.2s",
|
|
582
|
+
transition: "all 0.15s",
|
|
366
583
|
cursor: "pointer",
|
|
367
584
|
outline: "none",
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
boxShadow: '0 6px 18px rgba(0,0,0,0.35)',
|
|
585
|
+
}, children: _jsx(X, { style: { width: "0.9rem", height: "0.9rem" } }) }), _jsx("span", { id: xTooltipId, role: "tooltip", style: {
|
|
586
|
+
position: "absolute",
|
|
587
|
+
top: "calc(100% + 0.4rem)",
|
|
588
|
+
left: "50%",
|
|
589
|
+
transform: "translateX(-50%)",
|
|
590
|
+
whiteSpace: "nowrap",
|
|
591
|
+
background: "rgba(0,0,0,0.9)",
|
|
592
|
+
color: "#fff",
|
|
593
|
+
padding: "4px 6px",
|
|
594
|
+
borderRadius: "1.3rem",
|
|
595
|
+
fontSize: "0.55rem",
|
|
596
|
+
boxShadow: "0 6px 18px rgba(0,0,0,0.35)",
|
|
381
597
|
opacity: hoverTopClose ? 1 : 0,
|
|
382
|
-
pointerEvents:
|
|
383
|
-
transition:
|
|
598
|
+
pointerEvents: "none",
|
|
599
|
+
transition: "opacity 120ms ease-in-out",
|
|
384
600
|
zIndex: 30,
|
|
385
601
|
}, children: "Close" })] })] }) }) })), size === "xs" ? (_jsx("button", { type: "button", onClick: handleToggle, style: {
|
|
386
602
|
background: "transparent",
|
|
@@ -396,34 +612,26 @@ export function GojiSearchComponent() {
|
|
|
396
612
|
display: "flex",
|
|
397
613
|
flexDirection: "column",
|
|
398
614
|
gap: "0.75rem",
|
|
399
|
-
height: "100%"
|
|
615
|
+
height: "100%",
|
|
400
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: {
|
|
401
617
|
display: "flex",
|
|
402
618
|
alignItems: "center",
|
|
403
619
|
justifyContent: "center",
|
|
404
|
-
padding: "0.5rem",
|
|
405
|
-
borderRadius: "0.5rem",
|
|
406
|
-
border: "1px solid rgba(255, 255, 255, 0.3)",
|
|
407
|
-
backgroundColor: "rgba(255, 255, 255, 0.2)",
|
|
620
|
+
padding: "0.3rem 0 0.5rem 0.3rem",
|
|
408
621
|
color: "rgba(0, 0, 0, 0.75)",
|
|
409
622
|
cursor: "pointer",
|
|
410
623
|
transition: "all 0.2s",
|
|
411
624
|
alignSelf: "flex-start",
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}, onMouseEnter: (e) => {
|
|
415
|
-
e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.3)";
|
|
416
|
-
}, onMouseLeave: (e) => {
|
|
417
|
-
e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
|
|
418
|
-
}, children: _jsx(ArrowLeft, { style: { width: "1.125rem", height: "1.125rem" } }) }), _jsx("div", { style: {
|
|
625
|
+
outline: "none",
|
|
626
|
+
}, children: _jsx(ArrowLeft, { style: { width: "0.875rem", height: "0.875rem" } }) }), _jsx("div", { style: {
|
|
419
627
|
flex: 1,
|
|
420
628
|
borderRadius: "0.75rem",
|
|
421
629
|
overflow: "hidden",
|
|
422
|
-
backgroundColor: "
|
|
630
|
+
backgroundColor: "transparent",
|
|
423
631
|
minHeight: 0,
|
|
424
632
|
display: "flex",
|
|
425
633
|
flexDirection: "column",
|
|
426
|
-
}, children: _jsx(CalendarIntegration, { onBooked: handleBackToChat }) })] })) : (_jsxs(_Fragment, { children: [(size === "l" || size === "xl") && messages.length > 0 && (_jsx(MessageList, { messages: messages, isStreaming: isStreaming, aiAvatarSrc:
|
|
634
|
+
}, children: _jsx(CalendarIntegration, { onBooked: handleBackToChat }) })] })) : (_jsxs(_Fragment, { children: [(size === "l" || size === "xl") && messages.length > 0 && (_jsx(MessageList, { messages: messages, isStreaming: isStreaming, aiAvatarSrc: chatLogo, size: size })), size === "s" ? (_jsxs("div", { style: {
|
|
427
635
|
display: "flex",
|
|
428
636
|
alignItems: "center",
|
|
429
637
|
gap: "0.35rem",
|
|
@@ -438,12 +646,12 @@ export function GojiSearchComponent() {
|
|
|
438
646
|
cursor: "pointer",
|
|
439
647
|
outline: "none",
|
|
440
648
|
flexShrink: 0,
|
|
441
|
-
}, 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: {
|
|
442
650
|
display: "flex",
|
|
443
651
|
flexDirection: "column",
|
|
444
652
|
gap: "0.35rem",
|
|
445
653
|
flex: 1,
|
|
446
|
-
backgroundColor: "
|
|
654
|
+
backgroundColor: "transparent",
|
|
447
655
|
borderRadius: "1.5rem",
|
|
448
656
|
border: "1px solid rgba(85, 85, 85, 0.18)",
|
|
449
657
|
boxShadow: "0 4px 24px 0 rgba(0, 0, 0, 0.08), inset 0 1px 0 0 rgba(255, 255, 255, 0.3)",
|
|
@@ -456,50 +664,16 @@ export function GojiSearchComponent() {
|
|
|
456
664
|
gap: "0.375rem",
|
|
457
665
|
transition: "gap 0.3s ease",
|
|
458
666
|
flexShrink: 0,
|
|
459
|
-
}, 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, 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: {
|
|
460
668
|
display: "flex",
|
|
461
669
|
flexDirection: "column",
|
|
462
670
|
gap: "0.5rem",
|
|
463
|
-
paddingTop:
|
|
464
|
-
}, children:
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
alignItems: "center",
|
|
472
|
-
justifyContent: "center",
|
|
473
|
-
alignSelf: "flex-end",
|
|
474
|
-
width: "fit-content",
|
|
475
|
-
color: "rgba(0, 0, 0, 0.75)",
|
|
476
|
-
backdropFilter: "blur(20px)",
|
|
477
|
-
WebkitBackdropFilter: "blur(20px)",
|
|
478
|
-
transition: "all 0.2s",
|
|
479
|
-
cursor: "pointer",
|
|
480
|
-
outline: "none",
|
|
481
|
-
boxShadow: "inset 0 1px 0 rgba(255, 255, 255, 0.3)",
|
|
482
|
-
}, children: _jsx(X, { style: { width: "0.875rem", height: "0.875rem" } }) }), _jsx("span", { role: "tooltip", style: {
|
|
483
|
-
position: "absolute",
|
|
484
|
-
top: "calc(100% + 0.4rem)",
|
|
485
|
-
left: "50%",
|
|
486
|
-
transform: "translateX(-50%)",
|
|
487
|
-
whiteSpace: "nowrap",
|
|
488
|
-
background: "rgba(0,0,0,0.9)",
|
|
489
|
-
color: "#fff",
|
|
490
|
-
padding: "4px 6px",
|
|
491
|
-
borderRadius: "1.3rem",
|
|
492
|
-
fontSize: "0.55rem",
|
|
493
|
-
boxShadow: "0 6px 18px rgba(0,0,0,0.35)",
|
|
494
|
-
opacity: 0,
|
|
495
|
-
pointerEvents: "none",
|
|
496
|
-
transition: "opacity 120ms ease-in-out",
|
|
497
|
-
zIndex: 30,
|
|
498
|
-
}, children: "Close" }), showSuggestions && messages.length === 0 && !searchQuery && (_jsx(SuggestedQuestions, { questions: suggestedQuestions, onQuestionClick: handleSuggestionClick })), _jsxs("form", { onSubmit: handleSearch, style: {
|
|
499
|
-
display: "flex",
|
|
500
|
-
alignItems: "center",
|
|
501
|
-
gap: "0.375rem",
|
|
502
|
-
transition: "gap 0.3s ease",
|
|
503
|
-
flexShrink: 0,
|
|
504
|
-
}, 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, onSubmit: () => { }, onVoiceSearch: handleVoiceSearch, onClose: handleClose, onCalendarClick: handleCalendarClick })] })] }))] })) }))] }) }) }));
|
|
671
|
+
paddingTop: 0,
|
|
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 })] }) }))] })) }))] }) }) }));
|
|
505
679
|
}
|