//alert(111)
//test1
//test2 bottom right
function createFloatChatWindow(baseOrOptions) {
if (document.getElementById('float-chat-host')) return;
// --- Options
const opts = typeof baseOrOptions === 'string' ? { baseUrl: baseOrOptions } : (baseOrOptions || {});
const BASE_URL = (opts.baseUrl || '').replace(/\/+$/,'');
const TITLE = opts.title || 'fcic AI';
const PLACEHOLDER = opts.placeholder || 'Type your messageā¦';
const COLLAPSE_H = Number(opts.collapseHeight || 52);
const ENABLE_DRAG = opts.enableDrag !== false;
const CORS_PROXY = opts.corsProxy || null;
const buildPath = typeof opts.queryPathBuilder === 'function' ? opts.queryPathBuilder : (q) => `/${encodeURIComponent(q)}`;
// --- Icon pack (24x24 outline; currentColor)
const ICON_PATHS = {
chevronDown: ['M6 9l6 6 6-6'],
chevronUp: ['M18 15l-6-6-6 6'],
close: ['M6 6l12 12','M6 18L18 6'],
plane: ['M22 2 11 13','M22 2 15 22l-4-9-9-4 20-7z'],
copy: ['M9 9h8v8H9z','M5 5h8v8H5z'],
bot: ['M9 8h6a3 3 0 0 1 3 3v2a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3v-2a3 3 0 0 1 3-3z','M11 4h2v3h-2z','M10 12h.01','M14 12h.01'],
user: ['M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8','M4 20a8 8 0 0 1 16 0']
};
function makeIcon(name, size=18) {
const svg = document.createElementNS('http://www.w3.org/2000/svg','svg');
svg.setAttribute('class','icon'); svg.setAttribute('viewBox','0 0 24 24');
svg.setAttribute('width', String(size)); svg.setAttribute('height', String(size));
svg.setAttribute('fill','none'); svg.setAttribute('stroke','currentColor');
svg.setAttribute('stroke-width','2'); svg.setAttribute('stroke-linecap','round'); svg.setAttribute('stroke-linejoin','round');
(ICON_PATHS[name]||[]).forEach(d=>{ const p=document.createElementNS('http://www.w3.org/2000/svg','path'); p.setAttribute('d',d); svg.appendChild(p); });
return svg;
}
function setIcon(el, name) { el.textContent = ''; el.appendChild(makeIcon(name, 18)); }
// --- Host + Shadow
const host = document.createElement('div'); host.id = 'float-chat-host'; document.body.appendChild(host);
const shadow = host.attachShadow({ mode: 'open' });
// --- Styles
const css = `
:host, #float-chat { all: initial; font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, "Helvetica Neue", sans-serif; }
#float-chat {
position:fixed; bottom:20px; right:20px; width:360px; height:500px; display:flex; flex-direction:column;
background:#fff; border:1px solid rgba(15,23,42,.08); border-radius:16px; box-shadow:0 12px 32px rgba(0,0,0,.18);
overflow:hidden; z-index:2147483647; transition:height .28s ease, width .28s ease, transform .2s ease, box-shadow .2s ease;
backdrop-filter:saturate(1.1) blur(6px);
}
#float-chat.collapsed { height:${COLLAPSE_H}px; }
#loading-bar{position:absolute;top:0;left:0;height:3px;width:0;opacity:0;background:linear-gradient(90deg,#6d5efc,#a78bfa,#6d5efc);background-size:200% 100%;}
#float-chat.loading #loading-bar{opacity:1;animation:loadSlide 1.2s linear infinite;}
@keyframes loadSlide{0%{width:0;background-position:0 0}50%{width:70%;background-position:100% 0}100%{width:100%;background-position:0 0}}
#chat-header{
position:relative; display:flex; align-items:center; gap:8px;
height:${COLLAPSE_H}px; padding:0 10px; color:#fff;
background:linear-gradient(135deg,#6d5efc,#a78bfa);
box-shadow:inset 0 -1px 0 rgba(255,255,255,.25);
user-select:none;
}
.icon-btn{
background:transparent; border:none; color:inherit; font-size:0; cursor:pointer; line-height:0;
padding:8px; border-radius:10px; display:inline-flex; align-items:center; justify-content:center;
transition:background .15s ease, transform .12s ease;
}
.icon-btn:hover{ background:rgba(255,255,255,.15); }
.icon{ display:block; width:18px; height:18px; }
#title-wrap{ display:flex; align-items:center; gap:8px; flex:1; justify-content:center; }
#title{ font-weight:800; font-size:15px; letter-spacing:.25px; text-shadow:0 1px 0 rgba(0,0,0,.2); }
#history{ position:relative; flex:1; padding:12px; overflow-y:auto; background:#f6f7f9; }
.row{ display:flex; gap:8px; margin:10px 0; align-items:flex-end; }
.row.user{ justify-content:flex-end; }
.row.ai { justify-content:flex-start; }
.avatar{
width:28px; height:28px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center;
color:#fff; flex:0 0 auto; box-shadow:0 2px 6px rgba(0,0,0,.12);
}
.avatar.ai { background:linear-gradient(135deg,#38bdf8,#818cf8); }
.avatar.user { background:darkgreen }
.bubble{
position:relative; max-width:74%; padding:10px 12px; border-radius:14px; font-size:14px; line-height:1.45;
box-shadow:0 2px 8px rgba(0,0,0,.08); white-space:pre-wrap; word-wrap:break-word; animation:enter .2s ease;
}
@keyframes enter{ from{ transform:translateY(8px) scale(.98); opacity:0 } to{ transform:translateY(0) scale(1); opacity:1 } }
.row.user .bubble{ color:#fff; background:linear-gradient(135deg,#6366f1,#22d3ee); border-bottom-right-radius:6px; }
.row.ai .bubble { background:#fff; color:#0f172a; border:1px solid rgba(0,0,0,.06); border-bottom-left-radius:6px; }
.timestamp{ font-size:10px; color:#8e9aa6; margin-top:4px; text-align:right; }
.row.ai .timestamp{ text-align:left; }
.tools{ position:absolute; top:6px; right:6px; display:flex; gap:6px; opacity:0; transition:opacity .15s ease; }
.row.ai .bubble:hover .tools{ opacity:1; }
.tool{ background:rgba(0,0,0,.06); border:1px solid rgba(0,0,0,.08); width:22px; height:22px; border-radius:7px;
display:inline-flex; align-items:center; justify-content:center; cursor:pointer; color:#0f172a; }
.tool:hover{ filter:brightness(1.04); }
#controls{ display:flex; gap:8px; padding:10px; background:#eef1f5; border-top:1px solid #e6e8ec; }
#input{
flex:1; height:40px; padding:0 12px; border:1px solid #cfd6df; border-radius:12px; font-size:14px;
outline:none; background:#fff;
}
#send{
padding:0 14px; min-width:44px; border:none; border-radius:12px; background:#22c55e; color:#fff; font-weight:700;
cursor:pointer; display:inline-flex; align-items:center; justify-content:center; gap:6px; height:40px;
}
#send[disabled]{ opacity:.6; cursor:not-allowed; }
.typing{ display:inline-flex; align-items:center; gap:6px; height:1em; }
.dot{ width:6px; height:6px; background:#94a3b8; border-radius:50%; animation:bounce 1s infinite ease-in-out; }
.dot:nth-child(2){ animation-delay:.15s; } .dot:nth-child(3){ animation-delay:.3s; }
@keyframes bounce{ 0%,80%,100%{ transform:scale(.6); opacity:.6 } 40%{ transform:scale(1); opacity:1 } }
`;
const styleEl = document.createElement('style'); styleEl.textContent = css; shadow.appendChild(styleEl);
// --- Structure
const container = document.createElement('div'); container.id = 'float-chat';
const loadingBar = document.createElement('div'); loadingBar.id = 'loading-bar';
container.appendChild(loadingBar);
const header = document.createElement('div'); header.id = 'chat-header';
const toggleBtn = document.createElement('button'); toggleBtn.className = 'icon-btn'; toggleBtn.setAttribute('aria-label','Collapse/Expand'); setIcon(toggleBtn, 'chevronDown');
const titleWrap = document.createElement('div'); titleWrap.id = 'title-wrap';
const brand = makeIcon('bot', 18);
const title = document.createElement('div'); title.id = 'title'; title.textContent = TITLE;
titleWrap.appendChild(brand); titleWrap.appendChild(title);
const closeBtn = document.createElement('button'); closeBtn.className = 'icon-btn'; closeBtn.setAttribute('aria-label','Close'); setIcon(closeBtn, 'close');
header.appendChild(toggleBtn);
header.appendChild(titleWrap);
header.appendChild(closeBtn);
const history = document.createElement('div'); history.id = 'history';
const controls = document.createElement('div'); controls.id = 'controls';
const input = document.createElement('input'); input.id = 'input'; input.type = 'text'; input.placeholder = PLACEHOLDER; input.autocomplete = 'off'; input.spellcheck = false;
const sendBtn = document.createElement('button'); sendBtn.id = 'send'; sendBtn.appendChild(makeIcon('plane', 18)); sendBtn.appendChild(document.createTextNode('Send'));
controls.appendChild(input); controls.appendChild(sendBtn);
container.appendChild(header);
container.appendChild(history);
container.appendChild(controls);
shadow.appendChild(container);
// --- Helpers
function nowStamp(){ try { return new Date().toLocaleString(); } catch { return ''; } }
function makeRow(kind){
const row=document.createElement('div'); row.className=`row ${kind}`;
const avatar=document.createElement('div'); avatar.className=`avatar ${kind}`;
avatar.appendChild(makeIcon(kind === 'ai' ? 'bot' : 'user', 16));
// bubble + timestamp
const wrap=document.createElement('div'); wrap.style.display='flex'; wrap.style.flexDirection='column';
const bubble=document.createElement('div'); bubble.className='bubble';
if (kind==='ai') {
const tools = document.createElement('div'); tools.className = 'tools';
const copyBtn = document.createElement('div'); copyBtn.className='tool'; copyBtn.title='Copy';
copyBtn.appendChild(makeIcon('copy', 16));
copyBtn.addEventListener('click', async ()=> {
try { await navigator.clipboard.writeText(bubble.textContent || ''); } catch {}
});
tools.appendChild(copyBtn);
bubble.appendChild(tools);
}
const ts=document.createElement('div'); ts.className='timestamp'; ts.textContent=nowStamp();
wrap.appendChild(bubble); wrap.appendChild(ts);
// Order per side
if (kind==='ai') { row.appendChild(avatar); row.appendChild(wrap); }
else { row.appendChild(wrap); row.appendChild(avatar); }
return {row,bubble};
}
function appendUser(text){ const {row,bubble}=makeRow('user'); bubble.textContent=text; history.appendChild(row); history.scrollTop=history.scrollHeight; }
function appendTyping(){ const {row,bubble}=makeRow('ai'); const t=document.createElement('span'); t.className='typing'; t.innerHTML=''; bubble.appendChild(t); row.dataset.pending='1'; history.appendChild(row); history.scrollTop=history.scrollHeight; return {row,bubble}; }
function replaceTypingWithText(ref,text){
if(!ref||!ref.bubble) return;
ref.bubble.childNodes.forEach(n => { if (n.nodeType===1 && n.classList && n.classList.contains('typing')) n.remove(); });
const tools = ref.bubble.querySelector('.tools'); // preserve tools
ref.bubble.textContent = text;
if (tools) ref.bubble.appendChild(tools);
ref.row && (ref.row.dataset.pending='');
history.scrollTop=history.scrollHeight;
}
function setSending(on){ if(on){ container.classList.add('loading'); sendBtn.disabled=true; } else { container.classList.remove('loading'); sendBtn.disabled=false; } }
function buildFetchUrl(q){ let u = BASE_URL + buildPath(q); if (CORS_PROXY) u = `${CORS_PROXY}${encodeURIComponent(u)}`; return u; }
async function tryStreamResponse(res,el){
try{ if(!res.body || !('getReader' in res.body)) return false;
const r=res.body.getReader(); const dec=new TextDecoder(); let buf='';
while(true){ const {value,done}=await r.read(); if(done) break; buf+=dec.decode(value,{stream:true}); el.textContent=buf; history.scrollTop=history.scrollHeight; }
el.textContent=buf+dec.decode(); return true;
}catch{ return false; }
}
async function sendMessage(){
const q=(input.value||'').trim(); if(!q || !BASE_URL) return;
appendUser(q); input.value=''; input.focus();
const typing=appendTyping(); setSending(true);
try{
const res=await fetch(buildFetchUrl(q),{method:'GET'});
if(!res.ok){ const txt=await res.text().catch(()=> ''); replaceTypingWithText(typing,`Error ${res.status}: ${txt||res.statusText}`); return; }
const streamed=await tryStreamResponse(res.clone(), typing.bubble);
if(!streamed){ const data=await res.text(); replaceTypingWithText(typing,data); }
}catch(err){ replaceTypingWithText(typing,`Error: ${err?.message || String(err)}`); }
finally{ setSending(false); }
}
// --- Events
const applyChevron = () => { setIcon(toggleBtn, container.classList.contains('collapsed') ? 'chevronUp' : 'chevronDown'); };
function usingTopLeftAnchor(){ const hasTop = container.style.top && container.style.top !== ''; const rightUnset = container.style.right === 'unset'; return hasTop || rightUnset; }
function toggleCollapsed(){
const beforeH = container.getBoundingClientRect().height;
const anchoredTop = usingTopLeftAnchor();
const currentTop = anchoredTop ? parseFloat(container.style.top || '0') : null;
container.classList.toggle('collapsed');
const afterH = container.getBoundingClientRect().height;
if (anchoredTop && Number.isFinite(currentTop)) {
const delta = beforeH - afterH;
container.style.top = Math.max(8, currentTop + delta) + 'px';
}
applyChevron();
}
sendBtn.addEventListener('click', sendMessage);
input.addEventListener('keydown', (e)=>{ if(e.key==='Enter'){ e.preventDefault(); sendMessage(); } });
toggleBtn.addEventListener('click', (e)=>{ e.stopPropagation(); toggleCollapsed(); });
header.addEventListener('click', (e)=>{ if (e.target.closest('.icon-btn')) return; toggleCollapsed(); });
closeBtn.addEventListener('click', (e)=>{ e.stopPropagation(); host.remove(); });
// Drag (keeps bottom anchor when collapsing)
if (ENABLE_DRAG) {
let startX=0, startY=0, startLeft=0, startTop=0, dragging=false;
const onMove = (e) => {
if (!dragging) return;
const dx = e.clientX - startX; const dy = e.clientY - startY;
container.style.right = 'unset';
container.style.left = Math.max(8, startLeft + dx) + 'px';
container.style.top = Math.max(8, startTop + dy) + 'px';
};
const onUp = () => {
dragging = false;
window.removeEventListener('mousemove', onMove);
window.removeEventListener('mouseup', onUp);
container.style.transition = 'height .28s ease, width .28s ease, transform .2s ease, box-shadow .2s ease';
};
header.addEventListener('mousedown', (e) => {
if (e.target.closest('.icon-btn')) return;
dragging = true;
const rect = container.getBoundingClientRect();
startX = e.clientX; startY = e.clientY; startLeft = rect.left; startTop = rect.top;
container.style.transition = 'none';
window.addEventListener('mousemove', onMove);
window.addEventListener('mouseup', onUp);
});
}
// Focus on open
setTimeout(()=> input.focus(), 0);
}
// Use it
createFloatChatWindow("https://advice.us.kg");
//test3 top right
function createAdvancedFloatChatWindow(baseUrl, prompt, pollInterval = 10, displayMode = 'S', speed = 50) {
// Use a unique host ID to avoid conflicts
const hostId = 'advanced-float-chat-host';
const existingHost = document.getElementById(hostId);
if (existingHost) {
if (existingHost.pollTimer) {
clearInterval(existingHost.pollTimer);
}
existingHost.remove();
}
// 1. Create a new host element and attach a shadow root
const host = document.createElement('div');
host.id = hostId;
document.body.appendChild(host);
const shadow = host.attachShadow({ mode: 'open' });
const basePixelSpeed = 30 * (speed / 50); // pixels per second at given speed
// 2. Inject minimal CSS for the container and display
const css = `
#float-chat-container {
position: fixed;
top: 66px;
right: 20px;
width: 320px;
/* Adjust height based on displayMode */
height: ${displayMode === 'M' ? '120px' : 'auto'}; /* Assuming approx 24px per line * 5 lines */
border: 1px solid #ddd;
border-radius: 8px;
background: #fff;
font-family: "Helvetica Neue", Arial, sans-serif;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
z-index: 2147483647;
cursor: move;
padding: ${displayMode === 'M' ? '0' : '8px 12px'};
display: flex;
align-items: center;
overflow: hidden;
}
#flu-display {
font-size: 14px;
color: #333;
display: ${displayMode === 'S' ? 'inline-block' : 'block'};
${displayMode === 'S'
? `white-space: nowrap;`
: `white-space: pre-wrap; line-height: 24px;` /* Adjust line-height as needed */
}
}
@keyframes scrollText {
0% { transform: translateX(0); }
100% { transform: translateX(-100%); }
}
@keyframes scrollTextVertical {
0% { transform: translateY(0); }
100% { transform: translateY(-50%); }
}
/* Smooth animation for vertical scrolling */
#flu-display.scroll-vertical {
animation-timing-function: linear;
animation-iteration-count: infinite;
}
/* Smooth animation for horizontal scrolling */
#flu-display.scroll-horizontal {
animation-timing-function: linear;
animation-iteration-count: infinite;
}
`;
const styleEl = document.createElement('style');
styleEl.textContent = css;
shadow.appendChild(styleEl);
// 3. Create the floating container and display area
const container = document.createElement('div');
container.id = 'float-chat-container';
const fluDisplay = document.createElement('div');
fluDisplay.id = 'flu-display';
fluDisplay.innerText = 'Loading...';
container.appendChild(fluDisplay);
shadow.appendChild(container);
// Polling control functions
let pollTimer = null;
function startPolling() {
pollTimer = setInterval(fetchResponse, pollInterval * 1000);
host.pollTimer = pollTimer;
}
function stopPolling() {
if (pollTimer) {
clearInterval(pollTimer);
pollTimer = null;
host.pollTimer = null;
}
}
// 4. Polling logic
function fetchResponse() {
const queryParam = Array.isArray(prompt) ? prompt.join(' ') : prompt;
fetch(`${baseUrl}/${encodeURIComponent(queryParam)}`)
.then(response => response.text())
.then(text => {
container.title = text; // Tooltip with full text
if (displayMode === 'S') {
const singleLine = text.replace(/\r?\n/g, ' ');
fluDisplay.innerText = singleLine;
// Immediately show text at left
fluDisplay.style.transform = 'translateX(0)';
// Now calculate and start the horizontal animation
requestAnimationFrame(() => {
const textWidth = fluDisplay.scrollWidth;
const containerWidth = container.offsetWidth;
const totalDistance = textWidth + containerWidth;
const newDuration = totalDistance / basePixelSpeed;
// Remove previous animation classes
fluDisplay.classList.remove('scroll-vertical');
fluDisplay.classList.add('scroll-horizontal');
// Apply the animation
fluDisplay.style.animation = `scrollText ${newDuration}s linear infinite`;
});
} else {
// For multi-line vertical scrolling: limit to 5 lines and duplicate for seamless loop
const lines = text.split(/\r?\n/).slice(0, 5);
const limitedContent = lines.join('\n');
fluDisplay.innerText = limitedContent + '\n' + limitedContent;
// Immediately show text at top
fluDisplay.style.transform = 'translateY(0)';
// Now calculate and start the vertical animation
requestAnimationFrame(() => {
const textHeight = fluDisplay.scrollHeight / 2; // Since content is duplicated
const newDuration = textHeight / basePixelSpeed;
// Remove previous animation classes
fluDisplay.classList.remove('scroll-horizontal');
fluDisplay.classList.add('scroll-vertical');
// Apply the animation
fluDisplay.style.animation = `scrollTextVertical ${newDuration}s linear infinite`;
});
}
})
.catch(err => {
const errorMsg = 'Error: ' + err.message;
fluDisplay.innerText = errorMsg;
container.title = errorMsg;
});
}
fetchResponse();
startPolling();
let offsetX = 0, offsetY = 0;
let isDragging = false;
container.addEventListener('mousedown', (e) => {
isDragging = true;
const rect = container.getBoundingClientRect();
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
container.style.right = 'auto';
container.style.bottom = 'auto';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const x = e.clientX - offsetX;
const y = e.clientY - offsetY;
container.style.left = x + 'px';
container.style.top = y + 'px';
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
container.addEventListener('mouseenter', () => {
fluDisplay.style.animationPlayState = 'paused';
stopPolling();
});
container.addEventListener('mouseleave', () => {
fluDisplay.style.animationPlayState = 'running';
startPolling();
});
// Optional cleanup/destroy function
// return function destroy() {
// stopPolling();
// host.remove();
// };
}
setTimeout(x=>
createAdvancedFloatChatWindow(
"https://advice.us.kg", // baseUrl
"tell me somethings about solar panel system in 50 words", // prompt
600, // poll every 20 seconds
'S', // multi-line mode (anything not 'S')
50 // speed
)
,3000 )