// js/chatbot/ui/semantic-groups-ui.js
(function(window, document) {
'use strict';
if (window.SemanticGroupsUIScriptLoaded) return;
// 载入持久化设置
try {
const saved = localStorage.getItem('semanticGroupsSettings');
if (saved) window.semanticGroupsSettings = JSON.parse(saved);
} catch (_) {}
function createModalIfNeeded() {
let modal = document.getElementById('semantic-groups-modal');
if (modal) return modal;
modal = document.createElement('div');
modal.id = 'semantic-groups-modal';
modal.style.cssText = [
'position: fixed',
'right: 12px',
'bottom: 12px',
'width: 380px',
'height: 60vh',
'background: #fff',
'border: 1px solid #e5e7eb',
'border-radius: 8px',
'box-shadow: 0 10px 25px rgba(0,0,0,0.08)',
'display: none',
'flex-direction: column',
'z-index: 9999'
].join(';');
const header = document.createElement('div');
header.style.cssText = 'display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid #f1f5f9;background:#f8fafc;gap:8px;';
const title = document.createElement('div');
title.id = 'semantic-groups-title';
title.textContent = '意群预览';
title.style.cssText = 'font-weight:600;color:#111827;font-size:13px;';
const buttonsWrap = document.createElement('div');
buttonsWrap.style.cssText = 'display:flex;align-items:center;gap:6px;';
const settingsBtn = document.createElement('button');
settingsBtn.textContent = '⚙';
settingsBtn.style.cssText = 'border:none;background:transparent;font-size:14px;color:#6b7280;cursor:pointer;';
const closeBtn = document.createElement('button');
closeBtn.textContent = '×';
closeBtn.style.cssText = 'border:none;background:transparent;font-size:18px;color:#6b7280;cursor:pointer;';
closeBtn.onclick = () => { modal.style.display = 'none'; };
buttonsWrap.appendChild(settingsBtn);
buttonsWrap.appendChild(closeBtn);
header.appendChild(title);
header.appendChild(buttonsWrap);
const searchBar = document.createElement('div');
searchBar.style.cssText = 'padding:8px 12px;border-bottom:1px solid #f1f5f9;display:flex;gap:6px;align-items:center;flex-wrap:wrap;';
const input = document.createElement('input');
input.id = 'semantic-groups-search';
input.placeholder = '搜索关键词(quick/全文)';
input.style.cssText = 'flex:1;min-width:180px;padding:6px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;';
input.onkeydown = function(e){ if (e.key === 'Enter') renderList(input.value.trim()); };
const scopeSel = document.createElement('select');
scopeSel.id = 'semantic-groups-scope';
scopeSel.style.cssText = 'padding:6px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;background:#fff;color:#374151;';
;['quick','summary','digest','full'].forEach(v=>{ const o=document.createElement('option'); o.value=v; o.textContent=v; scopeSel.appendChild(o); });
const runBtn = document.createElement('button');
runBtn.textContent = '搜索';
runBtn.style.cssText = 'border:1px solid #e5e7eb;background:#fff;color:#111827;border-radius:6px;font-size:12px;padding:6px 10px;cursor:pointer;';
const clearBtn = document.createElement('button');
clearBtn.textContent = '清空';
clearBtn.style.cssText = 'border:1px solid #e5e7eb;background:#fff;color:#374151;border-radius:6px;font-size:12px;padding:6px 10px;cursor:pointer;';
clearBtn.onclick = function(){ input.value=''; renderList(''); };
runBtn.onclick = function(){
const q = input.value.trim();
const sc = scopeSel.value;
if (!q) { renderList(''); return; }
if (sc === 'quick') { renderList(q); return; }
try {
const results = (window.SemanticTools && typeof window.SemanticTools.findInGroups === 'function')
? window.SemanticTools.findInGroups(q, sc, 20) : [];
renderSearchResults(results, q);
} catch (e) { console.warn('[SemanticGroupsUI] findInGroups失败:', e); renderList(q); }
};
searchBar.appendChild(input);
searchBar.appendChild(scopeSel);
searchBar.appendChild(runBtn);
searchBar.appendChild(clearBtn);
const settings = document.createElement('div');
settings.id = 'semantic-groups-settings';
settings.style.cssText = 'display:none;padding:8px 12px;border-bottom:1px solid #f1f5f9;background:#fff;';
settings.innerHTML = `
`;
const list = document.createElement('div');
list.id = 'semantic-groups-list';
list.style.cssText = 'flex:1;overflow:auto;padding:8px 8px 12px 8px;';
modal.appendChild(header);
modal.appendChild(settings);
modal.appendChild(searchBar);
modal.appendChild(list);
document.body.appendChild(modal);
// 交互:初始化设置值并绑定事件
const initSettings = () => {
const s = window.semanticGroupsSettings || {};
const $ = (id) => document.getElementById(id);
const setVal = (id, v) => { const el = $(id); if (el) el.value = v; };
setVal('sg-set-concurrency', Number(s.concurrency) > 0 ? Number(s.concurrency) : 20);
setVal('sg-set-target', Number(s.targetChars) > 0 ? Number(s.targetChars) : 5000);
setVal('sg-set-min', Number(s.minChars) > 0 ? Number(s.minChars) : 2500);
setVal('sg-set-max', Number(s.maxChars) > 0 ? Number(s.maxChars) : 6000);
setVal('sg-set-maxrounds', Number(s.maxRounds) > 0 ? Number(s.maxRounds) : 3);
};
initSettings();
settingsBtn.onclick = function(){
settings.style.display = settings.style.display === 'none' ? 'block' : 'none';
};
const getSettingsObj = () => {
const $ = (id) => document.getElementById(id);
return {
concurrency: Number($('#sg-set-concurrency')?.value || 20),
targetChars: Number($('#sg-set-target')?.value || 5000),
minChars: Number($('#sg-set-min')?.value || 2500),
maxChars: Number($('#sg-set-max')?.value || 6000),
maxRounds: Number($('#sg-set-maxrounds')?.value || 3)
};
};
const saveBtn = document.getElementById('sg-set-save');
const saveRebuildBtn = document.getElementById('sg-set-save-rebuild');
if (saveBtn) saveBtn.onclick = function(){ window.semanticGroupsSettings = getSettingsObj(); try { localStorage.setItem('semanticGroupsSettings', JSON.stringify(window.semanticGroupsSettings)); } catch(_){ } };
if (saveRebuildBtn) saveRebuildBtn.onclick = async function(){
window.semanticGroupsSettings = getSettingsObj();
try { localStorage.setItem('semanticGroupsSettings', JSON.stringify(window.semanticGroupsSettings)); } catch(_){ }
if (window.ChatbotCore && typeof window.ChatbotCore.regenerateSemanticGroups === 'function') {
await window.ChatbotCore.regenerateSemanticGroups(window.semanticGroupsSettings);
}
};
return modal;
}
function renderList(query) {
const list = document.getElementById('semantic-groups-list');
const title = document.getElementById('semantic-groups-title');
if (!list) return;
const groups = (window.data && Array.isArray(window.data.semanticGroups)) ? window.data.semanticGroups : [];
const total = groups.length;
title.textContent = `意群预览(${total})`;
let visibleGroups = groups;
if (query && window.SemanticGrouper && typeof window.SemanticGrouper.quickMatch === 'function') {
visibleGroups = window.SemanticGrouper.quickMatch(query, groups);
}
list.innerHTML = '';
if (visibleGroups.length === 0) {
const empty = document.createElement('div');
empty.textContent = query ? '未匹配到相关意群' : '暂无意群数据';
empty.style.cssText = 'padding:12px;color:#6b7280;text-align:center;';
list.appendChild(empty);
return;
}
visibleGroups.forEach((group, idx) => {
const card = document.createElement('div');
card.style.cssText = 'border:1px solid #e5e7eb;border-radius:8px;padding:8px 10px;margin:6px 0;background:#fff;';
const h = document.createElement('div');
h.style.cssText = 'display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;';
const left = document.createElement('div');
left.style.cssText = 'font-weight:600;color:#111827;font-size:12px;';
left.textContent = `${group.groupId || 'group-' + idx} · ${group.charCount || 0} 字`;
const right = document.createElement('div');
right.style.cssText = 'color:#6b7280;font-size:11px;';
right.textContent = (group.keywords && group.keywords.length) ? group.keywords.join('、') : '';
h.appendChild(left); h.appendChild(right);
const summary = document.createElement('div');
summary.style.cssText = 'color:#374151;font-size:12px;line-height:1.5;margin-bottom:6px;';
summary.textContent = group.summary || '';
const actions = document.createElement('div');
actions.style.cssText = 'display:flex;gap:8px;';
const toggleBtn = document.createElement('button');
toggleBtn.textContent = '展开详细';
toggleBtn.style.cssText = 'border:1px solid #e5e7eb;background:#fff;color:#374151;border-radius:6px;font-size:12px;padding:4px 8px;cursor:pointer;';
const copyBtn = document.createElement('button');
copyBtn.textContent = '复制摘要';
copyBtn.style.cssText = 'border:1px solid #e5e7eb;background:#fff;color:#374151;border-radius:6px;font-size:12px;padding:4px 8px;cursor:pointer;';
const detail = document.createElement('div');
detail.style.cssText = 'display:none;margin-top:6px;padding:8px;border:1px dashed #e5e7eb;border-radius:6px;color:#4b5563;font-size:12px;white-space:pre-wrap;';
detail.textContent = group.digest || group.fullText || '';
toggleBtn.onclick = function(){
if (detail.style.display === 'none') { detail.style.display = 'block'; toggleBtn.textContent = '收起详细'; }
else { detail.style.display = 'none'; toggleBtn.textContent = '展开详细'; }
};
copyBtn.onclick = function(){
try { navigator.clipboard.writeText(group.summary || ''); } catch {}
};
actions.appendChild(toggleBtn);
actions.appendChild(copyBtn);
card.appendChild(h);
card.appendChild(summary);
card.appendChild(actions);
card.appendChild(detail);
list.appendChild(card);
});
}
function renderSearchResults(results, query) {
const list = document.getElementById('semantic-groups-list');
const title = document.getElementById('semantic-groups-title');
if (!list) return;
const groups = (window.data && Array.isArray(window.data.semanticGroups)) ? window.data.semanticGroups : [];
const byId = new Map(groups.map(g => [g.groupId, g]));
title.textContent = `意群预览(${groups.length}) - 全文搜索`;
list.innerHTML = '';
if (!Array.isArray(results) || results.length === 0) {
const empty = document.createElement('div');
empty.textContent = query ? `未在全文中匹配到:“${query}”` : '暂无匹配结果';
empty.style.cssText = 'padding:12px;color:#6b7280;text-align:center;';
list.appendChild(empty);
return;
}
results.forEach((r, idx) => {
const g = byId.get(r.groupId);
if (!g) return;
const card = document.createElement('div');
card.style.cssText = 'border:1px solid #e5e7eb;border-radius:8px;padding:8px 10px;margin:6px 0;background:#fff;';
const h = document.createElement('div');
h.style.cssText = 'display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;';
const left = document.createElement('div');
left.style.cssText = 'font-weight:600;color:#111827;font-size:12px;';
left.textContent = `${g.groupId || 'group-' + idx} · ${g.charCount || 0} 字`;
const right = document.createElement('div');
right.style.cssText = 'color:#6b7280;font-size:11px;';
right.textContent = (g.keywords && g.keywords.length) ? g.keywords.join('、') : '';
h.appendChild(left); h.appendChild(right);
const summary = document.createElement('div');
summary.style.cssText = 'color:#374151;font-size:12px;line-height:1.5;margin-bottom:6px;';
summary.textContent = `[匹配片段] ${r.snippet || g.summary || ''}`;
const actions = document.createElement('div');
actions.style.cssText = 'display:flex;gap:8px;';
const toggleBtn = document.createElement('button');
toggleBtn.textContent = '展开详细';
toggleBtn.style.cssText = 'border:1px solid #e5e7eb;background:#fff;color:#374151;border-radius:6px;font-size:12px;padding:4px 8px;cursor:pointer;';
const copyBtn = document.createElement('button');
copyBtn.textContent = '复制摘要';
copyBtn.style.cssText = 'border:1px solid #e5e7eb;background:#fff;color:#374151;border-radius:6px;font-size:12px;padding:4px 8px;cursor:pointer;';
const detail = document.createElement('div');
detail.style.cssText = 'display:none;margin-top:6px;padding:8px;border:1px dashed #e5e7eb;border-radius:6px;color:#4b5563;font-size:12px;white-space:pre-wrap;';
detail.textContent = g.digest || g.fullText || '';
toggleBtn.onclick = function(){
if (detail.style.display === 'none') { detail.style.display = 'block'; toggleBtn.textContent = '收起详细'; }
else { detail.style.display = 'none'; toggleBtn.textContent = '展开详细'; }
};
copyBtn.onclick = function(){ try { navigator.clipboard.writeText(g.summary || ''); } catch {} };
actions.appendChild(toggleBtn);
actions.appendChild(copyBtn);
card.appendChild(h);
card.appendChild(summary);
card.appendChild(actions);
card.appendChild(detail);
list.appendChild(card);
});
}
function open() {
const modal = createModalIfNeeded();
renderList('');
modal.style.display = 'flex';
}
function close() {
const modal = document.getElementById('semantic-groups-modal');
if (modal) modal.style.display = 'none';
}
function toggle() {
const modal = createModalIfNeeded();
if (modal.style.display === 'none' || !modal.style.display) { open(); }
else { close(); }
}
function update() {
const modal = document.getElementById('semantic-groups-modal');
if (modal && modal.style.display !== 'none') renderList(document.getElementById('semantic-groups-search')?.value || '');
}
window.SemanticGroupsUI = { open, close, toggle, update };
window.SemanticGroupsUIScriptLoaded = true;
})(window, document);