Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
import CopyToClipboard from '@/components/copy-to-clipboard';
|
2025-11-28 17:15:01 +08:00
|
|
|
import PdfSheet from '@/components/pdf-drawer';
|
2025-10-17 18:48:47 +08:00
|
|
|
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
2025-11-28 19:05:43 +08:00
|
|
|
import { MessageType, SharedFrom } from '@/constants/chat';
|
|
|
|
|
import { useFetchExternalAgentInputs } from '@/hooks/use-agent-request';
|
2025-09-22 05:03:33 +02:00
|
|
|
import { useFetchExternalChatInfo } from '@/hooks/use-chat-request';
|
2026-02-13 18:40:41 +08:00
|
|
|
import i18n, { changeLanguageAsync } from '@/locales/config';
|
2025-11-28 19:05:43 +08:00
|
|
|
import { useSendNextSharedMessage } from '@/pages/agent/hooks/use-send-shared-message';
|
2025-09-22 05:03:33 +02:00
|
|
|
import { MessageCircle, Minimize2, Send, X } from 'lucide-react';
|
2025-10-20 15:59:56 +08:00
|
|
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
2026-04-29 17:03:33 +08:00
|
|
|
import { useTranslation } from 'react-i18next';
|
2025-09-22 05:03:33 +02:00
|
|
|
import {
|
|
|
|
|
useGetSharedChatSearchParams,
|
|
|
|
|
useSendSharedMessage,
|
|
|
|
|
} from '../pages/next-chats/hooks/use-send-shared-message';
|
2025-09-28 07:58:10 +02:00
|
|
|
import FloatingChatWidgetMarkdown from './floating-chat-widget-markdown';
|
2025-09-22 05:03:33 +02:00
|
|
|
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
/**
|
|
|
|
|
* Normalizes a hex color input and falls back to a safe default when invalid.
|
|
|
|
|
*/
|
|
|
|
|
const normalizeHexColor = (value: string | null, fallback: string) => {
|
|
|
|
|
return value && /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(value)
|
|
|
|
|
? value
|
|
|
|
|
: fallback;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Darkens a hex color to derive hover and gradient variants for the widget chrome.
|
|
|
|
|
*/
|
|
|
|
|
const darkenHexColor = (hexColor: string, amount = 0.12) => {
|
|
|
|
|
const normalizedHex = hexColor.replace('#', '');
|
|
|
|
|
const expandedHex =
|
|
|
|
|
normalizedHex.length === 3
|
|
|
|
|
? normalizedHex
|
|
|
|
|
.split('')
|
|
|
|
|
.map((char) => `${char}${char}`)
|
|
|
|
|
.join('')
|
|
|
|
|
: normalizedHex;
|
|
|
|
|
const channels = expandedHex.match(/.{2}/g);
|
|
|
|
|
|
|
|
|
|
if (!channels) {
|
|
|
|
|
return hexColor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return `#${channels
|
|
|
|
|
.map((channel) => {
|
|
|
|
|
const value = parseInt(channel, 16);
|
|
|
|
|
const adjustedValue = Math.max(
|
|
|
|
|
0,
|
|
|
|
|
Math.min(255, Math.round(value * (1 - amount))),
|
|
|
|
|
);
|
|
|
|
|
return adjustedValue.toString(16).padStart(2, '0');
|
|
|
|
|
})
|
|
|
|
|
.join('')}`;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Accepts a footer link from the widget query string and returns a safe HTTP(S) URL.
|
|
|
|
|
*/
|
|
|
|
|
const normalizeWidgetFooterLink = (value: string | null) => {
|
|
|
|
|
const normalizedValue = value?.trim();
|
|
|
|
|
|
|
|
|
|
if (!normalizedValue) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const candidate = /^[a-z][a-z\d+.-]*:/i.test(normalizedValue)
|
|
|
|
|
? normalizedValue
|
|
|
|
|
: `https://${normalizedValue}`;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const url = new URL(candidate);
|
|
|
|
|
|
|
|
|
|
if (url.protocol === 'http:' || url.protocol === 'https:') {
|
|
|
|
|
return url.toString();
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Renders the embeddable floating chat widget and applies URL-driven widget settings.
|
|
|
|
|
*/
|
2025-09-22 05:03:33 +02:00
|
|
|
const FloatingChatWidget = () => {
|
2026-04-14 19:12:56 +07:00
|
|
|
const { t } = useTranslation();
|
2025-09-22 05:03:33 +02:00
|
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
|
|
|
const [isMinimized, setIsMinimized] = useState(false);
|
|
|
|
|
const [inputValue, setInputValue] = useState('');
|
|
|
|
|
const [lastResponseId, setLastResponseId] = useState<string | null>(null);
|
|
|
|
|
const [displayMessages, setDisplayMessages] = useState<any[]>([]);
|
|
|
|
|
const [isLoaded, setIsLoaded] = useState(false);
|
|
|
|
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
|
2025-11-28 19:05:43 +08:00
|
|
|
const {
|
|
|
|
|
sharedId: conversationId,
|
|
|
|
|
locale,
|
|
|
|
|
from,
|
|
|
|
|
} = useGetSharedChatSearchParams();
|
|
|
|
|
|
|
|
|
|
const isFromAgent = from === SharedFrom.Agent;
|
2025-09-22 05:03:33 +02:00
|
|
|
|
|
|
|
|
// Check if we're in button-only mode or window-only mode
|
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
|
const mode = urlParams.get('mode') || 'full'; // 'button', 'window', or 'full'
|
|
|
|
|
const enableStreaming = urlParams.get('streaming') === 'true'; // Only enable if explicitly set to true
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
const isMuted = urlParams.get('muted') === 'true';
|
|
|
|
|
const widgetTitle = urlParams.get('widget_title')?.trim();
|
|
|
|
|
const widgetSubtitle = urlParams.get('widget_subtitle')?.trim();
|
|
|
|
|
const widgetFooter = urlParams.get('widget_footer')?.trim();
|
|
|
|
|
const widgetFooterLink = normalizeWidgetFooterLink(
|
|
|
|
|
urlParams.get('widget_footer_link'),
|
|
|
|
|
);
|
|
|
|
|
const widgetAccentColor = normalizeHexColor(
|
|
|
|
|
urlParams.get('widget_accent_color'),
|
|
|
|
|
'#2563eb',
|
|
|
|
|
);
|
|
|
|
|
const widgetAccentColorStrong = darkenHexColor(widgetAccentColor);
|
|
|
|
|
const widgetBackgroundColor = normalizeHexColor(
|
|
|
|
|
urlParams.get('widget_background_color'),
|
|
|
|
|
'#ffffff',
|
|
|
|
|
);
|
|
|
|
|
const widgetTextColor = normalizeHexColor(
|
|
|
|
|
urlParams.get('widget_text_color'),
|
|
|
|
|
'#111827',
|
|
|
|
|
);
|
|
|
|
|
const widgetHeaderTextColor = normalizeHexColor(
|
|
|
|
|
urlParams.get('widget_header_text_color'),
|
|
|
|
|
'#ffffff',
|
|
|
|
|
);
|
|
|
|
|
const widgetFooterTextColor = normalizeHexColor(
|
|
|
|
|
urlParams.get('widget_footer_text_color'),
|
|
|
|
|
'#111827',
|
|
|
|
|
);
|
2025-09-22 05:03:33 +02:00
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
handlePressEnter,
|
|
|
|
|
handleInputChange,
|
|
|
|
|
value: hookValue,
|
|
|
|
|
sendLoading,
|
|
|
|
|
derivedMessages,
|
|
|
|
|
hasError,
|
2025-11-28 19:05:43 +08:00
|
|
|
} = (isFromAgent ? useSendNextSharedMessage : useSendSharedMessage)(() => {});
|
2025-09-22 05:03:33 +02:00
|
|
|
|
|
|
|
|
// Sync our local input with the hook's value when needed
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (hookValue && hookValue !== inputValue) {
|
|
|
|
|
setInputValue(hookValue);
|
|
|
|
|
}
|
|
|
|
|
}, [hookValue, inputValue]);
|
|
|
|
|
|
2025-11-28 19:05:43 +08:00
|
|
|
const { data } = (
|
|
|
|
|
isFromAgent ? useFetchExternalAgentInputs : useFetchExternalChatInfo
|
|
|
|
|
)();
|
|
|
|
|
|
|
|
|
|
const title = data.title;
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
const displayTitle = widgetTitle || title || t('chat.chatSupport');
|
|
|
|
|
const displaySubtitle = widgetSubtitle || t('chat.replyInstantly');
|
|
|
|
|
const displayFooter = widgetFooter || '';
|
|
|
|
|
const renderFooter = () => {
|
|
|
|
|
if (!displayFooter) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className="mt-3 -mx-4 -mb-4 px-4 py-3 text-center text-xs"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: widgetAccentColor,
|
|
|
|
|
color: widgetFooterTextColor,
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{widgetFooterLink ? (
|
|
|
|
|
<a
|
|
|
|
|
href={widgetFooterLink}
|
|
|
|
|
target="_blank"
|
|
|
|
|
rel="noreferrer"
|
|
|
|
|
className="underline underline-offset-2 hover:opacity-90"
|
|
|
|
|
style={{ color: widgetFooterTextColor }}
|
|
|
|
|
>
|
|
|
|
|
{displayFooter}
|
|
|
|
|
</a>
|
|
|
|
|
) : (
|
|
|
|
|
displayFooter
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
const bodyContainerStyle: React.CSSProperties = {
|
|
|
|
|
borderRadius: '0 0 16px 16px',
|
|
|
|
|
backgroundColor: widgetBackgroundColor,
|
|
|
|
|
color: widgetTextColor,
|
|
|
|
|
};
|
|
|
|
|
const inputStyle: React.CSSProperties = {
|
|
|
|
|
minHeight: '44px',
|
|
|
|
|
maxHeight: '120px',
|
|
|
|
|
color: widgetTextColor,
|
|
|
|
|
backgroundColor: widgetBackgroundColor,
|
|
|
|
|
};
|
2025-09-22 05:03:33 +02:00
|
|
|
|
2025-09-28 07:58:10 +02:00
|
|
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
|
|
|
|
useClickDrawer();
|
|
|
|
|
|
|
|
|
|
// PDF drawer state tracking
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
// Drawer state management
|
|
|
|
|
}, [visible, documentId, selectedChunk]);
|
|
|
|
|
|
2025-09-22 05:03:33 +02:00
|
|
|
// Play sound when opening
|
|
|
|
|
const playNotificationSound = useCallback(() => {
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
if (isMuted) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-22 05:03:33 +02:00
|
|
|
try {
|
2026-01-21 15:39:18 +08:00
|
|
|
const audioContext = new (
|
|
|
|
|
window.AudioContext || (window as any).webkitAudioContext
|
|
|
|
|
)();
|
2025-09-22 05:03:33 +02:00
|
|
|
const oscillator = audioContext.createOscillator();
|
|
|
|
|
const gainNode = audioContext.createGain();
|
|
|
|
|
|
|
|
|
|
oscillator.connect(gainNode);
|
|
|
|
|
gainNode.connect(audioContext.destination);
|
|
|
|
|
|
|
|
|
|
oscillator.frequency.value = 800;
|
|
|
|
|
oscillator.type = 'sine';
|
|
|
|
|
|
|
|
|
|
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
|
|
|
|
|
gainNode.gain.exponentialRampToValueAtTime(
|
|
|
|
|
0.01,
|
|
|
|
|
audioContext.currentTime + 0.3,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
oscillator.start(audioContext.currentTime);
|
|
|
|
|
oscillator.stop(audioContext.currentTime + 0.3);
|
|
|
|
|
} catch (error) {
|
2026-04-29 17:03:33 +08:00
|
|
|
console.warn(error);
|
2025-09-22 05:03:33 +02:00
|
|
|
// Silent fail if audio not supported
|
|
|
|
|
}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
}, [isMuted]);
|
2025-09-22 05:03:33 +02:00
|
|
|
|
|
|
|
|
// Play sound for AI responses (Intercom-style)
|
|
|
|
|
const playResponseSound = useCallback(() => {
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
if (isMuted) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-22 05:03:33 +02:00
|
|
|
try {
|
2026-01-21 15:39:18 +08:00
|
|
|
const audioContext = new (
|
|
|
|
|
window.AudioContext || (window as any).webkitAudioContext
|
|
|
|
|
)();
|
2025-09-22 05:03:33 +02:00
|
|
|
const oscillator = audioContext.createOscillator();
|
|
|
|
|
const gainNode = audioContext.createGain();
|
|
|
|
|
|
|
|
|
|
oscillator.connect(gainNode);
|
|
|
|
|
gainNode.connect(audioContext.destination);
|
|
|
|
|
|
|
|
|
|
oscillator.frequency.value = 600;
|
|
|
|
|
oscillator.type = 'sine';
|
|
|
|
|
|
|
|
|
|
gainNode.gain.setValueAtTime(0.2, audioContext.currentTime);
|
|
|
|
|
gainNode.gain.exponentialRampToValueAtTime(
|
|
|
|
|
0.01,
|
|
|
|
|
audioContext.currentTime + 0.2,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
oscillator.start(audioContext.currentTime);
|
|
|
|
|
oscillator.stop(audioContext.currentTime + 0.2);
|
|
|
|
|
} catch (error) {
|
2026-04-29 17:03:33 +08:00
|
|
|
console.warn(error);
|
|
|
|
|
|
2025-09-22 05:03:33 +02:00
|
|
|
// Silent fail if audio not supported
|
|
|
|
|
}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
}, [isMuted]);
|
2025-09-22 05:03:33 +02:00
|
|
|
|
|
|
|
|
// Set loaded state and locale
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
// Set component as loaded after a brief moment to prevent flash
|
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
|
setIsLoaded(true);
|
|
|
|
|
// Tell parent window that we're ready to be shown
|
|
|
|
|
window.parent.postMessage(
|
|
|
|
|
{
|
|
|
|
|
type: 'WIDGET_READY',
|
|
|
|
|
},
|
|
|
|
|
'*',
|
|
|
|
|
);
|
|
|
|
|
}, 50);
|
|
|
|
|
|
|
|
|
|
if (locale && i18n.language !== locale) {
|
2026-02-13 18:40:41 +08:00
|
|
|
changeLanguageAsync(locale);
|
2025-09-22 05:03:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return () => clearTimeout(timer);
|
|
|
|
|
}, [locale]);
|
|
|
|
|
|
|
|
|
|
// Handle message display based on streaming preference
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!derivedMessages) {
|
|
|
|
|
setDisplayMessages([]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (enableStreaming) {
|
|
|
|
|
// Show messages as they stream
|
|
|
|
|
setDisplayMessages(derivedMessages);
|
|
|
|
|
} else {
|
|
|
|
|
// Only show complete messages (non-streaming mode)
|
|
|
|
|
const completeMessages = derivedMessages.filter((msg, index) => {
|
|
|
|
|
// Always show user messages immediately
|
|
|
|
|
if (msg.role === MessageType.User) return true;
|
|
|
|
|
|
|
|
|
|
// For AI messages, only show when response is complete (not loading)
|
|
|
|
|
if (msg.role === MessageType.Assistant) {
|
|
|
|
|
return !sendLoading || index < derivedMessages.length - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
setDisplayMessages(completeMessages);
|
|
|
|
|
}
|
|
|
|
|
}, [derivedMessages, enableStreaming, sendLoading]);
|
|
|
|
|
|
|
|
|
|
// Auto-scroll to bottom when display messages change
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
|
|
|
}, [displayMessages]);
|
|
|
|
|
|
2025-10-20 15:59:56 +08:00
|
|
|
// Render different content based on mode
|
|
|
|
|
// Master mode - handles everything and creates second iframe dynamically
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (mode !== 'master') return;
|
2026-04-29 17:03:33 +08:00
|
|
|
|
|
|
|
|
const isInIframe = window.self !== window.top;
|
|
|
|
|
|
|
|
|
|
if (isInIframe) {
|
|
|
|
|
// Embedded: tell parent to create chat window iframe
|
2025-10-20 15:59:56 +08:00
|
|
|
window.parent.postMessage(
|
|
|
|
|
{
|
|
|
|
|
type: 'CREATE_CHAT_WINDOW',
|
|
|
|
|
src: window.location.href.replace('mode=master', 'mode=window'),
|
|
|
|
|
},
|
|
|
|
|
'*',
|
|
|
|
|
);
|
2026-04-29 17:03:33 +08:00
|
|
|
} else {
|
|
|
|
|
// Standalone: create chat window iframe ourselves
|
|
|
|
|
if (!document.getElementById('chat-win')) {
|
|
|
|
|
const i = document.createElement('iframe');
|
|
|
|
|
i.id = 'chat-win';
|
|
|
|
|
i.src = window.location.href.replace('mode=master', 'mode=window');
|
|
|
|
|
i.style.cssText =
|
|
|
|
|
'position:fixed;bottom:104px;right:24px;width:380px;height:500px;border:none;background:transparent;z-index:9998;display:none';
|
|
|
|
|
i.frameBorder = '0';
|
|
|
|
|
i.allow = 'microphone;camera';
|
|
|
|
|
document.body.appendChild(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-20 15:59:56 +08:00
|
|
|
|
2026-04-29 17:03:33 +08:00
|
|
|
// Listen for toggle messages to show/hide the chat window iframe
|
2025-10-20 15:59:56 +08:00
|
|
|
const handleToggle = (e: MessageEvent) => {
|
2026-04-29 17:03:33 +08:00
|
|
|
if (e.data.type === 'TOGGLE_CHAT') {
|
|
|
|
|
const chatWindow = document.getElementById(
|
|
|
|
|
'chat-win',
|
|
|
|
|
) as HTMLIFrameElement;
|
|
|
|
|
if (chatWindow) {
|
|
|
|
|
chatWindow.style.display = e.data.isOpen ? 'block' : 'none';
|
|
|
|
|
}
|
2025-10-20 15:59:56 +08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
window.addEventListener('message', handleToggle);
|
|
|
|
|
return () => window.removeEventListener('message', handleToggle);
|
|
|
|
|
}, [mode]);
|
|
|
|
|
|
2025-09-22 05:03:33 +02:00
|
|
|
// Play sound only when AI response is complete (not streaming chunks)
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (derivedMessages && derivedMessages.length > 0 && !sendLoading) {
|
|
|
|
|
const lastMessage = derivedMessages[derivedMessages.length - 1];
|
|
|
|
|
if (
|
|
|
|
|
lastMessage.role === MessageType.Assistant &&
|
|
|
|
|
lastMessage.id !== lastResponseId &&
|
|
|
|
|
derivedMessages.length > 1
|
|
|
|
|
) {
|
|
|
|
|
setLastResponseId(lastMessage.id || '');
|
|
|
|
|
playResponseSound();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, [derivedMessages, sendLoading, lastResponseId, playResponseSound]);
|
|
|
|
|
|
|
|
|
|
const toggleChat = useCallback(() => {
|
|
|
|
|
if (mode === 'button') {
|
|
|
|
|
// In button mode, communicate with parent window to show/hide chat window
|
|
|
|
|
window.parent.postMessage(
|
|
|
|
|
{
|
|
|
|
|
type: 'TOGGLE_CHAT',
|
|
|
|
|
isOpen: !isOpen,
|
|
|
|
|
},
|
|
|
|
|
'*',
|
|
|
|
|
);
|
|
|
|
|
setIsOpen(!isOpen);
|
|
|
|
|
if (!isOpen) {
|
|
|
|
|
playNotificationSound();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// In full mode, handle locally
|
|
|
|
|
if (!isOpen) {
|
|
|
|
|
setIsOpen(true);
|
|
|
|
|
setIsMinimized(false);
|
|
|
|
|
playNotificationSound();
|
|
|
|
|
} else {
|
|
|
|
|
setIsOpen(false);
|
|
|
|
|
setIsMinimized(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, [isOpen, mode, playNotificationSound]);
|
|
|
|
|
|
|
|
|
|
const minimizeChat = useCallback(() => {
|
|
|
|
|
setIsMinimized(true);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const handleSendMessage = useCallback(() => {
|
|
|
|
|
if (!inputValue.trim() || sendLoading) return;
|
|
|
|
|
|
|
|
|
|
// Update the hook's internal state first
|
|
|
|
|
const syntheticEvent = {
|
|
|
|
|
target: { value: inputValue },
|
|
|
|
|
currentTarget: { value: inputValue },
|
2025-10-17 18:48:47 +08:00
|
|
|
preventDefault: () => {},
|
2025-09-22 05:03:33 +02:00
|
|
|
} as any;
|
|
|
|
|
|
|
|
|
|
handleInputChange(syntheticEvent);
|
|
|
|
|
|
|
|
|
|
// Wait for state to update, then send
|
|
|
|
|
setTimeout(() => {
|
2026-01-23 09:33:50 +08:00
|
|
|
handlePressEnter({ enableThinking: false, enableInternet: false });
|
2025-09-22 05:03:33 +02:00
|
|
|
// Clear our local input after sending
|
|
|
|
|
setInputValue('');
|
|
|
|
|
}, 50);
|
|
|
|
|
}, [inputValue, sendLoading, handleInputChange, handlePressEnter]);
|
|
|
|
|
|
|
|
|
|
const handleKeyPress = useCallback(
|
|
|
|
|
(e: React.KeyboardEvent) => {
|
|
|
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
handleSendMessage();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[handleSendMessage],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!conversationId) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="fixed bottom-5 right-5 z-50">
|
|
|
|
|
<div className="bg-red-500 text-white p-4 rounded-lg shadow-lg">
|
|
|
|
|
Error: No conversation ID provided
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove the blocking return - we'll handle visibility with CSS instead
|
|
|
|
|
|
|
|
|
|
const messageCount = displayMessages?.length || 0;
|
|
|
|
|
|
2025-10-20 15:59:56 +08:00
|
|
|
// Show just the button in master mode
|
2025-09-22 05:03:33 +02:00
|
|
|
if (mode === 'master') {
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={`fixed bottom-6 right-6 z-50 transition-opacity duration-300 ${isLoaded ? 'opacity-100' : 'opacity-0'}`}
|
|
|
|
|
>
|
|
|
|
|
<button
|
2025-10-17 18:48:47 +08:00
|
|
|
type="button"
|
2025-09-22 05:03:33 +02:00
|
|
|
onClick={() => {
|
|
|
|
|
const newIsOpen = !isOpen;
|
|
|
|
|
setIsOpen(newIsOpen);
|
|
|
|
|
if (newIsOpen) playNotificationSound();
|
|
|
|
|
|
2026-04-29 17:03:33 +08:00
|
|
|
// Send toggle message to parent (if embedded) or self (if standalone)
|
|
|
|
|
const target = window.self !== window.top ? window.parent : window;
|
|
|
|
|
target.postMessage(
|
2025-09-22 05:03:33 +02:00
|
|
|
{
|
|
|
|
|
type: 'TOGGLE_CHAT',
|
|
|
|
|
isOpen: newIsOpen,
|
|
|
|
|
},
|
|
|
|
|
'*',
|
|
|
|
|
);
|
|
|
|
|
}}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className={`w-14 h-14 text-white rounded-full transition-all duration-300 flex items-center justify-center group ${
|
2025-10-17 18:48:47 +08:00
|
|
|
isOpen ? 'scale-95' : 'scale-100 hover:scale-105'
|
|
|
|
|
}`}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
style={{ backgroundColor: widgetAccentColor }}
|
2025-09-22 05:03:33 +02:00
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
className={`transition-transform duration-300 ${isOpen ? 'rotate-45' : 'rotate-0'}`}
|
|
|
|
|
>
|
|
|
|
|
{isOpen ? <X size={24} /> : <MessageCircle size={24} />}
|
|
|
|
|
</div>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
{/* Unread Badge */}
|
|
|
|
|
{!isOpen && messageCount > 0 && (
|
|
|
|
|
<div className="absolute -top-2 -right-2 w-6 h-6 bg-red-500 text-white text-xs font-bold rounded-full flex items-center justify-center animate-pulse">
|
|
|
|
|
{messageCount > 9 ? '9+' : messageCount}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mode === 'button') {
|
|
|
|
|
// Only render the floating button
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={`fixed bottom-6 right-6 z-50 transition-opacity duration-300 ${isLoaded ? 'opacity-100' : 'opacity-0'}`}
|
|
|
|
|
>
|
|
|
|
|
<button
|
2025-10-17 18:48:47 +08:00
|
|
|
type="button"
|
2025-09-22 05:03:33 +02:00
|
|
|
onClick={toggleChat}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className={`w-14 h-14 text-white rounded-full transition-all duration-300 flex items-center justify-center group ${
|
2025-10-17 18:48:47 +08:00
|
|
|
isOpen ? 'scale-95' : 'scale-100 hover:scale-105'
|
|
|
|
|
}`}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
style={{ backgroundColor: widgetAccentColor }}
|
2025-09-22 05:03:33 +02:00
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
className={`transition-transform duration-300 ${isOpen ? 'rotate-45' : 'rotate-0'}`}
|
|
|
|
|
>
|
|
|
|
|
{isOpen ? <X size={24} /> : <MessageCircle size={24} />}
|
|
|
|
|
</div>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
{/* Unread Badge */}
|
|
|
|
|
{!isOpen && messageCount > 0 && (
|
|
|
|
|
<div className="absolute -top-2 -right-2 w-6 h-6 bg-red-500 text-white text-xs font-bold rounded-full flex items-center justify-center animate-pulse">
|
|
|
|
|
{messageCount > 9 ? '9+' : messageCount}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mode === 'window') {
|
|
|
|
|
// Only render the chat window (always open)
|
|
|
|
|
return (
|
2025-09-28 07:58:10 +02:00
|
|
|
<>
|
|
|
|
|
<div
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className={`fixed top-0 left-0 z-50 rounded-2xl transition-all duration-300 ease-out h-[500px] w-[380px] overflow-hidden ${isLoaded ? 'opacity-100' : 'opacity-0'}`}
|
|
|
|
|
style={{ backgroundColor: widgetAccentColor }}
|
2025-09-28 07:58:10 +02:00
|
|
|
>
|
|
|
|
|
{/* Header */}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
<div
|
|
|
|
|
className="flex items-center justify-between p-4 text-white rounded-t-2xl"
|
|
|
|
|
style={{
|
|
|
|
|
background: `linear-gradient(to right, ${widgetAccentColor}, ${widgetAccentColorStrong})`,
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-09-28 07:58:10 +02:00
|
|
|
<div className="flex items-center space-x-3">
|
|
|
|
|
<div className="w-8 h-8 bg-white bg-opacity-20 rounded-full flex items-center justify-center">
|
|
|
|
|
<MessageCircle size={18} />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
<h3
|
|
|
|
|
className="font-semibold text-sm"
|
|
|
|
|
style={{ color: widgetHeaderTextColor }}
|
|
|
|
|
>
|
|
|
|
|
{displayTitle}
|
2025-09-28 07:58:10 +02:00
|
|
|
</h3>
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
<p className="text-xs" style={{ color: widgetHeaderTextColor }}>
|
|
|
|
|
{displaySubtitle}
|
2025-09-28 07:58:10 +02:00
|
|
|
</p>
|
|
|
|
|
</div>
|
2025-09-22 05:03:33 +02:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-09-28 07:58:10 +02:00
|
|
|
{/* Messages and Input */}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
<div className="flex flex-col h-[436px]" style={bodyContainerStyle}>
|
2025-09-28 07:58:10 +02:00
|
|
|
<div
|
|
|
|
|
className="flex-1 overflow-y-auto p-4 space-y-4"
|
|
|
|
|
onWheel={(e) => {
|
|
|
|
|
const element = e.currentTarget;
|
|
|
|
|
const isAtTop = element.scrollTop === 0;
|
|
|
|
|
const isAtBottom =
|
|
|
|
|
element.scrollTop + element.clientHeight >=
|
|
|
|
|
element.scrollHeight - 1;
|
|
|
|
|
|
|
|
|
|
// Allow scroll to pass through to parent when at boundaries
|
|
|
|
|
if ((isAtTop && e.deltaY < 0) || (isAtBottom && e.deltaY > 0)) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
// Let the parent handle the scroll
|
|
|
|
|
window.parent.postMessage(
|
|
|
|
|
{
|
|
|
|
|
type: 'SCROLL_PASSTHROUGH',
|
|
|
|
|
deltaY: e.deltaY,
|
|
|
|
|
},
|
|
|
|
|
'*',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{displayMessages?.map((message, index) => (
|
2025-09-22 05:03:33 +02:00
|
|
|
<div
|
2025-09-28 07:58:10 +02:00
|
|
|
key={index}
|
|
|
|
|
className={`flex ${message.role === MessageType.User ? 'justify-end' : 'justify-start'}`}
|
|
|
|
|
>
|
|
|
|
|
<div
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className={`group max-w-[280px] px-4 py-2 rounded-2xl ${
|
2025-10-17 18:48:47 +08:00
|
|
|
message.role === MessageType.User
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
? 'text-white rounded-br-md'
|
|
|
|
|
: 'rounded-bl-md'
|
2025-10-17 18:48:47 +08:00
|
|
|
}`}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
style={
|
|
|
|
|
message.role === MessageType.User
|
|
|
|
|
? { backgroundColor: widgetAccentColor }
|
|
|
|
|
: {
|
|
|
|
|
backgroundColor: 'rgba(148, 163, 184, 0.14)',
|
|
|
|
|
color: widgetTextColor,
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-28 07:58:10 +02:00
|
|
|
>
|
|
|
|
|
{message.role === MessageType.User ? (
|
|
|
|
|
<p className="text-sm leading-relaxed whitespace-pre-wrap">
|
|
|
|
|
{message.content}
|
|
|
|
|
</p>
|
|
|
|
|
) : (
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
<div className="space-y-2">
|
|
|
|
|
<FloatingChatWidgetMarkdown
|
|
|
|
|
loading={false}
|
|
|
|
|
content={message.content}
|
|
|
|
|
reference={
|
|
|
|
|
message.reference || {
|
|
|
|
|
doc_aggs: [],
|
|
|
|
|
chunks: [],
|
|
|
|
|
total: 0,
|
|
|
|
|
}
|
2025-10-17 18:48:47 +08:00
|
|
|
}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
clickDocumentButton={clickDocumentButton}
|
|
|
|
|
/>
|
|
|
|
|
<div
|
|
|
|
|
className="flex justify-end opacity-0 transition-opacity group-hover:opacity-100"
|
|
|
|
|
role="toolbar"
|
|
|
|
|
>
|
|
|
|
|
<CopyToClipboard
|
|
|
|
|
text={message.content}
|
|
|
|
|
className="border-0"
|
|
|
|
|
size="icon-xs"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-09-28 07:58:10 +02:00
|
|
|
)}
|
|
|
|
|
</div>
|
2025-09-22 05:03:33 +02:00
|
|
|
</div>
|
2025-09-28 07:58:10 +02:00
|
|
|
))}
|
2025-09-22 05:03:33 +02:00
|
|
|
|
2025-09-28 07:58:10 +02:00
|
|
|
{/* Clean Typing Indicator */}
|
|
|
|
|
{sendLoading && !enableStreaming && (
|
|
|
|
|
<div className="flex justify-start pl-4">
|
|
|
|
|
<div className="flex space-x-1">
|
|
|
|
|
<div
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className="w-2 h-2 rounded-full animate-bounce"
|
|
|
|
|
style={{ backgroundColor: widgetAccentColor }}
|
|
|
|
|
></div>
|
|
|
|
|
<div
|
|
|
|
|
className="w-2 h-2 rounded-full animate-bounce"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: widgetAccentColor,
|
|
|
|
|
animationDelay: '0.1s',
|
|
|
|
|
}}
|
2025-09-28 07:58:10 +02:00
|
|
|
></div>
|
|
|
|
|
<div
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className="w-2 h-2 rounded-full animate-bounce"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: widgetAccentColor,
|
|
|
|
|
animationDelay: '0.2s',
|
|
|
|
|
}}
|
2025-09-28 07:58:10 +02:00
|
|
|
></div>
|
|
|
|
|
</div>
|
2025-09-22 05:03:33 +02:00
|
|
|
</div>
|
2025-09-28 07:58:10 +02:00
|
|
|
)}
|
2025-09-22 05:03:33 +02:00
|
|
|
|
2025-09-28 07:58:10 +02:00
|
|
|
<div ref={messagesEndRef} />
|
|
|
|
|
</div>
|
2025-09-22 05:03:33 +02:00
|
|
|
|
2025-09-28 07:58:10 +02:00
|
|
|
{/* Input Area */}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
<div
|
|
|
|
|
className="border-t border-gray-200 p-4"
|
|
|
|
|
style={bodyContainerStyle}
|
|
|
|
|
>
|
2025-09-28 07:58:10 +02:00
|
|
|
<div className="flex items-end space-x-3">
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<textarea
|
|
|
|
|
value={inputValue}
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
const newValue = e.target.value;
|
|
|
|
|
setInputValue(newValue);
|
|
|
|
|
handleInputChange(e);
|
|
|
|
|
}}
|
|
|
|
|
onKeyPress={handleKeyPress}
|
2026-04-14 19:12:56 +07:00
|
|
|
placeholder={t('chat.typeYourMessage')}
|
2025-09-28 07:58:10 +02:00
|
|
|
rows={1}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className="w-full resize-none border border-gray-300 rounded-2xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:border-transparent"
|
|
|
|
|
style={inputStyle}
|
2025-09-28 07:58:10 +02:00
|
|
|
disabled={hasError || sendLoading}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<button
|
2025-10-17 18:48:47 +08:00
|
|
|
type="button"
|
2025-09-28 07:58:10 +02:00
|
|
|
onClick={handleSendMessage}
|
|
|
|
|
disabled={!inputValue.trim() || sendLoading}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className="p-3 text-white rounded-full disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
|
|
|
style={{ backgroundColor: widgetAccentColor }}
|
2025-09-28 07:58:10 +02:00
|
|
|
>
|
|
|
|
|
<Send size={18} />
|
|
|
|
|
</button>
|
2025-09-22 05:03:33 +02:00
|
|
|
</div>
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
{renderFooter()}
|
2025-09-22 05:03:33 +02:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-11-28 19:05:43 +08:00
|
|
|
{visible && (
|
|
|
|
|
<PdfSheet
|
|
|
|
|
visible={visible}
|
|
|
|
|
hideModal={hideModal}
|
|
|
|
|
documentId={documentId}
|
|
|
|
|
chunk={selectedChunk}
|
|
|
|
|
width={'100vw'}
|
|
|
|
|
height={'100vh'}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2025-09-28 07:58:10 +02:00
|
|
|
</>
|
2025-09-22 05:03:33 +02:00
|
|
|
);
|
2025-10-17 18:48:47 +08:00
|
|
|
} // Full mode - render everything together (original behavior)
|
2025-09-22 05:03:33 +02:00
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={`transition-opacity duration-300 ${isLoaded ? 'opacity-100' : 'opacity-0'}`}
|
|
|
|
|
>
|
|
|
|
|
{/* Chat Widget Container */}
|
|
|
|
|
{isOpen && (
|
|
|
|
|
<div
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className={`fixed bottom-24 right-6 z-50 rounded-2xl transition-all duration-300 ease-out ${
|
2025-10-17 18:48:47 +08:00
|
|
|
isMinimized ? 'h-16' : 'h-[500px]'
|
|
|
|
|
} w-[380px] overflow-hidden`}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
style={{ backgroundColor: widgetAccentColor }}
|
2025-09-22 05:03:33 +02:00
|
|
|
>
|
|
|
|
|
{/* Header */}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
<div
|
|
|
|
|
className="flex items-center justify-between p-4 text-white rounded-t-2xl"
|
|
|
|
|
style={{
|
|
|
|
|
background: `linear-gradient(to right, ${widgetAccentColor}, ${widgetAccentColorStrong})`,
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-09-22 05:03:33 +02:00
|
|
|
<div className="flex items-center space-x-3">
|
|
|
|
|
<div className="w-8 h-8 bg-white bg-opacity-20 rounded-full flex items-center justify-center">
|
|
|
|
|
<MessageCircle size={18} />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
<h3
|
|
|
|
|
className="font-semibold text-sm"
|
|
|
|
|
style={{ color: widgetHeaderTextColor }}
|
|
|
|
|
>
|
|
|
|
|
{displayTitle}
|
2025-09-22 05:03:33 +02:00
|
|
|
</h3>
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
<p className="text-xs" style={{ color: widgetHeaderTextColor }}>
|
|
|
|
|
{displaySubtitle}
|
2025-09-22 05:03:33 +02:00
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center space-x-1">
|
|
|
|
|
<button
|
2025-10-17 18:48:47 +08:00
|
|
|
type="button"
|
2025-09-22 05:03:33 +02:00
|
|
|
onClick={minimizeChat}
|
|
|
|
|
className="p-1.5 hover:bg-white hover:bg-opacity-20 rounded-full transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<Minimize2 size={16} />
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
2025-10-17 18:48:47 +08:00
|
|
|
type="button"
|
2025-09-22 05:03:33 +02:00
|
|
|
onClick={toggleChat}
|
|
|
|
|
className="p-1.5 hover:bg-white hover:bg-opacity-20 rounded-full transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<X size={16} />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Messages Container */}
|
|
|
|
|
{!isMinimized && (
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
<div className="flex flex-col h-[436px]" style={bodyContainerStyle}>
|
2025-09-22 05:03:33 +02:00
|
|
|
<div
|
|
|
|
|
className="flex-1 overflow-y-auto p-4 space-y-4"
|
|
|
|
|
onWheel={(e) => {
|
|
|
|
|
const element = e.currentTarget;
|
|
|
|
|
const isAtTop = element.scrollTop === 0;
|
|
|
|
|
const isAtBottom =
|
|
|
|
|
element.scrollTop + element.clientHeight >=
|
|
|
|
|
element.scrollHeight - 1;
|
|
|
|
|
|
|
|
|
|
// Allow scroll to pass through to parent when at boundaries
|
|
|
|
|
if (
|
|
|
|
|
(isAtTop && e.deltaY < 0) ||
|
|
|
|
|
(isAtBottom && e.deltaY > 0)
|
|
|
|
|
) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
// Let the parent handle the scroll
|
|
|
|
|
window.parent.postMessage(
|
|
|
|
|
{
|
|
|
|
|
type: 'SCROLL_PASSTHROUGH',
|
|
|
|
|
deltaY: e.deltaY,
|
|
|
|
|
},
|
|
|
|
|
'*',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{displayMessages?.map((message, index) => (
|
|
|
|
|
<div
|
|
|
|
|
key={index}
|
|
|
|
|
className={`flex ${message.role === MessageType.User ? 'justify-end' : 'justify-start'}`}
|
|
|
|
|
>
|
|
|
|
|
<div
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className={`group max-w-[280px] px-4 py-2 rounded-2xl ${
|
2025-10-17 18:48:47 +08:00
|
|
|
message.role === MessageType.User
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
? 'text-white rounded-br-md'
|
|
|
|
|
: 'rounded-bl-md'
|
2025-10-17 18:48:47 +08:00
|
|
|
}`}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
style={
|
|
|
|
|
message.role === MessageType.User
|
|
|
|
|
? { backgroundColor: widgetAccentColor }
|
|
|
|
|
: {
|
|
|
|
|
backgroundColor: 'rgba(148, 163, 184, 0.14)',
|
|
|
|
|
color: widgetTextColor,
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-22 05:03:33 +02:00
|
|
|
>
|
2025-09-28 07:58:10 +02:00
|
|
|
{message.role === MessageType.User ? (
|
|
|
|
|
<p className="text-sm leading-relaxed whitespace-pre-wrap">
|
|
|
|
|
{message.content}
|
|
|
|
|
</p>
|
|
|
|
|
) : (
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
<div className="space-y-2">
|
|
|
|
|
<FloatingChatWidgetMarkdown
|
|
|
|
|
loading={false}
|
|
|
|
|
content={message.content}
|
|
|
|
|
reference={
|
|
|
|
|
message.reference || {
|
|
|
|
|
doc_aggs: [],
|
|
|
|
|
chunks: [],
|
|
|
|
|
total: 0,
|
|
|
|
|
}
|
2025-10-17 18:48:47 +08:00
|
|
|
}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
clickDocumentButton={clickDocumentButton}
|
|
|
|
|
/>
|
|
|
|
|
<div
|
|
|
|
|
className="flex justify-end opacity-0 transition-opacity group-hover:opacity-100"
|
|
|
|
|
role="toolbar"
|
|
|
|
|
>
|
|
|
|
|
<CopyToClipboard
|
|
|
|
|
text={message.content}
|
|
|
|
|
className="border-0"
|
|
|
|
|
size="icon-xs"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-09-28 07:58:10 +02:00
|
|
|
)}
|
2025-09-22 05:03:33 +02:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
|
|
{/* Typing Indicator */}
|
|
|
|
|
{sendLoading && (
|
|
|
|
|
<div className="flex justify-start">
|
|
|
|
|
<div className="bg-gray-100 rounded-2xl rounded-bl-md px-4 py-3">
|
|
|
|
|
<div className="flex space-x-1">
|
|
|
|
|
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"></div>
|
|
|
|
|
<div
|
|
|
|
|
className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"
|
|
|
|
|
style={{ animationDelay: '0.1s' }}
|
|
|
|
|
></div>
|
|
|
|
|
<div
|
|
|
|
|
className="w-2 h-2 bg-gray-400 rounded-full animate-bounce"
|
|
|
|
|
style={{ animationDelay: '0.2s' }}
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div ref={messagesEndRef} />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Input Area */}
|
|
|
|
|
<div className="border-t border-gray-200 p-4">
|
|
|
|
|
<div className="flex items-end space-x-3">
|
|
|
|
|
<div className="flex-1">
|
|
|
|
|
<textarea
|
|
|
|
|
value={inputValue}
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
const newValue = e.target.value;
|
|
|
|
|
setInputValue(newValue);
|
|
|
|
|
// Also update the hook's state
|
|
|
|
|
handleInputChange(e);
|
|
|
|
|
}}
|
|
|
|
|
onKeyPress={handleKeyPress}
|
2026-04-14 19:12:56 +07:00
|
|
|
placeholder={t('chat.typeYourMessage')}
|
2025-09-22 05:03:33 +02:00
|
|
|
rows={1}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className="w-full resize-none border border-gray-300 rounded-2xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:border-transparent"
|
|
|
|
|
style={inputStyle}
|
2025-09-22 05:03:33 +02:00
|
|
|
disabled={hasError || sendLoading}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<button
|
2025-10-20 15:59:56 +08:00
|
|
|
type="button"
|
2025-09-22 05:03:33 +02:00
|
|
|
onClick={handleSendMessage}
|
|
|
|
|
disabled={!inputValue.trim() || sendLoading}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className="p-3 text-white rounded-full disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
|
|
|
style={{ backgroundColor: widgetAccentColor }}
|
2025-09-22 05:03:33 +02:00
|
|
|
>
|
|
|
|
|
<Send size={18} />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
{renderFooter()}
|
2025-09-22 05:03:33 +02:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Floating Button */}
|
|
|
|
|
<div className="fixed bottom-6 right-6 z-50">
|
|
|
|
|
<button
|
2025-10-17 18:48:47 +08:00
|
|
|
type="button"
|
2025-09-22 05:03:33 +02:00
|
|
|
onClick={toggleChat}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
className={`w-14 h-14 text-white rounded-full transition-all duration-300 flex items-center justify-center group ${
|
2025-10-17 18:48:47 +08:00
|
|
|
isOpen ? 'scale-95' : 'scale-100 hover:scale-105'
|
|
|
|
|
}`}
|
Add widget customization and persistence (#14603)
Introduce comprehensive floating widget customization: add new widget
settings (title, subtitle, footer, colors, mute, streaming) with types
and defaults, and expose them via EmbedDialog UI (split into Embed Setup
and Widget Customization tabs). Persist and load settings through Agent
page by reading/writing globals and wiring an onSaveWidgetSettings
handler to setAgent; show a loading ButtonLoading for saving. Update
embed iframe query params and FloatingChatWidget to honor URL params
(colors, text, mute/streaming) with validation/normalization, color
darkening for gradients, footer link normalization, and improved
styling. Also add copy-to-clipboard in message toolbar, adjust syntax
highlighter layout and Copy button, and add i18n key for muteWidget.
### What problem does this PR solve?
Adds a few fields to the embed widget modal to customize the appearance
of the floating widget when embedded into a page.
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
---------
Co-authored-by: Noah <Noah.Thompson@ecn.forces.gc.ca>
2026-05-13 09:13:11 -04:00
|
|
|
style={{ backgroundColor: widgetAccentColor }}
|
2025-09-22 05:03:33 +02:00
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
className={`transition-transform duration-300 ${isOpen ? 'rotate-45' : 'rotate-0'}`}
|
|
|
|
|
>
|
|
|
|
|
{isOpen ? <X size={24} /> : <MessageCircle size={24} />}
|
|
|
|
|
</div>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
{/* Unread Badge */}
|
|
|
|
|
{!isOpen && messageCount > 0 && (
|
|
|
|
|
<div className="absolute -top-2 -right-2 w-6 h-6 bg-red-500 text-white text-xs font-bold rounded-full flex items-center justify-center animate-pulse">
|
|
|
|
|
{messageCount > 9 ? '9+' : messageCount}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2025-11-28 17:15:01 +08:00
|
|
|
<PdfSheet
|
2025-09-28 07:58:10 +02:00
|
|
|
visible={visible}
|
|
|
|
|
hideModal={hideModal}
|
|
|
|
|
documentId={documentId}
|
|
|
|
|
chunk={selectedChunk}
|
|
|
|
|
/>
|
2025-09-22 05:03:33 +02:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default FloatingChatWidget;
|