let stylesInjected = false; const SELECTORS = { chat: '#chat', messages: '.mes', mesButtons: '.mes_block .mes_buttons', buttons: '.memory-button, .dynamic-prompt-analysis-btn, .mes_history_preview', collapse: '.xiaobaix-collapse-btn', }; const XPOS_KEY = 'xiaobaix_x_btn_position'; const getXBtnPosition = () => { try { return ( window?.extension_settings?.LittleWhiteBox?.xBtnPosition || localStorage.getItem(XPOS_KEY) || 'name-left' ); } catch { return 'name-left'; } }; const injectStyles = () => { if (stylesInjected) return; const css = ` .mes_block .mes_buttons{align-items:center} .xiaobaix-collapse-btn{ position:relative;display:inline-flex;width:32px;height:32px;justify-content:center;align-items:center; border-radius:50%;background:var(--SmartThemeBlurTintColor);cursor:pointer; box-shadow:inset 0 0 15px rgba(0,0,0,.6),0 2px 8px rgba(0,0,0,.2); transition:opacity .15s ease,transform .15s ease} .xiaobaix-xstack{position:relative;display:inline-flex;align-items:center;justify-content:center;pointer-events:none} .xiaobaix-xstack span{ position:absolute;font:italic 900 20px 'Arial Black',sans-serif;letter-spacing:-2px;transform:scaleX(.8); text-shadow:0 0 10px rgba(255,255,255,.5),0 0 20px rgba(100,200,255,.3);color:#fff} .xiaobaix-xstack span:nth-child(1){color:rgba(255,255,255,.1);transform:scaleX(.8) translateX(-8px);text-shadow:none} .xiaobaix-xstack span:nth-child(2){color:rgba(255,255,255,.2);transform:scaleX(.8) translateX(-4px);text-shadow:none} .xiaobaix-xstack span:nth-child(3){color:rgba(255,255,255,.4);transform:scaleX(.8) translateX(-2px);text-shadow:none} .xiaobaix-sub-container{display:none;position:absolute;right:38px;border-radius:8px;padding:4px;gap:8px;pointer-events:auto} .xiaobaix-collapse-btn.open .xiaobaix-sub-container{display:flex;background:var(--SmartThemeBlurTintColor)} .xiaobaix-collapse-btn.open,.xiaobaix-collapse-btn.open ~ *{pointer-events:auto!important} .mes_block .mes_buttons.xiaobaix-expanded{width:150px} .xiaobaix-sub-container,.xiaobaix-sub-container *{pointer-events:auto!important} .xiaobaix-sub-container .memory-button,.xiaobaix-sub-container .dynamic-prompt-analysis-btn,.xiaobaix-sub-container .mes_history_preview{opacity:1!important;filter:none!important} .xiaobaix-sub-container.dir-right{left:38px;right:auto;z-index:1000;margin-top:2px} `; const style = document.createElement('style'); style.textContent = css; document.head.appendChild(style); stylesInjected = true; }; const createCollapseButton = (dirRight) => { injectStyles(); const btn = document.createElement('div'); btn.className = 'mes_btn xiaobaix-collapse-btn'; // Template-only UI markup. // eslint-disable-next-line no-unsanitized/property btn.innerHTML = `
XXXX
`; const sub = btn.lastElementChild; ['click','pointerdown','pointerup'].forEach(t => { sub.addEventListener(t, e => e.stopPropagation(), { passive: true }); }); btn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); const open = btn.classList.toggle('open'); const mesButtons = btn.closest(SELECTORS.mesButtons); if (mesButtons) mesButtons.classList.toggle('xiaobaix-expanded', open); }); return btn; }; const findInsertPoint = (messageEl) => { return messageEl.querySelector( '.ch_name.flex-container.justifySpaceBetween .flex-container.flex1.alignitemscenter,' + '.ch_name.flex-container.justifySpaceBetween .flex-container.flex1.alignItemsCenter' ); }; const ensureCollapseForMessage = (messageEl, pos) => { const mesButtons = messageEl.querySelector(SELECTORS.mesButtons); if (!mesButtons) return null; let collapseBtn = messageEl.querySelector(SELECTORS.collapse); const dirRight = pos === 'edit-right'; if (!collapseBtn) collapseBtn = createCollapseButton(dirRight); else collapseBtn.querySelector('.xiaobaix-sub-container')?.classList.toggle('dir-right', dirRight); if (dirRight) { const container = findInsertPoint(messageEl); if (!container) return null; if (collapseBtn.parentNode !== container) container.appendChild(collapseBtn); } else { if (mesButtons.lastElementChild !== collapseBtn) mesButtons.appendChild(collapseBtn); } return collapseBtn; }; let processed = new WeakSet(); let io = null; let mo = null; let queue = []; let rafScheduled = false; const processOneMessage = (message) => { if (!message || processed.has(message)) return; const mesButtons = message.querySelector(SELECTORS.mesButtons); if (!mesButtons) { processed.add(message); return; } const pos = getXBtnPosition(); if (pos === 'edit-right' && !findInsertPoint(message)) { processed.add(message); return; } const targetBtns = mesButtons.querySelectorAll(SELECTORS.buttons); if (!targetBtns.length) { processed.add(message); return; } const collapseBtn = ensureCollapseForMessage(message, pos); if (!collapseBtn) { processed.add(message); return; } const sub = collapseBtn.querySelector('.xiaobaix-sub-container'); const frag = document.createDocumentFragment(); targetBtns.forEach(b => frag.appendChild(b)); sub.appendChild(frag); processed.add(message); }; const ensureIO = () => { if (io) return io; io = new IntersectionObserver((entries) => { for (const e of entries) { if (!e.isIntersecting) continue; processOneMessage(e.target); io.unobserve(e.target); } }, { root: document.querySelector(SELECTORS.chat) || null, rootMargin: '200px 0px', threshold: 0 }); return io; }; const observeVisibility = (nodes) => { const obs = ensureIO(); nodes.forEach(n => { if (n && !processed.has(n)) obs.observe(n); }); }; const hookMutations = () => { const chat = document.querySelector(SELECTORS.chat); if (!chat) return; if (!mo) { mo = new MutationObserver((muts) => { for (const m of muts) { m.addedNodes && m.addedNodes.forEach(n => { if (n.nodeType !== 1) return; const el = n; if (el.matches?.(SELECTORS.messages)) queue.push(el); else el.querySelectorAll?.(SELECTORS.messages)?.forEach(mes => queue.push(mes)); }); } if (!rafScheduled && queue.length) { rafScheduled = true; requestAnimationFrame(() => { observeVisibility(queue); queue = []; rafScheduled = false; }); } }); } mo.observe(chat, { childList: true, subtree: true }); }; const processExistingVisible = () => { const all = document.querySelectorAll(`${SELECTORS.chat} ${SELECTORS.messages}`); if (!all.length) return; const unprocessed = []; all.forEach(n => { if (!processed.has(n)) unprocessed.push(n); }); if (unprocessed.length) observeVisibility(unprocessed); }; const initButtonCollapse = () => { injectStyles(); hookMutations(); processExistingVisible(); if (window && window['registerModuleCleanup']) { try { window['registerModuleCleanup']('buttonCollapse', cleanup); } catch {} } }; const processButtonCollapse = () => { processExistingVisible(); }; const registerButtonToSubContainer = (messageId, buttonEl) => { if (!buttonEl) return false; const message = document.querySelector(`${SELECTORS.chat} ${SELECTORS.messages}[mesid="${messageId}"]`); if (!message) return false; processOneMessage(message); const pos = getXBtnPosition(); const collapseBtn = message.querySelector(SELECTORS.collapse) || ensureCollapseForMessage(message, pos); if (!collapseBtn) return false; const sub = collapseBtn.querySelector('.xiaobaix-sub-container'); sub.appendChild(buttonEl); buttonEl.style.pointerEvents = 'auto'; buttonEl.style.opacity = '1'; return true; }; const cleanup = () => { io?.disconnect(); io = null; mo?.disconnect(); mo = null; queue = []; rafScheduled = false; document.querySelectorAll(SELECTORS.collapse).forEach(btn => { const sub = btn.querySelector('.xiaobaix-sub-container'); const message = btn.closest(SELECTORS.messages) || btn.closest('.mes'); const mesButtons = message?.querySelector(SELECTORS.mesButtons) || message?.querySelector('.mes_buttons'); if (sub && mesButtons) { mesButtons.classList.remove('xiaobaix-expanded'); const frag = document.createDocumentFragment(); while (sub.firstChild) frag.appendChild(sub.firstChild); mesButtons.appendChild(frag); } btn.remove(); }); processed = new WeakSet(); }; if (typeof window !== 'undefined') { Object.assign(window, { initButtonCollapse, cleanupButtonCollapse: cleanup, registerButtonToSubContainer, processButtonCollapse, }); document.addEventListener('xiaobaixEnabledChanged', (e) => { const en = e && e.detail && e.detail.enabled; if (!en) cleanup(); }); } export { initButtonCollapse, cleanup, registerButtonToSubContainer, processButtonCollapse };