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