/**
* TalkingQ Platform - Hotword Library Page
* 热词库管理:统一维护 ASR 热词组,供应用配置选择绑定。
*/
(function() {
function ensureHotwordGroups() {
if (!window.__agentHotwordGroups) {
window.__agentHotwordGroups = [
{
id: 'hw-001',
name: '品牌基础热词',
mode: 'manual',
words: [
{ text: 'TalkingQ', lang: '英文' },
{ text: '小Q', lang: '中文' },
{ text: '故事机', lang: '中文' },
],
updatedAt: '2026-05-20 11:57:38',
},
{
id: 'hw-002',
name: '儿童内容热词',
mode: 'manual',
words: [
{ text: '白雪公主', lang: '中文' },
{ text: '睡前故事', lang: '中文' },
],
updatedAt: '2026-05-20 12:08:16',
},
];
}
return window.__agentHotwordGroups;
}
function escapeHtml(value) {
const div = document.createElement('div');
div.textContent = value ?? '';
return div.innerHTML;
}
function escapeAttr(value) {
return escapeHtml(value).replace(/"/g, '"').replace(/'/g, ''');
}
function formatHotwordTime(date = new Date()) {
return date.toLocaleString('zh-CN', {
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false,
}).replace(/\//g, '-');
}
function getHotwordPreview(group) {
const preview = group.words.slice(0, 2).map(word => word.text).join('、') || '暂无热词';
return group.words.length > 2 ? `${preview} 等 ${group.words.length} 个` : `${preview} · ${group.words.length} 个`;
}
function renderHotwordRows(words) {
if (!words.length) {
return `
点击“添加热词”开始配置,最多可添加 500 个热词。
`;
}
return words.map((word, index) => `
`).join('');
}
function renderHotwordGroupList(groups) {
if (!groups.length) {
return `
暂无热词组
创建热词组后,可在智能体配置里选择绑定。
`;
}
return groups.map(group => `
${escapeHtml(group.name)}
${escapeHtml(getHotwordPreview(group))}
${group.words.length} 个
${escapeHtml(group.updatedAt || '-')}
`).join('');
}
function notifyHotwordGroupsChanged() {
window.dispatchEvent(new CustomEvent('hotwordGroupsChanged', {
detail: { groups: ensureHotwordGroups() },
}));
}
function openHotwordEditor(groupId, afterSave) {
const groups = ensureHotwordGroups();
const editingGroup = groups.find(group => group.id === groupId);
let draftWords = editingGroup ? editingGroup.words.map(word => ({ ...word })) : [];
const modal = createModal({
title: editingGroup ? '编辑热词组' : '配置热词库',
size: 'xl',
cancelText: '取消',
confirmText: '确定',
content: `
热词列表
${draftWords.length}/500 个
${renderHotwordRows(draftWords)}
`,
onConfirm: async () => {
const nameInput = document.getElementById('hotwordGroupNameInput');
const groupName = nameInput?.value.trim() || '';
if (!groupName) {
Toast.warning('请填写热词组名字');
nameInput?.focus();
return false;
}
if (groupName.length > 50) {
Toast.warning('热词组名字不能超过 50 个字');
nameInput?.focus();
return false;
}
const words = readHotwordRows(modal.overlay);
if (!words.length) {
Toast.warning('请至少添加一个热词');
return false;
}
const invalid = words.find(word => !word.text || word.text.length > 15);
if (invalid) {
Toast.warning('请填写热词,且每个热词不能超过 15 个字');
return false;
}
const now = formatHotwordTime();
const payload = { name: groupName, mode: 'manual', words };
const api = typeof API !== 'undefined' ? API : window.API;
let result = null;
try {
if (editingGroup && api?.agentDev?.updateHotwordGroup) {
await api.agentDev.updateHotwordGroup(editingGroup.id, payload);
} else if (!editingGroup && api?.agentDev?.createHotwordGroup) {
result = await api.agentDev.createHotwordGroup(payload);
}
} catch (error) {
console.error('Save hotword group failed', error);
Toast.error('热词组保存失败,请稍后重试');
return false;
}
if (editingGroup) {
editingGroup.name = groupName;
editingGroup.mode = 'manual';
editingGroup.words = words;
editingGroup.updatedAt = now;
} else {
groups.unshift({
id: result?.data?.id || 'hw-' + Date.now(),
name: groupName,
mode: 'manual',
words,
updatedAt: now,
});
}
notifyHotwordGroupsChanged();
Toast.success(editingGroup ? '热词组已更新' : '热词组已创建');
afterSave?.();
},
});
const overlay = modal.overlay;
overlay.querySelector('.modal')?.classList.add('hotword-editor-modal');
const list = overlay.querySelector('#hotwordEntryList');
const countHint = overlay.querySelector('#hotwordCountHint');
function readHotwordRows(scope = overlay) {
return Array.from(scope.querySelectorAll('.hotword-entry-row')).map(row => ({
text: row.querySelector('.hotword-text-input')?.value.trim() || '',
lang: row.querySelector('.hotword-lang-select')?.value || '中文',
}));
}
function rerenderRows() {
list.innerHTML = renderHotwordRows(draftWords);
countHint.textContent = `${draftWords.length}/500 个`;
bindRowEvents();
lucide.createIcons({ elements: [overlay] });
}
function bindRowEvents() {
overlay.querySelectorAll('.hotword-row-delete').forEach(btn => {
btn.addEventListener('click', () => {
draftWords = readHotwordRows();
draftWords.splice(Number(btn.dataset.index), 1);
rerenderRows();
});
});
overlay.querySelectorAll('.hotword-text-input').forEach(input => {
input.addEventListener('input', () => {
if (input.value.length > 15) input.value = input.value.slice(0, 15);
});
});
}
overlay.querySelector('#addHotwordRowBtn')?.addEventListener('click', () => {
draftWords = readHotwordRows();
if (draftWords.length >= 500) {
Toast.warning('手动添加热词最多支持 500 个');
return;
}
draftWords.push({ text: '', lang: '中文' });
rerenderRows();
list.scrollTop = list.scrollHeight;
overlay.querySelector('.hotword-entry-row:last-child .hotword-text-input')?.focus();
});
overlay.querySelector('#hotwordGroupNameInput')?.addEventListener('input', e => {
overlay.querySelector('#hotwordGroupNameHint').textContent = `${e.target.value.length}/50 字`;
});
bindRowEvents();
lucide.createIcons({ elements: [overlay] });
}
window.__ensureAgentHotwordGroups = ensureHotwordGroups;
window.__getHotwordPreview = getHotwordPreview;
registerPage('hotword-library', function(container) {
function renderPage() {
const groups = ensureHotwordGroups();
container.innerHTML = `
${renderHotwordGroupList(groups)}
`;
lucide.createIcons({ elements: [container] });
bindEvents();
}
function bindEvents() {
container.querySelector('#createHotwordGroupPageBtn')?.addEventListener('click', () => {
openHotwordEditor(null, renderPage);
});
container.querySelectorAll('.hotword-edit-btn').forEach(btn => {
btn.addEventListener('click', () => openHotwordEditor(btn.dataset.groupId, renderPage));
});
container.querySelectorAll('.hotword-delete-btn').forEach(btn => {
btn.addEventListener('click', () => {
const groups = ensureHotwordGroups();
const group = groups.find(item => item.id === btn.dataset.groupId);
if (!group) return;
createModal({
title: '删除热词组',
content: `确定删除热词组 ${escapeHtml(group.name)} 吗?删除后已绑定的智能体将不再使用该热词组。
`,
confirmText: '删除',
onConfirm: async () => {
const api = typeof API !== 'undefined' ? API : window.API;
try {
if (api?.agentDev?.deleteHotwordGroup) {
await api.agentDev.deleteHotwordGroup(group.id);
}
} catch (error) {
console.error('Delete hotword group failed', error);
Toast.error('热词组删除失败,请稍后重试');
return false;
}
const index = groups.findIndex(item => item.id === group.id);
if (index >= 0) groups.splice(index, 1);
if (window.__agentHotwordBindings) {
Object.keys(window.__agentHotwordBindings).forEach(appId => {
if (Array.isArray(window.__agentHotwordBindings[appId])) {
window.__agentHotwordBindings[appId] = window.__agentHotwordBindings[appId].filter(id => id !== group.id);
}
});
}
notifyHotwordGroupsChanged();
renderPage();
Toast.success('热词组已删除');
},
});
});
});
}
renderPage();
});
})();