goji-search 1.1.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/dist/goji-search/assets/avatar1.jpeg +0 -0
  2. package/dist/goji-search/assets/avatar2.jpeg +0 -0
  3. package/dist/goji-search/assets/avatar3.jpeg +0 -0
  4. package/dist/goji-search/assets/chat-logo.png +0 -0
  5. package/dist/goji-search/components/elements/closing-card.d.ts +20 -0
  6. package/dist/goji-search/components/elements/inspiration-menu.d.ts +3 -1
  7. package/dist/goji-search/components/elements/language-selector.d.ts +10 -0
  8. package/dist/goji-search/components/elements/message-list.d.ts +3 -1
  9. package/dist/goji-search/components/elements/search-input.d.ts +3 -1
  10. package/dist/goji-search/components/goji-search-component.d.ts +21 -2
  11. package/dist/goji-search/hooks/useCompanyTheme.d.ts +21 -0
  12. package/dist/goji-search/lib/goji-client.d.ts +24 -4
  13. package/dist/index.js +11767 -4
  14. package/package.json +20 -17
  15. package/dist/goji-search/components/elements/action-buttons.js +0 -164
  16. package/dist/goji-search/components/elements/auto-expanding-textarea.js +0 -46
  17. package/dist/goji-search/components/elements/calendar-integration.js +0 -49
  18. package/dist/goji-search/components/elements/inspiration-menu.js +0 -87
  19. package/dist/goji-search/components/elements/message-list.js +0 -301
  20. package/dist/goji-search/components/elements/search-input.js +0 -136
  21. package/dist/goji-search/components/elements/suggested-questions.js +0 -57
  22. package/dist/goji-search/components/goji-search-component.js +0 -679
  23. package/dist/goji-search/components/goji-search.css +0 -1
  24. package/dist/goji-search/config/company.js +0 -100
  25. package/dist/goji-search/lib/calendar-config.js +0 -10
  26. package/dist/goji-search/lib/goji-client.js +0 -229
@@ -1,679 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import "./goji-search.css";
4
- import { ArrowLeft, X, Maximize2, Minimize2 } from "lucide-react";
5
- import { useState, useEffect, useRef, useId, useMemo } from "react";
6
- import { MessageList } from "./elements/message-list";
7
- import chatLogo from "../assets/chat-logo.png";
8
- import { SearchInput } from "./elements/search-input";
9
- import { ActionButtons } from "./elements/action-buttons";
10
- import CalendarIntegration from "./elements/calendar-integration";
11
- import { createGojiClient } from "../lib/goji-client";
12
- import { companyConfig } from "../config/company";
13
- export function GojiSearchComponent({ apiUrl } = {}) {
14
- // Create client instance based on provided API URL
15
- const client = useMemo(() => createGojiClient(apiUrl), [apiUrl]);
16
- const [searchQuery, setSearchQuery] = useState("");
17
- const [size, setSize] = useState("xs");
18
- const [isHovered, setIsHovered] = useState(false);
19
- const [messages, setMessages] = useState([]);
20
- const [isStreaming, setIsStreaming] = useState(false);
21
- const [showCalendar, setShowCalendar] = useState(false);
22
- const [sessionId, setSessionId] = useState();
23
- const inputRef = useRef(null);
24
- const sparkleRef = useRef(null);
25
- const streamingCancelRef = useRef(null);
26
- const mediaRecorderRef = useRef(null);
27
- const audioChunksRef = useRef([]);
28
- const componentRef = useRef(null);
29
- const [hoverTopExpand, setHoverTopExpand] = useState(false);
30
- const [hoverTopClose, setHoverTopClose] = useState(false);
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;
39
- const languageConfig = companyConfig.languages[currentLanguage];
40
- // Use backend-provided questions only (no hardcoded prompts)
41
- const inspirationQuestions = backendSuggestedQuestions || [];
42
- const idBase = useId();
43
- const xTooltipId = `${idBase}-x-tip`;
44
- const expandTooltipId = `${idBase}-expand-tip`;
45
- useEffect(() => {
46
- const timer = setTimeout(() => {
47
- setSize("s");
48
- }, 4000);
49
- return () => clearTimeout(timer);
50
- }, []);
51
- // Close language menu when clicking outside
52
- useEffect(() => {
53
- if (!showLanguageMenu)
54
- return;
55
- const handleClickOutside = (e) => {
56
- const target = e.target;
57
- if (!target.closest("[data-language-menu]")) {
58
- setShowLanguageMenu(false);
59
- }
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
- };
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
- })();
123
- return () => {
124
- if (streamingCancelRef.current) {
125
- streamingCancelRef.current();
126
- }
127
- client.closeWebSocket();
128
- };
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]);
142
- const handleSearch = async (e) => {
143
- e.preventDefault();
144
- if (!searchQuery.trim() || isStreaming)
145
- return;
146
- const userMessage = {
147
- role: "user",
148
- content: searchQuery.trim(),
149
- timestamp: Date.now(),
150
- };
151
- setMessages((prev) => [...prev, userMessage]);
152
- setSearchQuery("");
153
- // don't force-shrink xl -> l; only set to l if not currently xl
154
- setSize((prev) => (prev === "xl" ? "xl" : "l"));
155
- setIsStreaming(true);
156
- // Create placeholder for assistant message
157
- const assistantMessageIndex = messages.length + 1;
158
- setMessages((prev) => [...prev, { role: "assistant", content: "", timestamp: Date.now() }]);
159
- let accumulatedContent = "";
160
- try {
161
- const cancelFn = await client.streamChat({
162
- message: userMessage.content,
163
- sessionId,
164
- limit: 5,
165
- language: currentLanguage,
166
- onDelta: (delta) => {
167
- accumulatedContent += delta;
168
- setMessages((prev) => {
169
- const updated = [...prev];
170
- updated[assistantMessageIndex] = {
171
- ...updated[assistantMessageIndex],
172
- content: accumulatedContent,
173
- };
174
- return updated;
175
- });
176
- },
177
- onDone: (response) => {
178
- setSessionId(response.session_id);
179
- setMessages((prev) => {
180
- const updated = [...prev];
181
- updated[assistantMessageIndex] = {
182
- ...updated[assistantMessageIndex],
183
- content: response.answer,
184
- sources: response.sources,
185
- };
186
- return updated;
187
- });
188
- // Update suggested questions from backend
189
- if (response.suggested_questions && response.suggested_questions.length > 0) {
190
- setBackendSuggestedQuestions(response.suggested_questions);
191
- }
192
- setIsStreaming(false);
193
- streamingCancelRef.current = null;
194
- },
195
- onError: (error) => {
196
- console.error(" Streaming error:", error);
197
- setMessages((prev) => {
198
- const updated = [...prev];
199
- updated[assistantMessageIndex] = {
200
- ...updated[assistantMessageIndex],
201
- content: "I'm sorry, I encountered an error. Please try again.",
202
- };
203
- return updated;
204
- });
205
- setIsStreaming(false);
206
- streamingCancelRef.current = null;
207
- },
208
- });
209
- streamingCancelRef.current = cancelFn;
210
- }
211
- catch (error) {
212
- console.error(" Failed to start streaming:", error);
213
- setIsStreaming(false);
214
- }
215
- };
216
- const handleSuggestionClick = async (question) => {
217
- const userMessage = {
218
- role: "user",
219
- content: question,
220
- timestamp: Date.now(),
221
- };
222
- setMessages((prev) => [...prev, userMessage]);
223
- // preserve xl if already expanded
224
- setSize((prev) => (prev === "xl" ? "xl" : "l"));
225
- setIsStreaming(true);
226
- const assistantMessageIndex = messages.length + 1;
227
- setMessages((prev) => [...prev, { role: "assistant", content: "", timestamp: Date.now() }]);
228
- let accumulatedContent = "";
229
- try {
230
- const cancelFn = await client.streamChat({
231
- message: question,
232
- sessionId,
233
- limit: 5,
234
- language: currentLanguage,
235
- onDelta: (delta) => {
236
- accumulatedContent += delta;
237
- setMessages((prev) => {
238
- const updated = [...prev];
239
- updated[assistantMessageIndex] = {
240
- ...updated[assistantMessageIndex],
241
- content: accumulatedContent,
242
- };
243
- return updated;
244
- });
245
- },
246
- onDone: (response) => {
247
- setSessionId(response.session_id);
248
- setMessages((prev) => {
249
- const updated = [...prev];
250
- updated[assistantMessageIndex] = {
251
- ...updated[assistantMessageIndex],
252
- content: response.answer,
253
- sources: response.sources,
254
- };
255
- return updated;
256
- });
257
- // Update suggested questions from backend
258
- if (response.suggested_questions && response.suggested_questions.length > 0) {
259
- setBackendSuggestedQuestions(response.suggested_questions);
260
- }
261
- setIsStreaming(false);
262
- streamingCancelRef.current = null;
263
- },
264
- onError: (error) => {
265
- console.error(" Streaming error:", error);
266
- setMessages((prev) => {
267
- const updated = [...prev];
268
- updated[assistantMessageIndex] = {
269
- ...updated[assistantMessageIndex],
270
- content: "I'm sorry, I encountered an error. Please try again.",
271
- };
272
- return updated;
273
- });
274
- setIsStreaming(false);
275
- streamingCancelRef.current = null;
276
- },
277
- });
278
- streamingCancelRef.current = cancelFn;
279
- }
280
- catch (error) {
281
- console.error(" Failed to start streaming:", error);
282
- setIsStreaming(false);
283
- }
284
- };
285
- const handleClose = () => {
286
- setSearchQuery("");
287
- setSize("xs");
288
- setShowCalendar(false);
289
- setHoverTopClose(false);
290
- };
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
- }
415
- };
416
- const handleToggle = () => {
417
- if (size === "xs") {
418
- setSize(messages.length > 0 ? "l" : "s");
419
- }
420
- else {
421
- setSize("xs");
422
- }
423
- };
424
- const handleExpandToggle = () => {
425
- // toggle between large and extra-large
426
- if (size === "xl") {
427
- setSize("l");
428
- }
429
- else {
430
- setSize("xl");
431
- }
432
- setHoverTopExpand(false);
433
- };
434
- const handleInputFocus = () => {
435
- if (messages.length > 0) {
436
- // if currently xl, keep xl; otherwise go to l
437
- setSize((prev) => (prev === "xl" ? "xl" : "l"));
438
- }
439
- else {
440
- setSize("m");
441
- }
442
- };
443
- const handleInputChange = (e) => {
444
- setSearchQuery(e.target.value);
445
- if (e.target.value && size === "s") {
446
- setSize("m");
447
- }
448
- };
449
- const handleMouseEnter = () => {
450
- setIsHovered(true);
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;
458
- }
459
- mouseEnterTimeoutRef.current = setTimeout(() => {
460
- if (size === "s") {
461
- setSize("m");
462
- }
463
- mouseEnterTimeoutRef.current = null;
464
- }, 100);
465
- };
466
- const handleMouseLeave = () => {
467
- setIsHovered(false);
468
- if (mouseEnterTimeoutRef.current) {
469
- clearTimeout(mouseEnterTimeoutRef.current);
470
- mouseEnterTimeoutRef.current = null;
471
- }
472
- mouseLeaveTimeoutRef.current = setTimeout(() => {
473
- if (size === "m" && !searchQuery && messages.length === 0) {
474
- setSize("s");
475
- }
476
- mouseLeaveTimeoutRef.current = null;
477
- }, 150);
478
- };
479
- const handleInputBlur = () => {
480
- if (!searchQuery && messages.length === 0) {
481
- setSize("s");
482
- }
483
- };
484
- const handleCalendarClick = () => {
485
- setShowCalendar(true);
486
- setSize("xl");
487
- };
488
- const handleBackToChat = () => {
489
- setShowCalendar(false);
490
- };
491
- const sizeConfig = {
492
- xs: { maxWidth: "5.5rem", bottom: "0.5rem", padding: "0" },
493
- s: { maxWidth: "25.5rem", bottom: "0.5rem", padding: "0" },
494
- m: { maxWidth: "30rem", bottom: "0.75rem", padding: "0.25rem" },
495
- l: { maxWidth: "45vw", bottom: "1.25rem", padding: "0.5rem" },
496
- xl: { maxWidth: "70vw", bottom: "1.5rem", padding: "0.75rem" },
497
- };
498
- const currentConfig = sizeConfig[size];
499
- return (_jsx("div", { style: {
500
- position: "fixed",
501
- bottom: currentConfig.bottom,
502
- left: 0,
503
- right: 0,
504
- zIndex: 50,
505
- padding: "0.7rem",
506
- transition: "bottom 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
507
- }, children: _jsx("div", { style: {
508
- maxWidth: currentConfig.maxWidth,
509
- margin: "0 auto",
510
- transition: "max-width 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
511
- }, children: _jsxs("div", { ref: componentRef, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: {
512
- borderRadius: size === "xs" ? "0.75rem" : "1rem",
513
- border: size === "xs" || size === "s" ? "none" : "1px solid rgba(85, 85, 85, 0.18)",
514
- backgroundColor: size === "xs" || size === "s" ? "transparent" : "rgba(245, 245, 250, 0.12)",
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,
518
- height: size === "l" ? "55vh" : size === "xl" ? "70vh" : "auto",
519
- display: "flex",
520
- flexDirection: "column",
521
- position: "relative",
522
- boxShadow: size === "xs" || size === "s"
523
- ? "none"
524
- : size === "l"
525
- ? "0 8px 32px 0 rgba(0, 0, 0, 0.12), inset 0 1px 0 0 rgba(255, 255, 255, 0.4)"
526
- : "0 4px 24px 0 rgba(0, 0, 0, 0.08), inset 0 1px 0 0 rgba(255, 255, 255, 0.3)",
527
- backdropFilter: size === "xs" || size === "s" ? "none" : "blur(40px) saturate(180%) brightness(110%)",
528
- WebkitBackdropFilter: size === "xs" || size === "s" ? "none" : "blur(40px) saturate(180%) brightness(110%)",
529
- transition: "all 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
530
- }, children: [["m", "l", "xl"].includes(size) && (_jsx("div", { style: {
531
- position: "absolute",
532
- top: 0,
533
- right: "0.5rem",
534
- zIndex: 50,
535
- paddingBottom: "0.5rem",
536
- }, children: _jsx("div", { style: {
537
- position: "relative",
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)",
572
- opacity: hoverTopExpand ? 1 : 0,
573
- pointerEvents: "none",
574
- transition: "opacity 120ms ease-in-out",
575
- zIndex: 30,
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",
578
- display: "flex",
579
- alignItems: "center",
580
- justifyContent: "center",
581
- color: "rgba(0, 0, 0, 0.75)",
582
- transition: "all 0.15s",
583
- cursor: "pointer",
584
- outline: "none",
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)",
597
- opacity: hoverTopClose ? 1 : 0,
598
- pointerEvents: "none",
599
- transition: "opacity 120ms ease-in-out",
600
- zIndex: 30,
601
- }, children: "Close" })] })] }) }) })), size === "xs" ? (_jsx("button", { type: "button", onClick: handleToggle, style: {
602
- background: "transparent",
603
- border: "none",
604
- padding: 0,
605
- display: "flex",
606
- alignItems: "center",
607
- justifyContent: "center",
608
- cursor: "pointer",
609
- outline: "none",
610
- width: "100%",
611
- }, 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: {
612
- display: "flex",
613
- flexDirection: "column",
614
- gap: "0.75rem",
615
- height: "100%",
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: {
617
- display: "flex",
618
- alignItems: "center",
619
- justifyContent: "center",
620
- padding: "0.3rem 0 0.5rem 0.3rem",
621
- color: "rgba(0, 0, 0, 0.75)",
622
- cursor: "pointer",
623
- transition: "all 0.2s",
624
- alignSelf: "flex-start",
625
- outline: "none",
626
- }, children: _jsx(ArrowLeft, { style: { width: "0.875rem", height: "0.875rem" } }) }), _jsx("div", { style: {
627
- flex: 1,
628
- borderRadius: "0.75rem",
629
- overflow: "hidden",
630
- backgroundColor: "transparent",
631
- minHeight: 0,
632
- display: "flex",
633
- flexDirection: "column",
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: {
635
- display: "flex",
636
- alignItems: "center",
637
- gap: "0.35rem",
638
- transition: "max-width 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
639
- }, children: [_jsx("button", { type: "button", onClick: handleToggle, style: {
640
- background: "transparent",
641
- border: "none",
642
- padding: 0,
643
- display: "flex",
644
- alignItems: "center",
645
- justifyContent: "center",
646
- cursor: "pointer",
647
- outline: "none",
648
- flexShrink: 0,
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: {
650
- display: "flex",
651
- flexDirection: "column",
652
- gap: "0.35rem",
653
- flex: 1,
654
- backgroundColor: "transparent",
655
- borderRadius: "1.5rem",
656
- border: "1px solid rgba(85, 85, 85, 0.18)",
657
- boxShadow: "0 4px 24px 0 rgba(0, 0, 0, 0.08), inset 0 1px 0 0 rgba(255, 255, 255, 0.3)",
658
- backdropFilter: "blur(40px) saturate(180%) brightness(110%)",
659
- WebkitBackdropFilter: "blur(40px) saturate(180%) brightness(110%)",
660
- transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
661
- }, children: _jsxs("form", { onSubmit: handleSearch, style: {
662
- display: "flex",
663
- alignItems: "center",
664
- gap: "0.375rem",
665
- transition: "gap 0.3s ease",
666
- flexShrink: 0,
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: {
668
- display: "flex",
669
- flexDirection: "column",
670
- gap: "0.5rem",
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 })] }) }))] })) }))] }) }) }));
679
- }