goji-search 1.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 (25) hide show
  1. package/README.md +70 -0
  2. package/dist/goji-search/components/elements/action-buttons.d.ts +12 -0
  3. package/dist/goji-search/components/elements/action-buttons.js +135 -0
  4. package/dist/goji-search/components/elements/calendar-integration.d.ts +5 -0
  5. package/dist/goji-search/components/elements/calendar-integration.js +49 -0
  6. package/dist/goji-search/components/elements/inspiration-menu.d.ts +7 -0
  7. package/dist/goji-search/components/elements/inspiration-menu.js +79 -0
  8. package/dist/goji-search/components/elements/message-list.d.ts +24 -0
  9. package/dist/goji-search/components/elements/message-list.js +293 -0
  10. package/dist/goji-search/components/elements/search-input.d.ts +15 -0
  11. package/dist/goji-search/components/elements/search-input.js +90 -0
  12. package/dist/goji-search/components/elements/suggested-questions.d.ts +6 -0
  13. package/dist/goji-search/components/elements/suggested-questions.js +54 -0
  14. package/dist/goji-search/components/goji-search-component.d.ts +2 -0
  15. package/dist/goji-search/components/goji-search-component.js +505 -0
  16. package/dist/goji-search/config/company.d.ts +29 -0
  17. package/dist/goji-search/config/company.js +68 -0
  18. package/dist/goji-search/lib/calendar-config.d.ts +9 -0
  19. package/dist/goji-search/lib/calendar-config.js +10 -0
  20. package/dist/goji-search/lib/goji-client.d.ts +81 -0
  21. package/dist/goji-search/lib/goji-client.js +176 -0
  22. package/dist/goji-search.css +1 -0
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.js +2 -0
  25. package/package.json +47 -0
@@ -0,0 +1,505 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import "./goji-search.css";
3
+ import { ArrowLeft, X, Maximize2, Minimize2 } from "lucide-react";
4
+ import { useState, useEffect, useRef, useId } from "react";
5
+ import { SuggestedQuestions } from "./elements/suggested-questions";
6
+ import { MessageList } from "./elements/message-list";
7
+ import { SearchInput } from "./elements/search-input";
8
+ import { ActionButtons } from "./elements/action-buttons";
9
+ import CalendarIntegration from "./elements/calendar-integration";
10
+ import { gojiClient } from "../lib/goji-client";
11
+ import { companyConfig } from "../config/company";
12
+ export function GojiSearchComponent() {
13
+ const [searchQuery, setSearchQuery] = useState("");
14
+ const [size, setSize] = useState("xs");
15
+ const [isHovered, setIsHovered] = useState(false);
16
+ const [messages, setMessages] = useState([]);
17
+ const [isStreaming, setIsStreaming] = useState(false);
18
+ const [showSuggestions, setShowSuggestions] = useState(false);
19
+ const [showCalendar, setShowCalendar] = useState(false);
20
+ const [sessionId, setSessionId] = useState();
21
+ const [hasInteracted, setHasInteracted] = useState(false);
22
+ const inputRef = useRef(null);
23
+ const sparkleRef = useRef(null);
24
+ const streamingCancelRef = useRef(null);
25
+ const [isInputHovered, setIsInputHovered] = useState(false);
26
+ const [hoverTopExpand, setHoverTopExpand] = useState(false);
27
+ const [hoverTopClose, setHoverTopClose] = useState(false);
28
+ const currentLanguage = companyConfig.defaultLanguage;
29
+ const languageConfig = companyConfig.languages[currentLanguage];
30
+ const suggestedQuestions = languageConfig.suggestedPrompts;
31
+ const inspirationQuestions = languageConfig.suggestedPrompts;
32
+ const idBase = useId();
33
+ const xTooltipId = `${idBase}-x-tip`;
34
+ const expandTooltipId = `${idBase}-expand-tip`;
35
+ useEffect(() => {
36
+ const timer = setTimeout(() => {
37
+ setSize("s");
38
+ }, 4000);
39
+ return () => clearTimeout(timer);
40
+ }, []);
41
+ useEffect(() => {
42
+ const timer = setTimeout(() => {
43
+ if (!hasInteracted && size !== "xs") {
44
+ setSize("xs");
45
+ }
46
+ }, 12000);
47
+ return () => clearTimeout(timer);
48
+ }, [hasInteracted, size]);
49
+ useEffect(() => {
50
+ return () => {
51
+ if (streamingCancelRef.current) {
52
+ streamingCancelRef.current();
53
+ }
54
+ gojiClient.closeWebSocket();
55
+ };
56
+ }, []);
57
+ const handleSearch = async (e) => {
58
+ e.preventDefault();
59
+ if (!searchQuery.trim() || isStreaming)
60
+ return;
61
+ const userMessage = {
62
+ role: "user",
63
+ content: searchQuery.trim(),
64
+ timestamp: Date.now(),
65
+ };
66
+ setMessages((prev) => [...prev, userMessage]);
67
+ setSearchQuery("");
68
+ // don't force-shrink xl -> l; only set to l if not currently xl
69
+ setSize((prev) => (prev === "xl" ? "xl" : "l"));
70
+ setIsStreaming(true);
71
+ // Create placeholder for assistant message
72
+ const assistantMessageIndex = messages.length + 1;
73
+ setMessages((prev) => [...prev, { role: "assistant", content: "", timestamp: Date.now() }]);
74
+ let accumulatedContent = "";
75
+ try {
76
+ const cancelFn = await gojiClient.streamChat({
77
+ message: userMessage.content,
78
+ sessionId,
79
+ limit: 5,
80
+ language: "en",
81
+ onDelta: (delta) => {
82
+ accumulatedContent += delta;
83
+ setMessages((prev) => {
84
+ const updated = [...prev];
85
+ updated[assistantMessageIndex] = {
86
+ ...updated[assistantMessageIndex],
87
+ content: accumulatedContent,
88
+ };
89
+ return updated;
90
+ });
91
+ },
92
+ onDone: (response) => {
93
+ setSessionId(response.session_id);
94
+ setMessages((prev) => {
95
+ const updated = [...prev];
96
+ updated[assistantMessageIndex] = {
97
+ ...updated[assistantMessageIndex],
98
+ sources: response.sources,
99
+ };
100
+ return updated;
101
+ });
102
+ setIsStreaming(false);
103
+ streamingCancelRef.current = null;
104
+ },
105
+ onError: (error) => {
106
+ console.error(" Streaming error:", error);
107
+ setMessages((prev) => {
108
+ const updated = [...prev];
109
+ updated[assistantMessageIndex] = {
110
+ ...updated[assistantMessageIndex],
111
+ content: "I'm sorry, I encountered an error. Please try again.",
112
+ };
113
+ return updated;
114
+ });
115
+ setIsStreaming(false);
116
+ streamingCancelRef.current = null;
117
+ },
118
+ });
119
+ streamingCancelRef.current = cancelFn;
120
+ }
121
+ catch (error) {
122
+ console.error(" Failed to start streaming:", error);
123
+ setIsStreaming(false);
124
+ }
125
+ };
126
+ const handleSuggestionClick = async (question) => {
127
+ setShowSuggestions(false);
128
+ const userMessage = {
129
+ role: "user",
130
+ content: question,
131
+ timestamp: Date.now(),
132
+ };
133
+ setMessages((prev) => [...prev, userMessage]);
134
+ // preserve xl if already expanded
135
+ setSize((prev) => (prev === "xl" ? "xl" : "l"));
136
+ setIsStreaming(true);
137
+ const assistantMessageIndex = messages.length + 1;
138
+ setMessages((prev) => [...prev, { role: "assistant", content: "", timestamp: Date.now() }]);
139
+ let accumulatedContent = "";
140
+ try {
141
+ const cancelFn = await gojiClient.streamChat({
142
+ message: question,
143
+ sessionId,
144
+ limit: 5,
145
+ language: "en",
146
+ onDelta: (delta) => {
147
+ accumulatedContent += delta;
148
+ setMessages((prev) => {
149
+ const updated = [...prev];
150
+ updated[assistantMessageIndex] = {
151
+ ...updated[assistantMessageIndex],
152
+ content: accumulatedContent,
153
+ };
154
+ return updated;
155
+ });
156
+ },
157
+ onDone: (response) => {
158
+ setSessionId(response.session_id);
159
+ setMessages((prev) => {
160
+ const updated = [...prev];
161
+ updated[assistantMessageIndex] = {
162
+ ...updated[assistantMessageIndex],
163
+ sources: response.sources,
164
+ };
165
+ return updated;
166
+ });
167
+ setIsStreaming(false);
168
+ streamingCancelRef.current = null;
169
+ },
170
+ onError: (error) => {
171
+ console.error(" Streaming error:", error);
172
+ setMessages((prev) => {
173
+ const updated = [...prev];
174
+ updated[assistantMessageIndex] = {
175
+ ...updated[assistantMessageIndex],
176
+ content: "I'm sorry, I encountered an error. Please try again.",
177
+ };
178
+ return updated;
179
+ });
180
+ setIsStreaming(false);
181
+ streamingCancelRef.current = null;
182
+ },
183
+ });
184
+ streamingCancelRef.current = cancelFn;
185
+ }
186
+ catch (error) {
187
+ console.error(" Failed to start streaming:", error);
188
+ setIsStreaming(false);
189
+ }
190
+ };
191
+ const handleClose = () => {
192
+ setSearchQuery("");
193
+ setSize("xs");
194
+ setShowCalendar(false);
195
+ };
196
+ const handleVoiceSearch = () => {
197
+ console.log("Voice search activated");
198
+ };
199
+ const handleToggle = () => {
200
+ setHasInteracted(true);
201
+ if (size === "xs") {
202
+ setSize(messages.length > 0 ? "l" : "s");
203
+ }
204
+ else {
205
+ setSize("xs");
206
+ }
207
+ };
208
+ const handleExpandToggle = () => {
209
+ // toggle between large and extra-large
210
+ if (size === "xl") {
211
+ setSize("l");
212
+ }
213
+ else {
214
+ setSize("xl");
215
+ }
216
+ };
217
+ const handleInputFocus = () => {
218
+ setHasInteracted(true);
219
+ if (messages.length > 0) {
220
+ // if currently xl, keep xl; otherwise go to l
221
+ setSize((prev) => (prev === "xl" ? "xl" : "l"));
222
+ }
223
+ else {
224
+ 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
+ }
234
+ };
235
+ const handleInputAreaMouseLeave = () => {
236
+ setIsInputHovered(false);
237
+ setShowSuggestions(false);
238
+ };
239
+ const handleInputChange = (e) => {
240
+ setSearchQuery(e.target.value);
241
+ if (e.target.value && size === "s") {
242
+ setSize("m");
243
+ }
244
+ if (e.target.value.length > 0 && messages.length === 0) {
245
+ setShowSuggestions(true);
246
+ }
247
+ else {
248
+ setShowSuggestions(false);
249
+ }
250
+ };
251
+ const handleMouseEnter = () => {
252
+ setHasInteracted(true);
253
+ setIsHovered(true);
254
+ if (size === "s") {
255
+ setSize("m");
256
+ }
257
+ };
258
+ const handleMouseLeave = () => {
259
+ setIsHovered(false);
260
+ if (size === "m" && !searchQuery && messages.length === 0) {
261
+ setSize("s");
262
+ }
263
+ };
264
+ const handleInputBlur = () => {
265
+ if (!searchQuery && messages.length === 0) {
266
+ setSize("s");
267
+ }
268
+ };
269
+ const handleCalendarClick = () => {
270
+ setShowCalendar(true);
271
+ setSize("l");
272
+ };
273
+ const handleBackToChat = () => {
274
+ setShowCalendar(false);
275
+ };
276
+ const sizeConfig = {
277
+ xs: { maxWidth: "5.5rem", bottom: "0.5rem", padding: "0" },
278
+ s: { maxWidth: "25.5rem", bottom: "0.5rem", padding: "0" },
279
+ m: { maxWidth: "30rem", bottom: "0.75rem", padding: "0.25rem" },
280
+ l: { maxWidth: "45vw", bottom: "1.25rem", padding: "1rem" },
281
+ xl: { maxWidth: "70vw", bottom: "1.5rem", padding: "1.25rem" },
282
+ };
283
+ const currentConfig = sizeConfig[size];
284
+ return (_jsx("div", { style: {
285
+ position: "fixed",
286
+ bottom: currentConfig.bottom,
287
+ left: 0,
288
+ right: 0,
289
+ zIndex: 50,
290
+ padding: "0.7rem",
291
+ transition: "bottom 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
292
+ }, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: _jsx("div", { style: {
293
+ maxWidth: currentConfig.maxWidth,
294
+ margin: "0 auto",
295
+ transition: "max-width 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
296
+ }, children: _jsxs("div", { style: {
297
+ borderRadius: size === "xs" ? "0.75rem" : "1rem",
298
+ border: size === "xs" || size === "s" ? "none" : "1px solid rgba(85, 85, 85, 0.18)",
299
+ backgroundColor: size === "xs" || size === "s" ? "transparent" : "rgba(233, 210, 251, 0.15)",
300
+ padding: size === "s" ? "0" : currentConfig.padding,
301
+ height: size === "l" ? "55vh" : size === "xl" ? "70vh" : "auto",
302
+ display: "flex",
303
+ flexDirection: "column",
304
+ position: "relative",
305
+ boxShadow: size === "xs" || size === "s"
306
+ ? "none"
307
+ : size === "l"
308
+ ? "0 8px 32px 0 rgba(0, 0, 0, 0.12), inset 0 1px 0 0 rgba(255, 255, 255, 0.4)"
309
+ : "0 4px 24px 0 rgba(0, 0, 0, 0.08), inset 0 1px 0 0 rgba(255, 255, 255, 0.3)",
310
+ backdropFilter: size === "xs" || size === "s" ? "none" : "blur(40px) saturate(180%) brightness(110%)",
311
+ WebkitBackdropFilter: size === "xs" || size === "s" ? "none" : "blur(40px) saturate(180%) brightness(110%)",
312
+ transition: "all 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
313
+ }, children: [(size === "m" || size === "l" || size === "xl") && (_jsx("div", { style: {
314
+ position: "absolute",
315
+ top: size === "m" ? "0.5rem" : "0.75rem",
316
+ right: size === "m" ? "0.5rem" : "0.75rem",
317
+ zIndex: 50,
318
+ }, children: _jsx("div", { style: {
319
+ position: "relative",
320
+ // show the top-right controls when we have no messages OR when in compact (m) mode OR when calendar is open
321
+ // but hide them when only suggested questions are shown (messages.length === 0 && showSuggestions)
322
+ display: (size === 'm' || messages.length === 0 || showCalendar) && !(messages.length === 0 && showSuggestions) ? 'inline-flex' : 'none',
323
+ gap: '0.5rem',
324
+ alignItems: 'center',
325
+ }, children: _jsxs("div", { style: { display: 'inline-flex', gap: '0.375rem', alignItems: 'center' }, 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: {
326
+ borderRadius: '1.3rem',
327
+ border: '1px solid rgba(255, 255, 255, 0.3)',
328
+ backgroundColor: 'rgba(255, 255, 255, 0.2)',
329
+ padding: '0.375rem',
330
+ display: 'flex',
331
+ alignItems: 'center',
332
+ justifyContent: 'center',
333
+ color: 'rgba(0, 0, 0, 0.75)',
334
+ transition: 'all 0.2s',
335
+ cursor: 'pointer',
336
+ outline: 'none',
337
+ boxShadow: 'inset 0 1px 0 rgba(255, 255, 255, 0.3)',
338
+ }, 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: {
339
+ position: 'absolute',
340
+ top: 'calc(100% + 0.4rem)',
341
+ left: '50%',
342
+ transform: 'translateX(-50%)',
343
+ whiteSpace: 'nowrap',
344
+ background: 'rgba(0,0,0,0.9)',
345
+ color: '#fff',
346
+ padding: '4px 6px',
347
+ borderRadius: '1.3rem',
348
+ fontSize: '0.55rem',
349
+ boxShadow: '0 6px 18px rgba(0,0,0,0.35)',
350
+ opacity: hoverTopExpand ? 1 : 0,
351
+ pointerEvents: 'none',
352
+ transition: 'opacity 120ms ease-in-out',
353
+ zIndex: 30,
354
+ }, 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: {
355
+ borderRadius: "1.3rem",
356
+ border: "1px solid rgba(255, 255, 255, 0.3)",
357
+ backgroundColor: "rgba(255, 255, 255, 0.2)",
358
+ padding: "0.4rem",
359
+ display: "flex",
360
+ alignItems: "center",
361
+ justifyContent: "center",
362
+ color: "rgba(0, 0, 0, 0.75)",
363
+ backdropFilter: "blur(20px)",
364
+ WebkitBackdropFilter: "blur(20px)",
365
+ transition: "all 0.2s",
366
+ cursor: "pointer",
367
+ outline: "none",
368
+ boxShadow: "inset 0 1px 0 rgba(255, 255, 255, 0.3)",
369
+ }, children: _jsx(X, { style: { width: "0.875rem", height: "0.875rem" } }) }), _jsx("span", { id: xTooltipId, role: "tooltip", style: {
370
+ position: 'absolute',
371
+ top: 'calc(100% + 0.4rem)',
372
+ left: '50%',
373
+ transform: 'translateX(-50%)',
374
+ whiteSpace: 'nowrap',
375
+ background: 'rgba(0,0,0,0.9)',
376
+ color: '#fff',
377
+ padding: '4px 6px',
378
+ borderRadius: '1.3rem',
379
+ fontSize: '0.55rem',
380
+ boxShadow: '0 6px 18px rgba(0,0,0,0.35)',
381
+ opacity: hoverTopClose ? 1 : 0,
382
+ pointerEvents: 'none',
383
+ transition: 'opacity 120ms ease-in-out',
384
+ zIndex: 30,
385
+ }, children: "Close" })] })] }) }) })), size === "xs" ? (_jsx("button", { type: "button", onClick: handleToggle, style: {
386
+ background: "transparent",
387
+ border: "none",
388
+ padding: 0,
389
+ display: "flex",
390
+ alignItems: "center",
391
+ justifyContent: "center",
392
+ cursor: "pointer",
393
+ outline: "none",
394
+ width: "100%",
395
+ }, 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: {
396
+ display: "flex",
397
+ flexDirection: "column",
398
+ gap: "0.75rem",
399
+ height: "100%"
400
+ }, 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
+ display: "flex",
402
+ alignItems: "center",
403
+ 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)",
408
+ color: "rgba(0, 0, 0, 0.75)",
409
+ cursor: "pointer",
410
+ transition: "all 0.2s",
411
+ alignSelf: "flex-start",
412
+ width: "2.5rem",
413
+ height: "2.5rem",
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: {
419
+ flex: 1,
420
+ borderRadius: "0.75rem",
421
+ overflow: "hidden",
422
+ backgroundColor: "rgba(255, 255, 255, 0.5)",
423
+ minHeight: 0,
424
+ display: "flex",
425
+ 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: undefined, onClose: handleClose, onExpand: handleExpandToggle, size: size })), size === "s" ? (_jsxs("div", { style: {
427
+ display: "flex",
428
+ alignItems: "center",
429
+ gap: "0.35rem",
430
+ transition: "max-width 0.4s cubic-bezier(0.4, 0, 0.2, 1)",
431
+ }, children: [_jsx("button", { type: "button", onClick: handleToggle, style: {
432
+ background: "transparent",
433
+ border: "none",
434
+ padding: 0,
435
+ display: "flex",
436
+ alignItems: "center",
437
+ justifyContent: "center",
438
+ cursor: "pointer",
439
+ outline: "none",
440
+ 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", { onMouseEnter: handleInputAreaMouseEnter, onMouseLeave: handleInputAreaMouseLeave, style: {
442
+ display: "flex",
443
+ flexDirection: "column",
444
+ gap: "0.35rem",
445
+ flex: 1,
446
+ backgroundColor: "rgba(233, 210, 251, 0.15)",
447
+ borderRadius: "1.5rem",
448
+ border: "1px solid rgba(85, 85, 85, 0.18)",
449
+ boxShadow: "0 4px 24px 0 rgba(0, 0, 0, 0.08), inset 0 1px 0 0 rgba(255, 255, 255, 0.3)",
450
+ backdropFilter: "blur(40px) saturate(180%) brightness(110%)",
451
+ WebkitBackdropFilter: "blur(40px) saturate(180%) brightness(110%)",
452
+ transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
453
+ }, children: _jsxs("form", { onSubmit: handleSearch, style: {
454
+ display: "flex",
455
+ alignItems: "center",
456
+ gap: "0.375rem",
457
+ transition: "gap 0.3s ease",
458
+ 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 })] }) })] })) : (_jsxs("div", { onMouseEnter: handleInputAreaMouseEnter, onMouseLeave: handleInputAreaMouseLeave, style: {
460
+ display: "flex",
461
+ flexDirection: "column",
462
+ gap: "0.5rem",
463
+ paddingTop: size === "m" ? "0.5rem" : "0.75rem",
464
+ }, children: [_jsx("button", { type: "button", onClick: handleClose, style: {
465
+ borderRadius: "1.3rem",
466
+ border: "1px solid rgba(255, 255, 255, 0.3)",
467
+ backgroundColor: "rgba(255, 255, 255, 0.2)",
468
+ padding: "0.4rem",
469
+ // rendered only when size !== 'xs' so show the button
470
+ display: size === 'm' ? "flex" : "none",
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 })] })] }))] })) }))] }) }) }));
505
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Company-specific configuration for GojiSearch chat interface.
3
+ * Update these values to customize the chat for different clients.
4
+ */
5
+ interface LanguageConfig {
6
+ label: string;
7
+ welcome: {
8
+ title: string;
9
+ description: string;
10
+ };
11
+ suggestedPrompts: string[];
12
+ headerSubtitleExpanded: string;
13
+ headerSubtitleCollapsed: string;
14
+ sourcesLabel: string;
15
+ languageSelectorLabel: string;
16
+ generatingMessage: string;
17
+ inputPlaceholder: string;
18
+ bookDemoButton: string;
19
+ bookDemoDialogTitle: string;
20
+ bookDemoDialogDescription: string;
21
+ errorMessage: string;
22
+ }
23
+ export declare const companyConfig: {
24
+ brandName: string;
25
+ defaultLanguage: string;
26
+ languages: Record<string, LanguageConfig>;
27
+ };
28
+ export type SupportedLanguage = keyof typeof companyConfig.languages;
29
+ export {};
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Company-specific configuration for GojiSearch chat interface.
3
+ * Update these values to customize the chat for different clients.
4
+ */
5
+ export const companyConfig = {
6
+ brandName: "GojiSearch",
7
+ defaultLanguage: "en",
8
+ languages: {
9
+ en: {
10
+ label: "English",
11
+ welcome: {
12
+ title: "Welcome to GojiSearch",
13
+ description: "Ask me anything! I'll search through your knowledge base and provide accurate answers with sources.",
14
+ },
15
+ suggestedPrompts: ["Tell me how you engage with prospects", "How do you qualify my leads?", "How long does it take to have Goji on my website?"],
16
+ headerSubtitleExpanded: "Conversation powered by your docs",
17
+ headerSubtitleCollapsed: "Start typing to expand",
18
+ sourcesLabel: "Sources",
19
+ languageSelectorLabel: "Language",
20
+ generatingMessage: "{brand} is generating...",
21
+ inputPlaceholder: "Ask anything and press Enter",
22
+ bookDemoButton: "Book a Demo",
23
+ bookDemoDialogTitle: "Book a Demo with Chaimaa",
24
+ bookDemoDialogDescription: "Schedule a 30-minute call to discuss your needs and see how we can help.",
25
+ errorMessage: "I'm sorry, I encountered an error. Please try again.",
26
+ },
27
+ it: {
28
+ label: "Italiano",
29
+ welcome: {
30
+ title: "Benvenuto in GojiSearch",
31
+ description: "Chiedimi qualsiasi cosa! Cercherò nella tua knowledge base e fornirò risposte accurate con citazioni.",
32
+ },
33
+ suggestedPrompts: ["Parlami di come interagisci con i tuoi clienti", "Parlami di come qualifichi i tuoi leads", "Quanto tempo ci vuole per avere Goji sul mio sito?"],
34
+ headerSubtitleExpanded: "Conversazione alimentata dai tuoi documenti",
35
+ headerSubtitleCollapsed: "Inizia a digitare per espandere",
36
+ sourcesLabel: "Fonti",
37
+ languageSelectorLabel: "Lingua",
38
+ generatingMessage: "{brand} sta generando...",
39
+ inputPlaceholder: "Chiedi qualsiasi cosa e premi Invio",
40
+ bookDemoButton: "Prenota una demo",
41
+ bookDemoDialogTitle: "Prenota una demo con Chaimaa",
42
+ bookDemoDialogDescription: "Pianifica una chiamata di 30 minuti per discutere le tue esigenze e vedere come possiamo aiutarti.",
43
+ errorMessage: "Si è verificato un errore. Riprova più tardi.",
44
+ },
45
+ fr: {
46
+ label: "Français",
47
+ welcome: {
48
+ title: "Bienvenue sur GojiSearch",
49
+ description: "Posez-moi vos questions ! Je parcourrai votre base de connaissances et fournirai des réponses précises avec des sources.",
50
+ },
51
+ suggestedPrompts: [
52
+ "Qu'est-ce que Deel ?",
53
+ "Comment fonctionne la tarification ?",
54
+ "Parle-moi des fonctionnalités",
55
+ ],
56
+ headerSubtitleExpanded: "Conversation alimentée par vos contenus",
57
+ headerSubtitleCollapsed: "Commencez à écrire pour développer",
58
+ sourcesLabel: "Sources",
59
+ languageSelectorLabel: "Langue",
60
+ generatingMessage: "{brand} génère une réponse...",
61
+ inputPlaceholder: "Posez votre question et appuyez sur Entrée",
62
+ bookDemoButton: "Réserver une démo",
63
+ bookDemoDialogTitle: "Réserver une démo avec Chaimaa",
64
+ bookDemoDialogDescription: "Planifiez un appel de 30 minutes pour discuter de vos besoins et découvrir nos solutions.",
65
+ errorMessage: "Une erreur s'est produite. Merci de réessayer.",
66
+ },
67
+ },
68
+ };
@@ -0,0 +1,9 @@
1
+ export interface CalendarConfig {
2
+ provider: "calcom" | "calendly";
3
+ link: string;
4
+ namespace?: string;
5
+ theme?: "light" | "dark";
6
+ layout?: "month_view" | "week_view" | "agenda";
7
+ }
8
+ export declare const calendarConfig: CalendarConfig;
9
+ export declare function getCalendarConfig(): CalendarConfig;
@@ -0,0 +1,10 @@
1
+ export const calendarConfig = {
2
+ provider: "calcom",
3
+ link: "chaimaahrk/30min",
4
+ namespace: "30min",
5
+ theme: "light",
6
+ layout: "month_view",
7
+ };
8
+ export function getCalendarConfig() {
9
+ return calendarConfig;
10
+ }