@supernal/interface-nextjs 1.0.21 → 1.0.22

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.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/components/SupernalProvider.tsx
4
- import { useEffect as useEffect5 } from "react";
4
+ import { useEffect as useEffect8 } from "react";
5
5
 
6
6
  // src/contexts/ChatInputContext.tsx
7
7
  import { createContext, useContext, useCallback, useRef } from "react";
@@ -200,13 +200,13 @@ function getInitialMessages() {
200
200
  },
201
201
  {
202
202
  id: "3",
203
- text: '\u{1F3AE} Try these commands:\n\u2022 "open menu" or "close menu"\n\u2022 "toggle notifications"\n\u2022 "set priority high"',
203
+ text: '\u{1F3AE} **Try these commands:**\n\n- "open menu" or "close menu"\n- "toggle notifications"\n- "set priority high"',
204
204
  type: "system",
205
205
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
206
206
  },
207
207
  {
208
208
  id: "4",
209
- text: '\u{1F5FA}\uFE0F Navigate pages:\n\u2022 "architecture" or "dashboard"\n\u2022 "demo" or "home"\n\u2022 "docs" or "examples"',
209
+ text: '\u{1F5FA}\uFE0F **Navigate pages:**\n\n- "architecture" or "dashboard"\n- "demo" or "home"\n- "docs" or "examples"',
210
210
  type: "system",
211
211
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
212
212
  },
@@ -303,74 +303,40 @@ function ChatProvider({
303
303
  return /* @__PURE__ */ jsx2(ChatContext.Provider, { value: { messages, sendMessage, clearMessages, isLoading }, children });
304
304
  }
305
305
 
306
- // src/components/ChatBubble.tsx
307
- import { useState as useState2, useRef as useRef2, useEffect as useEffect2 } from "react";
306
+ // src/components/ChatBubble/constants.ts
307
+ import React3 from "react";
308
308
 
309
- // ../names/Components.ts
309
+ // src/names/Components.ts
310
310
  var Components = {
311
- // Chat components
311
+ // Chat component testids
312
+ ChatToggleButton: "chat-toggle-button",
312
313
  ChatInput: "chat-message-input",
313
314
  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"
315
+ ChatClearButton: "chat-clear-button"
316
+ };
317
+ var ChatBubbleVariant = {
318
+ full: "full",
319
+ // Full-screen chat panel
320
+ floating: "floating",
321
+ // Floating draggable widget
322
+ drawer: "drawer",
323
+ // Mobile bottom drawer
324
+ subtitle: "subtitle"
325
+ // Minimalist voice-first overlay (premium)
326
+ };
327
+ var PageLayout = {
328
+ default: "default",
329
+ // Standard page layout
330
+ landing: "landing",
331
+ // Landing page with hero
332
+ docs: "docs",
333
+ // Documentation layout
334
+ blog: "blog"
335
+ // Blog post layout
370
336
  };
371
337
 
372
- // src/components/ChatBubble.tsx
373
- import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
338
+ // src/components/ChatBubble/constants.ts
339
+ var DEFAULT_LOGO = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0ibG9nb0dyYWRpZW50IiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3R5bGU9InN0b3AtY29sb3I6IzAwNjZmZjtzdG9wLW9wYWNpdHk6MSIgLz4KICAgICAgPHN0b3Agb2Zmc2V0PSI1MCUiIHN0eWxlPSJzdG9wLWNvbG9yOiM0ZDk0ZmY7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3R5bGU9InN0b3AtY29sb3I6IzAwY2NmZjtzdG9wLW9wYWNpdHk6MSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgPC9kZWZzPgoKICA8IS0tIENpcmNsZSBiYWNrZ3JvdW5kIHdpdGggZ3JhZGllbnQgLS0+CiAgPGNpcmNsZSBjeD0iMjAiIGN5PSIyMCIgcj0iMTgiIGZpbGw9InVybCgjbG9nb0dyYWRpZW50KSIgb3BhY2l0eT0iMC4xNSIgLz4KCiAgPCEtLSBUZXh0OiAnQC8nIC0gZWFzeSB0byBjaGFuZ2UgdG8gJ34rJyBvciBhbnkgb3RoZXIgY2hhcmFjdGVycyAtLT4KICA8dGV4dCB4PSIyMCIgeT0iMjciCiAgICAgICAgZm9udC1mYW1pbHk9InN5c3RlbS11aSwgLWFwcGxlLXN5c3RlbSwgQmxpbmtNYWNTeXN0ZW1Gb250LCAnU2Vnb2UgVUknLCBzYW5zLXNlcmlmIgogICAgICAgIGZvbnQtc2l6ZT0iMjAiCiAgICAgICAgZm9udC13ZWlnaHQ9IjcwMCIKICAgICAgICB0ZXh0LWFuY2hvcj0ibWlkZGxlIgogICAgICAgIGZpbGw9InVybCgjbG9nb0dyYWRpZW50KSI+QC88L3RleHQ+Cjwvc3ZnPg==";
374
340
  var ChatNames = {
375
341
  bubble: Components.ChatToggleButton,
376
342
  input: Components.ChatInput,
@@ -511,9 +477,44 @@ var THEME_CLASSES = {
511
477
  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
478
  }
513
479
  };
480
+ var GLASS_RESPONSE_BUBBLE = {
481
+ light: {
482
+ background: "rgba(255, 255, 255, 0.7)",
483
+ border: "1px solid rgba(0, 0, 0, 0.08)",
484
+ backdropFilter: "blur(12px) saturate(180%)",
485
+ WebkitBackdropFilter: "blur(12px) saturate(180%)",
486
+ boxShadow: "0 2px 12px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.9)"
487
+ },
488
+ dark: {
489
+ background: "rgba(55, 65, 81, 0.6)",
490
+ border: "1px solid rgba(255, 255, 255, 0.1)",
491
+ backdropFilter: "blur(12px) saturate(180%)",
492
+ WebkitBackdropFilter: "blur(12px) saturate(180%)",
493
+ boxShadow: "0 2px 12px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.05)"
494
+ }
495
+ };
496
+ var GLASS_INVERTED = {
497
+ darkOnLight: {
498
+ background: "rgba(0, 0, 0, 0.6)",
499
+ border: "1px solid rgba(255, 255, 255, 0.15)",
500
+ backdropFilter: "blur(16px) saturate(150%)",
501
+ WebkitBackdropFilter: "blur(16px) saturate(150%)",
502
+ color: "rgba(255, 255, 255, 0.95)",
503
+ boxShadow: "0 4px 24px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)"
504
+ },
505
+ lightOnDark: {
506
+ background: "rgba(255, 255, 255, 0.7)",
507
+ border: "1px solid rgba(0, 0, 0, 0.1)",
508
+ backdropFilter: "blur(16px) saturate(180%)",
509
+ WebkitBackdropFilter: "blur(16px) saturate(180%)",
510
+ color: "rgba(0, 0, 0, 0.9)",
511
+ boxShadow: "0 4px 24px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.9)"
512
+ }
513
+ };
514
514
  var DEFAULT_CONFIG = {
515
515
  title: "Supernal Interface",
516
- avatar: /* @__PURE__ */ jsx3("img", { src: "/logo.svg", alt: "Supernal", className: "w-6 h-6" }),
516
+ logo: DEFAULT_LOGO,
517
+ avatar: React3.createElement("img", { src: DEFAULT_LOGO, alt: "Supernal", className: "w-6 h-6" }),
517
518
  description: "I'm a TOOL system AI can use to control this site",
518
519
  placeholder: "Try: toggle notifications",
519
520
  sendButtonLabel: "Send",
@@ -534,6 +535,9 @@ var DEFAULT_CONFIG = {
534
535
  background: "white"
535
536
  }
536
537
  };
538
+
539
+ // src/components/ChatBubble/InputField.tsx
540
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
537
541
  var InputField = ({
538
542
  compact = false,
539
543
  inputValue,
@@ -543,7 +547,11 @@ var InputField = ({
543
547
  glassClasses,
544
548
  theme,
545
549
  inputRef,
546
- sendButtonLabel
550
+ sendButtonLabel,
551
+ voiceEnabled = false,
552
+ isListening = false,
553
+ onMicClick,
554
+ modKey = "Ctrl"
547
555
  }) => /* @__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
556
  /* @__PURE__ */ jsx3(
549
557
  "input",
@@ -553,30 +561,1450 @@ var InputField = ({
553
561
  value: inputValue,
554
562
  onChange: (e) => onInputChange(e.target.value),
555
563
  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}`,
564
+ 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}` : `w-full pl-4 ${inputValue.trim() ? "pr-12" : "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 ${glassClasses}`,
557
565
  style: INLINE_STYLES.input(theme === "dark"),
558
- "data-testid": Components.ChatInput
566
+ "data-testid": ChatNames.input
559
567
  }
560
568
  ),
561
- /* @__PURE__ */ jsx3(
569
+ voiceEnabled && onMicClick && !compact && (!inputValue.trim() || isListening) && /* @__PURE__ */ jsx3(
570
+ "button",
571
+ {
572
+ type: "button",
573
+ onClick: onMicClick,
574
+ className: `absolute right-2 top-1/2 -translate-y-1/2 p-2 rounded-full transition-all ${isListening ? "bg-red-500 text-white animate-pulse" : "text-gray-500 hover:text-blue-600 hover:bg-gray-100 dark:hover:bg-gray-700"}`,
575
+ title: isListening ? "Stop recording (ESC)" : `Voice input (click or press ${modKey}+/)`,
576
+ "data-testid": "voice-input-button",
577
+ children: isListening ? /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) }) : /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", strokeWidth: "2", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx3("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" }) })
578
+ }
579
+ ),
580
+ inputValue.trim() && /* @__PURE__ */ jsx3(
562
581
  "button",
563
582
  {
564
583
  type: "submit",
565
- disabled: !inputValue.trim(),
566
584
  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,
585
+ "data-testid": ChatNames.sendButton,
568
586
  title: sendButtonLabel,
569
587
  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
588
  }
571
589
  )
572
590
  ] }) });
591
+
592
+ // src/components/ChatBubble/Avatar.tsx
593
+ import { Fragment, jsx as jsx4 } from "react/jsx-runtime";
573
594
  var Avatar = ({ avatar, size = "normal" }) => {
574
595
  if (!avatar) return null;
575
596
  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 }) });
597
+ return size === "small" ? /* @__PURE__ */ jsx4("span", { className: "text-lg", children: avatar }) : /* @__PURE__ */ jsx4("div", { className: "w-10 h-10 bg-blue-600 rounded-lg flex items-center justify-center shadow-md", children: /* @__PURE__ */ jsx4("span", { className: "text-white text-sm font-bold", children: avatar }) });
598
+ }
599
+ return /* @__PURE__ */ jsx4(Fragment, { children: avatar });
600
+ };
601
+
602
+ // src/components/ChatBubble/ChatBubble.tsx
603
+ import React7, { useState as useState7, useRef as useRef6, useEffect as useEffect5, useMemo as useMemo2 } from "react";
604
+
605
+ // src/components/MessageRenderer.tsx
606
+ import ReactMarkdown from "react-markdown";
607
+ import remarkGfm from "remark-gfm";
608
+ import remarkMath from "remark-math";
609
+ import remarkDirective from "remark-directive";
610
+ import rehypeKatex from "rehype-katex";
611
+
612
+ // src/components/CodeBlock.tsx
613
+ import React4, { useState as useState2 } from "react";
614
+ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
615
+ import { vscDarkPlus, vs } from "react-syntax-highlighter/dist/cjs/styles/prism";
616
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
617
+ function CodeBlock({ children, className, inline, theme = "dark" }) {
618
+ const [copied, setCopied] = useState2(false);
619
+ const match = /language-(\w+)/.exec(className || "");
620
+ const language = match ? match[1] : "text";
621
+ const handleCopy = async () => {
622
+ await navigator.clipboard.writeText(children);
623
+ setCopied(true);
624
+ setTimeout(() => setCopied(false), 2e3);
625
+ };
626
+ if (inline) {
627
+ return /* @__PURE__ */ jsx5("code", { className: `${className || ""} px-1.5 py-0.5 rounded text-sm font-mono ${theme === "dark" ? "bg-gray-800 text-gray-200" : "bg-gray-100 text-gray-800"}`, children });
628
+ }
629
+ return /* @__PURE__ */ jsxs2("div", { className: "relative group my-4", children: [
630
+ /* @__PURE__ */ jsx5(
631
+ "button",
632
+ {
633
+ onClick: handleCopy,
634
+ className: `absolute top-2 right-2 px-3 py-1.5 text-xs font-medium rounded transition-all ${theme === "dark" ? "bg-gray-700 hover:bg-gray-600 text-gray-200" : "bg-gray-200 hover:bg-gray-300 text-gray-700"} ${copied ? "opacity-100" : "opacity-0 group-hover:opacity-100"}`,
635
+ "aria-label": "Copy code",
636
+ children: copied ? /* @__PURE__ */ jsxs2("span", { className: "flex items-center gap-1", children: [
637
+ /* @__PURE__ */ jsx5("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) }),
638
+ "Copied!"
639
+ ] }) : /* @__PURE__ */ jsxs2("span", { className: "flex items-center gap-1", children: [
640
+ /* @__PURE__ */ jsx5("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx5("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
641
+ "Copy"
642
+ ] })
643
+ }
644
+ ),
645
+ React4.createElement(SyntaxHighlighter, {
646
+ language,
647
+ style: theme === "dark" ? vscDarkPlus : vs,
648
+ customStyle: {
649
+ margin: 0,
650
+ borderRadius: "0.5rem",
651
+ fontSize: "0.875rem",
652
+ padding: "1rem"
653
+ },
654
+ showLineNumbers: true,
655
+ wrapLines: true,
656
+ children
657
+ })
658
+ ] });
659
+ }
660
+
661
+ // src/components/MermaidDiagram.tsx
662
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
663
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
664
+ function MermaidDiagram({ chart, theme = "dark" }) {
665
+ const containerRef = useRef2(null);
666
+ const [error, setError] = useState3(null);
667
+ const [isLoading, setIsLoading] = useState3(true);
668
+ useEffect2(() => {
669
+ let mounted = true;
670
+ const renderDiagram = async () => {
671
+ if (!containerRef.current) return;
672
+ try {
673
+ setIsLoading(true);
674
+ setError(null);
675
+ const mermaid = (await import("mermaid")).default;
676
+ mermaid.initialize({
677
+ startOnLoad: false,
678
+ theme: theme === "dark" ? "dark" : "default",
679
+ securityLevel: "loose",
680
+ fontFamily: "ui-sans-serif, system-ui, -apple-system, sans-serif"
681
+ });
682
+ const id = `mermaid-${Math.random().toString(36).substr(2, 9)}`;
683
+ const { svg } = await mermaid.render(id, chart);
684
+ if (mounted && containerRef.current) {
685
+ containerRef.current.innerHTML = svg;
686
+ setIsLoading(false);
687
+ }
688
+ } catch (err) {
689
+ if (mounted) {
690
+ console.error("Mermaid rendering error:", err);
691
+ setError(err instanceof Error ? err.message : "Failed to render diagram");
692
+ setIsLoading(false);
693
+ }
694
+ }
695
+ };
696
+ renderDiagram();
697
+ return () => {
698
+ mounted = false;
699
+ };
700
+ }, [chart, theme]);
701
+ if (error) {
702
+ return /* @__PURE__ */ jsxs3("div", { className: `my-4 p-4 rounded-lg border ${theme === "dark" ? "bg-red-900/20 border-red-500 text-red-200" : "bg-red-50 border-red-300 text-red-800"}`, children: [
703
+ /* @__PURE__ */ jsx6("div", { className: "font-semibold mb-1", children: "Mermaid Diagram Error" }),
704
+ /* @__PURE__ */ jsx6("div", { className: "text-sm opacity-90", children: error }),
705
+ /* @__PURE__ */ jsxs3("details", { className: "mt-2 text-xs opacity-75", children: [
706
+ /* @__PURE__ */ jsx6("summary", { className: "cursor-pointer", children: "View diagram source" }),
707
+ /* @__PURE__ */ jsx6("pre", { className: "mt-2 whitespace-pre-wrap", children: chart })
708
+ ] })
709
+ ] });
710
+ }
711
+ return /* @__PURE__ */ jsxs3("div", { className: "my-4 flex justify-center", children: [
712
+ isLoading && /* @__PURE__ */ jsx6("div", { className: `py-8 text-sm ${theme === "dark" ? "text-gray-400" : "text-gray-600"}`, children: "Rendering diagram..." }),
713
+ /* @__PURE__ */ jsx6(
714
+ "div",
715
+ {
716
+ ref: containerRef,
717
+ className: `mermaid-diagram ${isLoading ? "hidden" : ""} ${theme === "dark" ? "mermaid-dark" : "mermaid-light"}`
718
+ }
719
+ )
720
+ ] });
721
+ }
722
+
723
+ // src/components/MessageRenderer.tsx
724
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
725
+ function MessageRenderer({ content, theme = "dark" }) {
726
+ const components = {
727
+ // Code blocks with syntax highlighting
728
+ code(props) {
729
+ const { node, className, children, ...rest } = props;
730
+ const value = String(children).replace(/\n$/, "");
731
+ const match = /language-(\w+)/.exec(className || "");
732
+ const language = match ? match[1] : "";
733
+ const inline = !className;
734
+ if (language === "mermaid") {
735
+ return /* @__PURE__ */ jsx7(MermaidDiagram, { chart: value, theme });
736
+ }
737
+ return /* @__PURE__ */ jsx7(
738
+ CodeBlock,
739
+ {
740
+ className,
741
+ inline,
742
+ theme,
743
+ children: value
744
+ }
745
+ );
746
+ },
747
+ // Headings with better styling
748
+ h1: ({ children }) => /* @__PURE__ */ jsx7("h1", { className: `text-2xl font-bold mt-6 mb-3 ${theme === "dark" ? "text-gray-100" : "text-gray-900"}`, children }),
749
+ h2: ({ children }) => /* @__PURE__ */ jsx7("h2", { className: `text-xl font-bold mt-5 mb-2.5 ${theme === "dark" ? "text-gray-100" : "text-gray-900"}`, children }),
750
+ h3: ({ children }) => /* @__PURE__ */ jsx7("h3", { className: `text-lg font-semibold mt-4 mb-2 ${theme === "dark" ? "text-gray-200" : "text-gray-800"}`, children }),
751
+ // Paragraphs
752
+ p: ({ children }) => /* @__PURE__ */ jsx7("p", { className: `mb-3 leading-relaxed ${theme === "dark" ? "text-gray-300" : "text-gray-700"}`, children }),
753
+ // Links
754
+ a: ({ href, children }) => /* @__PURE__ */ jsx7(
755
+ "a",
756
+ {
757
+ href,
758
+ target: "_blank",
759
+ rel: "noopener noreferrer",
760
+ className: `underline transition-colors ${theme === "dark" ? "text-blue-400 hover:text-blue-300" : "text-blue-600 hover:text-blue-700"}`,
761
+ children
762
+ }
763
+ ),
764
+ // Lists
765
+ ul: ({ children }) => /* @__PURE__ */ jsx7("ul", { className: `list-disc list-inside mb-3 space-y-1 ${theme === "dark" ? "text-gray-300" : "text-gray-700"}`, children }),
766
+ ol: ({ children }) => /* @__PURE__ */ jsx7("ol", { className: `list-decimal list-inside mb-3 space-y-1 ${theme === "dark" ? "text-gray-300" : "text-gray-700"}`, children }),
767
+ // Blockquotes
768
+ blockquote: ({ children }) => /* @__PURE__ */ jsx7("blockquote", { className: `border-l-4 pl-4 my-3 italic ${theme === "dark" ? "border-blue-500 bg-blue-900/20 py-2 pr-2" : "border-blue-400 bg-blue-50 py-2 pr-2"}`, children }),
769
+ // Tables
770
+ table: ({ children }) => /* @__PURE__ */ jsx7("div", { className: "overflow-x-auto my-4", children: /* @__PURE__ */ jsx7("table", { className: `min-w-full border-collapse ${theme === "dark" ? "border-gray-700" : "border-gray-300"}`, children }) }),
771
+ thead: ({ children }) => /* @__PURE__ */ jsx7("thead", { className: theme === "dark" ? "bg-gray-800" : "bg-gray-100", children }),
772
+ th: ({ children }) => /* @__PURE__ */ jsx7("th", { className: `px-4 py-2 text-left font-semibold border ${theme === "dark" ? "border-gray-700 text-gray-200" : "border-gray-300 text-gray-800"}`, children }),
773
+ td: ({ children }) => /* @__PURE__ */ jsx7("td", { className: `px-4 py-2 border ${theme === "dark" ? "border-gray-700 text-gray-300" : "border-gray-300 text-gray-700"}`, children }),
774
+ // Horizontal rule
775
+ hr: () => /* @__PURE__ */ jsx7("hr", { className: `my-4 border-t ${theme === "dark" ? "border-gray-700" : "border-gray-300"}` }),
776
+ // Admonitions support (using containerDirective from remark-directive)
777
+ div: ({ node, className, children, ...props }) => {
778
+ if (className?.includes("admonition")) {
779
+ const type = className.split("-")[1];
780
+ const styles = {
781
+ tip: {
782
+ border: theme === "dark" ? "border-green-500" : "border-green-600",
783
+ bg: theme === "dark" ? "bg-green-900/20" : "bg-green-50",
784
+ icon: "\u{1F4A1}"
785
+ },
786
+ warning: {
787
+ border: theme === "dark" ? "border-yellow-500" : "border-yellow-600",
788
+ bg: theme === "dark" ? "bg-yellow-900/20" : "bg-yellow-50",
789
+ icon: "\u26A0\uFE0F"
790
+ },
791
+ danger: {
792
+ border: theme === "dark" ? "border-red-500" : "border-red-600",
793
+ bg: theme === "dark" ? "bg-red-900/20" : "bg-red-50",
794
+ icon: "\u{1F6A8}"
795
+ },
796
+ info: {
797
+ border: theme === "dark" ? "border-blue-500" : "border-blue-600",
798
+ bg: theme === "dark" ? "bg-blue-900/20" : "bg-blue-50",
799
+ icon: "\u2139\uFE0F"
800
+ }
801
+ };
802
+ const style = styles[type] || styles.info;
803
+ return /* @__PURE__ */ jsx7("div", { className: `my-4 p-4 border-l-4 rounded-r-lg ${style.border} ${style.bg}`, children: /* @__PURE__ */ jsxs4("div", { className: "flex items-start gap-2", children: [
804
+ /* @__PURE__ */ jsx7("span", { className: "text-lg flex-shrink-0", children: style.icon }),
805
+ /* @__PURE__ */ jsx7("div", { className: "flex-1", children })
806
+ ] }) });
807
+ }
808
+ return /* @__PURE__ */ jsx7("div", { className, ...props, children });
809
+ }
810
+ };
811
+ return /* @__PURE__ */ jsx7("div", { className: "markdown-content", children: /* @__PURE__ */ jsx7(
812
+ ReactMarkdown,
813
+ {
814
+ remarkPlugins: [remarkGfm, remarkMath, remarkDirective],
815
+ rehypePlugins: [rehypeKatex],
816
+ components,
817
+ children: content
818
+ }
819
+ ) });
820
+ }
821
+
822
+ // src/hooks/useTTS.ts
823
+ import { useState as useState4, useRef as useRef3, useCallback as useCallback3 } from "react";
824
+ var Platform = {
825
+ isNative: typeof window !== "undefined" && "Capacitor" in window,
826
+ isWeb: typeof window !== "undefined" && !("Capacitor" in window)
827
+ };
828
+ function isNativeTTSSupported() {
829
+ if (Platform.isNative) {
830
+ return true;
831
+ }
832
+ if (Platform.isWeb && "speechSynthesis" in window) {
833
+ return true;
834
+ }
835
+ return false;
836
+ }
837
+ function useTTS() {
838
+ const [isPlaying, setIsPlaying] = useState4(false);
839
+ const [error, setError] = useState4(null);
840
+ const [currentText, setCurrentText] = useState4(null);
841
+ const audioRef = useRef3(null);
842
+ const utteranceRef = useRef3(null);
843
+ const isNativeSupported = isNativeTTSSupported();
844
+ const speakNative = useCallback3(
845
+ async (text, options = {}) => {
846
+ if (Platform.isNative) {
847
+ try {
848
+ const { TextToSpeech } = await import("@capacitor/text-to-speech");
849
+ await TextToSpeech.speak({
850
+ text,
851
+ lang: "en-US",
852
+ rate: options.speed || 1
853
+ });
854
+ } catch (err) {
855
+ throw new Error("Capacitor TTS failed: " + err.message);
856
+ }
857
+ return;
858
+ }
859
+ if ("speechSynthesis" in window) {
860
+ return new Promise((resolve, reject) => {
861
+ const utterance = new SpeechSynthesisUtterance(text);
862
+ utterance.rate = options.speed || 1;
863
+ if (options.voice) {
864
+ const voices = window.speechSynthesis.getVoices();
865
+ const voice = voices.find(
866
+ (v) => v.name.toLowerCase().includes(options.voice.toLowerCase())
867
+ );
868
+ if (voice) utterance.voice = voice;
869
+ }
870
+ utterance.onend = () => {
871
+ utteranceRef.current = null;
872
+ resolve();
873
+ };
874
+ utterance.onerror = (e) => {
875
+ utteranceRef.current = null;
876
+ reject(new Error("SpeechSynthesis error: " + e.error));
877
+ };
878
+ utteranceRef.current = utterance;
879
+ window.speechSynthesis.speak(utterance);
880
+ });
881
+ }
882
+ throw new Error("Native TTS not supported on this platform");
883
+ },
884
+ []
885
+ );
886
+ const speakAPI = useCallback3(
887
+ async (text, options = {}) => {
888
+ const apiUrl = process.env.NEXT_PUBLIC_TTS_API_URL || "https://tts.supernal.ai";
889
+ const apiKey = process.env.NEXT_PUBLIC_TTS_API_KEY || "";
890
+ if (!apiKey && !apiUrl.includes("localhost")) {
891
+ throw new Error("TTS_API_KEY not configured for premium voices");
892
+ }
893
+ const response = await fetch(`${apiUrl}/api/v1/generate`, {
894
+ method: "POST",
895
+ headers: {
896
+ "Content-Type": "application/json",
897
+ ...apiKey && { "X-API-Key": apiKey }
898
+ },
899
+ body: JSON.stringify({
900
+ text,
901
+ options: {
902
+ provider: "openai",
903
+ voice: options.voice || "alloy",
904
+ speed: 1
905
+ // Always generate at 1.0x, adjust playback client-side
906
+ }
907
+ })
908
+ });
909
+ if (!response.ok) {
910
+ throw new Error(`TTS API failed: ${response.status} ${response.statusText}`);
911
+ }
912
+ const contentType = response.headers.get("content-type") || "";
913
+ let audioUrl;
914
+ if (contentType.includes("audio")) {
915
+ const blob = await response.blob();
916
+ audioUrl = URL.createObjectURL(blob);
917
+ } else {
918
+ const data = await response.json();
919
+ if (data.audio) {
920
+ const blob = base64ToBlob(data.audio, "audio/mpeg");
921
+ audioUrl = URL.createObjectURL(blob);
922
+ } else if (data.audioUrl) {
923
+ audioUrl = data.audioUrl;
924
+ } else {
925
+ throw new Error("Invalid TTS API response format");
926
+ }
927
+ }
928
+ const audio = new Audio(audioUrl);
929
+ audio.playbackRate = options.speed || 1;
930
+ return new Promise((resolve, reject) => {
931
+ audio.onended = () => {
932
+ URL.revokeObjectURL(audioUrl);
933
+ audioRef.current = null;
934
+ resolve();
935
+ };
936
+ audio.onerror = () => {
937
+ URL.revokeObjectURL(audioUrl);
938
+ audioRef.current = null;
939
+ reject(new Error("Audio playback failed"));
940
+ };
941
+ audioRef.current = audio;
942
+ audio.play().catch(reject);
943
+ });
944
+ },
945
+ []
946
+ );
947
+ const speak = useCallback3(
948
+ async (options) => {
949
+ const {
950
+ text,
951
+ voice,
952
+ speed = 1,
953
+ preferNative = true,
954
+ usePremium = false,
955
+ onComplete,
956
+ onError
957
+ } = options;
958
+ stop();
959
+ setIsPlaying(true);
960
+ setError(null);
961
+ setCurrentText(text);
962
+ try {
963
+ if (preferNative && !usePremium && isNativeSupported) {
964
+ await speakNative(text, { voice, speed });
965
+ } else {
966
+ await speakAPI(text, { voice, speed });
967
+ }
968
+ onComplete?.();
969
+ } catch (err) {
970
+ const errorMsg = err instanceof Error ? err.message : "Unknown TTS error";
971
+ setError(errorMsg);
972
+ if (usePremium && isNativeSupported) {
973
+ try {
974
+ console.warn("Premium TTS failed, falling back to native");
975
+ await speakNative(text, { voice, speed });
976
+ onComplete?.();
977
+ setError(null);
978
+ } catch (fallbackErr) {
979
+ onError?.(fallbackErr);
980
+ }
981
+ } else {
982
+ onError?.(err);
983
+ }
984
+ } finally {
985
+ setIsPlaying(false);
986
+ setCurrentText(null);
987
+ }
988
+ },
989
+ [speakNative, speakAPI, isNativeSupported]
990
+ );
991
+ const stop = useCallback3(() => {
992
+ if ("speechSynthesis" in window && window.speechSynthesis.speaking) {
993
+ window.speechSynthesis.cancel();
994
+ utteranceRef.current = null;
995
+ }
996
+ if (audioRef.current) {
997
+ audioRef.current.pause();
998
+ audioRef.current.currentTime = 0;
999
+ audioRef.current = null;
1000
+ }
1001
+ setIsPlaying(false);
1002
+ setCurrentText(null);
1003
+ }, []);
1004
+ return {
1005
+ speak,
1006
+ stop,
1007
+ isPlaying,
1008
+ error,
1009
+ isNativeSupported,
1010
+ currentText
1011
+ };
1012
+ }
1013
+ function base64ToBlob(base64, mimeType) {
1014
+ const byteCharacters = atob(base64);
1015
+ const byteNumbers = new Array(byteCharacters.length);
1016
+ for (let i = 0; i < byteCharacters.length; i++) {
1017
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
1018
+ }
1019
+ const byteArray = new Uint8Array(byteNumbers);
1020
+ return new Blob([byteArray], { type: mimeType });
1021
+ }
1022
+
1023
+ // src/hooks/useSTT.ts
1024
+ import { useState as useState5, useRef as useRef4, useCallback as useCallback4, useMemo, useEffect as useEffect3 } from "react";
1025
+ var Platform2 = {
1026
+ isNative: typeof window !== "undefined" && "Capacitor" in window,
1027
+ isWeb: typeof window !== "undefined" && !("Capacitor" in window)
1028
+ };
1029
+ function useSTT() {
1030
+ const [isListening, setIsListening] = useState5(false);
1031
+ const [transcript, setTranscript] = useState5("");
1032
+ const [interimTranscript, setInterimTranscript] = useState5("");
1033
+ const [error, setError] = useState5(null);
1034
+ const recognitionRef = useRef4(null);
1035
+ const isSupported = useMemo(() => {
1036
+ if (Platform2.isNative) {
1037
+ return true;
1038
+ }
1039
+ if (Platform2.isWeb) {
1040
+ return "SpeechRecognition" in window || "webkitSpeechRecognition" in window;
1041
+ }
1042
+ return false;
1043
+ }, []);
1044
+ useEffect3(() => {
1045
+ if (!isSupported || Platform2.isNative) return;
1046
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1047
+ if (!SpeechRecognition) return;
1048
+ const recognition = new SpeechRecognition();
1049
+ recognition.continuous = false;
1050
+ recognition.interimResults = true;
1051
+ recognition.lang = "en-US";
1052
+ recognition.maxAlternatives = 1;
1053
+ recognition.onresult = (event) => {
1054
+ let interim = "";
1055
+ let final = "";
1056
+ for (let i = 0; i < event.results.length; i++) {
1057
+ const result = event.results[i];
1058
+ if (result.isFinal) {
1059
+ final += result[0].transcript;
1060
+ } else {
1061
+ interim += result[0].transcript;
1062
+ }
1063
+ }
1064
+ if (final) setTranscript(final);
1065
+ setInterimTranscript(interim);
1066
+ };
1067
+ recognition.onerror = (event) => {
1068
+ console.error("Speech recognition error:", event.error);
1069
+ setError(event.error);
1070
+ setIsListening(false);
1071
+ };
1072
+ recognition.onend = () => {
1073
+ setIsListening(false);
1074
+ };
1075
+ recognitionRef.current = recognition;
1076
+ return () => {
1077
+ if (recognition) {
1078
+ recognition.stop();
1079
+ }
1080
+ };
1081
+ }, [isSupported]);
1082
+ const startListening = useCallback4(async () => {
1083
+ setError(null);
1084
+ setTranscript("");
1085
+ setInterimTranscript("");
1086
+ setIsListening(true);
1087
+ try {
1088
+ if (Platform2.isNative) {
1089
+ const { SpeechRecognition: CapSpeech } = await import("@capacitor-community/speech-recognition");
1090
+ await CapSpeech.requestPermissions();
1091
+ const { matches } = await CapSpeech.start({
1092
+ language: "en-US",
1093
+ maxResults: 1,
1094
+ popup: false
1095
+ });
1096
+ setTranscript(matches[0] || "");
1097
+ setIsListening(false);
1098
+ return;
1099
+ }
1100
+ if (recognitionRef.current) {
1101
+ recognitionRef.current.start();
1102
+ } else {
1103
+ throw new Error("Speech recognition not initialized");
1104
+ }
1105
+ } catch (err) {
1106
+ console.error("Failed to start speech recognition:", err);
1107
+ setError(err instanceof Error ? err.message : "Failed to start microphone");
1108
+ setIsListening(false);
1109
+ }
1110
+ }, []);
1111
+ const stopListening = useCallback4(() => {
1112
+ if (Platform2.isNative) {
1113
+ setIsListening(false);
1114
+ return;
1115
+ }
1116
+ if (recognitionRef.current) {
1117
+ recognitionRef.current.stop();
1118
+ }
1119
+ }, []);
1120
+ const resetTranscript = useCallback4(() => {
1121
+ setTranscript("");
1122
+ setInterimTranscript("");
1123
+ setError(null);
1124
+ }, []);
1125
+ return {
1126
+ startListening,
1127
+ stopListening,
1128
+ resetTranscript,
1129
+ isListening,
1130
+ transcript,
1131
+ interimTranscript,
1132
+ error,
1133
+ isSupported
1134
+ };
1135
+ }
1136
+
1137
+ // src/components/TTSButton.tsx
1138
+ import { jsx as jsx8 } from "react/jsx-runtime";
1139
+ function TTSButton({
1140
+ text,
1141
+ usePremiumVoices = false,
1142
+ speed = 1,
1143
+ size = "small",
1144
+ theme = "light"
1145
+ }) {
1146
+ const { speak, stop, isPlaying, error, isNativeSupported } = useTTS();
1147
+ const handleClick = async (e) => {
1148
+ e.stopPropagation();
1149
+ if (isPlaying) {
1150
+ stop();
1151
+ } else {
1152
+ await speak({
1153
+ text,
1154
+ speed,
1155
+ usePremium: usePremiumVoices,
1156
+ preferNative: !usePremiumVoices
1157
+ });
1158
+ }
1159
+ };
1160
+ if (!isNativeSupported && !usePremiumVoices) {
1161
+ return null;
577
1162
  }
578
- return /* @__PURE__ */ jsx3(Fragment, { children: avatar });
1163
+ const isDark = theme === "dark";
1164
+ const iconSize = size === "small" ? "w-3 h-3" : "w-4 h-4";
1165
+ const buttonPadding = size === "small" ? "p-1" : "p-2";
1166
+ return /* @__PURE__ */ jsx8(
1167
+ "button",
1168
+ {
1169
+ onClick: handleClick,
1170
+ className: `${buttonPadding} rounded-lg transition-all ${isPlaying ? "bg-blue-500 text-white animate-pulse" : error ? "bg-red-500 text-white" : isDark ? "text-gray-400 hover:text-blue-400 hover:bg-gray-700/50" : "text-gray-500 hover:text-blue-600 hover:bg-gray-100/50"}`,
1171
+ title: isPlaying ? "Stop speaking" : "Read aloud",
1172
+ "data-testid": "tts-button",
1173
+ "aria-label": isPlaying ? "Stop speaking" : "Read aloud",
1174
+ children: isPlaying ? (
1175
+ // Stop icon (pulsing square)
1176
+ /* @__PURE__ */ jsx8("svg", { className: iconSize, fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx8("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) })
1177
+ ) : error ? (
1178
+ // Error icon
1179
+ /* @__PURE__ */ jsx8("svg", { className: iconSize, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx8("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) })
1180
+ ) : (
1181
+ // Speaker icon
1182
+ /* @__PURE__ */ jsx8("svg", { className: iconSize, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx8("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15.414a2 2 0 002.828 0l1.768-1.768a2 2 0 00 0-2.828l-1.768-1.768a2 2 0 00-2.828 0V15.414z" }) })
1183
+ )
1184
+ }
1185
+ );
1186
+ }
1187
+
1188
+ // src/components/SubtitleOverlay.tsx
1189
+ import { useState as useState6, useEffect as useEffect4, useRef as useRef5 } from "react";
1190
+
1191
+ // src/components/TTSPlaylistMenu.tsx
1192
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
1193
+ var TTSPlaylistMenu = ({
1194
+ isOpen,
1195
+ onClose,
1196
+ widgets,
1197
+ theme,
1198
+ onWidgetSelect
1199
+ }) => {
1200
+ if (!isOpen || widgets.length === 0) return null;
1201
+ const handleWidgetClick = (widget) => {
1202
+ onWidgetSelect(widget);
1203
+ };
1204
+ const glassStyles = theme === "dark" ? {
1205
+ background: "rgba(31, 41, 55, 0.9)",
1206
+ border: "1px solid rgba(255, 255, 255, 0.12)",
1207
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)"
1208
+ } : {
1209
+ background: "rgba(255, 255, 255, 0.9)",
1210
+ border: "1px solid rgba(0, 0, 0, 0.1)",
1211
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)"
1212
+ };
1213
+ return /* @__PURE__ */ jsx9(
1214
+ "div",
1215
+ {
1216
+ className: "fixed z-50",
1217
+ style: {
1218
+ bottom: "calc(env(safe-area-inset-bottom, 0px) + 80px)",
1219
+ left: "16px",
1220
+ maxHeight: "50vh",
1221
+ width: "min(90vw, 400px)"
1222
+ },
1223
+ "data-testid": "tts-playlist-menu",
1224
+ children: /* @__PURE__ */ jsxs5(
1225
+ "div",
1226
+ {
1227
+ style: {
1228
+ ...glassStyles,
1229
+ backdropFilter: "blur(20px) saturate(180%)",
1230
+ WebkitBackdropFilter: "blur(20px) saturate(180%)",
1231
+ borderRadius: "16px"
1232
+ },
1233
+ children: [
1234
+ /* @__PURE__ */ jsxs5(
1235
+ "div",
1236
+ {
1237
+ className: `p-4 border-b flex items-center justify-between ${theme === "dark" ? "border-white/10" : "border-black/10"}`,
1238
+ children: [
1239
+ /* @__PURE__ */ jsx9(
1240
+ "h3",
1241
+ {
1242
+ className: `font-semibold text-sm ${theme === "dark" ? "text-white" : "text-gray-900"}`,
1243
+ children: "Readable Sections"
1244
+ }
1245
+ ),
1246
+ /* @__PURE__ */ jsx9(
1247
+ "button",
1248
+ {
1249
+ onClick: onClose,
1250
+ className: `p-1.5 rounded-lg transition-all ${theme === "dark" ? "text-gray-400 hover:text-white hover:bg-white/10" : "text-gray-600 hover:text-gray-900 hover:bg-black/5"}`,
1251
+ title: "Close",
1252
+ "data-testid": "close-playlist-menu",
1253
+ children: /* @__PURE__ */ jsx9("span", { className: "text-lg font-bold", "aria-hidden": "true", children: "\xD7" })
1254
+ }
1255
+ )
1256
+ ]
1257
+ }
1258
+ ),
1259
+ /* @__PURE__ */ jsx9("div", { className: "overflow-y-auto max-h-80 p-2", children: widgets.length === 0 ? /* @__PURE__ */ jsx9(
1260
+ "div",
1261
+ {
1262
+ className: `p-6 text-center text-sm ${theme === "dark" ? "text-gray-400" : "text-gray-600"}`,
1263
+ children: "No readable sections found on this page"
1264
+ }
1265
+ ) : widgets.map((widget) => /* @__PURE__ */ jsx9(
1266
+ "button",
1267
+ {
1268
+ onClick: () => handleWidgetClick(widget),
1269
+ className: `w-full text-left p-3 rounded-lg transition-all ${theme === "dark" ? "hover:bg-white/10" : "hover:bg-black/5"}`,
1270
+ style: {
1271
+ backdropFilter: "blur(8px)",
1272
+ WebkitBackdropFilter: "blur(8px)"
1273
+ },
1274
+ "data-testid": "playlist-widget-item",
1275
+ children: /* @__PURE__ */ jsxs5(
1276
+ "div",
1277
+ {
1278
+ className: `font-medium text-sm flex items-center ${theme === "dark" ? "text-white" : "text-gray-900"}`,
1279
+ children: [
1280
+ /* @__PURE__ */ jsx9("span", { className: "mr-2", "aria-hidden": "true", children: "\u{1F50A}" }),
1281
+ widget.label || `TTS Widget ${widget.id}`
1282
+ ]
1283
+ }
1284
+ )
1285
+ },
1286
+ widget.id
1287
+ )) })
1288
+ ]
1289
+ }
1290
+ )
1291
+ }
1292
+ );
579
1293
  };
1294
+
1295
+ // src/utils/pageContentParser.ts
1296
+ var GENERIC_SUGGESTIONS = [
1297
+ { text: "Try: navigate to...", type: "generic" },
1298
+ { text: "Ask: what can I do here?", type: "generic" },
1299
+ { text: "Show me around", type: "generic" },
1300
+ { text: "What's on this page?", type: "generic" }
1301
+ ];
1302
+ function extractPageSpecificSuggestions() {
1303
+ if (typeof window === "undefined") return [];
1304
+ const headings = document.querySelectorAll("h1, h2, h3");
1305
+ const suggestions = [];
1306
+ Array.from(headings).slice(0, 3).forEach((heading) => {
1307
+ const text = heading.textContent?.trim();
1308
+ if (text && text.length > 0 && text.length < 50) {
1309
+ suggestions.push({
1310
+ text: `Ask about: ${text}`,
1311
+ type: "page-specific"
1312
+ });
1313
+ }
1314
+ });
1315
+ return suggestions;
1316
+ }
1317
+ function extractPageSuggestions(maxSuggestions = 7, genericRatio = 0.4) {
1318
+ const pageSpecific = extractPageSpecificSuggestions();
1319
+ const numGeneric = Math.ceil(maxSuggestions * genericRatio);
1320
+ const numPageSpecific = maxSuggestions - numGeneric;
1321
+ const suggestions = [
1322
+ ...GENERIC_SUGGESTIONS.slice(0, numGeneric),
1323
+ ...pageSpecific.slice(0, numPageSpecific)
1324
+ ];
1325
+ return suggestions;
1326
+ }
1327
+
1328
+ // src/utils/backgroundDetection.ts
1329
+ function calculateLuminance(rgbColor) {
1330
+ const rgb = rgbColor.match(/\d+/g)?.map(Number);
1331
+ if (!rgb || rgb.length < 3) {
1332
+ console.warn("Invalid RGB color format, defaulting to 0.5 luminance");
1333
+ return 0.5;
1334
+ }
1335
+ const r = rgb[0] / 255;
1336
+ const g = rgb[1] / 255;
1337
+ const b = rgb[2] / 255;
1338
+ return 0.299 * r + 0.587 * g + 0.114 * b;
1339
+ }
1340
+ function sampleBackgroundColor(x, y) {
1341
+ if (typeof window === "undefined") return null;
1342
+ const element = document.elementFromPoint(x, y);
1343
+ if (!element) return null;
1344
+ return getComputedStyle(element).backgroundColor;
1345
+ }
1346
+ function detectBackgroundContext() {
1347
+ if (typeof window === "undefined") {
1348
+ return {
1349
+ pageTheme: "light",
1350
+ glassTheme: "dark",
1351
+ luminance: 0.5
1352
+ };
1353
+ }
1354
+ const explicitTheme = document.documentElement.getAttribute("data-theme");
1355
+ const overlayY = window.innerHeight - 100;
1356
+ const overlayX = window.innerWidth / 2;
1357
+ const bgColor = sampleBackgroundColor(overlayX, overlayY);
1358
+ const luminance = bgColor ? calculateLuminance(bgColor) : 0.5;
1359
+ const sampledTheme = luminance > 0.5 ? "light" : "dark";
1360
+ const pageTheme = explicitTheme === "dark" || explicitTheme === "light" ? explicitTheme : sampledTheme;
1361
+ const glassTheme = sampledTheme === "light" ? "dark" : "light";
1362
+ return {
1363
+ pageTheme,
1364
+ glassTheme,
1365
+ luminance
1366
+ };
1367
+ }
1368
+
1369
+ // src/utils/ttsDetection.ts
1370
+ function detectTTSWidgets() {
1371
+ if (typeof window === "undefined") return false;
1372
+ const widgetWrappers = document.querySelectorAll(".supernal-tts-widget[data-text]");
1373
+ console.log("[TTS Detection] Found widget wrappers:", widgetWrappers.length);
1374
+ if (widgetWrappers.length > 0) {
1375
+ console.log("[TTS Detection] Wrapper visible?", widgetWrappers[0].offsetParent !== null);
1376
+ console.log("[TTS Detection] Has play button?", !!widgetWrappers[0].querySelector(".supernal-tts-play"));
1377
+ }
1378
+ return widgetWrappers.length > 0;
1379
+ }
1380
+ function extractWidgetLabel(element, fallbackIndex) {
1381
+ const ariaLabel = element.getAttribute("aria-label");
1382
+ if (ariaLabel && ariaLabel.trim()) return ariaLabel.trim();
1383
+ const dataLabel = element.getAttribute("data-label");
1384
+ if (dataLabel && dataLabel.trim()) return dataLabel.trim();
1385
+ const parent = element.closest("section, article, div, p");
1386
+ if (parent) {
1387
+ const heading = parent.querySelector("h1, h2, h3, h4, h5, h6");
1388
+ if (heading?.textContent?.trim()) {
1389
+ return heading.textContent.trim();
1390
+ }
1391
+ let sibling = parent.previousElementSibling;
1392
+ while (sibling) {
1393
+ if (sibling.matches("h1, h2, h3, h4, h5, h6")) {
1394
+ return sibling.textContent?.trim() || `Readable Section ${fallbackIndex}`;
1395
+ }
1396
+ sibling = sibling.previousElementSibling;
1397
+ }
1398
+ }
1399
+ const parentSection = element.closest("section, article");
1400
+ if (parentSection) {
1401
+ const sectionHeading = parentSection.querySelector("h1, h2, h3, h4, h5, h6");
1402
+ if (sectionHeading?.textContent?.trim()) {
1403
+ return sectionHeading.textContent.trim();
1404
+ }
1405
+ }
1406
+ if (parent?.textContent) {
1407
+ const text = parent.textContent.trim().slice(0, 50);
1408
+ if (text.length > 10) return text;
1409
+ }
1410
+ return `Readable Section ${fallbackIndex}`;
1411
+ }
1412
+ function extractTTSWidgets() {
1413
+ if (typeof window === "undefined") return [];
1414
+ const widgets = [];
1415
+ const ttsWidgets = document.querySelectorAll(".supernal-tts-widget[data-text]");
1416
+ ttsWidgets.forEach((wrapper, idx) => {
1417
+ const element = wrapper;
1418
+ if (element.offsetParent === null) {
1419
+ console.log("[TTS Extract] Skipping hidden widget:", wrapper);
1420
+ return;
1421
+ }
1422
+ const label = extractWidgetLabel(wrapper, widgets.length + 1);
1423
+ const playButton = element.querySelector(".supernal-tts-play");
1424
+ const widgetId = `tts-widget-${idx}`;
1425
+ if (!element.hasAttribute("data-testid")) {
1426
+ element.setAttribute("data-testid", widgetId);
1427
+ }
1428
+ if (playButton && !playButton.hasAttribute("data-testid")) {
1429
+ playButton.setAttribute("data-testid", `${widgetId}-play`);
1430
+ }
1431
+ console.log("[TTS Extract] Widget:", {
1432
+ id: widgetId,
1433
+ label,
1434
+ hasButton: !!playButton,
1435
+ wrapper: element,
1436
+ playButton,
1437
+ testId: element.getAttribute("data-testid")
1438
+ });
1439
+ widgets.push({
1440
+ id: widgetId,
1441
+ element: wrapper,
1442
+ // Always use wrapper as the element to scroll to
1443
+ label
1444
+ });
1445
+ });
1446
+ console.log("[TTS Extract] Total widgets found:", widgets.length);
1447
+ return widgets;
1448
+ }
1449
+
1450
+ // src/components/SubtitleOverlay.tsx
1451
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
1452
+ var opacityStates = {
1453
+ mobile: {
1454
+ idle: 1,
1455
+ // Always full opacity
1456
+ listening: 1,
1457
+ typing: 1,
1458
+ speaking: 1
1459
+ },
1460
+ desktop: {
1461
+ idle: 0.4,
1462
+ // Increased from 0.1 for better visibility
1463
+ listening: 0.7,
1464
+ typing: 0.9,
1465
+ speaking: 0.5
1466
+ }
1467
+ };
1468
+ function detectMode(context) {
1469
+ if (context.isListening) return "voice";
1470
+ if (context.inputValue.trim().length > 0) return "text";
1471
+ if (context.lastInputMethod && context.timeSinceLastInput < 1e4) {
1472
+ return context.lastInputMethod;
1473
+ }
1474
+ return "voice";
1475
+ }
1476
+ var getOverlayOpacity = (state, isMobile) => {
1477
+ return isMobile ? opacityStates.mobile[state] : opacityStates.desktop[state];
1478
+ };
1479
+ var SubtitleOverlay = ({
1480
+ messages,
1481
+ inputValue,
1482
+ onInputChange,
1483
+ onSendMessage,
1484
+ voiceEnabled,
1485
+ isListening,
1486
+ onMicClick,
1487
+ theme,
1488
+ config,
1489
+ sttTranscript,
1490
+ resetTranscript
1491
+ }) => {
1492
+ const [overlayState, setOverlayState] = useState6("idle");
1493
+ const [opacity, setOpacity] = useState6(1);
1494
+ const [lastInputMethod, setLastInputMethod] = useState6(null);
1495
+ const [isMobile, setIsMobile] = useState6(false);
1496
+ const [messageOpacity, setMessageOpacity] = useState6(1);
1497
+ const [expansionState, setExpansionState] = useState6("collapsed");
1498
+ const [touchStartY, setTouchStartY] = useState6(null);
1499
+ const lastInputTimeRef = useRef5(Date.now());
1500
+ const autoFadeTimerRef = useRef5(null);
1501
+ const messageFadeTimerRef = useRef5(null);
1502
+ const inputRef = useRef5(null);
1503
+ const [suggestions, setSuggestions] = useState6([]);
1504
+ const [currentSuggestionIndex, setCurrentSuggestionIndex] = useState6(0);
1505
+ const [glassTheme, setGlassTheme] = useState6("auto");
1506
+ const [detectedGlassTheme, setDetectedGlassTheme] = useState6("light");
1507
+ const [hasTTSWidgets, setHasTTSWidgets] = useState6(false);
1508
+ const [showPlaylist, setShowPlaylist] = useState6(false);
1509
+ const [ttsWidgets, setTTSWidgets] = useState6([]);
1510
+ const [touchStartYInput, setTouchStartYInput] = useState6(null);
1511
+ useEffect4(() => {
1512
+ const checkMobile = () => {
1513
+ const mobile = window.innerWidth < 768;
1514
+ setIsMobile(mobile);
1515
+ if (!mobile && expansionState === "collapsed") {
1516
+ setExpansionState("expanded");
1517
+ }
1518
+ };
1519
+ checkMobile();
1520
+ window.addEventListener("resize", checkMobile);
1521
+ return () => window.removeEventListener("resize", checkMobile);
1522
+ }, []);
1523
+ useEffect4(() => {
1524
+ if (autoFadeTimerRef.current) {
1525
+ clearTimeout(autoFadeTimerRef.current);
1526
+ autoFadeTimerRef.current = null;
1527
+ }
1528
+ if (!isMobile && overlayState === "idle") {
1529
+ autoFadeTimerRef.current = setTimeout(() => {
1530
+ setOpacity(0.4);
1531
+ }, 5e3);
1532
+ }
1533
+ return () => {
1534
+ if (autoFadeTimerRef.current) {
1535
+ clearTimeout(autoFadeTimerRef.current);
1536
+ }
1537
+ };
1538
+ }, [overlayState, isMobile]);
1539
+ useEffect4(() => {
1540
+ if (messageFadeTimerRef.current) {
1541
+ clearTimeout(messageFadeTimerRef.current);
1542
+ messageFadeTimerRef.current = null;
1543
+ }
1544
+ setMessageOpacity(1);
1545
+ messageFadeTimerRef.current = setTimeout(() => {
1546
+ setMessageOpacity(0);
1547
+ }, 8e3);
1548
+ return () => {
1549
+ if (messageFadeTimerRef.current) {
1550
+ clearTimeout(messageFadeTimerRef.current);
1551
+ }
1552
+ };
1553
+ }, [messages.filter((m) => m.type === "ai").slice(-1)[0]?.text]);
1554
+ useEffect4(() => {
1555
+ if (sttTranscript && voiceEnabled && resetTranscript) {
1556
+ onSendMessage(sttTranscript);
1557
+ resetTranscript();
1558
+ setExpansionState("expanded");
1559
+ setLastInputMethod("voice");
1560
+ lastInputTimeRef.current = Date.now();
1561
+ }
1562
+ }, [sttTranscript, voiceEnabled, resetTranscript, onSendMessage]);
1563
+ useEffect4(() => {
1564
+ const pageSuggestions = extractPageSuggestions();
1565
+ setSuggestions(pageSuggestions);
1566
+ }, []);
1567
+ useEffect4(() => {
1568
+ const detectWidgets = () => {
1569
+ const detected = detectTTSWidgets();
1570
+ setHasTTSWidgets(detected);
1571
+ if (detected) {
1572
+ const widgets = extractTTSWidgets();
1573
+ setTTSWidgets(widgets);
1574
+ } else {
1575
+ setTTSWidgets([]);
1576
+ }
1577
+ };
1578
+ detectWidgets();
1579
+ let timeoutId;
1580
+ const observer = new MutationObserver(() => {
1581
+ clearTimeout(timeoutId);
1582
+ timeoutId = setTimeout(detectWidgets, 500);
1583
+ });
1584
+ observer.observe(document.body, {
1585
+ childList: true,
1586
+ subtree: true
1587
+ });
1588
+ return () => {
1589
+ observer.disconnect();
1590
+ clearTimeout(timeoutId);
1591
+ };
1592
+ }, []);
1593
+ useEffect4(() => {
1594
+ if (glassTheme !== "auto") return;
1595
+ const { glassTheme: detected } = detectBackgroundContext();
1596
+ setDetectedGlassTheme(detected);
1597
+ let lastDetection = Date.now();
1598
+ const handleScroll = () => {
1599
+ const now = Date.now();
1600
+ if (now - lastDetection > 500) {
1601
+ const { glassTheme: newDetected } = detectBackgroundContext();
1602
+ setDetectedGlassTheme(newDetected);
1603
+ lastDetection = now;
1604
+ }
1605
+ };
1606
+ window.addEventListener("scroll", handleScroll);
1607
+ return () => window.removeEventListener("scroll", handleScroll);
1608
+ }, [glassTheme]);
1609
+ useEffect4(() => {
1610
+ const handleGlobalKeyDown = (e) => {
1611
+ if (e.key === "/" && expansionState === "collapsed" && !(e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)) {
1612
+ e.preventDefault();
1613
+ setExpansionState("expanded");
1614
+ }
1615
+ if ((e.ctrlKey || e.metaKey) && e.key === "k") {
1616
+ e.preventDefault();
1617
+ if (expansionState === "collapsed") {
1618
+ setExpansionState("expanded");
1619
+ } else {
1620
+ inputRef.current?.focus();
1621
+ }
1622
+ }
1623
+ };
1624
+ window.addEventListener("keydown", handleGlobalKeyDown);
1625
+ return () => window.removeEventListener("keydown", handleGlobalKeyDown);
1626
+ }, [expansionState]);
1627
+ useEffect4(() => {
1628
+ if (expansionState === "expanded" && inputRef.current) {
1629
+ setTimeout(() => {
1630
+ inputRef.current?.focus();
1631
+ }, 100);
1632
+ }
1633
+ }, [expansionState]);
1634
+ useEffect4(() => {
1635
+ const timeSinceLastInput = Date.now() - lastInputTimeRef.current;
1636
+ const context = {
1637
+ isListening,
1638
+ inputValue,
1639
+ lastInputMethod,
1640
+ timeSinceLastInput
1641
+ };
1642
+ const detectedMode = detectMode(context);
1643
+ if (isListening) {
1644
+ setOverlayState("listening");
1645
+ setOpacity(getOverlayOpacity("listening", isMobile));
1646
+ } else if (inputValue.trim()) {
1647
+ setOverlayState("typing");
1648
+ setOpacity(getOverlayOpacity("typing", isMobile));
1649
+ } else {
1650
+ setOverlayState("idle");
1651
+ if (isMobile) {
1652
+ setOpacity(1);
1653
+ }
1654
+ }
1655
+ }, [isListening, inputValue, lastInputMethod, isMobile]);
1656
+ const handleInputChange = (value) => {
1657
+ onInputChange(value);
1658
+ setLastInputMethod("text");
1659
+ lastInputTimeRef.current = Date.now();
1660
+ };
1661
+ const handleIconClick = () => {
1662
+ if (expansionState === "collapsed") {
1663
+ setExpansionState("expanded");
1664
+ if (!isListening && voiceEnabled) {
1665
+ onMicClick();
1666
+ setLastInputMethod("voice");
1667
+ lastInputTimeRef.current = Date.now();
1668
+ }
1669
+ } else {
1670
+ onMicClick();
1671
+ setLastInputMethod("voice");
1672
+ lastInputTimeRef.current = Date.now();
1673
+ }
1674
+ };
1675
+ const handleTouchStart = (e) => {
1676
+ setTouchStartY(e.touches[0].clientY);
1677
+ };
1678
+ const handleTouchMove = (e) => {
1679
+ if (touchStartY === null) return;
1680
+ const touchY = e.touches[0].clientY;
1681
+ const deltaY = touchY - touchStartY;
1682
+ if (deltaY > 50 && expansionState === "expanded") {
1683
+ setExpansionState("collapsed");
1684
+ setTouchStartY(null);
1685
+ }
1686
+ };
1687
+ const handleTouchEnd = () => {
1688
+ setTouchStartY(null);
1689
+ };
1690
+ const handleSend = () => {
1691
+ if (inputValue.trim()) {
1692
+ onSendMessage(inputValue);
1693
+ lastInputTimeRef.current = Date.now();
1694
+ }
1695
+ };
1696
+ const handleKeyDown = (e) => {
1697
+ if (e.key === "Enter" && inputValue.trim()) {
1698
+ e.preventDefault();
1699
+ handleSend();
1700
+ }
1701
+ if (e.key === "Escape" && isListening) {
1702
+ onMicClick();
1703
+ }
1704
+ };
1705
+ const handleSuggestionTap = () => {
1706
+ if (suggestions.length > 0 && !inputValue.trim()) {
1707
+ onInputChange(suggestions[currentSuggestionIndex].text);
1708
+ }
1709
+ };
1710
+ const handleInputTouchStart = (e) => {
1711
+ setTouchStartYInput(e.touches[0].clientY);
1712
+ };
1713
+ const handleInputTouchMove = (e) => {
1714
+ if (touchStartYInput === null || inputValue.trim()) return;
1715
+ const touchY = e.touches[0].clientY;
1716
+ const deltaY = touchY - touchStartYInput;
1717
+ if (deltaY < -30) {
1718
+ setCurrentSuggestionIndex((prev) => (prev + 1) % suggestions.length);
1719
+ setTouchStartYInput(null);
1720
+ } else if (deltaY > 30) {
1721
+ setCurrentSuggestionIndex((prev) => (prev - 1 + suggestions.length) % suggestions.length);
1722
+ setTouchStartYInput(null);
1723
+ }
1724
+ };
1725
+ const handleInputTouchEnd = () => {
1726
+ setTouchStartYInput(null);
1727
+ };
1728
+ const handleWidgetSelect = (widget) => {
1729
+ console.log("[SubtitleOverlay] Selecting widget:", widget);
1730
+ const wrapper = widget.element.closest(".supernal-tts-widget") || widget.element;
1731
+ const playButton = wrapper.querySelector(".supernal-tts-play");
1732
+ const scrollTarget = playButton || wrapper;
1733
+ console.log("[SubtitleOverlay] Scrolling to:", scrollTarget);
1734
+ scrollTarget.scrollIntoView({
1735
+ behavior: "smooth",
1736
+ block: "start",
1737
+ inline: "nearest"
1738
+ });
1739
+ setShowPlaylist(false);
1740
+ setTimeout(() => {
1741
+ if (playButton) {
1742
+ console.log("[SubtitleOverlay] Clicking Supernal TTS play button:", playButton);
1743
+ playButton.click();
1744
+ } else {
1745
+ console.warn("[SubtitleOverlay] No play button found in widget. Widget may not be initialized yet.");
1746
+ console.warn("[SubtitleOverlay] Widget element:", wrapper);
1747
+ console.warn("[SubtitleOverlay] Widget HTML:", wrapper.innerHTML.substring(0, 300));
1748
+ }
1749
+ }, 800);
1750
+ };
1751
+ const handleTTSPlaylistClick = () => {
1752
+ if (ttsWidgets.length === 1) {
1753
+ handleWidgetSelect(ttsWidgets[0]);
1754
+ } else {
1755
+ setShowPlaylist(!showPlaylist);
1756
+ }
1757
+ };
1758
+ const effectiveGlassTheme = glassTheme === "auto" ? detectedGlassTheme : glassTheme;
1759
+ const glassStyles = effectiveGlassTheme === "dark" ? GLASS_INVERTED.darkOnLight : GLASS_INVERTED.lightOnDark;
1760
+ const lastAiMessage = messages.filter((m) => m.type === "ai").slice(-1)[0];
1761
+ const getIcon = () => {
1762
+ if (expansionState === "collapsed") return "@/";
1763
+ if (isListening) return "~/";
1764
+ return "</";
1765
+ };
1766
+ const getIconTitle = () => {
1767
+ if (expansionState === "collapsed") return "Tap to open chat";
1768
+ if (isListening) return "Tap to stop recording";
1769
+ return "Tap to start voice input";
1770
+ };
1771
+ if (expansionState === "collapsed") {
1772
+ return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1773
+ hasTTSWidgets && /* @__PURE__ */ jsx10(
1774
+ "button",
1775
+ {
1776
+ type: "button",
1777
+ onClick: handleTTSPlaylistClick,
1778
+ className: `fixed p-3 rounded-full transition-all ${theme === "dark" ? "text-gray-400 hover:text-purple-400" : "text-gray-600 hover:text-purple-600"}`,
1779
+ style: {
1780
+ bottom: isMobile ? "calc(env(safe-area-inset-bottom, 0px) + 16px)" : "16px",
1781
+ left: "16px",
1782
+ zIndex: 55,
1783
+ background: theme === "dark" ? "rgba(55, 65, 81, 0.7)" : "rgba(255, 255, 255, 0.7)",
1784
+ backdropFilter: "blur(12px) saturate(180%)",
1785
+ WebkitBackdropFilter: "blur(12px) saturate(180%)",
1786
+ border: theme === "dark" ? "1px solid rgba(255, 255, 255, 0.12)" : "1px solid rgba(0, 0, 0, 0.1)",
1787
+ boxShadow: theme === "dark" ? "0 4px 16px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.1)" : "0 4px 16px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.9)"
1788
+ },
1789
+ title: ttsWidgets.length === 1 ? "Go to readable section" : "View readable sections",
1790
+ "data-testid": "tts-playlist-button",
1791
+ "aria-label": ttsWidgets.length === 1 ? "Go to readable section" : "View readable sections",
1792
+ children: /* @__PURE__ */ jsx10("span", { className: "text-xl font-bold select-none", "aria-hidden": "true", children: "~+" })
1793
+ }
1794
+ ),
1795
+ /* @__PURE__ */ jsx10(
1796
+ TTSPlaylistMenu,
1797
+ {
1798
+ isOpen: showPlaylist,
1799
+ onClose: () => setShowPlaylist(false),
1800
+ widgets: ttsWidgets,
1801
+ theme,
1802
+ onWidgetSelect: handleWidgetSelect
1803
+ }
1804
+ ),
1805
+ /* @__PURE__ */ jsx10(
1806
+ "div",
1807
+ {
1808
+ className: "fixed",
1809
+ style: {
1810
+ bottom: isMobile ? "calc(env(safe-area-inset-bottom, 0px) + 16px)" : "16px",
1811
+ right: "16px",
1812
+ zIndex: 55
1813
+ },
1814
+ "data-testid": "subtitle-overlay-collapsed",
1815
+ children: /* @__PURE__ */ jsx10(
1816
+ "button",
1817
+ {
1818
+ type: "button",
1819
+ onClick: handleIconClick,
1820
+ className: `p-3 rounded-full transition-all ${theme === "dark" ? "text-gray-400 hover:text-blue-400" : "text-gray-600 hover:text-blue-600"}`,
1821
+ style: {
1822
+ background: theme === "dark" ? "rgba(55, 65, 81, 0.7)" : "rgba(255, 255, 255, 0.7)",
1823
+ backdropFilter: "blur(12px) saturate(180%)",
1824
+ WebkitBackdropFilter: "blur(12px) saturate(180%)",
1825
+ border: theme === "dark" ? "1px solid rgba(255, 255, 255, 0.12)" : "1px solid rgba(0, 0, 0, 0.1)",
1826
+ boxShadow: theme === "dark" ? "0 4px 16px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.1)" : "0 4px 16px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.9)"
1827
+ },
1828
+ title: getIconTitle(),
1829
+ "data-testid": "voice-input-button",
1830
+ "aria-label": getIconTitle(),
1831
+ children: /* @__PURE__ */ jsx10("span", { className: "text-xl font-bold select-none", "aria-hidden": "true", children: getIcon() })
1832
+ }
1833
+ )
1834
+ }
1835
+ )
1836
+ ] });
1837
+ }
1838
+ return /* @__PURE__ */ jsxs6(
1839
+ "div",
1840
+ {
1841
+ className: `fixed bottom-0 left-0 right-0 transition-all duration-300 ease-in-out`,
1842
+ style: {
1843
+ opacity,
1844
+ bottom: isMobile ? "env(safe-area-inset-bottom, 0px)" : "0px",
1845
+ zIndex: 55,
1846
+ maxWidth: isMobile ? "100vw" : "650px",
1847
+ marginLeft: isMobile ? "0" : "auto",
1848
+ marginRight: isMobile ? "0" : "auto",
1849
+ padding: isMobile ? "12px" : "16px"
1850
+ },
1851
+ onTouchStart: handleTouchStart,
1852
+ onTouchMove: handleTouchMove,
1853
+ onTouchEnd: handleTouchEnd,
1854
+ "data-testid": "subtitle-overlay",
1855
+ role: "complementary",
1856
+ "aria-label": "Chat overlay",
1857
+ children: [
1858
+ lastAiMessage && messageOpacity > 0 && /* @__PURE__ */ jsxs6(
1859
+ "div",
1860
+ {
1861
+ className: `mb-2 px-4 py-2 text-sm rounded-2xl transition-opacity duration-1000 ease-out animate-popup-in ${theme === "dark" ? "text-white" : "text-gray-900"}`,
1862
+ style: {
1863
+ opacity: messageOpacity,
1864
+ pointerEvents: messageOpacity === 0 ? "none" : "auto",
1865
+ ...theme === "dark" ? GLASS_RESPONSE_BUBBLE.dark : GLASS_RESPONSE_BUBBLE.light,
1866
+ animation: messageOpacity === 0 ? "popupFadeOut 0.5s forwards" : void 0
1867
+ },
1868
+ role: "status",
1869
+ "aria-live": "polite",
1870
+ children: [
1871
+ /* @__PURE__ */ jsx10("span", { className: "font-medium opacity-70", children: "AI:" }),
1872
+ " ",
1873
+ lastAiMessage.text
1874
+ ]
1875
+ }
1876
+ ),
1877
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center space-x-2", children: [
1878
+ hasTTSWidgets && /* @__PURE__ */ jsx10(
1879
+ "button",
1880
+ {
1881
+ type: "button",
1882
+ onClick: handleTTSPlaylistClick,
1883
+ className: `p-2 rounded-full transition-all flex-shrink-0 ${theme === "dark" ? "text-gray-400 hover:text-purple-400" : "text-gray-600 hover:text-purple-600"}`,
1884
+ style: {
1885
+ background: theme === "dark" ? "rgba(55, 65, 81, 0.5)" : "rgba(243, 244, 246, 0.5)",
1886
+ backdropFilter: "blur(8px)",
1887
+ WebkitBackdropFilter: "blur(8px)",
1888
+ border: theme === "dark" ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid rgba(0, 0, 0, 0.08)"
1889
+ },
1890
+ title: ttsWidgets.length === 1 ? "Go to readable section" : "View readable sections",
1891
+ "data-testid": "tts-playlist-button-expanded",
1892
+ "aria-label": ttsWidgets.length === 1 ? "Go to readable section" : "View readable sections",
1893
+ children: /* @__PURE__ */ jsx10("span", { className: "text-lg font-bold select-none", "aria-hidden": "true", children: "~+" })
1894
+ }
1895
+ ),
1896
+ /* @__PURE__ */ jsxs6(
1897
+ "div",
1898
+ {
1899
+ className: "flex-1 flex items-center space-x-2 px-4 py-2 rounded-3xl",
1900
+ style: {
1901
+ ...glassStyles,
1902
+ border: effectiveGlassTheme === "dark" ? "1px solid rgba(255, 255, 255, 0.12)" : "1px solid rgba(0, 0, 0, 0.1)"
1903
+ },
1904
+ children: [
1905
+ /* @__PURE__ */ jsx10(
1906
+ "input",
1907
+ {
1908
+ ref: inputRef,
1909
+ type: "text",
1910
+ value: inputValue,
1911
+ onChange: (e) => handleInputChange(e.target.value),
1912
+ onKeyDown: handleKeyDown,
1913
+ onTouchStart: handleInputTouchStart,
1914
+ onTouchMove: handleInputTouchMove,
1915
+ onTouchEnd: handleInputTouchEnd,
1916
+ onClick: handleSuggestionTap,
1917
+ placeholder: suggestions.length > 0 && !inputValue.trim() ? `${suggestions[currentSuggestionIndex].text} (tap to use, swipe \u2195 to change)` : config.placeholder || "Type or speak...",
1918
+ className: "flex-1 px-3 py-2 text-sm bg-transparent focus:outline-none placeholder:opacity-60",
1919
+ style: {
1920
+ color: effectiveGlassTheme === "dark" ? "rgba(255, 255, 255, 0.95)" : "rgba(0, 0, 0, 0.9)"
1921
+ },
1922
+ "data-testid": "chat-input",
1923
+ "aria-label": "Chat message input"
1924
+ }
1925
+ ),
1926
+ isListening && /* @__PURE__ */ jsx10("div", { className: "flex space-x-1 items-center", "aria-hidden": "true", children: [...Array(5)].map((_, i) => /* @__PURE__ */ jsx10(
1927
+ "div",
1928
+ {
1929
+ className: "w-0.5 bg-red-500 rounded-full animate-pulse",
1930
+ style: {
1931
+ height: `${8 + Math.random() * 8}px`,
1932
+ animationDelay: `${i * 0.1}s`
1933
+ }
1934
+ },
1935
+ i
1936
+ )) }),
1937
+ inputValue.trim() && !isListening && /* @__PURE__ */ jsx10(
1938
+ "button",
1939
+ {
1940
+ type: "button",
1941
+ onClick: handleSend,
1942
+ className: "p-2 rounded-full text-white transition-all flex-shrink-0",
1943
+ style: {
1944
+ background: "linear-gradient(135deg, rgba(59, 130, 246, 0.9), rgba(37, 99, 235, 0.95))",
1945
+ backdropFilter: "blur(8px)",
1946
+ WebkitBackdropFilter: "blur(8px)",
1947
+ boxShadow: "0 0 12px rgba(59, 130, 246, 0.3), 0 2px 6px rgba(0, 0, 0, 0.2)",
1948
+ border: "1px solid rgba(255, 255, 255, 0.2)"
1949
+ },
1950
+ title: "Send message",
1951
+ "data-testid": "send-button",
1952
+ "aria-label": "Send message",
1953
+ children: /* @__PURE__ */ jsx10("span", { className: "text-base select-none", "aria-hidden": "true", children: "\u2192" })
1954
+ }
1955
+ ),
1956
+ voiceEnabled && /* @__PURE__ */ jsx10(
1957
+ "button",
1958
+ {
1959
+ type: "button",
1960
+ onClick: handleIconClick,
1961
+ className: `p-2 rounded-full transition-all flex-shrink-0 ${isListening ? "text-white animate-pulse" : `${theme === "dark" ? "text-gray-400 hover:text-blue-400" : "text-gray-600 hover:text-blue-600"}`}`,
1962
+ style: isListening ? {
1963
+ background: "linear-gradient(135deg, rgba(239, 68, 68, 0.8), rgba(220, 38, 38, 0.9))",
1964
+ backdropFilter: "blur(8px)",
1965
+ WebkitBackdropFilter: "blur(8px)",
1966
+ boxShadow: "0 0 16px rgba(239, 68, 68, 0.5), 0 2px 6px rgba(0, 0, 0, 0.2)"
1967
+ } : {
1968
+ background: theme === "dark" ? "rgba(55, 65, 81, 0.5)" : "rgba(243, 244, 246, 0.5)",
1969
+ backdropFilter: "blur(8px)",
1970
+ WebkitBackdropFilter: "blur(8px)",
1971
+ border: theme === "dark" ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid rgba(0, 0, 0, 0.08)"
1972
+ },
1973
+ title: getIconTitle(),
1974
+ "data-testid": "voice-input-button",
1975
+ "aria-label": getIconTitle(),
1976
+ children: /* @__PURE__ */ jsx10("span", { className: "text-lg font-bold select-none", "aria-hidden": "true", children: getIcon() })
1977
+ }
1978
+ ),
1979
+ /* @__PURE__ */ jsx10(
1980
+ "button",
1981
+ {
1982
+ type: "button",
1983
+ onClick: () => setExpansionState("collapsed"),
1984
+ className: `p-2 rounded-full transition-all flex-shrink-0 ${theme === "dark" ? "text-gray-400 hover:text-red-400" : "text-gray-600 hover:text-red-600"}`,
1985
+ style: {
1986
+ background: theme === "dark" ? "rgba(55, 65, 81, 0.5)" : "rgba(243, 244, 246, 0.5)",
1987
+ backdropFilter: "blur(8px)",
1988
+ WebkitBackdropFilter: "blur(8px)",
1989
+ border: theme === "dark" ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid rgba(0, 0, 0, 0.08)"
1990
+ },
1991
+ title: "Collapse",
1992
+ "data-testid": "collapse-button",
1993
+ "aria-label": "Collapse overlay",
1994
+ children: /* @__PURE__ */ jsx10("span", { className: "text-lg font-bold select-none", "aria-hidden": "true", children: "\xD7" })
1995
+ }
1996
+ )
1997
+ ]
1998
+ }
1999
+ )
2000
+ ] })
2001
+ ]
2002
+ }
2003
+ );
2004
+ };
2005
+
2006
+ // src/components/ChatBubble/ChatBubble.tsx
2007
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
580
2008
  var ChatBubble = ({
581
2009
  messages,
582
2010
  onSendMessage,
@@ -585,27 +2013,78 @@ var ChatBubble = ({
585
2013
  variant = "full",
586
2014
  config: userConfig,
587
2015
  defaultExpanded = true,
588
- storageKey = "chat-bubble-state"
2016
+ storageKey = "chat-bubble-state",
2017
+ displayMode: propDisplayMode = "auto",
2018
+ drawerSide: propDrawerSide = "right"
589
2019
  }) => {
590
- const config = { ...DEFAULT_CONFIG, ...userConfig };
591
- const [isExpanded, setIsExpanded] = useState2(defaultExpanded);
592
- const [isMinimized, setIsMinimized] = useState2(false);
593
- const [inputValue, setInputValue] = useState2("");
594
- const [lastReadMessageCount, setLastReadMessageCount] = useState2(0);
595
- const [showWelcome, setShowWelcome] = useState2(
2020
+ const mergedConfig = { ...DEFAULT_CONFIG, ...userConfig };
2021
+ if (userConfig?.logo && !userConfig?.avatar) {
2022
+ mergedConfig.avatar = /* @__PURE__ */ jsx11("img", { src: userConfig.logo, alt: "Supernal", className: "w-6 h-6" });
2023
+ }
2024
+ const config = mergedConfig;
2025
+ const [isExpanded, setIsExpanded] = useState7(defaultExpanded);
2026
+ const [isMinimized, setIsMinimized] = useState7(false);
2027
+ const [inputValue, setInputValue] = useState7("");
2028
+ const [lastReadMessageCount, setLastReadMessageCount] = useState7(0);
2029
+ const [showWelcome, setShowWelcome] = useState7(
596
2030
  config.welcome?.enabled && messages.length === 0
597
2031
  );
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);
605
- const messagesEndRef = useRef2(null);
606
- const inputRef = useRef2(null);
607
- const panelRef = useRef2(null);
608
- const dragRef = useRef2(null);
2032
+ const [isDragging, setIsDragging] = useState7(false);
2033
+ const [dragInitiated, setDragInitiated] = useState7(false);
2034
+ const [isDocked, setIsDocked] = useState7(true);
2035
+ const [dockPosition, setDockPosition] = useState7(position);
2036
+ const [panelPosition, setPanelPosition] = useState7({ x: 0, y: 0 });
2037
+ const [theme, setTheme] = useState7("light");
2038
+ const [showMoreMenu, setShowMoreMenu] = useState7(false);
2039
+ const [, setTimestampTick] = useState7(0);
2040
+ const [localGlassMode, setLocalGlassMode] = useState7(config.glassMode ?? true);
2041
+ const [glassOpacity, setGlassOpacity] = useState7("medium");
2042
+ const [notifications, setNotifications] = useState7(true);
2043
+ const [voiceEnabled, setVoiceEnabled] = useState7(true);
2044
+ const [usePremiumVoices, setUsePremiumVoices] = useState7(false);
2045
+ const [autoReadResponses, setAutoReadResponses] = useState7(false);
2046
+ const [ttsSpeed, setTtsSpeed] = useState7(1);
2047
+ const [sttAutoRecordTimeout, setSttAutoRecordTimeout] = useState7(5e3);
2048
+ const [sttAutoExecute, setSttAutoExecute] = useState7(true);
2049
+ const sttAutoRecordTimeoutRef = useRef6(null);
2050
+ const { speak: speakTTS, stop: stopTTS, isPlaying: isTTSPlaying } = useTTS();
2051
+ const { startListening, stopListening, transcript: sttTranscript, isListening, resetTranscript } = useSTT();
2052
+ const [displayMode, setDisplayMode] = useState7(propDisplayMode);
2053
+ const [drawerSide, setDrawerSide] = useState7(propDrawerSide);
2054
+ useEffect5(() => {
2055
+ setDisplayMode(propDisplayMode);
2056
+ }, [propDisplayMode]);
2057
+ const [drawerOpen, setDrawerOpen] = useState7(false);
2058
+ const [touchStart, setTouchStart] = useState7(null);
2059
+ const [swipeProgress, setSwipeProgress] = useState7(0);
2060
+ const [isMobile, setIsMobile] = useState7(false);
2061
+ const [isMac, setIsMac] = useState7(false);
2062
+ const [currentHintIndex, setCurrentHintIndex] = useState7(0);
2063
+ const [isHydrated, setIsHydrated] = useState7(false);
2064
+ useEffect5(() => {
2065
+ if (typeof window !== "undefined") {
2066
+ const platform = window.navigator.platform.toLowerCase();
2067
+ const isMacPlatform = platform.includes("mac");
2068
+ setIsMac(isMacPlatform);
2069
+ }
2070
+ }, []);
2071
+ const inputHints = useMemo2(() => {
2072
+ const modKey = isMac ? "Cmd" : "Ctrl";
2073
+ return [
2074
+ `Press ${modKey}+/ to start voice recording`,
2075
+ "Press ESC to close this chat",
2076
+ "Type your message or click the mic",
2077
+ sttAutoExecute ? `Voice commands execute automatically` : "Voice commands fill this input"
2078
+ ];
2079
+ }, [isMac, sttAutoExecute]);
2080
+ useEffect5(() => {
2081
+ setCurrentHintIndex((prev) => (prev + 1) % inputHints.length);
2082
+ }, [messages.length, inputHints.length]);
2083
+ const messagesEndRef = useRef6(null);
2084
+ const inputRef = useRef6(null);
2085
+ const panelRef = useRef6(null);
2086
+ const dragRef = useRef6(null);
2087
+ const rafRef = useRef6(null);
609
2088
  const formatRelativeTime = (timestamp) => {
610
2089
  const now = /* @__PURE__ */ new Date();
611
2090
  const messageTime = new Date(timestamp);
@@ -620,48 +2099,178 @@ var ChatBubble = ({
620
2099
  if (diffDays < 7) return `${diffDays} ${diffDays === 1 ? "day" : "days"} ago`;
621
2100
  return messageTime.toLocaleDateString();
622
2101
  };
623
- useEffect2(() => {
2102
+ useEffect5(() => {
624
2103
  if (variant === "full") {
625
2104
  try {
626
2105
  const stored = localStorage.getItem(storageKey);
627
2106
  if (stored !== null) {
628
2107
  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");
2108
+ const windowWidth = typeof window !== "undefined" ? window.innerWidth : 1920;
2109
+ const windowHeight = typeof window !== "undefined" ? window.innerHeight : 1080;
2110
+ const pos = state.panelPosition || { x: 0, y: 0 };
2111
+ const isInvalidPosition = Math.abs(pos.x) > windowWidth * 2 || Math.abs(pos.y) > windowHeight * 2;
2112
+ if (isInvalidPosition) {
2113
+ console.warn("ChatBubble: Invalid stored position detected, resetting to docked");
2114
+ setIsExpanded(defaultExpanded);
2115
+ setIsMinimized(false);
2116
+ setIsDocked(true);
2117
+ setDockPosition(position);
2118
+ setPanelPosition({ x: 0, y: 0 });
2119
+ setTheme(state.theme || "light");
2120
+ if (state.localGlassMode !== void 0) {
2121
+ setLocalGlassMode(state.localGlassMode);
2122
+ }
2123
+ if (state.glassOpacity !== void 0) {
2124
+ setGlassOpacity(state.glassOpacity);
2125
+ }
2126
+ if (state.notifications !== void 0) {
2127
+ setNotifications(state.notifications);
2128
+ }
2129
+ } else {
2130
+ setIsExpanded(state.isExpanded ?? defaultExpanded);
2131
+ setIsMinimized(state.isMinimized ?? false);
2132
+ setIsDocked(state.isDocked ?? true);
2133
+ setDockPosition(state.dockPosition || position);
2134
+ setPanelPosition(pos);
2135
+ setTheme(state.theme || "light");
2136
+ if (state.localGlassMode !== void 0) {
2137
+ setLocalGlassMode(state.localGlassMode);
2138
+ }
2139
+ if (state.notifications !== void 0) {
2140
+ setNotifications(state.notifications);
2141
+ }
2142
+ if (state.displayMode !== void 0) {
2143
+ setDisplayMode(state.displayMode);
2144
+ }
2145
+ if (state.drawerSide !== void 0) {
2146
+ setDrawerSide(state.drawerSide);
2147
+ }
2148
+ if (state.drawerOpen !== void 0) {
2149
+ setDrawerOpen(state.drawerOpen);
2150
+ }
2151
+ if (state.glassOpacity !== void 0) {
2152
+ setGlassOpacity(state.glassOpacity);
2153
+ }
2154
+ if (state.voiceEnabled !== void 0) {
2155
+ setVoiceEnabled(state.voiceEnabled);
2156
+ }
2157
+ if (state.usePremiumVoices !== void 0) {
2158
+ setUsePremiumVoices(state.usePremiumVoices);
2159
+ }
2160
+ if (state.autoReadResponses !== void 0) {
2161
+ setAutoReadResponses(state.autoReadResponses);
2162
+ }
2163
+ if (state.ttsSpeed !== void 0) {
2164
+ setTtsSpeed(state.ttsSpeed);
2165
+ }
2166
+ if (state.sttAutoRecordTimeout !== void 0) {
2167
+ setSttAutoRecordTimeout(state.sttAutoRecordTimeout);
2168
+ }
2169
+ if (state.sttAutoExecute !== void 0) {
2170
+ setSttAutoExecute(state.sttAutoExecute);
2171
+ }
2172
+ }
634
2173
  }
635
2174
  } catch {
636
2175
  }
637
2176
  }
638
- }, [storageKey, variant, defaultExpanded]);
639
- useEffect2(() => {
2177
+ setIsHydrated(true);
2178
+ }, [storageKey, variant, defaultExpanded, position]);
2179
+ useEffect5(() => {
2180
+ if (variant !== "full") {
2181
+ setIsHydrated(true);
2182
+ }
2183
+ }, [variant]);
2184
+ useEffect5(() => {
2185
+ if (!isExpanded || isDocked || !panelRef.current) return;
2186
+ const checkBounds = () => {
2187
+ const rect = panelRef.current?.getBoundingClientRect();
2188
+ if (!rect) return;
2189
+ const windowWidth = window.innerWidth;
2190
+ const windowHeight = window.innerHeight;
2191
+ const isOffScreen = rect.right < 0 || rect.left > windowWidth || rect.bottom < 0 || rect.top > windowHeight;
2192
+ if (isOffScreen) {
2193
+ console.warn("ChatBubble detected off-screen, resetting to docked position");
2194
+ setIsDocked(true);
2195
+ setPanelPosition({ x: 0, y: 0 });
2196
+ }
2197
+ };
2198
+ const timeoutId = setTimeout(checkBounds, 100);
2199
+ return () => clearTimeout(timeoutId);
2200
+ }, [isExpanded, isDocked]);
2201
+ useEffect5(() => {
2202
+ const handleKeyDown = (e) => {
2203
+ if (e.key === "Escape" && isExpanded && !isDocked) {
2204
+ console.log("ChatBubble reset via Escape key");
2205
+ setIsDocked(true);
2206
+ setDockPosition(position);
2207
+ setPanelPosition({ x: 0, y: 0 });
2208
+ }
2209
+ };
2210
+ window.addEventListener("keydown", handleKeyDown);
2211
+ return () => window.removeEventListener("keydown", handleKeyDown);
2212
+ }, [isExpanded, isDocked, position]);
2213
+ useEffect5(() => {
640
2214
  if (typeof window !== "undefined") {
641
2215
  const isDark = document.documentElement.getAttribute("data-theme") === "dark";
642
2216
  setTheme(isDark ? "dark" : "light");
643
2217
  }
644
2218
  }, []);
645
- useEffect2(() => {
2219
+ useEffect5(() => {
2220
+ if (typeof window === "undefined") return;
2221
+ const mediaQuery = window.matchMedia("(max-width: 767px)");
2222
+ const handleChange = (e) => {
2223
+ setIsMobile(e.matches);
2224
+ };
2225
+ handleChange(mediaQuery);
2226
+ mediaQuery.addEventListener("change", handleChange);
2227
+ return () => mediaQuery.removeEventListener("change", handleChange);
2228
+ }, []);
2229
+ const currentVariant = React7.useMemo(() => {
2230
+ if (displayMode !== "auto") {
2231
+ return displayMode;
2232
+ }
2233
+ return isMobile ? "drawer" : variant;
2234
+ }, [displayMode, isMobile, variant]);
2235
+ useEffect5(() => {
646
2236
  const interval = setInterval(() => {
647
2237
  setTimestampTick((tick) => tick + 1);
648
2238
  }, 6e4);
649
2239
  return () => clearInterval(interval);
650
2240
  }, []);
651
- useEffect2(() => {
652
- if (variant === "full") {
2241
+ useEffect5(() => {
2242
+ if (variant === "full" || variant === "drawer") {
653
2243
  try {
654
2244
  localStorage.setItem(
655
2245
  storageKey,
656
- JSON.stringify({ isExpanded, isMinimized, isDocked, panelPosition, theme })
2246
+ JSON.stringify({
2247
+ isExpanded,
2248
+ isMinimized,
2249
+ isDocked,
2250
+ dockPosition,
2251
+ panelPosition,
2252
+ theme,
2253
+ localGlassMode,
2254
+ notifications,
2255
+ displayMode,
2256
+ drawerSide,
2257
+ drawerOpen,
2258
+ glassOpacity,
2259
+ voiceEnabled,
2260
+ usePremiumVoices,
2261
+ autoReadResponses,
2262
+ ttsSpeed,
2263
+ sttAutoRecordTimeout,
2264
+ sttAutoExecute
2265
+ })
657
2266
  );
658
2267
  } catch (error) {
659
2268
  console.error("Failed to save chat state:", error);
660
2269
  }
661
2270
  }
662
- }, [isExpanded, isMinimized, isDocked, panelPosition, theme, storageKey, variant]);
2271
+ }, [isExpanded, isMinimized, isDocked, dockPosition, panelPosition, theme, localGlassMode, notifications, displayMode, drawerSide, drawerOpen, glassOpacity, voiceEnabled, usePremiumVoices, autoReadResponses, ttsSpeed, sttAutoRecordTimeout, sttAutoExecute, storageKey, variant]);
663
2272
  const { registerInput } = useChatInput();
664
- useEffect2(() => {
2273
+ useEffect5(() => {
665
2274
  registerInput((text, submit = false) => {
666
2275
  setInputValue(text);
667
2276
  if (!isExpanded && variant === "full") {
@@ -678,7 +2287,7 @@ var ChatBubble = ({
678
2287
  }, [registerInput, onSendMessage]);
679
2288
  const unreadCount = Math.max(0, messages.length - lastReadMessageCount);
680
2289
  const hasUnread = unreadCount > 0 && !isExpanded && variant === "full";
681
- useEffect2(() => {
2290
+ useEffect5(() => {
682
2291
  if (isExpanded || variant === "floating") {
683
2292
  messagesEndRef.current?.scrollIntoView({ behavior: "auto" });
684
2293
  setLastReadMessageCount(messages.length);
@@ -690,15 +2299,67 @@ var ChatBubble = ({
690
2299
  }
691
2300
  }
692
2301
  }, [messages, isExpanded, variant]);
693
- useEffect2(() => {
2302
+ useEffect5(() => {
694
2303
  if (isExpanded && variant === "full") {
695
2304
  inputRef.current?.focus();
696
2305
  }
697
2306
  }, [isExpanded, variant]);
698
- useEffect2(() => {
2307
+ useEffect5(() => {
2308
+ if (!voiceEnabled || !autoReadResponses || messages.length === 0) return;
2309
+ const lastMessage = messages[messages.length - 1];
2310
+ if (lastMessage.type === "ai") {
2311
+ speakTTS({
2312
+ text: lastMessage.text,
2313
+ speed: ttsSpeed,
2314
+ usePremium: usePremiumVoices,
2315
+ preferNative: !usePremiumVoices
2316
+ });
2317
+ }
2318
+ }, [messages, voiceEnabled, autoReadResponses, ttsSpeed, usePremiumVoices, speakTTS]);
2319
+ useEffect5(() => {
2320
+ if (sttTranscript && voiceEnabled) {
2321
+ setInputValue(sttTranscript);
2322
+ resetTranscript();
2323
+ if (sttAutoExecute && sttAutoRecordTimeoutRef.current) {
2324
+ onSendMessage(sttTranscript);
2325
+ setInputValue("");
2326
+ }
2327
+ if (sttAutoRecordTimeoutRef.current) {
2328
+ clearTimeout(sttAutoRecordTimeoutRef.current);
2329
+ sttAutoRecordTimeoutRef.current = null;
2330
+ }
2331
+ }
2332
+ }, [sttTranscript, voiceEnabled, resetTranscript, sttAutoExecute, onSendMessage]);
2333
+ useEffect5(() => {
699
2334
  const handleKeyDown = (e) => {
700
2335
  if (variant !== "full") return;
701
- if (e.key === "/" && !isExpanded) {
2336
+ if ((e.metaKey || e.ctrlKey) && e.key === "/" && voiceEnabled) {
2337
+ e.preventDefault();
2338
+ const wasExpanded = isExpanded;
2339
+ if (!isExpanded) {
2340
+ setIsExpanded(true);
2341
+ }
2342
+ if (!isListening) {
2343
+ const startRecording = async () => {
2344
+ try {
2345
+ await startListening();
2346
+ sttAutoRecordTimeoutRef.current = setTimeout(() => {
2347
+ stopListening();
2348
+ sttAutoRecordTimeoutRef.current = null;
2349
+ }, sttAutoRecordTimeout);
2350
+ } catch (error) {
2351
+ console.error("[ChatBubble] Failed to start recording:", error);
2352
+ }
2353
+ };
2354
+ if (wasExpanded) {
2355
+ startRecording();
2356
+ } else {
2357
+ setTimeout(startRecording, 300);
2358
+ }
2359
+ }
2360
+ return;
2361
+ }
2362
+ if (e.key === "/" && !e.shiftKey && !e.ctrlKey && !e.metaKey && !isExpanded) {
702
2363
  const target = e.target;
703
2364
  if (target.tagName !== "INPUT" && target.tagName !== "TEXTAREA") {
704
2365
  e.preventDefault();
@@ -707,26 +2368,37 @@ var ChatBubble = ({
707
2368
  }
708
2369
  }
709
2370
  if (e.key === "Escape") {
2371
+ if (sttAutoRecordTimeoutRef.current) {
2372
+ clearTimeout(sttAutoRecordTimeoutRef.current);
2373
+ sttAutoRecordTimeoutRef.current = null;
2374
+ stopListening();
2375
+ }
710
2376
  if (showMoreMenu) {
711
2377
  setShowMoreMenu(false);
712
- } else if (showInfo) {
713
- setShowInfo(false);
714
2378
  } else if (isExpanded) {
715
2379
  setIsExpanded(false);
716
2380
  }
717
2381
  }
718
- if ((e.metaKey || e.ctrlKey) && e.key === "/") {
719
- e.preventDefault();
720
- if (!isExpanded) {
721
- setIsExpanded(true);
722
- }
723
- inputRef.current?.focus();
2382
+ };
2383
+ window.addEventListener("keydown", handleKeyDown);
2384
+ return () => {
2385
+ window.removeEventListener("keydown", handleKeyDown);
2386
+ if (sttAutoRecordTimeoutRef.current) {
2387
+ clearTimeout(sttAutoRecordTimeoutRef.current);
2388
+ }
2389
+ };
2390
+ }, [isExpanded, showMoreMenu, variant, voiceEnabled, isListening, startListening, stopListening, sttAutoRecordTimeout]);
2391
+ useEffect5(() => {
2392
+ if (currentVariant !== "drawer") return;
2393
+ const handleKeyDown = (e) => {
2394
+ if (e.key === "Escape" && drawerOpen) {
2395
+ setDrawerOpen(false);
724
2396
  }
725
2397
  };
726
2398
  window.addEventListener("keydown", handleKeyDown);
727
2399
  return () => window.removeEventListener("keydown", handleKeyDown);
728
- }, [isExpanded, showInfo, showMoreMenu, variant]);
729
- useEffect2(() => {
2400
+ }, [currentVariant, drawerOpen]);
2401
+ useEffect5(() => {
730
2402
  if (!showMoreMenu) return;
731
2403
  const handleClickOutside = (e) => {
732
2404
  const target = e.target;
@@ -737,6 +2409,59 @@ var ChatBubble = ({
737
2409
  document.addEventListener("mousedown", handleClickOutside);
738
2410
  return () => document.removeEventListener("mousedown", handleClickOutside);
739
2411
  }, [showMoreMenu]);
2412
+ useEffect5(() => {
2413
+ if (typeof window === "undefined" || currentVariant !== "drawer") return;
2414
+ const EDGE_ZONE_PX = 20;
2415
+ const SWIPE_THRESHOLD = 0.4;
2416
+ const VELOCITY_THRESHOLD = 0.5;
2417
+ const handleTouchStart = (e) => {
2418
+ const touch = e.touches[0];
2419
+ const isRightEdge = touch.clientX > window.innerWidth - EDGE_ZONE_PX;
2420
+ const isLeftEdge = touch.clientX < EDGE_ZONE_PX;
2421
+ if (drawerSide === "right" && isRightEdge || drawerSide === "left" && isLeftEdge) {
2422
+ setTouchStart({
2423
+ x: touch.clientX,
2424
+ y: touch.clientY,
2425
+ time: Date.now()
2426
+ });
2427
+ }
2428
+ };
2429
+ const handleTouchMove = (e) => {
2430
+ if (!touchStart || drawerOpen) return;
2431
+ const touch = e.touches[0];
2432
+ const deltaX = touch.clientX - touchStart.x;
2433
+ const deltaY = Math.abs(touch.clientY - touchStart.y);
2434
+ if (deltaY > 10 && Math.abs(deltaX) < deltaY) {
2435
+ setTouchStart(null);
2436
+ return;
2437
+ }
2438
+ const screenWidth = window.innerWidth;
2439
+ const direction = drawerSide === "right" ? -1 : 1;
2440
+ const progress = Math.max(0, Math.min(100, deltaX * direction / screenWidth * 100));
2441
+ setSwipeProgress(progress);
2442
+ if (Math.abs(deltaX) > 10) {
2443
+ e.preventDefault();
2444
+ }
2445
+ };
2446
+ const handleTouchEnd = () => {
2447
+ if (!touchStart) return;
2448
+ const deltaTime = Date.now() - touchStart.time;
2449
+ const velocity = swipeProgress / deltaTime;
2450
+ if (swipeProgress > SWIPE_THRESHOLD * 100 || velocity > VELOCITY_THRESHOLD) {
2451
+ setDrawerOpen(true);
2452
+ }
2453
+ setTouchStart(null);
2454
+ setSwipeProgress(0);
2455
+ };
2456
+ window.addEventListener("touchstart", handleTouchStart, { passive: true });
2457
+ window.addEventListener("touchmove", handleTouchMove, { passive: false });
2458
+ window.addEventListener("touchend", handleTouchEnd);
2459
+ return () => {
2460
+ window.removeEventListener("touchstart", handleTouchStart);
2461
+ window.removeEventListener("touchmove", handleTouchMove);
2462
+ window.removeEventListener("touchend", handleTouchEnd);
2463
+ };
2464
+ }, [touchStart, swipeProgress, drawerOpen, drawerSide, currentVariant]);
740
2465
  const handlePanelMouseDown = (e) => {
741
2466
  if (variant !== "full" || !isExpanded) return;
742
2467
  const target = e.target;
@@ -745,56 +2470,105 @@ var ChatBubble = ({
745
2470
  return;
746
2471
  }
747
2472
  e.preventDefault();
748
- setIsDragging(true);
2473
+ setDragInitiated(true);
749
2474
  const rect = panelRef.current?.getBoundingClientRect();
750
2475
  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
- };
2476
+ if (isDocked) {
2477
+ const currentCenterX = rect.left + rect.width / 2;
2478
+ const currentCenterY = rect.top + rect.height / 2;
2479
+ const viewportCenterX = window.innerWidth / 2;
2480
+ const viewportCenterY = window.innerHeight / 2;
2481
+ const targetX = currentCenterX - viewportCenterX;
2482
+ const targetY = currentCenterY - viewportCenterY;
2483
+ setPanelPosition({ x: targetX, y: targetY });
2484
+ dragRef.current = {
2485
+ startX: e.clientX,
2486
+ startY: e.clientY,
2487
+ initialX: targetX,
2488
+ initialY: targetY,
2489
+ thresholdMet: false
2490
+ };
2491
+ } else {
2492
+ dragRef.current = {
2493
+ startX: e.clientX,
2494
+ startY: e.clientY,
2495
+ initialX: panelPosition.x,
2496
+ initialY: panelPosition.y,
2497
+ thresholdMet: false
2498
+ };
2499
+ }
765
2500
  };
766
- useEffect2(() => {
767
- if (!isDragging || !dragRef.current) return;
2501
+ useEffect5(() => {
2502
+ if (!dragInitiated || !dragRef.current) return;
2503
+ const dragThresholdPx = 5;
768
2504
  const handleMouseMove = (e) => {
769
2505
  if (!dragRef.current) return;
770
2506
  const deltaX = e.clientX - dragRef.current.startX;
771
2507
  const deltaY = e.clientY - dragRef.current.startY;
772
- setPanelPosition({
773
- x: dragRef.current.initialX + deltaX,
774
- y: dragRef.current.initialY + deltaY
2508
+ const distance = Math.sqrt(deltaX ** 2 + deltaY ** 2);
2509
+ if (!dragRef.current.thresholdMet && distance < dragThresholdPx) {
2510
+ return;
2511
+ }
2512
+ if (!dragRef.current.thresholdMet) {
2513
+ dragRef.current.thresholdMet = true;
2514
+ setIsDragging(true);
2515
+ setIsDocked(false);
2516
+ }
2517
+ if (rafRef.current) {
2518
+ cancelAnimationFrame(rafRef.current);
2519
+ }
2520
+ rafRef.current = requestAnimationFrame(() => {
2521
+ setPanelPosition({
2522
+ x: dragRef.current.initialX + deltaX,
2523
+ y: dragRef.current.initialY + deltaY
2524
+ });
775
2525
  });
776
2526
  };
777
2527
  const handleMouseUp = () => {
778
- setIsDragging(false);
779
- dragRef.current = null;
780
- if (panelRef.current) {
2528
+ if (rafRef.current) {
2529
+ cancelAnimationFrame(rafRef.current);
2530
+ rafRef.current = null;
2531
+ }
2532
+ if (dragRef.current?.thresholdMet && panelRef.current) {
781
2533
  const rect = panelRef.current.getBoundingClientRect();
782
- const threshold = 50;
2534
+ const threshold = 20;
783
2535
  const windowWidth = window.innerWidth;
784
2536
  const windowHeight = window.innerHeight;
785
- if (rect.right > windowWidth - threshold || rect.left < threshold || rect.top < threshold || rect.bottom > windowHeight - threshold) {
2537
+ const distanceToRight = windowWidth - rect.right;
2538
+ const distanceToLeft = rect.left;
2539
+ const distanceToTop = rect.top;
2540
+ const distanceToBottom = windowHeight - rect.bottom;
2541
+ const minDistance = Math.min(distanceToRight, distanceToLeft, distanceToTop, distanceToBottom);
2542
+ if (minDistance < threshold) {
2543
+ let newDockPosition;
2544
+ if (minDistance === distanceToRight) {
2545
+ newDockPosition = rect.top < windowHeight / 3 ? "top-right" : rect.bottom > windowHeight * 2 / 3 ? "bottom-right" : "right-center";
2546
+ } else if (minDistance === distanceToLeft) {
2547
+ newDockPosition = rect.top < windowHeight / 3 ? "top-left" : rect.bottom > windowHeight * 2 / 3 ? "bottom-left" : "left-center";
2548
+ } else if (minDistance === distanceToTop) {
2549
+ newDockPosition = rect.left < windowWidth / 3 ? "top-left" : rect.right > windowWidth * 2 / 3 ? "top-right" : "top-right";
2550
+ } else {
2551
+ newDockPosition = rect.left < windowWidth / 3 ? "bottom-left" : rect.right > windowWidth * 2 / 3 ? "bottom-right" : "bottom-center";
2552
+ }
2553
+ setDockPosition(newDockPosition);
786
2554
  setIsDocked(true);
787
2555
  setPanelPosition({ x: 0, y: 0 });
788
2556
  }
789
2557
  }
2558
+ setIsDragging(false);
2559
+ setDragInitiated(false);
2560
+ dragRef.current = null;
790
2561
  };
791
2562
  window.addEventListener("mousemove", handleMouseMove);
792
2563
  window.addEventListener("mouseup", handleMouseUp);
793
2564
  return () => {
794
2565
  window.removeEventListener("mousemove", handleMouseMove);
795
2566
  window.removeEventListener("mouseup", handleMouseUp);
2567
+ if (rafRef.current) {
2568
+ cancelAnimationFrame(rafRef.current);
2569
+ }
796
2570
  };
797
- }, [isDragging]);
2571
+ }, [dragInitiated]);
798
2572
  const handleSend = (e) => {
799
2573
  e.preventDefault();
800
2574
  if (!inputValue.trim()) return;
@@ -808,10 +2582,12 @@ var ChatBubble = ({
808
2582
  setIsExpanded(!isExpanded);
809
2583
  };
810
2584
  const handleDock = () => {
2585
+ setDockPosition(position);
811
2586
  setIsDocked(true);
812
2587
  setPanelPosition({ x: 0, y: 0 });
813
2588
  };
814
2589
  const handleHome = () => {
2590
+ setDockPosition(position);
815
2591
  setIsDocked(true);
816
2592
  setPanelPosition({ x: 0, y: 0 });
817
2593
  setIsMinimized(false);
@@ -822,56 +2598,287 @@ var ChatBubble = ({
822
2598
  setShowWelcome(true);
823
2599
  }
824
2600
  };
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);
2601
+ const handleMicClick = () => {
2602
+ if (isListening) {
2603
+ stopListening();
2604
+ } else {
2605
+ startListening();
2606
+ }
2607
+ };
2608
+ const handleDrawerTouchStart = (e) => {
2609
+ if (!drawerOpen) return;
2610
+ const touch = e.touches[0];
2611
+ setTouchStart({
2612
+ x: touch.clientX,
2613
+ y: touch.clientY,
2614
+ time: Date.now()
2615
+ });
2616
+ };
2617
+ const handleDrawerTouchMove = (e) => {
2618
+ if (!touchStart || !drawerOpen) return;
2619
+ const touch = e.touches[0];
2620
+ const deltaX = touch.clientX - touchStart.x;
2621
+ const deltaY = Math.abs(touch.clientY - touchStart.y);
2622
+ if (deltaY > 10 && Math.abs(deltaX) < deltaY) {
2623
+ return;
830
2624
  }
2625
+ const screenWidth = window.innerWidth;
2626
+ const direction = drawerSide === "right" ? 1 : -1;
2627
+ const progress = Math.max(0, Math.min(100, deltaX * direction / screenWidth * 100));
2628
+ setSwipeProgress(progress);
831
2629
  };
832
- const dockClasses = DOCK_POSITIONS[position];
2630
+ const handleDrawerTouchEnd = () => {
2631
+ if (!touchStart) return;
2632
+ const SWIPE_THRESHOLD = 0.4;
2633
+ if (swipeProgress > SWIPE_THRESHOLD * 100) {
2634
+ setDrawerOpen(false);
2635
+ }
2636
+ setTouchStart(null);
2637
+ setSwipeProgress(0);
2638
+ };
2639
+ const dockClasses = DOCK_POSITIONS[dockPosition];
833
2640
  const primaryColor = config.theme?.primary || "blue";
834
- const glassMode = config.glassMode ?? true;
2641
+ const glassMode = localGlassMode;
2642
+ const glassClasses = glassMode ? glassOpacity === "low" ? "backdrop-blur-xl bg-white/90 dark:bg-gray-900/90 border border-white/20 dark:border-white/10" : glassOpacity === "high" ? "backdrop-blur-xl bg-white/50 dark:bg-gray-900/50 border border-white/20 dark:border-white/10" : "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";
2643
+ const glassGradient = glassMode ? glassOpacity === "low" ? "bg-gradient-to-br from-white/95 via-white/85 to-white/70 dark:from-gray-900/90 dark:via-gray-900/80 dark:to-gray-900/70" : glassOpacity === "high" ? "bg-gradient-to-br from-white/70 via-white/50 to-white/30 dark:from-gray-900/60 dark:via-gray-900/50 dark:to-gray-900/40" : "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";
2644
+ const getFloatingPositionStyle = () => {
2645
+ if (dockPosition.includes("top") && dockPosition.includes("left")) {
2646
+ return {
2647
+ top: 0,
2648
+ left: 0,
2649
+ transform: `translate(${panelPosition.x}px, ${panelPosition.y}px)`
2650
+ };
2651
+ } else if (dockPosition.includes("top") && dockPosition.includes("right")) {
2652
+ return {
2653
+ top: 0,
2654
+ right: 0,
2655
+ transform: `translate(${-panelPosition.x}px, ${panelPosition.y}px)`
2656
+ };
2657
+ } else if (dockPosition.includes("bottom") && dockPosition.includes("left")) {
2658
+ return {
2659
+ bottom: 0,
2660
+ left: 0,
2661
+ transform: `translate(${panelPosition.x}px, ${-panelPosition.y}px)`
2662
+ };
2663
+ } else if (dockPosition.includes("bottom") && dockPosition.includes("right")) {
2664
+ return {
2665
+ bottom: 0,
2666
+ right: 0,
2667
+ transform: `translate(${-panelPosition.x}px, ${-panelPosition.y}px)`
2668
+ };
2669
+ } else if (dockPosition.includes("left-center")) {
2670
+ return {
2671
+ left: 0,
2672
+ top: "50%",
2673
+ transform: `translate(${panelPosition.x}px, calc(-50% + ${panelPosition.y}px))`
2674
+ };
2675
+ } else if (dockPosition.includes("right-center")) {
2676
+ return {
2677
+ right: 0,
2678
+ top: "50%",
2679
+ transform: `translate(${-panelPosition.x}px, calc(-50% + ${panelPosition.y}px))`
2680
+ };
2681
+ } else if (dockPosition.includes("bottom-center")) {
2682
+ return {
2683
+ bottom: 0,
2684
+ left: "50%",
2685
+ transform: `translate(calc(-50% + ${panelPosition.x}px), ${-panelPosition.y}px)`
2686
+ };
2687
+ } else {
2688
+ return {
2689
+ left: "50%",
2690
+ top: "50%",
2691
+ transform: `translate(calc(-50% + ${panelPosition.x}px), calc(-50% + ${panelPosition.y}px))`
2692
+ };
2693
+ }
2694
+ };
835
2695
  const maxHeightVh = 80;
836
2696
  const dynamicHeight = `min(${maxHeightVh}vh, 700px)`;
837
2697
  const panelWidth = "min(650px, calc(100vw - 2rem))";
2698
+ const getDrawerTransform = () => {
2699
+ if (touchStart && swipeProgress > 0) {
2700
+ return drawerSide === "right" ? `translateX(${100 - swipeProgress}%)` : `translateX(${-100 + swipeProgress}%)`;
2701
+ }
2702
+ return drawerOpen ? "translateX(0%)" : drawerSide === "right" ? "translateX(100%)" : "translateX(-100%)";
2703
+ };
2704
+ if (!isHydrated) {
2705
+ return null;
2706
+ }
2707
+ if (currentVariant === "subtitle") {
2708
+ return /* @__PURE__ */ jsx11(
2709
+ SubtitleOverlay,
2710
+ {
2711
+ messages,
2712
+ inputValue,
2713
+ onInputChange: setInputValue,
2714
+ onSendMessage: () => {
2715
+ const syntheticEvent = {
2716
+ preventDefault: () => {
2717
+ },
2718
+ stopPropagation: () => {
2719
+ }
2720
+ };
2721
+ handleSend(syntheticEvent);
2722
+ },
2723
+ voiceEnabled,
2724
+ isListening,
2725
+ onMicClick: handleMicClick,
2726
+ theme,
2727
+ config,
2728
+ sttTranscript,
2729
+ resetTranscript
2730
+ }
2731
+ );
2732
+ }
2733
+ if (currentVariant === "drawer") {
2734
+ const drawerWidth = "100vw";
2735
+ return /* @__PURE__ */ jsxs7(Fragment3, { children: [
2736
+ (drawerOpen || swipeProgress > 0) && /* @__PURE__ */ jsx11(
2737
+ "div",
2738
+ {
2739
+ className: "fixed inset-0 bg-black z-[99998] transition-opacity duration-300",
2740
+ style: {
2741
+ opacity: touchStart && swipeProgress > 0 ? swipeProgress / 100 * 0.5 : 0.5,
2742
+ zIndex: 999998
2743
+ // Force high z-index for overlay
2744
+ },
2745
+ onClick: () => setDrawerOpen(false)
2746
+ }
2747
+ ),
2748
+ /* @__PURE__ */ jsxs7(
2749
+ "div",
2750
+ {
2751
+ className: `fixed ${drawerSide === "right" ? "right-0" : "left-0"} top-0 h-full z-[99999] flex flex-col ${glassClasses} shadow-2xl`,
2752
+ style: {
2753
+ width: drawerWidth,
2754
+ transform: getDrawerTransform(),
2755
+ transition: touchStart ? "none" : "transform 300ms cubic-bezier(0.4, 0, 0.2, 1)",
2756
+ willChange: "transform",
2757
+ zIndex: 999999
2758
+ // Force extremely high z-index
2759
+ },
2760
+ role: "dialog",
2761
+ "aria-modal": "true",
2762
+ "aria-label": "Chat drawer",
2763
+ onTouchStart: handleDrawerTouchStart,
2764
+ onTouchMove: handleDrawerTouchMove,
2765
+ onTouchEnd: handleDrawerTouchEnd,
2766
+ children: [
2767
+ /* @__PURE__ */ jsxs7("div", { className: `${THEME_CLASSES.bg.header} ${glassMode ? THEME_CLASSES.bg.headerGradient : THEME_CLASSES.bg.headerLight}`, children: [
2768
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center space-x-3", children: [
2769
+ config.avatar && /* @__PURE__ */ jsxs7("div", { className: "relative flex-shrink-0", children: [
2770
+ /* @__PURE__ */ jsx11(Avatar, { avatar: config.avatar }),
2771
+ /* @__PURE__ */ jsx11("div", { className: "absolute -bottom-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white" })
2772
+ ] }),
2773
+ config.title && /* @__PURE__ */ jsx11("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx11("h3", { className: THEME_CLASSES.text.title, children: config.title }) })
2774
+ ] }),
2775
+ /* @__PURE__ */ jsx11("button", { onClick: () => setDrawerOpen(false), className: THEME_CLASSES.button.close, title: "Close drawer", children: /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })
2776
+ ] }),
2777
+ /* @__PURE__ */ jsxs7("div", { className: "flex-1 overflow-y-auto p-4 space-y-2", children: [
2778
+ showWelcome && messages.length === 0 && config.welcome?.enabled && /* @__PURE__ */ jsxs7("div", { className: THEME_CLASSES.welcome.container, children: [
2779
+ config.welcome.title && /* @__PURE__ */ jsx11("h4", { className: THEME_CLASSES.welcome.title, style: INLINE_STYLES.welcomeTitle(theme === "dark"), children: config.welcome.title }),
2780
+ config.welcome.content && /* @__PURE__ */ jsx11("p", { className: THEME_CLASSES.welcome.content, style: INLINE_STYLES.welcomeContent(theme === "dark"), children: config.welcome.content })
2781
+ ] }),
2782
+ messages.map((message) => /* @__PURE__ */ jsxs7("div", { className: `group flex items-center gap-1 mb-2 ${message.type === "user" ? "flex-row-reverse" : "flex-row"}`, children: [
2783
+ /* @__PURE__ */ jsx11(
2784
+ "div",
2785
+ {
2786
+ className: `inline-block px-4 py-2.5 rounded-2xl max-w-[95%] text-sm shadow-sm transition-all ${message.type === "user" ? THEME_CLASSES.message.user : message.type === "ai" ? THEME_CLASSES.message.ai : THEME_CLASSES.message.system}`,
2787
+ style: message.type === "user" ? INLINE_STYLES.messageUser() : message.type === "ai" ? INLINE_STYLES.messageAI(theme === "dark") : INLINE_STYLES.messageSystem(theme === "dark"),
2788
+ children: /* @__PURE__ */ jsx11(MessageRenderer, { content: message.text, theme })
2789
+ }
2790
+ ),
2791
+ message.type === "ai" && voiceEnabled && /* @__PURE__ */ jsx11(
2792
+ TTSButton,
2793
+ {
2794
+ text: message.text,
2795
+ usePremiumVoices,
2796
+ speed: ttsSpeed,
2797
+ theme,
2798
+ size: "small"
2799
+ }
2800
+ ),
2801
+ /* @__PURE__ */ jsx11(
2802
+ "div",
2803
+ {
2804
+ 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"}`,
2805
+ title: typeof window !== "undefined" ? new Date(message.timestamp).toLocaleString() : "",
2806
+ children: typeof window !== "undefined" ? formatRelativeTime(message.timestamp) : ""
2807
+ }
2808
+ )
2809
+ ] }, message.id)),
2810
+ /* @__PURE__ */ jsx11("div", { ref: messagesEndRef })
2811
+ ] }),
2812
+ /* @__PURE__ */ jsx11(
2813
+ InputField,
2814
+ {
2815
+ inputValue,
2816
+ onInputChange: setInputValue,
2817
+ onSubmit: handleSend,
2818
+ placeholder: inputHints[currentHintIndex],
2819
+ glassClasses: "",
2820
+ theme,
2821
+ inputRef,
2822
+ sendButtonLabel: config.sendButtonLabel,
2823
+ voiceEnabled,
2824
+ isListening,
2825
+ onMicClick: handleMicClick,
2826
+ modKey: isMac ? "Cmd" : "Ctrl"
2827
+ }
2828
+ )
2829
+ ]
2830
+ }
2831
+ ),
2832
+ !drawerOpen && /* @__PURE__ */ jsx11(
2833
+ "div",
2834
+ {
2835
+ className: `fixed ${drawerSide === "right" ? "right-0" : "left-0"} bottom-20 opacity-30 hover:opacity-90 transition-opacity duration-300 z-[99999] cursor-pointer`,
2836
+ style: { zIndex: 999999 },
2837
+ onClick: () => setDrawerOpen(true),
2838
+ children: /* @__PURE__ */ jsx11("div", { className: `${glassMode ? "backdrop-blur-md bg-white/50 dark:bg-gray-800/50" : "bg-white dark:bg-gray-800"} text-gray-700 dark:text-gray-200 px-3 py-3 ${drawerSide === "right" ? "rounded-l-xl" : "rounded-r-xl"} shadow-md hover:shadow-lg flex items-center justify-center transition-all`, children: voiceEnabled ? /* @__PURE__ */ jsx11("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" }) }) : /* @__PURE__ */ jsx11("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" }) }) })
2839
+ }
2840
+ )
2841
+ ] });
2842
+ }
838
2843
  if (variant === "floating") {
839
2844
  const recentMessage = messages[messages.length - 1];
840
- return /* @__PURE__ */ jsx3(
2845
+ return /* @__PURE__ */ jsx11(
841
2846
  "div",
842
2847
  {
843
- className: `fixed z-50 ${isDragging ? "cursor-grabbing" : "cursor-grab"}`,
2848
+ className: `fixed z-[99999] ${isDragging ? "cursor-grabbing" : "cursor-grab"}`,
844
2849
  style: {
845
2850
  transform: `translate(${panelPosition.x}px, ${panelPosition.y}px)`,
2851
+ zIndex: 999999,
2852
+ // Force extremely high z-index
846
2853
  ...!isDragging && { transition: "transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)" }
847
2854
  },
848
2855
  onMouseDown: handlePanelMouseDown,
849
- children: /* @__PURE__ */ jsxs("div", { className: glassMode ? "backdrop-blur-xl bg-white/70 dark:bg-gray-900/70 border border-white/20 dark:border-white/10 rounded-2xl shadow-2xl p-3 max-w-xs" : "bg-white dark:bg-gray-900 border-gray-200 rounded-2xl shadow-2xl border p-3 max-w-xs", children: [
850
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
851
- /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
852
- /* @__PURE__ */ jsx3(Avatar, { avatar: config.avatar, size: "small" }),
853
- config.title && /* @__PURE__ */ jsx3("span", { className: THEME_CLASSES.text.floatingTitle, children: config.title })
2856
+ children: /* @__PURE__ */ jsxs7("div", { className: `${glassClasses} rounded-2xl shadow-2xl p-3 max-w-xs ${!glassMode && "border-gray-200 border"}`, children: [
2857
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between mb-2", children: [
2858
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center space-x-2", children: [
2859
+ /* @__PURE__ */ jsx11(Avatar, { avatar: config.avatar, size: "small" }),
2860
+ config.title && /* @__PURE__ */ jsx11("span", { className: THEME_CLASSES.text.floatingTitle, children: config.title })
854
2861
  ] }),
855
- onClearChat && /* @__PURE__ */ jsx3(
2862
+ onClearChat && /* @__PURE__ */ jsx11(
856
2863
  "button",
857
2864
  {
858
2865
  onClick: onClearChat,
859
2866
  className: THEME_CLASSES.button.floatingClear,
860
2867
  title: "Clear chat",
861
2868
  "data-testid": ChatNames.clearButton,
862
- 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" }) })
2869
+ children: /* @__PURE__ */ jsx11("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
863
2870
  }
864
2871
  )
865
2872
  ] }),
866
- recentMessage && /* @__PURE__ */ jsxs("div", { className: `mb-2 group flex items-center gap-2 ${recentMessage.type === "user" ? "flex-row-reverse" : "flex-row"}`, children: [
867
- /* @__PURE__ */ jsx3(
2873
+ recentMessage && /* @__PURE__ */ jsxs7("div", { className: `mb-2 group flex items-center gap-2 ${recentMessage.type === "user" ? "flex-row-reverse" : "flex-row"}`, children: [
2874
+ /* @__PURE__ */ jsx11(
868
2875
  "div",
869
2876
  {
870
2877
  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"}`,
871
2878
  children: recentMessage.text.length > 60 ? `${recentMessage.text.slice(0, 60)}...` : recentMessage.text
872
2879
  }
873
2880
  ),
874
- /* @__PURE__ */ jsx3(
2881
+ /* @__PURE__ */ jsx11(
875
2882
  "div",
876
2883
  {
877
2884
  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,29 +2887,33 @@ var ChatBubble = ({
880
2887
  }
881
2888
  )
882
2889
  ] }),
883
- /* @__PURE__ */ jsx3(
2890
+ /* @__PURE__ */ jsx11(
884
2891
  InputField,
885
2892
  {
886
2893
  compact: true,
887
2894
  inputValue,
888
2895
  onInputChange: setInputValue,
889
2896
  onSubmit: handleSend,
890
- placeholder: config.placeholder,
2897
+ placeholder: inputHints[currentHintIndex],
891
2898
  glassClasses: "",
892
2899
  theme,
893
- sendButtonLabel: config.sendButtonLabel
2900
+ sendButtonLabel: config.sendButtonLabel,
2901
+ modKey: isMac ? "Cmd" : "Ctrl"
894
2902
  }
895
2903
  )
896
2904
  ] })
897
2905
  }
898
2906
  );
899
2907
  }
900
- return /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsxs(
2908
+ return /* @__PURE__ */ jsx11(Fragment3, { children: /* @__PURE__ */ jsxs7(
901
2909
  "div",
902
2910
  {
903
- className: "fixed z-50",
2911
+ className: "fixed",
904
2912
  style: {
905
2913
  ...dockClasses.container,
2914
+ zIndex: 2147483647,
2915
+ // Maximum z-index value
2916
+ position: "fixed",
906
2917
  ...isExpanded ? {
907
2918
  width: panelWidth,
908
2919
  height: isMinimized ? "auto" : dynamicHeight
@@ -912,107 +2923,247 @@ var ChatBubble = ({
912
2923
  }
913
2924
  },
914
2925
  children: [
915
- isExpanded && isMinimized && /* @__PURE__ */ jsxs(
2926
+ isExpanded && isMinimized && /* @__PURE__ */ jsxs7(
916
2927
  "div",
917
2928
  {
918
- className: glassMode ? "absolute backdrop-blur-xl bg-white/70 dark:bg-gray-900/70 border border-white/20 dark:border-white/10 rounded-3xl shadow-2xl p-4 transition-all duration-300" : "absolute bg-white dark:bg-gray-900 border-gray-200 rounded-3xl shadow-2xl border p-4 transition-all duration-300",
2929
+ ref: panelRef,
2930
+ className: `${isDocked ? "absolute" : "fixed z-[99999]"} ${glassGradient} rounded-3xl shadow-2xl border border-white/20 dark:border-white/10 ${!isDragging && "backdrop-blur-xl"} flex flex-col overflow-hidden ${!isDragging && "transition-all duration-300"}`,
919
2931
  style: {
920
- ...dockClasses.panel,
921
2932
  width: panelWidth,
922
- maxWidth: "400px"
2933
+ maxWidth: "400px",
2934
+ zIndex: 999999,
2935
+ // Force extremely high z-index
2936
+ ...isDocked ? {
2937
+ ...dockClasses.panel,
2938
+ // Only clear transform if dock position doesn't use transform
2939
+ ...dockClasses.panel.transform ? {} : { transform: "none" }
2940
+ } : getFloatingPositionStyle(),
2941
+ ...isDragging && { cursor: "grabbing" }
923
2942
  },
924
2943
  children: [
925
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
926
- /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
927
- /* @__PURE__ */ jsx3(Avatar, { avatar: config.avatar, size: "small" }),
928
- config.title && /* @__PURE__ */ jsx3("span", { className: THEME_CLASSES.text.floatingTitle, children: config.title })
929
- ] }),
930
- /* @__PURE__ */ jsx3(
931
- "button",
2944
+ /* @__PURE__ */ jsxs7(
2945
+ "div",
2946
+ {
2947
+ "data-drag-handle": true,
2948
+ className: `${THEME_CLASSES.bg.header} ${glassMode ? THEME_CLASSES.bg.headerGradient : THEME_CLASSES.bg.headerLight} cursor-move`,
2949
+ onMouseDown: handlePanelMouseDown,
2950
+ onClick: (e) => {
2951
+ if (!dragRef.current?.thresholdMet) {
2952
+ const target = e.target;
2953
+ if (target.closest("button") || target.closest('[role="button"]')) {
2954
+ return;
2955
+ }
2956
+ setIsMinimized(false);
2957
+ }
2958
+ },
2959
+ children: [
2960
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center space-x-3", children: [
2961
+ config.avatar && /* @__PURE__ */ jsxs7("div", { className: "relative flex-shrink-0", children: [
2962
+ /* @__PURE__ */ jsx11(Avatar, { avatar: config.avatar }),
2963
+ /* @__PURE__ */ jsx11("div", { className: "absolute -bottom-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white" })
2964
+ ] }),
2965
+ config.title && /* @__PURE__ */ jsx11("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx11("h3", { className: THEME_CLASSES.text.title, children: config.title }) })
2966
+ ] }),
2967
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center space-x-1 flex-shrink-0", children: [
2968
+ /* @__PURE__ */ jsx11(
2969
+ "button",
2970
+ {
2971
+ onClick: (e) => {
2972
+ e.stopPropagation();
2973
+ setIsMinimized(true);
2974
+ },
2975
+ className: THEME_CLASSES.button.minimize,
2976
+ title: "Minimize chat",
2977
+ children: /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })
2978
+ }
2979
+ ),
2980
+ /* @__PURE__ */ jsx11(
2981
+ "button",
2982
+ {
2983
+ onClick: (e) => {
2984
+ e.stopPropagation();
2985
+ setIsExpanded(false);
2986
+ },
2987
+ className: THEME_CLASSES.button.close,
2988
+ title: "Close chat",
2989
+ children: /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
2990
+ }
2991
+ )
2992
+ ] })
2993
+ ]
2994
+ }
2995
+ ),
2996
+ /* @__PURE__ */ jsxs7("div", { className: "p-4", children: [
2997
+ (() => {
2998
+ const lastAiMessage = [...messages].reverse().find((m) => m.type === "ai");
2999
+ return lastAiMessage ? /* @__PURE__ */ jsx11("div", { className: "mb-3", children: /* @__PURE__ */ jsx11("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__ */ jsx11("div", { className: "mb-3", children: /* @__PURE__ */ jsx11("div", { className: THEME_CLASSES.text.minimizedMessage, style: INLINE_STYLES.minimizedMessage(theme === "dark"), children: "No AI responses yet" }) });
3000
+ })(),
3001
+ /* @__PURE__ */ jsx11(
3002
+ InputField,
932
3003
  {
933
- onClick: () => setIsMinimized(false),
934
- className: "p-1 text-gray-400 hover:text-gray-600 dark:text-gray-300 dark:hover:text-gray-200 transition-colors",
935
- title: "Expand chat",
936
- 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" }) })
3004
+ compact: true,
3005
+ inputValue,
3006
+ onInputChange: setInputValue,
3007
+ onSubmit: handleSend,
3008
+ placeholder: inputHints[currentHintIndex],
3009
+ glassClasses: "",
3010
+ theme,
3011
+ sendButtonLabel: config.sendButtonLabel,
3012
+ modKey: isMac ? "Cmd" : "Ctrl"
937
3013
  }
938
3014
  )
939
- ] }),
940
- (() => {
941
- const lastAiMessage = [...messages].reverse().find((m) => m.type === "ai");
942
- 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" }) });
943
- })(),
944
- /* @__PURE__ */ jsx3(
945
- InputField,
946
- {
947
- compact: true,
948
- inputValue,
949
- onInputChange: setInputValue,
950
- onSubmit: handleSend,
951
- placeholder: config.placeholder,
952
- glassClasses: "",
953
- theme,
954
- sendButtonLabel: config.sendButtonLabel
955
- }
956
- )
3015
+ ] })
957
3016
  ]
958
3017
  }
959
3018
  ),
960
- isExpanded && !isMinimized && /* @__PURE__ */ jsxs(
3019
+ isExpanded && !isMinimized && /* @__PURE__ */ jsxs7(
961
3020
  "div",
962
3021
  {
963
3022
  ref: panelRef,
964
- className: `${isDocked ? "absolute" : "fixed"} ${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"} rounded-3xl shadow-2xl border border-white/20 dark:border-white/10 backdrop-blur-xl flex flex-col overflow-hidden transition-all duration-300`,
3023
+ className: `${isDocked ? "absolute" : "fixed z-[99999]"} ${glassGradient} rounded-3xl shadow-2xl border border-white/20 dark:border-white/10 ${!isDragging && "backdrop-blur-xl"} flex flex-col overflow-hidden ${!isDragging && "transition-all duration-300"}`,
965
3024
  style: {
966
3025
  width: panelWidth,
967
3026
  height: dynamicHeight,
968
- ...isDocked ? dockClasses.panel : {
969
- left: "50%",
970
- top: "50%",
971
- transform: `translate(calc(-50% + ${panelPosition.x}px), calc(-50% + ${panelPosition.y}px))`
972
- },
3027
+ zIndex: 999999,
3028
+ // Force extremely high z-index
3029
+ ...isDocked ? {
3030
+ ...dockClasses.panel,
3031
+ // Only clear transform if dock position doesn't use transform
3032
+ ...dockClasses.panel.transform ? {} : { transform: "none" }
3033
+ } : getFloatingPositionStyle(),
973
3034
  ...isDragging && { cursor: "grabbing" }
974
3035
  },
975
3036
  children: [
976
- /* @__PURE__ */ jsxs(
3037
+ /* @__PURE__ */ jsxs7(
977
3038
  "div",
978
3039
  {
979
3040
  "data-drag-handle": true,
980
3041
  className: `${THEME_CLASSES.bg.header} ${glassMode ? THEME_CLASSES.bg.headerGradient : THEME_CLASSES.bg.headerLight} cursor-move`,
981
3042
  onMouseDown: handlePanelMouseDown,
982
3043
  children: [
983
- /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
984
- config.avatar && /* @__PURE__ */ jsxs("div", { className: "relative flex-shrink-0", children: [
985
- /* @__PURE__ */ jsx3(Avatar, { avatar: config.avatar }),
986
- /* @__PURE__ */ jsx3("div", { className: "absolute -bottom-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white" })
3044
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center space-x-3", children: [
3045
+ config.avatar && /* @__PURE__ */ jsxs7("div", { className: "relative flex-shrink-0", children: [
3046
+ /* @__PURE__ */ jsx11(Avatar, { avatar: config.avatar }),
3047
+ /* @__PURE__ */ jsx11("div", { className: "absolute -bottom-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white" })
987
3048
  ] }),
988
- config.title && /* @__PURE__ */ jsx3("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx3("h3", { className: THEME_CLASSES.text.title, children: config.title }) })
3049
+ config.title && /* @__PURE__ */ jsx11("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx11("h3", { className: THEME_CLASSES.text.title, children: config.title }) })
989
3050
  ] }),
990
- /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-1 flex-shrink-0 relative", "data-more-menu": true, children: [
991
- /* @__PURE__ */ jsx3(
3051
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-center space-x-1 flex-shrink-0 relative", "data-more-menu": true, children: [
3052
+ /* @__PURE__ */ jsx11(
3053
+ "a",
3054
+ {
3055
+ href: "https://www.interface.supernal.ai",
3056
+ target: "_blank",
3057
+ rel: "noopener noreferrer",
3058
+ className: "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",
3059
+ title: "Visit Supernal Interface Documentation",
3060
+ children: /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" }) })
3061
+ }
3062
+ ),
3063
+ /* @__PURE__ */ jsx11(
992
3064
  "button",
993
3065
  {
994
3066
  onClick: () => setShowMoreMenu(!showMoreMenu),
995
3067
  className: THEME_CLASSES.button.more,
996
3068
  title: "More options",
997
- 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" }) })
3069
+ children: /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("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" }) })
998
3070
  }
999
3071
  ),
1000
- 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: [
1001
- /* @__PURE__ */ jsxs(
3072
+ showMoreMenu && /* @__PURE__ */ jsxs7("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 min-w-[220px] z-50", "data-more-menu": true, children: [
3073
+ /* @__PURE__ */ jsx11("div", { className: "px-3 py-2 border-b border-gray-200 dark:border-gray-600 mb-2", children: /* @__PURE__ */ jsxs7("div", { className: "grid grid-cols-4 gap-1", children: [
3074
+ /* @__PURE__ */ jsx11(
3075
+ "button",
3076
+ {
3077
+ onClick: () => setLocalGlassMode(false),
3078
+ className: `flex items-center justify-center p-2 rounded transition-all ${!localGlassMode ? "bg-blue-600 text-white shadow-sm" : "bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600"}`,
3079
+ title: "Glass Off",
3080
+ children: /* @__PURE__ */ jsx11("svg", { className: "w-5 h-5", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("rect", { x: "8", y: "8", width: "8", height: "8", rx: "1" }) })
3081
+ }
3082
+ ),
3083
+ /* @__PURE__ */ jsx11(
3084
+ "button",
3085
+ {
3086
+ onClick: () => {
3087
+ setLocalGlassMode(true);
3088
+ setGlassOpacity("low");
3089
+ },
3090
+ className: `flex items-center justify-center p-2 rounded transition-all ${localGlassMode && glassOpacity === "low" ? "bg-blue-600 text-white shadow-sm" : "bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600"}`,
3091
+ title: "Glass Low",
3092
+ children: /* @__PURE__ */ jsx11("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("rect", { x: "9", y: "9", width: "6", height: "6", rx: "1", strokeWidth: "2" }) })
3093
+ }
3094
+ ),
3095
+ /* @__PURE__ */ jsx11(
3096
+ "button",
3097
+ {
3098
+ onClick: () => {
3099
+ setLocalGlassMode(true);
3100
+ setGlassOpacity("medium");
3101
+ },
3102
+ className: `flex items-center justify-center p-2 rounded transition-all ${localGlassMode && glassOpacity === "medium" ? "bg-blue-600 text-white shadow-sm" : "bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600"}`,
3103
+ title: "Glass Medium",
3104
+ children: /* @__PURE__ */ jsxs7("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [
3105
+ /* @__PURE__ */ jsx11("rect", { x: "8", y: "8", width: "8", height: "8", rx: "1", strokeWidth: "2" }),
3106
+ /* @__PURE__ */ jsx11("rect", { x: "10", y: "10", width: "4", height: "4", rx: "0.5", strokeWidth: "1.5" })
3107
+ ] })
3108
+ }
3109
+ ),
3110
+ /* @__PURE__ */ jsx11(
3111
+ "button",
3112
+ {
3113
+ onClick: () => {
3114
+ setLocalGlassMode(true);
3115
+ setGlassOpacity("high");
3116
+ },
3117
+ className: `flex items-center justify-center p-2 rounded transition-all ${localGlassMode && glassOpacity === "high" ? "bg-blue-600 text-white shadow-sm" : "bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600"}`,
3118
+ title: "Glass High",
3119
+ children: /* @__PURE__ */ jsxs7("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [
3120
+ /* @__PURE__ */ jsx11("rect", { x: "7", y: "7", width: "10", height: "10", rx: "1", strokeWidth: "2" }),
3121
+ /* @__PURE__ */ jsx11("rect", { x: "9", y: "9", width: "6", height: "6", rx: "0.5", strokeWidth: "1.5" }),
3122
+ /* @__PURE__ */ jsx11("rect", { x: "11", y: "11", width: "2", height: "2", rx: "0.5", strokeWidth: "1" })
3123
+ ] })
3124
+ }
3125
+ )
3126
+ ] }) }),
3127
+ /* @__PURE__ */ jsxs7(
1002
3128
  "button",
1003
3129
  {
1004
3130
  onClick: () => {
1005
- handleToggleTheme();
1006
- setShowMoreMenu(false);
3131
+ const newTheme = theme === "light" ? "dark" : "light";
3132
+ setTheme(newTheme);
3133
+ if (typeof window !== "undefined") {
3134
+ document.documentElement.setAttribute("data-theme", newTheme);
3135
+ }
3136
+ },
3137
+ 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",
3138
+ children: [
3139
+ /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("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" }) }),
3140
+ /* @__PURE__ */ jsxs7("span", { children: [
3141
+ theme === "light" ? "Dark" : "Light",
3142
+ " Mode"
3143
+ ] })
3144
+ ]
3145
+ }
3146
+ ),
3147
+ /* @__PURE__ */ jsxs7(
3148
+ "button",
3149
+ {
3150
+ onClick: () => {
3151
+ setVoiceEnabled(!voiceEnabled);
3152
+ if (!voiceEnabled) {
3153
+ onSendMessage("Voice control enabled! Use the microphone button to speak, or click speaker icons to hear messages.");
3154
+ }
1007
3155
  },
1008
3156
  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",
1009
3157
  children: [
1010
- 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" }) }),
1011
- /* @__PURE__ */ jsx3("span", { children: theme === "light" ? "Dark mode" : "Light mode" })
3158
+ /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" }) }),
3159
+ /* @__PURE__ */ jsxs7("span", { children: [
3160
+ voiceEnabled ? "Disable" : "Enable",
3161
+ " Voice"
3162
+ ] })
1012
3163
  ]
1013
3164
  }
1014
3165
  ),
1015
- /* @__PURE__ */ jsxs(
3166
+ /* @__PURE__ */ jsxs7(
1016
3167
  "button",
1017
3168
  {
1018
3169
  onClick: () => {
@@ -1021,26 +3172,33 @@ var ChatBubble = ({
1021
3172
  },
1022
3173
  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",
1023
3174
  children: [
1024
- /* @__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" }) }),
1025
- /* @__PURE__ */ jsx3("span", { children: "Reset position" })
3175
+ /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("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" }) }),
3176
+ /* @__PURE__ */ jsx11("span", { children: "Reset position" })
1026
3177
  ]
1027
3178
  }
1028
3179
  ),
1029
- /* @__PURE__ */ jsxs(
3180
+ /* @__PURE__ */ jsxs7(
1030
3181
  "button",
1031
3182
  {
1032
3183
  onClick: () => {
1033
- setShowInfo(!showInfo);
3184
+ const helpMessages = [
3185
+ '**How to Use This Chat**\n\n- **Theme**: Toggle between light and dark modes\n- **Glass Effect**: Adjust transparency (Off/Low/Medium/High)\n- **Reset Position**: Return chat to default corner\n- **Minimize**: Compact view showing last message\n- **Clear**: Delete all messages and start fresh\n- **Drag**: Click and drag header to reposition chat\n- **Keyboard**: Press "/" to focus input, Esc to reset position'
3186
+ ];
3187
+ helpMessages.forEach((text, index) => {
3188
+ setTimeout(() => {
3189
+ onSendMessage(text);
3190
+ }, index * 100);
3191
+ });
1034
3192
  setShowMoreMenu(false);
1035
3193
  },
1036
3194
  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",
1037
3195
  children: [
1038
- /* @__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" }) }),
1039
- /* @__PURE__ */ jsx3("span", { children: "How to use" })
3196
+ /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("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" }) }),
3197
+ /* @__PURE__ */ jsx11("span", { children: "How to use" })
1040
3198
  ]
1041
3199
  }
1042
3200
  ),
1043
- onClearChat && /* @__PURE__ */ jsxs(
3201
+ onClearChat && /* @__PURE__ */ jsxs7(
1044
3202
  "button",
1045
3203
  {
1046
3204
  onClick: () => {
@@ -1049,77 +3207,41 @@ var ChatBubble = ({
1049
3207
  },
1050
3208
  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",
1051
3209
  children: [
1052
- /* @__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" }) }),
1053
- /* @__PURE__ */ jsx3("span", { children: "Clear chat" })
3210
+ /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("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" }) }),
3211
+ /* @__PURE__ */ jsx11("span", { children: "Clear chat" })
1054
3212
  ]
1055
3213
  }
1056
3214
  )
1057
3215
  ] }),
1058
- /* @__PURE__ */ jsx3(
3216
+ /* @__PURE__ */ jsx11(
1059
3217
  "button",
1060
3218
  {
1061
3219
  onClick: () => setIsMinimized(true),
1062
3220
  className: THEME_CLASSES.button.minimize,
1063
3221
  title: "Minimize",
1064
- 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" }) })
3222
+ children: /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20 12H4" }) })
1065
3223
  }
1066
3224
  ),
1067
- /* @__PURE__ */ jsx3(
3225
+ /* @__PURE__ */ jsx11(
1068
3226
  "button",
1069
3227
  {
1070
3228
  onClick: handleToggle,
1071
3229
  className: THEME_CLASSES.button.close,
1072
3230
  title: "Close",
1073
- 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" }) })
3231
+ children: /* @__PURE__ */ jsx11("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx11("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
1074
3232
  }
1075
3233
  )
1076
3234
  ] })
1077
3235
  ]
1078
3236
  }
1079
3237
  ),
1080
- showInfo && /* @__PURE__ */ jsxs("div", { className: THEME_CLASSES.text.infoPopup, style: INLINE_STYLES.infoText(theme === "dark"), children: [
1081
- /* @__PURE__ */ jsx3("div", { className: "font-bold mb-2", children: "How to Use" }),
1082
- /* @__PURE__ */ jsxs("div", { className: "space-y-2 text-xs", style: INLINE_STYLES.infoText(theme === "dark"), children: [
1083
- /* @__PURE__ */ jsxs("div", { children: [
1084
- "\u2022 ",
1085
- /* @__PURE__ */ jsx3("strong", { children: "Theme:" }),
1086
- " Toggle between light and dark modes"
1087
- ] }),
1088
- /* @__PURE__ */ jsxs("div", { children: [
1089
- "\u2022 ",
1090
- /* @__PURE__ */ jsx3("strong", { children: "Home:" }),
1091
- " Reset chat position to default"
1092
- ] }),
1093
- /* @__PURE__ */ jsxs("div", { children: [
1094
- "\u2022 ",
1095
- /* @__PURE__ */ jsx3("strong", { children: "Minimize:" }),
1096
- " Compact view with last message"
1097
- ] }),
1098
- /* @__PURE__ */ jsxs("div", { children: [
1099
- "\u2022 ",
1100
- /* @__PURE__ */ jsx3("strong", { children: "Clear:" }),
1101
- " Delete all messages and start fresh"
1102
- ] }),
1103
- /* @__PURE__ */ jsxs("div", { children: [
1104
- "\u2022 ",
1105
- /* @__PURE__ */ jsx3("strong", { children: "Drag:" }),
1106
- " Click and drag header to reposition"
1107
- ] }),
1108
- /* @__PURE__ */ jsxs("div", { children: [
1109
- "\u2022 ",
1110
- /* @__PURE__ */ jsx3("strong", { children: "Keyboard:" }),
1111
- ' Press "/" to open, Esc to close'
1112
- ] })
1113
- ] }),
1114
- config.description && /* @__PURE__ */ jsx3("div", { className: "mt-3 pt-3 border-t border-gray-300/30 dark:border-gray-600/30", children: config.description })
1115
- ] }),
1116
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-4 space-y-2", children: [
1117
- showWelcome && messages.length === 0 && config.welcome?.enabled && /* @__PURE__ */ jsxs("div", { className: THEME_CLASSES.welcome.container, children: [
1118
- config.welcome.title && /* @__PURE__ */ jsx3("h4", { className: THEME_CLASSES.welcome.title, style: INLINE_STYLES.welcomeTitle(theme === "dark"), children: config.welcome.title }),
1119
- config.welcome.content && /* @__PURE__ */ jsx3("p", { className: THEME_CLASSES.welcome.content, style: INLINE_STYLES.welcomeContent(theme === "dark"), children: config.welcome.content }),
1120
- config.welcome.suggestedCommands && config.welcome.suggestedCommands.length > 0 && /* @__PURE__ */ jsxs("div", { className: THEME_CLASSES.welcome.commandsContainer, children: [
1121
- /* @__PURE__ */ jsx3("p", { className: THEME_CLASSES.welcome.commandsHeader, children: "Try these commands:" }),
1122
- /* @__PURE__ */ jsx3("div", { className: "space-y-1", children: config.welcome.suggestedCommands.map((cmd, idx) => /* @__PURE__ */ jsxs(
3238
+ /* @__PURE__ */ jsxs7("div", { className: "flex-1 overflow-y-auto p-4 space-y-2", children: [
3239
+ showWelcome && messages.length === 0 && config.welcome?.enabled && /* @__PURE__ */ jsxs7("div", { className: THEME_CLASSES.welcome.container, children: [
3240
+ config.welcome.title && /* @__PURE__ */ jsx11("h4", { className: THEME_CLASSES.welcome.title, style: INLINE_STYLES.welcomeTitle(theme === "dark"), children: config.welcome.title }),
3241
+ config.welcome.content && /* @__PURE__ */ jsx11("p", { className: THEME_CLASSES.welcome.content, style: INLINE_STYLES.welcomeContent(theme === "dark"), children: config.welcome.content }),
3242
+ config.welcome.suggestedCommands && config.welcome.suggestedCommands.length > 0 && /* @__PURE__ */ jsxs7("div", { className: THEME_CLASSES.welcome.commandsContainer, children: [
3243
+ /* @__PURE__ */ jsx11("p", { className: THEME_CLASSES.welcome.commandsHeader, children: "Try these commands:" }),
3244
+ /* @__PURE__ */ jsx11("div", { className: "space-y-1", children: config.welcome.suggestedCommands.map((cmd, idx) => /* @__PURE__ */ jsxs7(
1123
3245
  "button",
1124
3246
  {
1125
3247
  onClick: () => {
@@ -1129,29 +3251,39 @@ var ChatBubble = ({
1129
3251
  },
1130
3252
  className: THEME_CLASSES.welcome.commandButton,
1131
3253
  children: [
1132
- /* @__PURE__ */ jsxs("div", { className: THEME_CLASSES.welcome.commandText, style: INLINE_STYLES.commandText(theme === "dark"), children: [
3254
+ /* @__PURE__ */ jsxs7("div", { className: THEME_CLASSES.welcome.commandText, style: INLINE_STYLES.commandText(theme === "dark"), children: [
1133
3255
  '"',
1134
3256
  cmd.text,
1135
3257
  '"'
1136
3258
  ] }),
1137
- cmd.desc && /* @__PURE__ */ jsx3("div", { className: THEME_CLASSES.welcome.commandDesc, style: INLINE_STYLES.commandDesc(theme === "dark"), children: cmd.desc })
3259
+ cmd.desc && /* @__PURE__ */ jsx11("div", { className: THEME_CLASSES.welcome.commandDesc, style: INLINE_STYLES.commandDesc(theme === "dark"), children: cmd.desc })
1138
3260
  ]
1139
3261
  },
1140
3262
  idx
1141
3263
  )) })
1142
3264
  ] })
1143
3265
  ] }),
1144
- messages.map((message) => /* @__PURE__ */ jsxs("div", { className: `group flex items-center gap-2 mb-2 ${message.type === "user" ? "flex-row-reverse" : "flex-row"}`, children: [
1145
- /* @__PURE__ */ jsx3(
3266
+ messages.map((message) => /* @__PURE__ */ jsxs7("div", { className: `group flex items-center gap-2 mb-2 ${message.type === "user" ? "flex-row-reverse" : "flex-row"}`, children: [
3267
+ /* @__PURE__ */ jsx11(
1146
3268
  "div",
1147
3269
  {
1148
3270
  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}`,
1149
3271
  style: message.type === "user" ? INLINE_STYLES.messageUser() : message.type === "ai" ? INLINE_STYLES.messageAI(theme === "dark") : INLINE_STYLES.messageSystem(theme === "dark"),
1150
3272
  "data-testid": `chat-message-${message.type}`,
1151
- children: /* @__PURE__ */ jsx3("div", { className: "break-words leading-relaxed", children: message.text })
3273
+ children: /* @__PURE__ */ jsx11(MessageRenderer, { content: message.text, theme })
3274
+ }
3275
+ ),
3276
+ message.type === "ai" && voiceEnabled && /* @__PURE__ */ jsx11(
3277
+ TTSButton,
3278
+ {
3279
+ text: message.text,
3280
+ usePremiumVoices,
3281
+ speed: ttsSpeed,
3282
+ theme,
3283
+ size: "small"
1152
3284
  }
1153
3285
  ),
1154
- /* @__PURE__ */ jsx3(
3286
+ /* @__PURE__ */ jsx11(
1155
3287
  "div",
1156
3288
  {
1157
3289
  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"}`,
@@ -1160,34 +3292,38 @@ var ChatBubble = ({
1160
3292
  }
1161
3293
  )
1162
3294
  ] }, message.id)),
1163
- /* @__PURE__ */ jsx3("div", { ref: messagesEndRef })
3295
+ /* @__PURE__ */ jsx11("div", { ref: messagesEndRef })
1164
3296
  ] }),
1165
- /* @__PURE__ */ jsx3(
3297
+ /* @__PURE__ */ jsx11(
1166
3298
  InputField,
1167
3299
  {
1168
3300
  inputValue,
1169
3301
  onInputChange: setInputValue,
1170
3302
  onSubmit: handleSend,
1171
- placeholder: config.placeholder,
3303
+ placeholder: inputHints[currentHintIndex],
1172
3304
  glassClasses: "",
1173
3305
  theme,
1174
3306
  inputRef,
1175
- sendButtonLabel: config.sendButtonLabel
3307
+ sendButtonLabel: config.sendButtonLabel,
3308
+ voiceEnabled,
3309
+ isListening,
3310
+ onMicClick: handleMicClick,
3311
+ modKey: isMac ? "Cmd" : "Ctrl"
1176
3312
  }
1177
3313
  )
1178
3314
  ]
1179
3315
  }
1180
3316
  ),
1181
- !isExpanded && /* @__PURE__ */ jsxs(
3317
+ !isExpanded && /* @__PURE__ */ jsxs7(
1182
3318
  "button",
1183
3319
  {
1184
3320
  onClick: handleToggle,
1185
3321
  className: THEME_CLASSES.bg.bubble,
1186
3322
  "data-testid": ChatNames.bubble,
1187
- title: "Open chat",
3323
+ title: `Open chat (press ${isMac ? "Cmd" : "Ctrl"}+/ for voice recording)`,
1188
3324
  children: [
1189
- /* @__PURE__ */ jsx3("img", { src: "/logo.svg", alt: "Supernal", className: "w-8 h-8" }),
1190
- 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 }) })
3325
+ /* @__PURE__ */ jsx11("img", { src: config.logo, alt: "Supernal", className: "w-8 h-8" }),
3326
+ hasUnread && notifications && /* @__PURE__ */ jsx11("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__ */ jsx11("span", { className: "text-xs text-white font-bold", children: unreadCount > 9 ? "9+" : unreadCount }) })
1191
3327
  ]
1192
3328
  }
1193
3329
  )
@@ -1197,24 +3333,24 @@ var ChatBubble = ({
1197
3333
  };
1198
3334
 
1199
3335
  // src/components/AutoNavigationContext.tsx
1200
- import { useEffect as useEffect4, useState as useState4 } from "react";
3336
+ import { useEffect as useEffect7, useState as useState9 } from "react";
1201
3337
 
1202
3338
  // src/hooks/useNavigationGraph.tsx
1203
- import { useEffect as useEffect3, useState as useState3, useCallback as useCallback3, createContext as createContext3, useContext as useContext3 } from "react";
3339
+ import { useEffect as useEffect6, useState as useState8, useCallback as useCallback5, createContext as createContext3, useContext as useContext3 } from "react";
1204
3340
  import {
1205
3341
  NavigationGraph
1206
3342
  } from "@supernal/interface/browser";
1207
- import { jsx as jsx4 } from "react/jsx-runtime";
3343
+ import { jsx as jsx12 } from "react/jsx-runtime";
1208
3344
  var NavigationContextContext = createContext3("global");
1209
3345
  function NavigationContextProvider({
1210
3346
  value,
1211
3347
  children
1212
3348
  }) {
1213
3349
  const graph = useNavigationGraph();
1214
- useEffect3(() => {
3350
+ useEffect6(() => {
1215
3351
  graph.setCurrentContext(value);
1216
3352
  }, [value, graph]);
1217
- return /* @__PURE__ */ jsx4(NavigationContextContext.Provider, { value, children });
3353
+ return /* @__PURE__ */ jsx12(NavigationContextContext.Provider, { value, children });
1218
3354
  }
1219
3355
  function useNavigationGraph() {
1220
3356
  return NavigationGraph.getInstance();
@@ -1222,8 +3358,8 @@ function useNavigationGraph() {
1222
3358
  function useCurrentContext() {
1223
3359
  const contextFromProvider = useContext3(NavigationContextContext);
1224
3360
  const graph = useNavigationGraph();
1225
- const [graphContext, setGraphContext] = useState3("");
1226
- useEffect3(() => {
3361
+ const [graphContext, setGraphContext] = useState8("");
3362
+ useEffect6(() => {
1227
3363
  const interval = setInterval(() => {
1228
3364
  const current = graph.getCurrentContext?.() || "";
1229
3365
  if (current !== graphContext) {
@@ -1237,7 +3373,7 @@ function useCurrentContext() {
1237
3373
  function useRegisterTool(toolId, contextId, metadata) {
1238
3374
  const graph = useNavigationGraph();
1239
3375
  const currentContext = useCurrentContext();
1240
- useEffect3(() => {
3376
+ useEffect6(() => {
1241
3377
  let targetContext = contextId || currentContext;
1242
3378
  if (!contextId && metadata) {
1243
3379
  const detection = graph.detectToolContext?.(toolId, metadata);
@@ -1251,10 +3387,10 @@ function useRegisterTool(toolId, contextId, metadata) {
1251
3387
  function useNavigationPath(targetContextOrToolId, isToolId = false) {
1252
3388
  const graph = useNavigationGraph();
1253
3389
  const currentContext = useCurrentContext();
1254
- const [path, setPath] = useState3(null);
1255
- const [loading, setLoading] = useState3(true);
1256
- const [error, setError] = useState3();
1257
- useEffect3(() => {
3390
+ const [path, setPath] = useState8(null);
3391
+ const [loading, setLoading] = useState8(true);
3392
+ const [error, setError] = useState8();
3393
+ useEffect6(() => {
1258
3394
  try {
1259
3395
  let targetContext = targetContextOrToolId;
1260
3396
  if (isToolId) {
@@ -1282,9 +3418,9 @@ function useNavigationPath(targetContextOrToolId, isToolId = false) {
1282
3418
  function useNavigate() {
1283
3419
  const graph = useNavigationGraph();
1284
3420
  const currentContext = useCurrentContext();
1285
- const [navigating, setNavigating] = useState3(false);
1286
- const [error, setError] = useState3();
1287
- const navigateTo = useCallback3(async (targetContextOrToolId, isToolId = false, executeNavigation) => {
3421
+ const [navigating, setNavigating] = useState8(false);
3422
+ const [error, setError] = useState8();
3423
+ const navigateTo = useCallback5(async (targetContextOrToolId, isToolId = false, executeNavigation) => {
1288
3424
  setNavigating(true);
1289
3425
  setError(void 0);
1290
3426
  try {
@@ -1326,8 +3462,8 @@ function useNavigate() {
1326
3462
  }
1327
3463
  function useAllContexts() {
1328
3464
  const graph = useNavigationGraph();
1329
- const [contexts, setContexts] = useState3(graph.getAllContexts?.());
1330
- useEffect3(() => {
3465
+ const [contexts, setContexts] = useState8(graph.getAllContexts?.());
3466
+ useEffect6(() => {
1331
3467
  const interval = setInterval(() => {
1332
3468
  setContexts(graph.getAllContexts?.());
1333
3469
  }, 1e3);
@@ -1337,28 +3473,28 @@ function useAllContexts() {
1337
3473
  }
1338
3474
 
1339
3475
  // src/components/AutoNavigationContext.tsx
1340
- import { Fragment as Fragment2, jsx as jsx5 } from "react/jsx-runtime";
3476
+ import { Fragment as Fragment4, jsx as jsx13 } from "react/jsx-runtime";
1341
3477
  function AutoNavigationContext({
1342
3478
  children,
1343
3479
  routes,
1344
3480
  onNavigate
1345
3481
  }) {
1346
- const [pathname, setPathname] = useState4("/");
1347
- useEffect4(() => {
3482
+ const [pathname, setPathname] = useState9("/");
3483
+ useEffect7(() => {
1348
3484
  if (typeof window !== "undefined") {
1349
3485
  setPathname(window.location.pathname);
1350
3486
  }
1351
3487
  }, []);
1352
3488
  const context = routes ? inferContextFromPath(pathname, routes) : null;
1353
- useEffect4(() => {
3489
+ useEffect7(() => {
1354
3490
  if (onNavigate && context) {
1355
3491
  onNavigate(context);
1356
3492
  }
1357
3493
  }, [context, onNavigate]);
1358
3494
  if (!context) {
1359
- return /* @__PURE__ */ jsx5(Fragment2, { children });
3495
+ return /* @__PURE__ */ jsx13(Fragment4, { children });
1360
3496
  }
1361
- return /* @__PURE__ */ jsx5(NavigationContextProvider, { value: context, children });
3497
+ return /* @__PURE__ */ jsx13(NavigationContextProvider, { value: context, children });
1362
3498
  }
1363
3499
  function inferContextFromPath(path, customRoutes) {
1364
3500
  if (customRoutes) {
@@ -1389,22 +3525,35 @@ function inferContextFromPath(path, customRoutes) {
1389
3525
 
1390
3526
  // src/components/SupernalProvider.tsx
1391
3527
  import { ExposureCollector, ToolRegistry } from "@supernal/interface/browser";
1392
- import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
3528
+ import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
1393
3529
  function ChatBubbleConnector({
1394
3530
  theme,
1395
3531
  position,
1396
- welcomeMessage
3532
+ welcomeMessage,
3533
+ glassMode,
3534
+ logo,
3535
+ variant,
3536
+ displayMode,
3537
+ drawerSide
1397
3538
  }) {
1398
3539
  const { messages, sendMessage, clearMessages } = useChatContext();
1399
- return /* @__PURE__ */ jsx6(
3540
+ console.log("[ChatBubbleConnector] Props received:", { variant, displayMode, position });
3541
+ const config = {
3542
+ glassMode,
3543
+ ...logo ? { logo } : {}
3544
+ };
3545
+ return /* @__PURE__ */ jsx14(
1400
3546
  ChatBubble,
1401
3547
  {
1402
3548
  messages,
1403
3549
  onSendMessage: sendMessage,
1404
3550
  onClearChat: clearMessages,
1405
3551
  position,
1406
- variant: "full",
1407
- defaultExpanded: true
3552
+ variant: variant || "full",
3553
+ defaultExpanded: true,
3554
+ config,
3555
+ displayMode,
3556
+ drawerSide
1408
3557
  }
1409
3558
  );
1410
3559
  }
@@ -1417,13 +3566,20 @@ function SupernalProvider({
1417
3566
  welcomeMessage,
1418
3567
  routes,
1419
3568
  disabled = false,
3569
+ glassMode = true,
3570
+ logo,
3571
+ variant = "full",
3572
+ displayMode = "auto",
3573
+ drawerSide = "right",
1420
3574
  onNavigate,
1421
3575
  onToolExecute
1422
3576
  }) {
1423
3577
  const shouldRenderChatBubble = !disabled;
3578
+ const effectiveDisplayMode = variant !== "full" ? variant : displayMode;
1424
3579
  console.log("[SupernalProvider] disabled:", disabled, "type:", typeof disabled);
1425
3580
  console.log("[SupernalProvider] shouldRenderChatBubble:", shouldRenderChatBubble);
1426
- useEffect5(() => {
3581
+ console.log("[SupernalProvider] variant:", variant, "effectiveDisplayMode:", effectiveDisplayMode);
3582
+ useEffect8(() => {
1427
3583
  if (typeof window === "undefined") return;
1428
3584
  const collector = ExposureCollector.getInstance();
1429
3585
  const registeredToolIds = /* @__PURE__ */ new Set();
@@ -1455,19 +3611,426 @@ function SupernalProvider({
1455
3611
  collector.destroy();
1456
3612
  };
1457
3613
  }, []);
1458
- return /* @__PURE__ */ jsx6(ChatInputProvider, { children: /* @__PURE__ */ jsxs2(ChatProvider, { mode, apiKey, onToolExecute, children: [
1459
- /* @__PURE__ */ jsx6(AutoNavigationContext, { routes, onNavigate, children }),
1460
- shouldRenderChatBubble ? /* @__PURE__ */ jsx6(
3614
+ return /* @__PURE__ */ jsx14(ChatInputProvider, { children: /* @__PURE__ */ jsxs8(ChatProvider, { mode, apiKey, onToolExecute, children: [
3615
+ /* @__PURE__ */ jsx14(AutoNavigationContext, { routes, onNavigate, children }),
3616
+ shouldRenderChatBubble ? /* @__PURE__ */ jsx14(
1461
3617
  ChatBubbleConnector,
1462
3618
  {
1463
3619
  theme,
1464
3620
  position,
1465
- welcomeMessage
3621
+ welcomeMessage,
3622
+ glassMode,
3623
+ logo,
3624
+ variant,
3625
+ displayMode: effectiveDisplayMode,
3626
+ drawerSide
1466
3627
  }
1467
3628
  ) : null
1468
3629
  ] }) });
1469
3630
  }
1470
3631
 
3632
+ // src/components/ChatBubbleSettingsModal.tsx
3633
+ import React11, { useEffect as useEffect9 } from "react";
3634
+ import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
3635
+ function ChatBubbleSettingsModal({
3636
+ isOpen,
3637
+ onClose,
3638
+ settings,
3639
+ onSettingsChange
3640
+ }) {
3641
+ const [localSettings, setLocalSettings] = React11.useState(settings);
3642
+ useEffect9(() => {
3643
+ if (isOpen) {
3644
+ setLocalSettings(settings);
3645
+ }
3646
+ }, [isOpen, settings]);
3647
+ useEffect9(() => {
3648
+ const handleEscape = (e) => {
3649
+ if (e.key === "Escape" && isOpen) {
3650
+ onClose();
3651
+ }
3652
+ };
3653
+ window.addEventListener("keydown", handleEscape);
3654
+ return () => window.removeEventListener("keydown", handleEscape);
3655
+ }, [isOpen, onClose]);
3656
+ if (!isOpen) {
3657
+ return null;
3658
+ }
3659
+ const handleSave = () => {
3660
+ onSettingsChange(localSettings);
3661
+ onClose();
3662
+ };
3663
+ const handleCancel = () => {
3664
+ setLocalSettings(settings);
3665
+ onClose();
3666
+ };
3667
+ const isDark = localSettings.theme === "dark";
3668
+ return /* @__PURE__ */ jsxs9(Fragment5, { children: [
3669
+ /* @__PURE__ */ jsx15(
3670
+ "div",
3671
+ {
3672
+ className: "fixed inset-0 bg-black bg-opacity-50 z-[60] backdrop-blur-sm",
3673
+ onClick: handleCancel,
3674
+ "aria-hidden": "true"
3675
+ }
3676
+ ),
3677
+ /* @__PURE__ */ jsx15(
3678
+ "div",
3679
+ {
3680
+ className: "fixed inset-0 z-[70] flex items-center justify-center p-4",
3681
+ "data-testid": "chat-settings-modal",
3682
+ children: /* @__PURE__ */ jsxs9(
3683
+ "div",
3684
+ {
3685
+ className: `${isDark ? "bg-gray-800 text-white" : "bg-white text-gray-900"} rounded-2xl shadow-2xl max-w-md w-full p-6 border ${isDark ? "border-gray-700" : "border-gray-200"}`,
3686
+ role: "dialog",
3687
+ "aria-modal": "true",
3688
+ "aria-labelledby": "settings-modal-title",
3689
+ onClick: (e) => e.stopPropagation(),
3690
+ children: [
3691
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between mb-6", children: [
3692
+ /* @__PURE__ */ jsx15(
3693
+ "h2",
3694
+ {
3695
+ id: "settings-modal-title",
3696
+ className: "text-2xl font-bold",
3697
+ children: "Chat Settings"
3698
+ }
3699
+ ),
3700
+ /* @__PURE__ */ jsx15(
3701
+ "button",
3702
+ {
3703
+ onClick: handleCancel,
3704
+ className: `${isDark ? "text-gray-400 hover:text-gray-200" : "text-gray-400 hover:text-gray-600"} transition-colors p-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700`,
3705
+ "aria-label": "Close modal",
3706
+ children: /* @__PURE__ */ jsx15("svg", { className: "w-6 h-6", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx15("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
3707
+ }
3708
+ )
3709
+ ] }),
3710
+ /* @__PURE__ */ jsxs9("div", { className: "space-y-5 mb-6", children: [
3711
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
3712
+ /* @__PURE__ */ jsxs9("div", { children: [
3713
+ /* @__PURE__ */ jsx15("label", { className: "block text-base font-medium mb-1", children: "Theme" }),
3714
+ /* @__PURE__ */ jsx15("p", { className: `text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`, children: "Switch between light and dark modes" })
3715
+ ] }),
3716
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center space-x-2", children: [
3717
+ /* @__PURE__ */ jsx15(
3718
+ "button",
3719
+ {
3720
+ onClick: () => setLocalSettings({ ...localSettings, theme: "light" }),
3721
+ className: `p-2 rounded-lg transition-all ${localSettings.theme === "light" ? "bg-blue-600 text-white shadow-lg" : isDark ? "bg-gray-700 text-gray-300 hover:bg-gray-600" : "bg-gray-100 text-gray-600 hover:bg-gray-200"}`,
3722
+ title: "Light mode",
3723
+ children: /* @__PURE__ */ jsx15("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx15("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" }) })
3724
+ }
3725
+ ),
3726
+ /* @__PURE__ */ jsx15(
3727
+ "button",
3728
+ {
3729
+ onClick: () => setLocalSettings({ ...localSettings, theme: "dark" }),
3730
+ className: `p-2 rounded-lg transition-all ${localSettings.theme === "dark" ? "bg-blue-600 text-white shadow-lg" : isDark ? "bg-gray-700 text-gray-300 hover:bg-gray-600" : "bg-gray-100 text-gray-600 hover:bg-gray-200"}`,
3731
+ title: "Dark mode",
3732
+ children: /* @__PURE__ */ jsx15("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx15("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" }) })
3733
+ }
3734
+ )
3735
+ ] })
3736
+ ] }),
3737
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
3738
+ /* @__PURE__ */ jsxs9("div", { children: [
3739
+ /* @__PURE__ */ jsx15("label", { className: "block text-base font-medium mb-1", children: "Glass Mode" }),
3740
+ /* @__PURE__ */ jsx15("p", { className: `text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`, children: "Enable glassmorphism transparency effect" })
3741
+ ] }),
3742
+ /* @__PURE__ */ jsx15(
3743
+ "button",
3744
+ {
3745
+ onClick: () => setLocalSettings({ ...localSettings, glassMode: !localSettings.glassMode }),
3746
+ className: `relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${localSettings.glassMode ? "bg-blue-600" : isDark ? "bg-gray-700" : "bg-gray-300"}`,
3747
+ role: "switch",
3748
+ "aria-checked": localSettings.glassMode,
3749
+ children: /* @__PURE__ */ jsx15(
3750
+ "span",
3751
+ {
3752
+ className: `inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${localSettings.glassMode ? "translate-x-6" : "translate-x-1"}`
3753
+ }
3754
+ )
3755
+ }
3756
+ )
3757
+ ] }),
3758
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
3759
+ /* @__PURE__ */ jsxs9("div", { children: [
3760
+ /* @__PURE__ */ jsx15("label", { className: "block text-base font-medium mb-1", children: "Notifications" }),
3761
+ /* @__PURE__ */ jsx15("p", { className: `text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`, children: "Show unread message indicators" })
3762
+ ] }),
3763
+ /* @__PURE__ */ jsx15(
3764
+ "button",
3765
+ {
3766
+ onClick: () => setLocalSettings({ ...localSettings, notifications: !localSettings.notifications }),
3767
+ className: `relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${localSettings.notifications ? "bg-blue-600" : isDark ? "bg-gray-700" : "bg-gray-300"}`,
3768
+ role: "switch",
3769
+ "aria-checked": localSettings.notifications,
3770
+ children: /* @__PURE__ */ jsx15(
3771
+ "span",
3772
+ {
3773
+ className: `inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${localSettings.notifications ? "translate-x-6" : "translate-x-1"}`
3774
+ }
3775
+ )
3776
+ }
3777
+ )
3778
+ ] }),
3779
+ /* @__PURE__ */ jsx15("div", { className: `border-t ${isDark ? "border-gray-700" : "border-gray-200"} my-2` }),
3780
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
3781
+ /* @__PURE__ */ jsxs9("div", { children: [
3782
+ /* @__PURE__ */ jsxs9("label", { className: "block text-base font-medium mb-1 flex items-center space-x-2", children: [
3783
+ /* @__PURE__ */ jsx15("span", { children: "Subtitle Overlay" }),
3784
+ /* @__PURE__ */ jsx15("span", { className: "inline-block px-2 py-0.5 text-xs font-semibold rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200", children: "BETA" })
3785
+ ] }),
3786
+ /* @__PURE__ */ jsx15("p", { className: `text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`, children: "Minimalist voice-first overlay with @/ icon" })
3787
+ ] }),
3788
+ /* @__PURE__ */ jsx15(
3789
+ "button",
3790
+ {
3791
+ onClick: () => setLocalSettings({
3792
+ ...localSettings,
3793
+ subtitleOverlayEnabled: !localSettings.subtitleOverlayEnabled,
3794
+ // When enabling subtitle overlay, switch displayMode to subtitle
3795
+ ...!localSettings.subtitleOverlayEnabled && { displayMode: "subtitle" }
3796
+ }),
3797
+ className: `relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${localSettings.subtitleOverlayEnabled ? "bg-blue-600" : isDark ? "bg-gray-700" : "bg-gray-300"}`,
3798
+ role: "switch",
3799
+ "aria-checked": localSettings.subtitleOverlayEnabled,
3800
+ children: /* @__PURE__ */ jsx15(
3801
+ "span",
3802
+ {
3803
+ className: `inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${localSettings.subtitleOverlayEnabled ? "translate-x-6" : "translate-x-1"}`
3804
+ }
3805
+ )
3806
+ }
3807
+ )
3808
+ ] }),
3809
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
3810
+ /* @__PURE__ */ jsxs9("div", { children: [
3811
+ /* @__PURE__ */ jsx15("label", { className: "block text-base font-medium mb-1", children: "Display Mode" }),
3812
+ /* @__PURE__ */ jsx15("p", { className: `text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`, children: "Auto switches drawer on mobile, panel on desktop" })
3813
+ ] }),
3814
+ /* @__PURE__ */ jsxs9(
3815
+ "select",
3816
+ {
3817
+ value: localSettings.displayMode || "auto",
3818
+ onChange: (e) => setLocalSettings({
3819
+ ...localSettings,
3820
+ displayMode: e.target.value
3821
+ }),
3822
+ className: `px-3 py-2 rounded-lg border ${isDark ? "bg-gray-700 border-gray-600 text-white" : "bg-white border-gray-300 text-gray-900"} focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all`,
3823
+ children: [
3824
+ /* @__PURE__ */ jsx15("option", { value: "auto", children: "Auto (Recommended)" }),
3825
+ /* @__PURE__ */ jsx15("option", { value: "drawer", children: "Always Drawer" }),
3826
+ /* @__PURE__ */ jsx15("option", { value: "full", children: "Always Panel" }),
3827
+ /* @__PURE__ */ jsx15("option", { value: "floating", children: "Always Floating" }),
3828
+ /* @__PURE__ */ jsx15("option", { value: "subtitle", children: "Subtitle Overlay (Beta)" })
3829
+ ]
3830
+ }
3831
+ )
3832
+ ] }),
3833
+ (localSettings.displayMode === "auto" || localSettings.displayMode === "drawer") && /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
3834
+ /* @__PURE__ */ jsxs9("div", { children: [
3835
+ /* @__PURE__ */ jsx15("label", { className: "block text-base font-medium mb-1", children: "Drawer Side" }),
3836
+ /* @__PURE__ */ jsx15("p", { className: `text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`, children: "Which edge drawer slides from" })
3837
+ ] }),
3838
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center space-x-2", children: [
3839
+ /* @__PURE__ */ jsx15(
3840
+ "button",
3841
+ {
3842
+ onClick: () => setLocalSettings({ ...localSettings, drawerSide: "left" }),
3843
+ className: `px-4 py-2 rounded-lg transition-all ${localSettings.drawerSide === "left" ? "bg-blue-600 text-white shadow-lg" : isDark ? "bg-gray-700 text-gray-300 hover:bg-gray-600" : "bg-gray-100 text-gray-600 hover:bg-gray-200"}`,
3844
+ children: "Left"
3845
+ }
3846
+ ),
3847
+ /* @__PURE__ */ jsx15(
3848
+ "button",
3849
+ {
3850
+ onClick: () => setLocalSettings({ ...localSettings, drawerSide: "right" }),
3851
+ className: `px-4 py-2 rounded-lg transition-all ${localSettings.drawerSide === "right" ? "bg-blue-600 text-white shadow-lg" : isDark ? "bg-gray-700 text-gray-300 hover:bg-gray-600" : "bg-gray-100 text-gray-600 hover:bg-gray-200"}`,
3852
+ children: "Right"
3853
+ }
3854
+ )
3855
+ ] })
3856
+ ] }),
3857
+ /* @__PURE__ */ jsx15("div", { className: `border-t ${isDark ? "border-gray-700" : "border-gray-200"} my-2` }),
3858
+ /* @__PURE__ */ jsxs9("div", { className: "mb-3", children: [
3859
+ /* @__PURE__ */ jsx15("h3", { className: "text-lg font-semibold mb-1", children: "Voice Control" }),
3860
+ /* @__PURE__ */ jsx15("p", { className: `text-xs ${isDark ? "text-gray-400" : "text-gray-500"}`, children: "Enable voice input and audio feedback" })
3861
+ ] }),
3862
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
3863
+ /* @__PURE__ */ jsxs9("div", { children: [
3864
+ /* @__PURE__ */ jsx15("label", { className: "block text-base font-medium mb-1", children: "Voice Control" }),
3865
+ /* @__PURE__ */ jsx15("p", { className: `text-sm ${isDark ? "text-gray-400" : "text-gray-500"}`, children: "Enable voice input and TTS responses" })
3866
+ ] }),
3867
+ /* @__PURE__ */ jsx15(
3868
+ "button",
3869
+ {
3870
+ onClick: () => setLocalSettings({ ...localSettings, voiceEnabled: !localSettings.voiceEnabled }),
3871
+ className: `relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${localSettings.voiceEnabled ? "bg-blue-600" : isDark ? "bg-gray-700" : "bg-gray-300"}`,
3872
+ role: "switch",
3873
+ "aria-checked": localSettings.voiceEnabled,
3874
+ children: /* @__PURE__ */ jsx15(
3875
+ "span",
3876
+ {
3877
+ className: `inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${localSettings.voiceEnabled ? "translate-x-6" : "translate-x-1"}`
3878
+ }
3879
+ )
3880
+ }
3881
+ )
3882
+ ] }),
3883
+ localSettings.voiceEnabled && /* @__PURE__ */ jsxs9(Fragment5, { children: [
3884
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between pl-4 border-l-2 border-blue-500/30", children: [
3885
+ /* @__PURE__ */ jsxs9("div", { children: [
3886
+ /* @__PURE__ */ jsx15("label", { className: "block text-sm font-medium mb-1", children: "Auto-read AI Responses" }),
3887
+ /* @__PURE__ */ jsx15("p", { className: `text-xs ${isDark ? "text-gray-400" : "text-gray-500"}`, children: "Automatically read AI messages aloud" })
3888
+ ] }),
3889
+ /* @__PURE__ */ jsx15(
3890
+ "button",
3891
+ {
3892
+ onClick: () => setLocalSettings({ ...localSettings, autoReadResponses: !localSettings.autoReadResponses }),
3893
+ className: `relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${localSettings.autoReadResponses ? "bg-blue-600" : isDark ? "bg-gray-700" : "bg-gray-300"}`,
3894
+ role: "switch",
3895
+ "aria-checked": localSettings.autoReadResponses,
3896
+ children: /* @__PURE__ */ jsx15(
3897
+ "span",
3898
+ {
3899
+ className: `inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${localSettings.autoReadResponses ? "translate-x-6" : "translate-x-1"}`
3900
+ }
3901
+ )
3902
+ }
3903
+ )
3904
+ ] }),
3905
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between pl-4 border-l-2 border-blue-500/30", children: [
3906
+ /* @__PURE__ */ jsxs9("div", { children: [
3907
+ /* @__PURE__ */ jsx15("label", { className: "block text-sm font-medium mb-1", children: "Premium Voices \u{1F48E}" }),
3908
+ /* @__PURE__ */ jsx15("p", { className: `text-xs ${isDark ? "text-gray-400" : "text-gray-500"}`, children: "Use high-quality OpenAI voices (requires network)" })
3909
+ ] }),
3910
+ /* @__PURE__ */ jsx15(
3911
+ "button",
3912
+ {
3913
+ onClick: () => setLocalSettings({ ...localSettings, usePremiumVoices: !localSettings.usePremiumVoices }),
3914
+ className: `relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${localSettings.usePremiumVoices ? "bg-blue-600" : isDark ? "bg-gray-700" : "bg-gray-300"}`,
3915
+ role: "switch",
3916
+ "aria-checked": localSettings.usePremiumVoices,
3917
+ children: /* @__PURE__ */ jsx15(
3918
+ "span",
3919
+ {
3920
+ className: `inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${localSettings.usePremiumVoices ? "translate-x-6" : "translate-x-1"}`
3921
+ }
3922
+ )
3923
+ }
3924
+ )
3925
+ ] }),
3926
+ /* @__PURE__ */ jsxs9("div", { className: "pl-4 border-l-2 border-blue-500/30", children: [
3927
+ /* @__PURE__ */ jsxs9("label", { className: "block text-sm font-medium mb-2", children: [
3928
+ "Voice Speed: ",
3929
+ localSettings.ttsSpeed.toFixed(1),
3930
+ "x"
3931
+ ] }),
3932
+ /* @__PURE__ */ jsx15(
3933
+ "input",
3934
+ {
3935
+ type: "range",
3936
+ min: "0.5",
3937
+ max: "2.0",
3938
+ step: "0.1",
3939
+ value: localSettings.ttsSpeed,
3940
+ onChange: (e) => setLocalSettings({ ...localSettings, ttsSpeed: parseFloat(e.target.value) }),
3941
+ className: "w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-600"
3942
+ }
3943
+ ),
3944
+ /* @__PURE__ */ jsxs9("div", { className: "flex justify-between text-xs text-gray-400 mt-1", children: [
3945
+ /* @__PURE__ */ jsx15("span", { children: "0.5x (Slow)" }),
3946
+ /* @__PURE__ */ jsx15("span", { children: "1.0x (Normal)" }),
3947
+ /* @__PURE__ */ jsx15("span", { children: "2.0x (Fast)" })
3948
+ ] })
3949
+ ] }),
3950
+ /* @__PURE__ */ jsx15("div", { className: `border-t ${isDark ? "border-gray-700" : "border-gray-200"} my-2` }),
3951
+ /* @__PURE__ */ jsxs9("div", { className: "mb-3", children: [
3952
+ /* @__PURE__ */ jsx15("h4", { className: "text-sm font-semibold mb-1", children: "Voice Quick Record (Ctrl+/)" }),
3953
+ /* @__PURE__ */ jsx15("p", { className: `text-xs ${isDark ? "text-gray-400" : "text-gray-500"}`, children: "Press Ctrl+/ (or Cmd+/ on Mac) to auto-record and execute voice commands" })
3954
+ ] }),
3955
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between pl-4 border-l-2 border-purple-500/30", children: [
3956
+ /* @__PURE__ */ jsxs9("div", { children: [
3957
+ /* @__PURE__ */ jsx15("label", { className: "block text-sm font-medium mb-1", children: "Auto-Execute Commands" }),
3958
+ /* @__PURE__ */ jsx15("p", { className: `text-xs ${isDark ? "text-gray-400" : "text-gray-500"}`, children: "Automatically run recognized voice commands" })
3959
+ ] }),
3960
+ /* @__PURE__ */ jsx15(
3961
+ "button",
3962
+ {
3963
+ onClick: () => setLocalSettings({ ...localSettings, sttAutoExecute: !localSettings.sttAutoExecute }),
3964
+ className: `relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${localSettings.sttAutoExecute ? "bg-purple-600" : isDark ? "bg-gray-700" : "bg-gray-300"}`,
3965
+ role: "switch",
3966
+ "aria-checked": localSettings.sttAutoExecute,
3967
+ children: /* @__PURE__ */ jsx15(
3968
+ "span",
3969
+ {
3970
+ className: `inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${localSettings.sttAutoExecute ? "translate-x-6" : "translate-x-1"}`
3971
+ }
3972
+ )
3973
+ }
3974
+ )
3975
+ ] }),
3976
+ /* @__PURE__ */ jsxs9("div", { className: "pl-4 border-l-2 border-purple-500/30", children: [
3977
+ /* @__PURE__ */ jsxs9("label", { className: "block text-sm font-medium mb-2", children: [
3978
+ "Recording Timeout: ",
3979
+ (localSettings.sttAutoRecordTimeout / 1e3).toFixed(1),
3980
+ "s"
3981
+ ] }),
3982
+ /* @__PURE__ */ jsx15(
3983
+ "input",
3984
+ {
3985
+ type: "range",
3986
+ min: "2000",
3987
+ max: "15000",
3988
+ step: "1000",
3989
+ value: localSettings.sttAutoRecordTimeout,
3990
+ onChange: (e) => setLocalSettings({ ...localSettings, sttAutoRecordTimeout: parseInt(e.target.value) }),
3991
+ className: "w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-purple-600"
3992
+ }
3993
+ ),
3994
+ /* @__PURE__ */ jsxs9("div", { className: "flex justify-between text-xs text-gray-400 mt-1", children: [
3995
+ /* @__PURE__ */ jsx15("span", { children: "2s" }),
3996
+ /* @__PURE__ */ jsx15("span", { children: "8s" }),
3997
+ /* @__PURE__ */ jsx15("span", { children: "15s" })
3998
+ ] })
3999
+ ] }),
4000
+ /* @__PURE__ */ jsxs9("div", { className: `p-3 rounded-lg ${isDark ? "bg-purple-900/20 border border-purple-500/30" : "bg-purple-50 border border-purple-200"}`, children: [
4001
+ /* @__PURE__ */ jsx15("p", { className: `text-xs ${isDark ? "text-purple-300" : "text-purple-800"} mb-1 font-medium`, children: "\u26A1 Quick Tip: Press Ctrl+/ anywhere (even while typing!)" }),
4002
+ /* @__PURE__ */ jsx15("p", { className: `text-xs ${isDark ? "text-purple-400" : "text-purple-700"}`, children: localSettings.sttAutoExecute ? "Recording auto-stops and executes your command" : "Recording auto-stops and fills the input (press Enter to send)" })
4003
+ ] }),
4004
+ !localSettings.usePremiumVoices && /* @__PURE__ */ jsx15("div", { className: `p-3 rounded-lg ${isDark ? "bg-green-900/20 border border-green-500/30" : "bg-green-50 border border-green-200"}`, children: /* @__PURE__ */ jsx15("p", { className: `text-xs ${isDark ? "text-green-300" : "text-green-800"}`, children: "\u{1F49A} Using free device voices (works offline, zero cost)" }) }),
4005
+ localSettings.usePremiumVoices && /* @__PURE__ */ jsx15("div", { className: `p-3 rounded-lg ${isDark ? "bg-purple-900/20 border border-purple-500/30" : "bg-purple-50 border border-purple-200"}`, children: /* @__PURE__ */ jsx15("p", { className: `text-xs ${isDark ? "text-purple-300" : "text-purple-800"}`, children: "\u{1F48E} Using premium OpenAI voices (requires internet connection)" }) })
4006
+ ] })
4007
+ ] }),
4008
+ /* @__PURE__ */ jsxs9("div", { className: "flex justify-end space-x-3 pt-4 border-t border-gray-200 dark:border-gray-700", children: [
4009
+ /* @__PURE__ */ jsx15(
4010
+ "button",
4011
+ {
4012
+ onClick: handleCancel,
4013
+ className: `px-4 py-2 rounded-lg transition-colors ${isDark ? "bg-gray-700 text-gray-200 hover:bg-gray-600" : "bg-gray-100 text-gray-700 hover:bg-gray-200"}`,
4014
+ children: "Cancel"
4015
+ }
4016
+ ),
4017
+ /* @__PURE__ */ jsx15(
4018
+ "button",
4019
+ {
4020
+ onClick: handleSave,
4021
+ className: "px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors shadow-lg",
4022
+ children: "Save Changes"
4023
+ }
4024
+ )
4025
+ ] })
4026
+ ]
4027
+ }
4028
+ )
4029
+ }
4030
+ )
4031
+ ] });
4032
+ }
4033
+
1471
4034
  // src/lib/FuzzyMatcher.ts
1472
4035
  var FuzzyMatcher = class {
1473
4036
  /**
@@ -1589,11 +4152,19 @@ function findBestMatch(query, tools) {
1589
4152
  export {
1590
4153
  AutoNavigationContext,
1591
4154
  ChatBubble,
4155
+ ChatBubbleSettingsModal,
4156
+ ChatBubbleVariant,
1592
4157
  ChatInputProvider,
1593
4158
  ChatProvider,
4159
+ CodeBlock,
4160
+ Components,
1594
4161
  DemoAIInterface,
1595
4162
  FuzzyMatcher,
4163
+ MermaidDiagram,
4164
+ MessageRenderer,
1596
4165
  NavigationContextProvider,
4166
+ PageLayout,
4167
+ SubtitleOverlay,
1597
4168
  SupernalProvider,
1598
4169
  ToolManager,
1599
4170
  findBestMatch,