@supernal/interface-nextjs 0.1.1 → 1.0.9
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/index.d.mts +69 -30
- package/dist/index.d.ts +69 -30
- package/dist/index.js +844 -145
- package/dist/index.mjs +844 -145
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
// src/components/SupernalProvider.tsx
|
|
4
|
+
import { useEffect as useEffect5 } from "react";
|
|
5
|
+
|
|
3
6
|
// src/contexts/ChatInputContext.tsx
|
|
4
7
|
import { createContext, useContext, useCallback, useRef } from "react";
|
|
5
8
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -302,78 +305,416 @@ function ChatProvider({
|
|
|
302
305
|
|
|
303
306
|
// src/components/ChatBubble.tsx
|
|
304
307
|
import { useState as useState2, useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
308
|
+
|
|
309
|
+
// ../names/Components.ts
|
|
310
|
+
var Components = {
|
|
311
|
+
// Chat components
|
|
312
|
+
ChatInput: "chat-message-input",
|
|
313
|
+
ChatSendButton: "chat-send-button",
|
|
314
|
+
ChatClearButton: "chat-clear-button",
|
|
315
|
+
ChatToggleButton: "chat-bubble-toggle",
|
|
316
|
+
ChatMessageList: "chat-message-list",
|
|
317
|
+
ChatTypingIndicator: "chat-typing-indicator",
|
|
318
|
+
// Demo widget components - buttons
|
|
319
|
+
OpenMenuButton: "open-main-menu",
|
|
320
|
+
CloseMenuButton: "close-main-menu",
|
|
321
|
+
// Demo widget components - checkboxes
|
|
322
|
+
FeatureToggle: "feature-toggle",
|
|
323
|
+
NotificationsToggle: "notification-toggle",
|
|
324
|
+
// Demo widget components - radios
|
|
325
|
+
PriorityHighRadio: "priority-high",
|
|
326
|
+
PriorityMediumRadio: "priority-medium",
|
|
327
|
+
PriorityLowRadio: "priority-low",
|
|
328
|
+
// Demo widget components - selects
|
|
329
|
+
StatusSelect: "status-dropdown",
|
|
330
|
+
ThemeSelect: "theme-toggle",
|
|
331
|
+
// Demo widget components - form
|
|
332
|
+
FormNameInput: "form-name",
|
|
333
|
+
DemoFormSubmitButton: "form-submit",
|
|
334
|
+
// Generic widget components
|
|
335
|
+
WidgetButton: "widget-button",
|
|
336
|
+
WidgetInput: "widget-input",
|
|
337
|
+
WidgetSelect: "widget-select",
|
|
338
|
+
WidgetCheckbox: "widget-checkbox",
|
|
339
|
+
WidgetRadio: "widget-radio",
|
|
340
|
+
WidgetTextarea: "widget-textarea",
|
|
341
|
+
// Tool command components
|
|
342
|
+
ToolCommandsList: "tool-commands-list",
|
|
343
|
+
ToolExecuteButton: "tool-execute-button",
|
|
344
|
+
ToolApprovalButton: "tool-approval-button",
|
|
345
|
+
ToolMetadataDisplay: "tool-metadata-display",
|
|
346
|
+
// Navigation components
|
|
347
|
+
NavMainMenu: "nav-main-menu",
|
|
348
|
+
NavHomeLink: "nav-home-link",
|
|
349
|
+
NavToolsLink: "nav-tools-link",
|
|
350
|
+
NavSettingsLink: "nav-settings-link",
|
|
351
|
+
NavBackButton: "nav-back-button",
|
|
352
|
+
// Form components
|
|
353
|
+
FormSubmitButton: "form-submit-button",
|
|
354
|
+
FormCancelButton: "form-cancel-button",
|
|
355
|
+
FormResetButton: "form-reset-button",
|
|
356
|
+
FormTextInput: "form-text-input",
|
|
357
|
+
FormEmailInput: "form-email-input",
|
|
358
|
+
FormPasswordInput: "form-password-input",
|
|
359
|
+
// Modal components
|
|
360
|
+
ModalCloseButton: "modal-close-button",
|
|
361
|
+
ModalConfirmButton: "modal-confirm-button",
|
|
362
|
+
ModalCancelButton: "modal-cancel-button",
|
|
363
|
+
ModalOverlay: "modal-overlay",
|
|
364
|
+
// Status/Feedback components
|
|
365
|
+
StatusSuccessMessage: "status-success-message",
|
|
366
|
+
StatusErrorMessage: "status-error-message",
|
|
367
|
+
StatusWarningMessage: "status-warning-message",
|
|
368
|
+
StatusLoadingSpinner: "status-loading-spinner",
|
|
369
|
+
StatusProgressBar: "status-progress-bar"
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// src/components/ChatBubble.tsx
|
|
305
373
|
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
306
374
|
var ChatNames = {
|
|
307
|
-
bubble:
|
|
308
|
-
input:
|
|
309
|
-
sendButton:
|
|
310
|
-
clearButton:
|
|
311
|
-
|
|
375
|
+
bubble: Components.ChatToggleButton,
|
|
376
|
+
input: Components.ChatInput,
|
|
377
|
+
sendButton: Components.ChatSendButton,
|
|
378
|
+
clearButton: Components.ChatClearButton
|
|
379
|
+
};
|
|
380
|
+
var DOCK_POSITIONS = {
|
|
381
|
+
"bottom-right": {
|
|
382
|
+
container: "bottom-4 right-4 sm:bottom-6 sm:right-6",
|
|
383
|
+
panel: "bottom-0 right-0"
|
|
384
|
+
},
|
|
385
|
+
"bottom-left": {
|
|
386
|
+
container: "bottom-4 left-4 sm:bottom-6 sm:left-6",
|
|
387
|
+
panel: "bottom-0 left-0"
|
|
388
|
+
},
|
|
389
|
+
"top-right": {
|
|
390
|
+
container: "top-4 right-4 sm:top-6 sm:right-6",
|
|
391
|
+
panel: "top-0 right-0"
|
|
392
|
+
},
|
|
393
|
+
"top-left": {
|
|
394
|
+
container: "top-4 left-4 sm:top-6 sm:left-6",
|
|
395
|
+
panel: "top-0 left-0"
|
|
396
|
+
},
|
|
397
|
+
"left-center": {
|
|
398
|
+
container: "left-4 top-1/2 -translate-y-1/2",
|
|
399
|
+
panel: "left-0 top-0"
|
|
400
|
+
},
|
|
401
|
+
"right-center": {
|
|
402
|
+
container: "right-4 top-1/2 -translate-y-1/2",
|
|
403
|
+
panel: "right-0 top-0"
|
|
404
|
+
},
|
|
405
|
+
"bottom-center": {
|
|
406
|
+
container: "bottom-4 left-1/2 -translate-x-1/2",
|
|
407
|
+
panel: "bottom-0 left-1/2 -translate-x-1/2"
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
var INLINE_STYLES = {
|
|
411
|
+
// Input field
|
|
412
|
+
input: (isDark) => ({
|
|
413
|
+
color: isDark ? "#ffffff" : "#111827"
|
|
414
|
+
// Fallback for placeholder handled via ::placeholder CSS or separate element
|
|
415
|
+
}),
|
|
416
|
+
// Message bubbles
|
|
417
|
+
messageUser: () => ({
|
|
418
|
+
background: "linear-gradient(to bottom right, rgb(37, 99, 235), rgb(147, 51, 234))",
|
|
419
|
+
color: "#ffffff"
|
|
420
|
+
}),
|
|
421
|
+
messageAI: (isDark) => ({
|
|
422
|
+
backgroundColor: isDark ? "rgba(31, 41, 55, 1)" : "rgba(255, 255, 255, 1)",
|
|
423
|
+
color: isDark ? "#ffffff" : "#111827"
|
|
424
|
+
}),
|
|
425
|
+
messageSystem: (isDark) => ({
|
|
426
|
+
backgroundColor: isDark ? "rgba(31, 41, 55, 0.95)" : "rgba(255, 255, 255, 0.95)",
|
|
427
|
+
color: isDark ? "#e5e7eb" : "#374151"
|
|
428
|
+
}),
|
|
429
|
+
// Welcome message
|
|
430
|
+
welcomeTitle: (isDark) => ({
|
|
431
|
+
color: isDark ? "#ffffff" : "#111827",
|
|
432
|
+
fontWeight: "bold"
|
|
433
|
+
}),
|
|
434
|
+
welcomeContent: (isDark) => ({
|
|
435
|
+
color: isDark ? "#ffffff" : "#374151"
|
|
436
|
+
}),
|
|
437
|
+
commandText: (isDark) => ({
|
|
438
|
+
color: isDark ? "#93c5fd" : "#1d4ed8"
|
|
439
|
+
}),
|
|
440
|
+
commandDesc: (isDark) => ({
|
|
441
|
+
color: isDark ? "#ffffff" : "#6b7280"
|
|
442
|
+
}),
|
|
443
|
+
// Info popup
|
|
444
|
+
infoText: (isDark) => ({
|
|
445
|
+
color: isDark ? "#ffffff" : "#374151"
|
|
446
|
+
}),
|
|
447
|
+
// Minimized message
|
|
448
|
+
minimizedMessage: (isDark) => ({
|
|
449
|
+
color: isDark ? "#ffffff" : "#374151"
|
|
450
|
+
}),
|
|
451
|
+
minimizedPrompt: (isDark) => ({
|
|
452
|
+
color: isDark ? "#ffffff" : "#9ca3af"
|
|
453
|
+
})
|
|
454
|
+
};
|
|
455
|
+
var THEME_CLASSES = {
|
|
456
|
+
// Message bubbles
|
|
457
|
+
message: {
|
|
458
|
+
user: "bg-gradient-to-br from-blue-600 to-purple-600 text-white ml-auto",
|
|
459
|
+
ai: "bg-white dark:bg-gray-800 text-gray-900 dark:text-white border border-gray-200 dark:border-gray-600",
|
|
460
|
+
system: "bg-white/70 dark:bg-gray-800/70 text-gray-700 dark:text-gray-200 border border-gray-200/40 dark:border-gray-600/40",
|
|
461
|
+
timestamp: {
|
|
462
|
+
user: "border-white/20 text-white/80",
|
|
463
|
+
other: "border-gray-300/30 dark:border-gray-500/30 text-gray-600 dark:text-gray-300"
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
// Header action buttons
|
|
467
|
+
button: {
|
|
468
|
+
theme: "p-2 text-yellow-600 dark:text-yellow-400 hover:text-yellow-700 dark:hover:text-yellow-300 transition-colors rounded-lg hover:bg-white/30",
|
|
469
|
+
home: "p-2 text-green-600 dark:text-green-400 hover:text-green-700 dark:hover:text-green-300 transition-colors rounded-lg hover:bg-white/30",
|
|
470
|
+
dock: "p-2 text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 transition-colors rounded-lg hover:bg-white/30",
|
|
471
|
+
info: "p-2 text-cyan-600 dark:text-cyan-400 hover:text-cyan-700 dark:hover:text-cyan-300 transition-colors rounded-lg hover:bg-white/30",
|
|
472
|
+
more: "p-2 text-gray-600 dark:text-gray-300 hover:text-gray-800 dark:hover:text-gray-100 transition-colors rounded-lg hover:bg-white/30",
|
|
473
|
+
minimize: "p-2 text-purple-600 dark:text-purple-400 hover:text-purple-700 dark:hover:text-purple-300 transition-colors rounded-lg hover:bg-white/30",
|
|
474
|
+
clear: "p-2 text-red-600 dark:text-red-400 hover:text-red-700 dark:hover:text-red-300 transition-colors rounded-lg hover:bg-white/30",
|
|
475
|
+
close: "p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors rounded-lg hover:bg-white/30",
|
|
476
|
+
floatingClear: "p-1 text-gray-400 dark:text-gray-300 hover:text-gray-600 dark:hover:text-gray-200 transition-colors"
|
|
477
|
+
},
|
|
478
|
+
// Input field
|
|
479
|
+
input: {
|
|
480
|
+
field: "w-full pl-4 pr-12 py-3 text-sm text-gray-900 dark:text-white placeholder:text-gray-500 dark:placeholder:text-gray-300 rounded-3xl focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all shadow-sm",
|
|
481
|
+
sendButton: "absolute right-2 top-1/2 -translate-y-1/2 p-2 bg-blue-600 text-white rounded-full hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed transition-all hover:scale-110"
|
|
482
|
+
},
|
|
483
|
+
// Welcome message
|
|
484
|
+
welcome: {
|
|
485
|
+
container: "bg-gradient-to-br from-blue-500/20 to-purple-500/20 backdrop-blur-sm p-4 rounded-2xl border border-blue-200/30 dark:border-blue-500/30 shadow-lg",
|
|
486
|
+
title: "font-bold text-gray-900 dark:text-white mb-2 text-sm",
|
|
487
|
+
content: "text-sm text-gray-700 dark:text-white mb-3 leading-relaxed",
|
|
488
|
+
commandsContainer: "bg-white/60 dark:bg-gray-800/60 backdrop-blur-sm p-3 rounded-xl border border-gray-200/30 dark:border-gray-600/30 shadow-sm",
|
|
489
|
+
commandsHeader: "text-xs font-medium text-gray-900 dark:text-white mb-2",
|
|
490
|
+
commandButton: "w-full text-left px-3 py-2 rounded-xl hover:bg-white/70 dark:hover:bg-gray-700/70 transition-all group border border-transparent hover:border-blue-200/50 dark:hover:border-blue-400/50 hover:shadow-md",
|
|
491
|
+
commandText: "text-sm font-medium text-blue-700 dark:text-blue-200 group-hover:text-blue-900 dark:group-hover:text-blue-100",
|
|
492
|
+
commandDesc: "text-xs text-gray-500 dark:text-gray-100 mt-0.5"
|
|
493
|
+
},
|
|
494
|
+
// Text colors for various elements
|
|
495
|
+
text: {
|
|
496
|
+
title: "font-bold text-gray-900 dark:text-white text-base truncate",
|
|
497
|
+
floatingTitle: "font-medium text-sm text-gray-900 dark:text-white",
|
|
498
|
+
minimizedMessage: "text-sm text-gray-700 dark:text-white line-clamp-2",
|
|
499
|
+
minimizedUser: "text-xs text-blue-600 dark:text-blue-200 line-clamp-1",
|
|
500
|
+
minimizedPrompt: "text-xs text-gray-400 dark:text-gray-100 text-center",
|
|
501
|
+
floatingTimestamp: "text-xs text-gray-400 dark:text-gray-100 mt-1 px-1 opacity-0 group-hover:opacity-100 transition-opacity",
|
|
502
|
+
infoPopup: "px-4 py-3 bg-blue-500/10 backdrop-blur-sm border-b border-blue-200/30 text-sm text-gray-700 dark:text-white"
|
|
503
|
+
},
|
|
504
|
+
// Backgrounds and containers
|
|
505
|
+
bg: {
|
|
506
|
+
header: "flex items-center justify-between p-4 border-b border-white/20",
|
|
507
|
+
headerGradient: "bg-gradient-to-r from-blue-500/20 to-purple-500/20",
|
|
508
|
+
headerLight: "bg-gradient-to-r from-blue-50 to-purple-50",
|
|
509
|
+
inputForm: "p-4",
|
|
510
|
+
inputFormLight: "bg-gray-50 dark:bg-gray-900",
|
|
511
|
+
bubble: "w-14 h-14 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-xl hover:shadow-2xl transition-all duration-300 flex items-center justify-center relative hover:scale-110"
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
var DEFAULT_CONFIG = {
|
|
515
|
+
title: "Supernal Interface",
|
|
516
|
+
avatar: /* @__PURE__ */ jsx3("img", { src: "/logo.svg", alt: "Supernal", className: "w-6 h-6" }),
|
|
517
|
+
description: "I'm a TOOL system AI can use to control this site",
|
|
518
|
+
placeholder: "Try: toggle notifications",
|
|
519
|
+
sendButtonLabel: "Send",
|
|
520
|
+
glassMode: true,
|
|
521
|
+
welcome: {
|
|
522
|
+
enabled: true,
|
|
523
|
+
title: "Welcome - I'm NOT an AI",
|
|
524
|
+
content: "I'm a tool system that AI assistants (like Claude, GPT) can use to navigate and control this site. This enables agentic UX \u2014 instead of clicking around, you tell an AI what you want, and it uses me to do it.",
|
|
525
|
+
suggestedCommands: [
|
|
526
|
+
{ text: "open the docs", desc: "Navigate to documentation" },
|
|
527
|
+
{ text: "show me the story system", desc: "View story system guide" },
|
|
528
|
+
{ text: "go to examples", desc: "Browse code examples" }
|
|
529
|
+
]
|
|
530
|
+
},
|
|
531
|
+
theme: {
|
|
532
|
+
primary: "blue",
|
|
533
|
+
secondary: "purple",
|
|
534
|
+
background: "white"
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
var InputField = ({
|
|
538
|
+
compact = false,
|
|
539
|
+
inputValue,
|
|
540
|
+
onInputChange,
|
|
541
|
+
onSubmit,
|
|
542
|
+
placeholder,
|
|
543
|
+
glassClasses,
|
|
544
|
+
theme,
|
|
545
|
+
inputRef,
|
|
546
|
+
sendButtonLabel
|
|
547
|
+
}) => /* @__PURE__ */ jsx3("form", { onSubmit, className: compact ? "flex space-x-2" : THEME_CLASSES.bg.inputForm + " bg-transparent", children: /* @__PURE__ */ jsxs("div", { className: compact ? "flex space-x-2 flex-1" : "relative", children: [
|
|
548
|
+
/* @__PURE__ */ jsx3(
|
|
549
|
+
"input",
|
|
550
|
+
{
|
|
551
|
+
ref: compact ? void 0 : inputRef,
|
|
552
|
+
type: "text",
|
|
553
|
+
value: inputValue,
|
|
554
|
+
onChange: (e) => onInputChange(e.target.value),
|
|
555
|
+
placeholder,
|
|
556
|
+
className: compact ? `flex-1 px-3 py-2 text-xs border rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all ${glassClasses}` : `${THEME_CLASSES.input.field} ${glassClasses}`,
|
|
557
|
+
style: INLINE_STYLES.input(theme === "dark"),
|
|
558
|
+
"data-testid": Components.ChatInput
|
|
559
|
+
}
|
|
560
|
+
),
|
|
561
|
+
/* @__PURE__ */ jsx3(
|
|
562
|
+
"button",
|
|
563
|
+
{
|
|
564
|
+
type: "submit",
|
|
565
|
+
disabled: !inputValue.trim(),
|
|
566
|
+
className: compact ? "px-3 py-2 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl hover:from-blue-600 hover:to-blue-700 disabled:from-gray-300 disabled:to-gray-400 disabled:cursor-not-allowed transition-all text-xs font-medium shadow-md hover:shadow-lg" : THEME_CLASSES.input.sendButton,
|
|
567
|
+
"data-testid": Components.ChatSendButton,
|
|
568
|
+
title: sendButtonLabel,
|
|
569
|
+
children: compact ? "\u2192" : /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", strokeWidth: "2.5", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 5l7 7m0 0l-7 7m7-7H3" }) })
|
|
570
|
+
}
|
|
571
|
+
)
|
|
572
|
+
] }) });
|
|
573
|
+
var Avatar = ({ avatar, size = "normal" }) => {
|
|
574
|
+
if (!avatar) return null;
|
|
575
|
+
if (typeof avatar === "string") {
|
|
576
|
+
return size === "small" ? /* @__PURE__ */ jsx3("span", { className: "text-lg", children: avatar }) : /* @__PURE__ */ jsx3("div", { className: "w-10 h-10 bg-blue-600 rounded-lg flex items-center justify-center shadow-md", children: /* @__PURE__ */ jsx3("span", { className: "text-white text-sm font-bold", children: avatar }) });
|
|
577
|
+
}
|
|
578
|
+
return /* @__PURE__ */ jsx3(Fragment, { children: avatar });
|
|
312
579
|
};
|
|
313
|
-
var CHAT_EXPANDED_KEY = "supernal-chat-expanded";
|
|
314
580
|
var ChatBubble = ({
|
|
315
|
-
|
|
581
|
+
messages,
|
|
582
|
+
onSendMessage,
|
|
583
|
+
onClearChat,
|
|
316
584
|
position = "bottom-right",
|
|
317
|
-
|
|
585
|
+
variant = "full",
|
|
586
|
+
config: userConfig,
|
|
587
|
+
defaultExpanded = true,
|
|
588
|
+
storageKey = "chat-bubble-state"
|
|
318
589
|
}) => {
|
|
319
|
-
const {
|
|
320
|
-
const [isExpanded, setIsExpanded] = useState2(
|
|
590
|
+
const config = { ...DEFAULT_CONFIG, ...userConfig };
|
|
591
|
+
const [isExpanded, setIsExpanded] = useState2(defaultExpanded);
|
|
592
|
+
const [isMinimized, setIsMinimized] = useState2(false);
|
|
321
593
|
const [inputValue, setInputValue] = useState2("");
|
|
322
594
|
const [lastReadMessageCount, setLastReadMessageCount] = useState2(0);
|
|
323
|
-
const [showWelcome, setShowWelcome] = useState2(
|
|
324
|
-
|
|
595
|
+
const [showWelcome, setShowWelcome] = useState2(
|
|
596
|
+
config.welcome?.enabled && messages.length === 0
|
|
597
|
+
);
|
|
598
|
+
const [showInfo, setShowInfo] = useState2(false);
|
|
599
|
+
const [isDragging, setIsDragging] = useState2(false);
|
|
600
|
+
const [isDocked, setIsDocked] = useState2(true);
|
|
601
|
+
const [panelPosition, setPanelPosition] = useState2({ x: 0, y: 0 });
|
|
602
|
+
const [theme, setTheme] = useState2("light");
|
|
603
|
+
const [showMoreMenu, setShowMoreMenu] = useState2(false);
|
|
604
|
+
const [, setTimestampTick] = useState2(0);
|
|
325
605
|
const messagesEndRef = useRef2(null);
|
|
326
606
|
const inputRef = useRef2(null);
|
|
607
|
+
const panelRef = useRef2(null);
|
|
608
|
+
const dragRef = useRef2(null);
|
|
609
|
+
const formatRelativeTime = (timestamp) => {
|
|
610
|
+
const now = /* @__PURE__ */ new Date();
|
|
611
|
+
const messageTime = new Date(timestamp);
|
|
612
|
+
const diffMs = now.getTime() - messageTime.getTime();
|
|
613
|
+
const diffSeconds = Math.floor(diffMs / 1e3);
|
|
614
|
+
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
615
|
+
const diffHours = Math.floor(diffMinutes / 60);
|
|
616
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
617
|
+
if (diffSeconds < 60) return "just now";
|
|
618
|
+
if (diffMinutes < 60) return `${diffMinutes} ${diffMinutes === 1 ? "minute" : "minutes"} ago`;
|
|
619
|
+
if (diffHours < 24) return `${diffHours} ${diffHours === 1 ? "hour" : "hours"} ago`;
|
|
620
|
+
if (diffDays < 7) return `${diffDays} ${diffDays === 1 ? "day" : "days"} ago`;
|
|
621
|
+
return messageTime.toLocaleDateString();
|
|
622
|
+
};
|
|
327
623
|
useEffect2(() => {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
624
|
+
if (variant === "full") {
|
|
625
|
+
try {
|
|
626
|
+
const stored = localStorage.getItem(storageKey);
|
|
627
|
+
if (stored !== null) {
|
|
628
|
+
const state = JSON.parse(stored);
|
|
629
|
+
setIsExpanded(state.isExpanded ?? defaultExpanded);
|
|
630
|
+
setIsMinimized(state.isMinimized ?? false);
|
|
631
|
+
setIsDocked(state.isDocked ?? true);
|
|
632
|
+
setPanelPosition(state.panelPosition || { x: 0, y: 0 });
|
|
633
|
+
setTheme(state.theme || "light");
|
|
634
|
+
}
|
|
635
|
+
} catch {
|
|
636
|
+
}
|
|
331
637
|
}
|
|
332
|
-
}, [
|
|
638
|
+
}, [storageKey, variant, defaultExpanded]);
|
|
333
639
|
useEffect2(() => {
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
setIsExpanded(JSON.parse(stored));
|
|
338
|
-
}
|
|
339
|
-
} catch {
|
|
640
|
+
if (typeof window !== "undefined") {
|
|
641
|
+
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
|
642
|
+
setTheme(isDark ? "dark" : "light");
|
|
340
643
|
}
|
|
341
644
|
}, []);
|
|
645
|
+
useEffect2(() => {
|
|
646
|
+
const interval = setInterval(() => {
|
|
647
|
+
setTimestampTick((tick) => tick + 1);
|
|
648
|
+
}, 6e4);
|
|
649
|
+
return () => clearInterval(interval);
|
|
650
|
+
}, []);
|
|
651
|
+
useEffect2(() => {
|
|
652
|
+
if (variant === "full") {
|
|
653
|
+
try {
|
|
654
|
+
localStorage.setItem(
|
|
655
|
+
storageKey,
|
|
656
|
+
JSON.stringify({ isExpanded, isMinimized, isDocked, panelPosition, theme })
|
|
657
|
+
);
|
|
658
|
+
} catch (error) {
|
|
659
|
+
console.error("Failed to save chat state:", error);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}, [isExpanded, isMinimized, isDocked, panelPosition, theme, storageKey, variant]);
|
|
342
663
|
const { registerInput } = useChatInput();
|
|
343
664
|
useEffect2(() => {
|
|
344
665
|
registerInput((text, submit = false) => {
|
|
345
666
|
setInputValue(text);
|
|
346
|
-
if (!isExpanded) {
|
|
667
|
+
if (!isExpanded && variant === "full") {
|
|
347
668
|
setIsExpanded(true);
|
|
348
669
|
}
|
|
349
670
|
setTimeout(() => {
|
|
350
671
|
inputRef.current?.focus();
|
|
351
672
|
if (submit) {
|
|
352
|
-
|
|
673
|
+
onSendMessage(text);
|
|
353
674
|
setInputValue("");
|
|
354
675
|
}
|
|
355
676
|
}, 100);
|
|
356
677
|
});
|
|
357
|
-
}, [registerInput,
|
|
678
|
+
}, [registerInput, onSendMessage]);
|
|
358
679
|
const unreadCount = Math.max(0, messages.length - lastReadMessageCount);
|
|
359
|
-
const hasUnread = unreadCount > 0 && !isExpanded;
|
|
680
|
+
const hasUnread = unreadCount > 0 && !isExpanded && variant === "full";
|
|
360
681
|
useEffect2(() => {
|
|
361
|
-
if (isExpanded) {
|
|
682
|
+
if (isExpanded || variant === "floating") {
|
|
362
683
|
messagesEndRef.current?.scrollIntoView({ behavior: "auto" });
|
|
363
684
|
setLastReadMessageCount(messages.length);
|
|
364
685
|
if (messages.length > 0) {
|
|
365
686
|
setShowWelcome(false);
|
|
366
687
|
}
|
|
367
|
-
|
|
688
|
+
if (variant === "full") {
|
|
689
|
+
inputRef.current?.focus();
|
|
690
|
+
}
|
|
368
691
|
}
|
|
369
|
-
}, [messages, isExpanded]);
|
|
692
|
+
}, [messages, isExpanded, variant]);
|
|
370
693
|
useEffect2(() => {
|
|
371
|
-
if (isExpanded) {
|
|
694
|
+
if (isExpanded && variant === "full") {
|
|
372
695
|
inputRef.current?.focus();
|
|
373
696
|
}
|
|
374
|
-
}, [isExpanded]);
|
|
697
|
+
}, [isExpanded, variant]);
|
|
375
698
|
useEffect2(() => {
|
|
376
699
|
const handleKeyDown = (e) => {
|
|
700
|
+
if (variant !== "full") return;
|
|
701
|
+
if (e.key === "/" && !isExpanded) {
|
|
702
|
+
const target = e.target;
|
|
703
|
+
if (target.tagName !== "INPUT" && target.tagName !== "TEXTAREA") {
|
|
704
|
+
e.preventDefault();
|
|
705
|
+
setIsExpanded(true);
|
|
706
|
+
setTimeout(() => inputRef.current?.focus(), 0);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (e.key === "Escape") {
|
|
710
|
+
if (showMoreMenu) {
|
|
711
|
+
setShowMoreMenu(false);
|
|
712
|
+
} else if (showInfo) {
|
|
713
|
+
setShowInfo(false);
|
|
714
|
+
} else if (isExpanded) {
|
|
715
|
+
setIsExpanded(false);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
377
718
|
if ((e.metaKey || e.ctrlKey) && e.key === "/") {
|
|
378
719
|
e.preventDefault();
|
|
379
720
|
if (!isExpanded) {
|
|
@@ -384,151 +725,456 @@ var ChatBubble = ({
|
|
|
384
725
|
};
|
|
385
726
|
window.addEventListener("keydown", handleKeyDown);
|
|
386
727
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
387
|
-
}, [isExpanded]);
|
|
728
|
+
}, [isExpanded, showInfo, showMoreMenu, variant]);
|
|
729
|
+
useEffect2(() => {
|
|
730
|
+
if (!showMoreMenu) return;
|
|
731
|
+
const handleClickOutside = (e) => {
|
|
732
|
+
const target = e.target;
|
|
733
|
+
if (!target.closest("[data-more-menu]")) {
|
|
734
|
+
setShowMoreMenu(false);
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
738
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
739
|
+
}, [showMoreMenu]);
|
|
740
|
+
const handlePanelMouseDown = (e) => {
|
|
741
|
+
if (variant !== "full" || !isExpanded) return;
|
|
742
|
+
const target = e.target;
|
|
743
|
+
if (!target.closest("[data-drag-handle]")) return;
|
|
744
|
+
if (target.closest("button") || target.closest("svg") || target.closest('[role="button"]')) {
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
e.preventDefault();
|
|
748
|
+
setIsDragging(true);
|
|
749
|
+
const rect = panelRef.current?.getBoundingClientRect();
|
|
750
|
+
if (!rect) return;
|
|
751
|
+
const currentCenterX = rect.left + rect.width / 2;
|
|
752
|
+
const currentCenterY = rect.top + rect.height / 2;
|
|
753
|
+
const viewportCenterX = window.innerWidth / 2;
|
|
754
|
+
const viewportCenterY = window.innerHeight / 2;
|
|
755
|
+
const targetX = currentCenterX - viewportCenterX;
|
|
756
|
+
const targetY = currentCenterY - viewportCenterY;
|
|
757
|
+
setIsDocked(false);
|
|
758
|
+
setPanelPosition({ x: targetX, y: targetY });
|
|
759
|
+
dragRef.current = {
|
|
760
|
+
startX: e.clientX,
|
|
761
|
+
startY: e.clientY,
|
|
762
|
+
initialX: targetX,
|
|
763
|
+
initialY: targetY
|
|
764
|
+
};
|
|
765
|
+
};
|
|
766
|
+
useEffect2(() => {
|
|
767
|
+
if (!isDragging || !dragRef.current) return;
|
|
768
|
+
const handleMouseMove = (e) => {
|
|
769
|
+
if (!dragRef.current) return;
|
|
770
|
+
const deltaX = e.clientX - dragRef.current.startX;
|
|
771
|
+
const deltaY = e.clientY - dragRef.current.startY;
|
|
772
|
+
setPanelPosition({
|
|
773
|
+
x: dragRef.current.initialX + deltaX,
|
|
774
|
+
y: dragRef.current.initialY + deltaY
|
|
775
|
+
});
|
|
776
|
+
};
|
|
777
|
+
const handleMouseUp = () => {
|
|
778
|
+
setIsDragging(false);
|
|
779
|
+
dragRef.current = null;
|
|
780
|
+
if (panelRef.current) {
|
|
781
|
+
const rect = panelRef.current.getBoundingClientRect();
|
|
782
|
+
const threshold = 50;
|
|
783
|
+
const windowWidth = window.innerWidth;
|
|
784
|
+
const windowHeight = window.innerHeight;
|
|
785
|
+
if (rect.right > windowWidth - threshold || rect.left < threshold || rect.top < threshold || rect.bottom > windowHeight - threshold) {
|
|
786
|
+
setIsDocked(true);
|
|
787
|
+
setPanelPosition({ x: 0, y: 0 });
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
792
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
793
|
+
return () => {
|
|
794
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
795
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
796
|
+
};
|
|
797
|
+
}, [isDragging]);
|
|
388
798
|
const handleSend = (e) => {
|
|
389
799
|
e.preventDefault();
|
|
390
800
|
if (!inputValue.trim()) return;
|
|
391
|
-
|
|
801
|
+
onSendMessage(inputValue.trim());
|
|
392
802
|
setInputValue("");
|
|
393
|
-
|
|
803
|
+
if (variant === "full") {
|
|
804
|
+
setTimeout(() => inputRef.current?.focus(), 0);
|
|
805
|
+
}
|
|
394
806
|
};
|
|
395
807
|
const handleToggle = () => {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
808
|
+
setIsExpanded(!isExpanded);
|
|
809
|
+
};
|
|
810
|
+
const handleDock = () => {
|
|
811
|
+
setIsDocked(true);
|
|
812
|
+
setPanelPosition({ x: 0, y: 0 });
|
|
813
|
+
};
|
|
814
|
+
const handleHome = () => {
|
|
815
|
+
setIsDocked(true);
|
|
816
|
+
setPanelPosition({ x: 0, y: 0 });
|
|
817
|
+
setIsMinimized(false);
|
|
818
|
+
};
|
|
819
|
+
const handleClearChat = () => {
|
|
820
|
+
if (onClearChat) {
|
|
821
|
+
onClearChat();
|
|
822
|
+
setShowWelcome(true);
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
const handleToggleTheme = () => {
|
|
826
|
+
const newTheme = theme === "light" ? "dark" : "light";
|
|
827
|
+
setTheme(newTheme);
|
|
828
|
+
if (typeof window !== "undefined") {
|
|
829
|
+
document.documentElement.setAttribute("data-theme", newTheme);
|
|
401
830
|
}
|
|
402
831
|
};
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
832
|
+
const dockClasses = DOCK_POSITIONS[position];
|
|
833
|
+
const primaryColor = config.theme?.primary || "blue";
|
|
834
|
+
const glassMode = config.glassMode ?? true;
|
|
835
|
+
const maxHeightVh = 80;
|
|
836
|
+
const dynamicHeight = `min(${maxHeightVh}vh, 700px)`;
|
|
837
|
+
const panelWidth = "min(650px, calc(100vw - 2rem))";
|
|
838
|
+
const glassClasses = glassMode ? "backdrop-blur-xl bg-white/70 dark:bg-gray-900/70 border border-white/20 dark:border-white/10" : "bg-white dark:bg-gray-900 border-gray-200";
|
|
839
|
+
const glassGradient = glassMode ? "bg-gradient-to-br from-white/90 via-white/70 to-white/50 dark:from-gray-900/80 dark:via-gray-900/70 dark:to-gray-900/60" : "bg-white dark:bg-gray-900";
|
|
840
|
+
if (variant === "floating") {
|
|
841
|
+
const recentMessage = messages[messages.length - 1];
|
|
842
|
+
return /* @__PURE__ */ jsx3(
|
|
843
|
+
"div",
|
|
844
|
+
{
|
|
845
|
+
className: `fixed z-50 ${isDragging ? "cursor-grabbing" : "cursor-grab"}`,
|
|
846
|
+
style: {
|
|
847
|
+
transform: `translate(${panelPosition.x}px, ${panelPosition.y}px)`,
|
|
848
|
+
...!isDragging && { transition: "transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)" }
|
|
849
|
+
},
|
|
850
|
+
onMouseDown: handlePanelMouseDown,
|
|
851
|
+
children: /* @__PURE__ */ jsxs("div", { className: `${glassClasses} rounded-2xl shadow-2xl border p-3 max-w-xs`, children: [
|
|
852
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
853
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
854
|
+
/* @__PURE__ */ jsx3(Avatar, { avatar: config.avatar, size: "small" }),
|
|
855
|
+
config.title && /* @__PURE__ */ jsx3("span", { className: THEME_CLASSES.text.floatingTitle, children: config.title })
|
|
856
|
+
] }),
|
|
857
|
+
onClearChat && /* @__PURE__ */ jsx3(
|
|
858
|
+
"button",
|
|
859
|
+
{
|
|
860
|
+
onClick: onClearChat,
|
|
861
|
+
className: THEME_CLASSES.button.floatingClear,
|
|
862
|
+
title: "Clear chat",
|
|
863
|
+
"data-testid": ChatNames.clearButton,
|
|
864
|
+
children: /* @__PURE__ */ jsx3("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
865
|
+
}
|
|
866
|
+
)
|
|
867
|
+
] }),
|
|
868
|
+
recentMessage && /* @__PURE__ */ jsxs("div", { className: `mb-2 group flex items-center gap-2 ${recentMessage.type === "user" ? "flex-row-reverse" : "flex-row"}`, children: [
|
|
869
|
+
/* @__PURE__ */ jsx3(
|
|
870
|
+
"div",
|
|
871
|
+
{
|
|
872
|
+
className: `text-xs px-3 py-2 rounded-xl transition-all ${recentMessage.type === "user" ? "bg-gradient-to-br from-blue-500 to-blue-600 text-white shadow-lg" : recentMessage.type === "ai" ? "bg-gradient-to-br from-gray-100 to-gray-200 dark:from-gray-700 dark:to-gray-800 text-gray-900 dark:text-white shadow-md" : "bg-gradient-to-br from-yellow-100 to-yellow-200 text-yellow-900 shadow-md"}`,
|
|
873
|
+
children: recentMessage.text.length > 60 ? `${recentMessage.text.slice(0, 60)}...` : recentMessage.text
|
|
874
|
+
}
|
|
875
|
+
),
|
|
876
|
+
/* @__PURE__ */ jsx3(
|
|
877
|
+
"div",
|
|
878
|
+
{
|
|
879
|
+
className: `text-xs opacity-0 group-hover:opacity-70 transition-opacity whitespace-nowrap flex-shrink-0 ${recentMessage.type === "user" ? "text-gray-400 dark:text-gray-500 text-left" : "text-gray-600 dark:text-gray-400 text-right"}`,
|
|
880
|
+
title: typeof window !== "undefined" ? new Date(recentMessage.timestamp).toLocaleString() : "",
|
|
881
|
+
children: typeof window !== "undefined" ? formatRelativeTime(recentMessage.timestamp) : ""
|
|
882
|
+
}
|
|
883
|
+
)
|
|
410
884
|
] }),
|
|
411
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
412
|
-
/* @__PURE__ */ jsx3("h3", { className: "font-bold text-gray-900 text-sm sm:text-base", children: "Supernal Intelligence Interface" }),
|
|
413
|
-
/* @__PURE__ */ jsx3("p", { className: "text-xs text-gray-600 hidden sm:block", children: "I'm a TOOL system AI can use to control this site" })
|
|
414
|
-
] })
|
|
415
|
-
] }),
|
|
416
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1", children: [
|
|
417
|
-
/* @__PURE__ */ jsx3(
|
|
418
|
-
"button",
|
|
419
|
-
{
|
|
420
|
-
onClick: clearMessages,
|
|
421
|
-
className: "p-1 text-gray-400 hover:text-gray-600 transition-colors",
|
|
422
|
-
title: "Clear chat",
|
|
423
|
-
"data-testid": ChatNames.clearButton,
|
|
424
|
-
children: /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) })
|
|
425
|
-
}
|
|
426
|
-
),
|
|
427
885
|
/* @__PURE__ */ jsx3(
|
|
428
|
-
|
|
886
|
+
InputField,
|
|
429
887
|
{
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
888
|
+
compact: true,
|
|
889
|
+
inputValue,
|
|
890
|
+
onInputChange: setInputValue,
|
|
891
|
+
onSubmit: handleSend,
|
|
892
|
+
placeholder: config.placeholder,
|
|
893
|
+
glassClasses,
|
|
894
|
+
theme,
|
|
895
|
+
sendButtonLabel: config.sendButtonLabel
|
|
434
896
|
}
|
|
435
897
|
)
|
|
436
898
|
] })
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
{ text: "go to examples", desc: "Browse code examples" }
|
|
454
|
-
].map((cmd) => /* @__PURE__ */ jsxs(
|
|
899
|
+
}
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
return /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: `fixed ${dockClasses.container} z-50`, children: [
|
|
903
|
+
isExpanded && isMinimized && /* @__PURE__ */ jsxs(
|
|
904
|
+
"div",
|
|
905
|
+
{
|
|
906
|
+
className: `absolute ${dockClasses.panel} ${glassClasses} rounded-3xl shadow-2xl border p-4 transition-all duration-300`,
|
|
907
|
+
style: { width: panelWidth, maxWidth: "400px" },
|
|
908
|
+
children: [
|
|
909
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
|
|
910
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
911
|
+
/* @__PURE__ */ jsx3(Avatar, { avatar: config.avatar, size: "small" }),
|
|
912
|
+
config.title && /* @__PURE__ */ jsx3("span", { className: THEME_CLASSES.text.floatingTitle, children: config.title })
|
|
913
|
+
] }),
|
|
914
|
+
/* @__PURE__ */ jsx3(
|
|
455
915
|
"button",
|
|
456
916
|
{
|
|
457
|
-
onClick: () =>
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
children: [
|
|
464
|
-
/* @__PURE__ */ jsxs("div", { className: "text-xs font-mono text-blue-700 group-hover:text-blue-900", children: [
|
|
465
|
-
'"',
|
|
466
|
-
cmd.text,
|
|
467
|
-
'"'
|
|
468
|
-
] }),
|
|
469
|
-
/* @__PURE__ */ jsx3("div", { className: "text-xs text-gray-500 hidden sm:block", children: cmd.desc })
|
|
470
|
-
]
|
|
471
|
-
},
|
|
472
|
-
cmd.text
|
|
473
|
-
)) })
|
|
917
|
+
onClick: () => setIsMinimized(false),
|
|
918
|
+
className: "p-1 text-gray-400 hover:text-gray-600 dark:text-gray-300 dark:hover:text-gray-200 transition-colors",
|
|
919
|
+
title: "Expand chat",
|
|
920
|
+
children: /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 15l7-7 7 7" }) })
|
|
921
|
+
}
|
|
922
|
+
)
|
|
474
923
|
] }),
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
924
|
+
(() => {
|
|
925
|
+
const lastAiMessage = [...messages].reverse().find((m) => m.type === "ai");
|
|
926
|
+
return lastAiMessage ? /* @__PURE__ */ jsx3("div", { className: "mb-3", children: /* @__PURE__ */ jsx3("div", { className: `text-xs px-3 py-2 rounded-xl ${THEME_CLASSES.message.ai}`, style: INLINE_STYLES.messageAI(theme === "dark"), children: lastAiMessage.text.length > 100 ? `${lastAiMessage.text.slice(0, 100)}...` : lastAiMessage.text }) }) : /* @__PURE__ */ jsx3("div", { className: "mb-3", children: /* @__PURE__ */ jsx3("div", { className: THEME_CLASSES.text.minimizedMessage, style: INLINE_STYLES.minimizedMessage(theme === "dark"), children: "No AI responses yet" }) });
|
|
927
|
+
})(),
|
|
478
928
|
/* @__PURE__ */ jsx3(
|
|
479
|
-
|
|
929
|
+
InputField,
|
|
480
930
|
{
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
931
|
+
compact: true,
|
|
932
|
+
inputValue,
|
|
933
|
+
onInputChange: setInputValue,
|
|
934
|
+
onSubmit: handleSend,
|
|
935
|
+
placeholder: config.placeholder,
|
|
936
|
+
glassClasses,
|
|
937
|
+
theme,
|
|
938
|
+
sendButtonLabel: config.sendButtonLabel
|
|
484
939
|
}
|
|
485
|
-
)
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
940
|
+
)
|
|
941
|
+
]
|
|
942
|
+
}
|
|
943
|
+
),
|
|
944
|
+
isExpanded && !isMinimized && /* @__PURE__ */ jsxs(
|
|
945
|
+
"div",
|
|
946
|
+
{
|
|
947
|
+
ref: panelRef,
|
|
948
|
+
className: `${isDocked ? "absolute " + dockClasses.panel : "fixed"} ${glassGradient} rounded-3xl shadow-2xl border border-white/20 dark:border-white/10 backdrop-blur-xl flex flex-col overflow-hidden transition-all duration-300`,
|
|
949
|
+
style: {
|
|
950
|
+
width: panelWidth,
|
|
951
|
+
height: dynamicHeight,
|
|
952
|
+
...!isDocked && {
|
|
953
|
+
left: "50%",
|
|
954
|
+
top: "50%",
|
|
955
|
+
transform: `translate(calc(-50% + ${panelPosition.x}px), calc(-50% + ${panelPosition.y}px))`
|
|
956
|
+
},
|
|
957
|
+
...isDragging && { cursor: "grabbing" }
|
|
958
|
+
},
|
|
959
|
+
children: [
|
|
960
|
+
/* @__PURE__ */ jsxs(
|
|
961
|
+
"div",
|
|
494
962
|
{
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
963
|
+
"data-drag-handle": true,
|
|
964
|
+
className: `${THEME_CLASSES.bg.header} ${glassMode ? THEME_CLASSES.bg.headerGradient : THEME_CLASSES.bg.headerLight} cursor-move`,
|
|
965
|
+
onMouseDown: handlePanelMouseDown,
|
|
966
|
+
children: [
|
|
967
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
|
|
968
|
+
config.avatar && /* @__PURE__ */ jsxs("div", { className: "relative flex-shrink-0", children: [
|
|
969
|
+
/* @__PURE__ */ jsx3(Avatar, { avatar: config.avatar }),
|
|
970
|
+
/* @__PURE__ */ jsx3("div", { className: "absolute -bottom-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white" })
|
|
971
|
+
] }),
|
|
972
|
+
config.title && /* @__PURE__ */ jsx3("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx3("h3", { className: THEME_CLASSES.text.title, children: config.title }) })
|
|
973
|
+
] }),
|
|
974
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1 flex-shrink-0 relative", "data-more-menu": true, children: [
|
|
975
|
+
/* @__PURE__ */ jsx3(
|
|
976
|
+
"button",
|
|
977
|
+
{
|
|
978
|
+
onClick: () => setShowMoreMenu(!showMoreMenu),
|
|
979
|
+
className: THEME_CLASSES.button.more,
|
|
980
|
+
title: "More options",
|
|
981
|
+
children: /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z" }) })
|
|
982
|
+
}
|
|
983
|
+
),
|
|
984
|
+
showMoreMenu && /* @__PURE__ */ jsxs("div", { className: "absolute right-0 top-10 bg-white dark:bg-gray-800 rounded-lg shadow-xl border border-gray-200 dark:border-gray-600 p-2 z-[100] min-w-[160px]", "data-more-menu": true, children: [
|
|
985
|
+
/* @__PURE__ */ jsxs(
|
|
986
|
+
"button",
|
|
987
|
+
{
|
|
988
|
+
onClick: () => {
|
|
989
|
+
handleToggleTheme();
|
|
990
|
+
setShowMoreMenu(false);
|
|
991
|
+
},
|
|
992
|
+
className: "w-full flex items-center space-x-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",
|
|
993
|
+
children: [
|
|
994
|
+
theme === "light" ? /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" }) }) : /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) }),
|
|
995
|
+
/* @__PURE__ */ jsx3("span", { children: theme === "light" ? "Dark mode" : "Light mode" })
|
|
996
|
+
]
|
|
997
|
+
}
|
|
998
|
+
),
|
|
999
|
+
/* @__PURE__ */ jsxs(
|
|
1000
|
+
"button",
|
|
1001
|
+
{
|
|
1002
|
+
onClick: () => {
|
|
1003
|
+
handleHome();
|
|
1004
|
+
setShowMoreMenu(false);
|
|
1005
|
+
},
|
|
1006
|
+
className: "w-full flex items-center space-x-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",
|
|
1007
|
+
children: [
|
|
1008
|
+
/* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }),
|
|
1009
|
+
/* @__PURE__ */ jsx3("span", { children: "Reset position" })
|
|
1010
|
+
]
|
|
1011
|
+
}
|
|
1012
|
+
),
|
|
1013
|
+
/* @__PURE__ */ jsxs(
|
|
1014
|
+
"button",
|
|
1015
|
+
{
|
|
1016
|
+
onClick: () => {
|
|
1017
|
+
setShowInfo(!showInfo);
|
|
1018
|
+
setShowMoreMenu(false);
|
|
1019
|
+
},
|
|
1020
|
+
className: "w-full flex items-center space-x-2 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",
|
|
1021
|
+
children: [
|
|
1022
|
+
/* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
1023
|
+
/* @__PURE__ */ jsx3("span", { children: "How to use" })
|
|
1024
|
+
]
|
|
1025
|
+
}
|
|
1026
|
+
),
|
|
1027
|
+
onClearChat && /* @__PURE__ */ jsxs(
|
|
1028
|
+
"button",
|
|
1029
|
+
{
|
|
1030
|
+
onClick: () => {
|
|
1031
|
+
handleClearChat();
|
|
1032
|
+
setShowMoreMenu(false);
|
|
1033
|
+
},
|
|
1034
|
+
className: "w-full flex items-center space-x-2 px-3 py-2 text-sm text-red-600 dark:text-red-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",
|
|
1035
|
+
children: [
|
|
1036
|
+
/* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) }),
|
|
1037
|
+
/* @__PURE__ */ jsx3("span", { children: "Clear chat" })
|
|
1038
|
+
]
|
|
1039
|
+
}
|
|
1040
|
+
)
|
|
1041
|
+
] }),
|
|
1042
|
+
/* @__PURE__ */ jsx3(
|
|
1043
|
+
"button",
|
|
1044
|
+
{
|
|
1045
|
+
onClick: () => setIsMinimized(true),
|
|
1046
|
+
className: THEME_CLASSES.button.minimize,
|
|
1047
|
+
title: "Minimize",
|
|
1048
|
+
children: /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20 12H4" }) })
|
|
1049
|
+
}
|
|
1050
|
+
),
|
|
1051
|
+
/* @__PURE__ */ jsx3(
|
|
1052
|
+
"button",
|
|
1053
|
+
{
|
|
1054
|
+
onClick: handleToggle,
|
|
1055
|
+
className: THEME_CLASSES.button.close,
|
|
1056
|
+
title: "Close",
|
|
1057
|
+
children: /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
1058
|
+
}
|
|
1059
|
+
)
|
|
1060
|
+
] })
|
|
1061
|
+
]
|
|
502
1062
|
}
|
|
503
1063
|
),
|
|
1064
|
+
showInfo && /* @__PURE__ */ jsxs("div", { className: THEME_CLASSES.text.infoPopup, style: INLINE_STYLES.infoText(theme === "dark"), children: [
|
|
1065
|
+
/* @__PURE__ */ jsx3("div", { className: "font-bold mb-2", children: "How to Use" }),
|
|
1066
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2 text-xs", style: INLINE_STYLES.infoText(theme === "dark"), children: [
|
|
1067
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1068
|
+
"\u2022 ",
|
|
1069
|
+
/* @__PURE__ */ jsx3("strong", { children: "Theme:" }),
|
|
1070
|
+
" Toggle between light and dark modes"
|
|
1071
|
+
] }),
|
|
1072
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1073
|
+
"\u2022 ",
|
|
1074
|
+
/* @__PURE__ */ jsx3("strong", { children: "Home:" }),
|
|
1075
|
+
" Reset chat position to default"
|
|
1076
|
+
] }),
|
|
1077
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1078
|
+
"\u2022 ",
|
|
1079
|
+
/* @__PURE__ */ jsx3("strong", { children: "Minimize:" }),
|
|
1080
|
+
" Compact view with last message"
|
|
1081
|
+
] }),
|
|
1082
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1083
|
+
"\u2022 ",
|
|
1084
|
+
/* @__PURE__ */ jsx3("strong", { children: "Clear:" }),
|
|
1085
|
+
" Delete all messages and start fresh"
|
|
1086
|
+
] }),
|
|
1087
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1088
|
+
"\u2022 ",
|
|
1089
|
+
/* @__PURE__ */ jsx3("strong", { children: "Drag:" }),
|
|
1090
|
+
" Click and drag header to reposition"
|
|
1091
|
+
] }),
|
|
1092
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1093
|
+
"\u2022 ",
|
|
1094
|
+
/* @__PURE__ */ jsx3("strong", { children: "Keyboard:" }),
|
|
1095
|
+
' Press "/" to open, Esc to close'
|
|
1096
|
+
] })
|
|
1097
|
+
] }),
|
|
1098
|
+
config.description && /* @__PURE__ */ jsx3("div", { className: "mt-3 pt-3 border-t border-gray-300/30 dark:border-gray-600/30", children: config.description })
|
|
1099
|
+
] }),
|
|
1100
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 space-y-2", children: [
|
|
1101
|
+
showWelcome && messages.length === 0 && config.welcome?.enabled && /* @__PURE__ */ jsxs("div", { className: THEME_CLASSES.welcome.container, children: [
|
|
1102
|
+
config.welcome.title && /* @__PURE__ */ jsx3("h4", { className: THEME_CLASSES.welcome.title, style: INLINE_STYLES.welcomeTitle(theme === "dark"), children: config.welcome.title }),
|
|
1103
|
+
config.welcome.content && /* @__PURE__ */ jsx3("p", { className: THEME_CLASSES.welcome.content, style: INLINE_STYLES.welcomeContent(theme === "dark"), children: config.welcome.content }),
|
|
1104
|
+
config.welcome.suggestedCommands && config.welcome.suggestedCommands.length > 0 && /* @__PURE__ */ jsxs("div", { className: THEME_CLASSES.welcome.commandsContainer, children: [
|
|
1105
|
+
/* @__PURE__ */ jsx3("p", { className: THEME_CLASSES.welcome.commandsHeader, children: "Try these commands:" }),
|
|
1106
|
+
/* @__PURE__ */ jsx3("div", { className: "space-y-1", children: config.welcome.suggestedCommands.map((cmd, idx) => /* @__PURE__ */ jsxs(
|
|
1107
|
+
"button",
|
|
1108
|
+
{
|
|
1109
|
+
onClick: () => {
|
|
1110
|
+
setInputValue(cmd.text);
|
|
1111
|
+
setShowWelcome(false);
|
|
1112
|
+
setTimeout(() => inputRef.current?.focus(), 0);
|
|
1113
|
+
},
|
|
1114
|
+
className: THEME_CLASSES.welcome.commandButton,
|
|
1115
|
+
children: [
|
|
1116
|
+
/* @__PURE__ */ jsxs("div", { className: THEME_CLASSES.welcome.commandText, style: INLINE_STYLES.commandText(theme === "dark"), children: [
|
|
1117
|
+
'"',
|
|
1118
|
+
cmd.text,
|
|
1119
|
+
'"'
|
|
1120
|
+
] }),
|
|
1121
|
+
cmd.desc && /* @__PURE__ */ jsx3("div", { className: THEME_CLASSES.welcome.commandDesc, style: INLINE_STYLES.commandDesc(theme === "dark"), children: cmd.desc })
|
|
1122
|
+
]
|
|
1123
|
+
},
|
|
1124
|
+
idx
|
|
1125
|
+
)) })
|
|
1126
|
+
] })
|
|
1127
|
+
] }),
|
|
1128
|
+
messages.map((message) => /* @__PURE__ */ jsxs("div", { className: `group flex items-center gap-2 mb-2 ${message.type === "user" ? "flex-row-reverse" : "flex-row"}`, children: [
|
|
1129
|
+
/* @__PURE__ */ jsx3(
|
|
1130
|
+
"div",
|
|
1131
|
+
{
|
|
1132
|
+
className: `inline-block px-4 py-2.5 rounded-2xl max-w-[80%] text-sm shadow-sm transition-all ${message.type === "user" ? THEME_CLASSES.message.user : message.type === "ai" ? THEME_CLASSES.message.ai : THEME_CLASSES.message.system}`,
|
|
1133
|
+
style: message.type === "user" ? INLINE_STYLES.messageUser() : message.type === "ai" ? INLINE_STYLES.messageAI(theme === "dark") : INLINE_STYLES.messageSystem(theme === "dark"),
|
|
1134
|
+
"data-testid": `chat-message-${message.type}`,
|
|
1135
|
+
children: /* @__PURE__ */ jsx3("div", { className: "break-words leading-relaxed", children: message.text })
|
|
1136
|
+
}
|
|
1137
|
+
),
|
|
1138
|
+
/* @__PURE__ */ jsx3(
|
|
1139
|
+
"div",
|
|
1140
|
+
{
|
|
1141
|
+
className: `text-xs opacity-0 group-hover:opacity-70 transition-opacity whitespace-nowrap flex-shrink-0 ${message.type === "user" ? "text-gray-400 dark:text-gray-500 text-left" : "text-gray-600 dark:text-gray-400 text-right"}`,
|
|
1142
|
+
title: typeof window !== "undefined" ? new Date(message.timestamp).toLocaleString() : "",
|
|
1143
|
+
children: typeof window !== "undefined" ? formatRelativeTime(message.timestamp) : ""
|
|
1144
|
+
}
|
|
1145
|
+
)
|
|
1146
|
+
] }, message.id)),
|
|
1147
|
+
/* @__PURE__ */ jsx3("div", { ref: messagesEndRef })
|
|
1148
|
+
] }),
|
|
504
1149
|
/* @__PURE__ */ jsx3(
|
|
505
|
-
|
|
1150
|
+
InputField,
|
|
506
1151
|
{
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
1152
|
+
inputValue,
|
|
1153
|
+
onInputChange: setInputValue,
|
|
1154
|
+
onSubmit: handleSend,
|
|
1155
|
+
placeholder: config.placeholder,
|
|
1156
|
+
glassClasses,
|
|
1157
|
+
theme,
|
|
1158
|
+
inputRef,
|
|
1159
|
+
sendButtonLabel: config.sendButtonLabel
|
|
512
1160
|
}
|
|
513
1161
|
)
|
|
514
|
-
]
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
] })
|
|
519
|
-
] })
|
|
520
|
-
] }),
|
|
521
|
-
/* @__PURE__ */ jsx3(
|
|
1162
|
+
]
|
|
1163
|
+
}
|
|
1164
|
+
),
|
|
1165
|
+
!isExpanded && /* @__PURE__ */ jsxs(
|
|
522
1166
|
"button",
|
|
523
1167
|
{
|
|
524
1168
|
onClick: handleToggle,
|
|
525
|
-
className:
|
|
1169
|
+
className: THEME_CLASSES.bg.bubble,
|
|
526
1170
|
"data-testid": ChatNames.bubble,
|
|
527
|
-
title:
|
|
528
|
-
children:
|
|
1171
|
+
title: "Open chat",
|
|
1172
|
+
children: [
|
|
1173
|
+
/* @__PURE__ */ jsx3("img", { src: "/logo.svg", alt: "Supernal", className: "w-8 h-8" }),
|
|
1174
|
+
hasUnread && /* @__PURE__ */ jsx3("div", { className: "absolute -top-1 -right-1 w-5 h-5 bg-red-500 rounded-full flex items-center justify-center animate-pulse shadow-lg", "data-testid": "unread-indicator", children: /* @__PURE__ */ jsx3("span", { className: "text-xs text-white font-bold", children: unreadCount > 9 ? "9+" : unreadCount }) })
|
|
1175
|
+
]
|
|
529
1176
|
}
|
|
530
|
-
)
|
|
531
|
-
hasUnread && /* @__PURE__ */ jsx3("div", { className: "absolute -top-1 -right-1 w-4 h-4 sm:w-5 sm:h-5 bg-red-500 rounded-full flex items-center justify-center animate-pulse", "data-testid": "unread-indicator", children: /* @__PURE__ */ jsx3("span", { className: "text-xs text-white font-bold", children: unreadCount > 9 ? "9+" : unreadCount }) })
|
|
1177
|
+
)
|
|
532
1178
|
] }) });
|
|
533
1179
|
};
|
|
534
1180
|
|
|
@@ -724,7 +1370,26 @@ function inferContextFromPath(path, customRoutes) {
|
|
|
724
1370
|
}
|
|
725
1371
|
|
|
726
1372
|
// src/components/SupernalProvider.tsx
|
|
1373
|
+
import { ExposureCollector, ToolRegistry } from "@supernal/interface/browser";
|
|
727
1374
|
import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1375
|
+
function ChatBubbleConnector({
|
|
1376
|
+
theme,
|
|
1377
|
+
position,
|
|
1378
|
+
welcomeMessage
|
|
1379
|
+
}) {
|
|
1380
|
+
const { messages, sendMessage, clearMessages } = useChatContext();
|
|
1381
|
+
return /* @__PURE__ */ jsx6(
|
|
1382
|
+
ChatBubble,
|
|
1383
|
+
{
|
|
1384
|
+
messages,
|
|
1385
|
+
onSendMessage: sendMessage,
|
|
1386
|
+
onClearChat: clearMessages,
|
|
1387
|
+
position,
|
|
1388
|
+
variant: "full",
|
|
1389
|
+
defaultExpanded: true
|
|
1390
|
+
}
|
|
1391
|
+
);
|
|
1392
|
+
}
|
|
728
1393
|
function SupernalProvider({
|
|
729
1394
|
children,
|
|
730
1395
|
theme = "auto",
|
|
@@ -738,10 +1403,44 @@ function SupernalProvider({
|
|
|
738
1403
|
onToolExecute
|
|
739
1404
|
}) {
|
|
740
1405
|
const shouldRenderChatBubble = !disabled;
|
|
1406
|
+
console.log("[SupernalProvider] disabled:", disabled, "type:", typeof disabled);
|
|
1407
|
+
console.log("[SupernalProvider] shouldRenderChatBubble:", shouldRenderChatBubble);
|
|
1408
|
+
useEffect5(() => {
|
|
1409
|
+
if (typeof window === "undefined") return;
|
|
1410
|
+
const collector = ExposureCollector.getInstance();
|
|
1411
|
+
const registeredToolIds = /* @__PURE__ */ new Set();
|
|
1412
|
+
const registerTools = () => {
|
|
1413
|
+
const allTools = ToolRegistry.getAllTools();
|
|
1414
|
+
allTools.forEach((tool) => {
|
|
1415
|
+
if (tool.elementId && !registeredToolIds.has(tool.toolId)) {
|
|
1416
|
+
const element = document.querySelector(`[data-testid="${tool.elementId}"]`);
|
|
1417
|
+
if (element) {
|
|
1418
|
+
collector.registerTool(tool.toolId, element, {
|
|
1419
|
+
name: tool.name,
|
|
1420
|
+
description: tool.description
|
|
1421
|
+
});
|
|
1422
|
+
registeredToolIds.add(tool.toolId);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
};
|
|
1427
|
+
registerTools();
|
|
1428
|
+
const observer = new MutationObserver(() => {
|
|
1429
|
+
registerTools();
|
|
1430
|
+
});
|
|
1431
|
+
observer.observe(document.body, {
|
|
1432
|
+
childList: true,
|
|
1433
|
+
subtree: true
|
|
1434
|
+
});
|
|
1435
|
+
return () => {
|
|
1436
|
+
observer.disconnect();
|
|
1437
|
+
collector.destroy();
|
|
1438
|
+
};
|
|
1439
|
+
}, []);
|
|
741
1440
|
return /* @__PURE__ */ jsx6(ChatInputProvider, { children: /* @__PURE__ */ jsxs2(ChatProvider, { mode, apiKey, onToolExecute, children: [
|
|
742
1441
|
/* @__PURE__ */ jsx6(AutoNavigationContext, { routes, onNavigate, children }),
|
|
743
1442
|
shouldRenderChatBubble ? /* @__PURE__ */ jsx6(
|
|
744
|
-
|
|
1443
|
+
ChatBubbleConnector,
|
|
745
1444
|
{
|
|
746
1445
|
theme,
|
|
747
1446
|
position,
|