Funzionalità avanzate
Vantaggi del Widget Preact
Il nuovo widget Preact offre diversi vantaggi rispetto alla versione JavaScript vanilla:
🚀 Performance Ottimizzate
resources/js/shadowDomWidgetPreact.jsx
// Componenti ottimizzati con memo() per evitare re-render inutili
const ProductCard = memo(({ id, url, addToCartUrl, ... }) => {
// Componente ottimizzato che si ri-renderizza solo quando le props cambiano
});
// Hooks ottimizzati con useCallback per evitare ricreazioni di funzioni
const handleSubmit = useCallback(async (e) => {
// Funzione ottimizzata che mantiene la stessa referenza tra i render
}, [currentMessage, isRunning, sendMessage]);
// Valori memoizzati con useMemo per calcoli costosi
const wrapperStyle = useMemo(() => {
const style = {};
// Calcoli complessi per gli stili CSS
return style;
}, [options, fullHeight, getContrastColor, hexToRGB, getShimmerColors]);
🧩 Architettura Modulare
resources/js/shadowDomWidgetPreact.jsx
// Separazione chiara delle responsabilità
const TidikoWidget = ({ options, payload, ... }) => {
// State management centralizzato
const [currentThreadId, setCurrentThreadId] = useState(null);
const [isRunning, setIsRunning] = useState(false);
const [messages, setMessages] = useState([]);
// Effetti separati per diverse responsabilità
useEffect(() => {
// Socket setup
}, [socketUrl, assistantTextClass, userTextClass]);
useEffect(() => {
// Authentication setup
}, [jwt, jwtRefresh]);
useEffect(() => {
// Thread loading
}, [widgetAssistantId]);
// Componenti composabili
return (
<div className={wrapperClass} style={wrapperStyle}>
<ChatHeader {...headerProps} />
<ChatHistory {...historyProps} />
<MessageForm {...formProps} />
</div>
);
};
🎨 Gestione Stili Dinamici
resources/js/shadowDomWidgetPreact.jsx
// CSS Variables dinamiche con useMemo
const wrapperStyle = useMemo(() => {
const style = {};
if (options.userBg) {
style["--user-message-bg"] = options.userBg;
style["--user-message-color"] = getContrastColor(options.userBg);
const rgb = Object.values(hexToRGB(options.userBg)).join(" ");
style["--faq-bg-color"] = `rgb(${rgb}/1)`;
style["--faq-bg-color-hover"] = `rgb(${rgb}/.7)`;
}
if (options.aiBg) {
style["--assistant-message-bg"] = options.aiBg;
const assistantTextColor = getContrastColor(options.aiBg);
style["--assistant-message-color"] = assistantTextColor;
// Set shimmer colors based on assistant text color
const shimmerColors = getShimmerColors(
assistantTextColor,
options.aiBg
);
style["--shimmer-accent-color"] = shimmerColors.accentColor;
style["--shimmer-main-color"] = shimmerColors.mainColor;
}
if (options.buttonColor) {
style["--button-bg"] = options.buttonColor;
style["--button-color"] = getContrastColor(options.buttonColor);
}
if (fullHeight) {
style["--widget-max-height"] = "100%";
}
return style;
}, [options, fullHeight, getContrastColor, hexToRGB, getShimmerColors]);
🔄 Gestione Stato Reattiva
resources/js/shadowDomWidgetPreact.jsx
// State management reattivo con hooks
const [currentThreadId, setCurrentThreadId] = useState(null);
const [isRunning, setIsRunning] = useState(false);
const [tmpMsg, setTmpMsg] = useState("");
const [tmpMsgType, setTmpMsgType] = useState("");
const [autoScrollEnabled, setAutoScrollEnabled] = useState(true);
const [messages, setMessages] = useState([]);
const [currentMessage, setCurrentMessage] = useState("");
const [showTools, setShowTools] = useState(false);
const [copyMessage, setCopyMessage] = useState("");
const [showPrivacy, setShowPrivacy] = useState(false);
const [showFAQ, setShowFAQ] = useState(false);
const [inputDisabled, setInputDisabled] = useState(false);
const [isCollapsed, setIsCollapsed] = useState(false);
const [isInitialized, setIsInitialized] = useState(false);
const [isVisible, setIsVisible] = useState(true);
const [isSocketConnected, setIsSocketConnected] = useState(false);
const [isHistoryLoading, setIsHistoryLoading] = useState(true);
const [loadedMessages, setLoadedMessages] = useState([]);
🎯 Gestione Eventi Ottimizzata
resources/js/shadowDomWidgetPreact.jsx
// Event handlers ottimizzati con useCallback
const handleSubmit = useCallback(async (e) => {
e.preventDefault();
if (!currentMessage.trim() || isRunning) return;
const text = currentMessage.trim();
setCurrentMessage("");
await sendMessage(text);
}, [currentMessage, isRunning, sendMessage]);
const handleKeyDown = useCallback((e) => {
if (e.key === "Enter" && !e.shiftKey && !e.ctrlKey && !e.metaKey) {
e.preventDefault();
if (!inputDisabled && !isRunning && currentMessage.trim()) {
handleSubmit(e);
}
}
}, [handleSubmit, inputDisabled, isRunning, currentMessage]);
const handleFAQQuestionClick = useCallback(async (question) => {
if (isRunning || inputDisabled || !question.trim()) return;
setShowFAQ(false);
await sendMessage(question.trim());
}, [isRunning, inputDisabled, sendMessage]);
🔧 Utility Functions Avanzate
resources/js/shadowDomWidgetPreact.jsx
// Funzioni di utilità per la gestione dei colori
const hexToRGB = useCallback((hex) => ({
r: parseInt(hex.slice(1, 3), 16),
g: parseInt(hex.slice(3, 5), 16),
b: parseInt(hex.slice(5, 7), 16),
}), []);
const rgbToHSL = useCallback((r, g, b) => {
// Normalize RGB values to 0-1 range
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s, l;
// Calculate lightness
l = (max + min) / 2;
if (max === min) {
// Achromatic (gray)
h = s = 0;
} else {
const d = max - min;
// Calculate saturation
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
// Calculate hue
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
default:
h = 0;
}
h /= 6;
}
return {
h: Math.round(h * 360), // Convert to degrees
s: Math.round(s * 100), // Convert to percentage
l: Math.round(l * 100), // Convert to percentage
};
}, []);
// Funzione per calcolare il contrasto dei colori
const checkAPCAContrast = useCallback((bg, tx) => {
const rgbBg = hexToRGB(bg);
const rgbTx = hexToRGB(tx);
const bgY =
0.2126 * sRGBtoY(rgbBg.r) +
0.7152 * sRGBtoY(rgbBg.g) +
0.0722 * sRGBtoY(rgbBg.b);
const txY =
0.2126 * sRGBtoY(rgbTx.r) +
0.7152 * sRGBtoY(rgbTx.g) +
0.0722 * sRGBtoY(rgbTx.b);
const contrast = Math.abs(
(bgY > txY
? bgY ** 0.56 - txY ** 0.57
: txY ** 0.56 - bgY ** 0.57) * 1.14
);
return contrast * 100;
}, [hexToRGB, sRGBtoY]);
📦 Estrazione Product Cards
resources/js/shadowDomWidgetPreact.jsx
// Funzione avanzata per l'estrazione delle product cards
const extractProductCardsData = (content) => {
// First decode HTML entities
const decodedContent = content
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, '"')
.replace(/'/g, "'");
const productCardsData = [];
let processedBuilder = "";
let cursor = 0;
const lowerContent = decodedContent.toLowerCase();
// Greedy scan: detect even partial opening tags (case-insensitive)
while (true) {
// Find earliest occurrence among patterns to also catch partial tokens like "<prod"
const patterns = ["<product-card", "<product", "<prod"];
let startIdx = -1;
for (let i = 0; i < patterns.length; i++) {
const idx = lowerContent.indexOf(patterns[i], cursor);
if (idx !== -1 && (startIdx === -1 || idx < startIdx)) {
startIdx = idx;
}
}
if (startIdx === -1) break;
// Process product card data...
// (continua con la logica di estrazione)
}
return {
content: processedBuilder.trim(),
productCards: productCardsData,
};
};
Il sistema di widget Preact di Tidiko AI fornisce un'integrazione moderna, performante e completamente isolata per chatbot intelligenti, con supporto per personalizzazione avanzata, comunicazione real-time, gestione stato reattiva e conformità GDPR. L'architettura a componenti Preact garantisce maggiore manutenibilità, performance ottimizzate e un'esperienza di sviluppo moderna.