Files
DemonGatanjieu-Anomalous_Mo…/web/modules/ui_audio_sidebar.js
T

164 lines
6.9 KiB
JavaScript

import { t } from './interface_settings.js';
/**
* Audio Sidebar Navigation Module
* Renders structured category trees for voices, characters, and generated audio history.
*/
let activeFilter = { type: 'all', value: null };
export function getActiveAudioFilter() {
return activeFilter;
}
export function setActiveAudioFilter(filter) {
activeFilter = filter || { type: 'all', value: null };
}
const SIDEBAR_SVG = {
MIC: `<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="22"/></svg>`,
ALL_VOICES: `<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>`,
USER: `<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>`,
DISC: `<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg>`,
REFRESH: `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/><path d="M16 21h5v-5"/></svg>`
};
function renderSidebarHeader(owner) {
const topBar = document.createElement('div');
topBar.style.display = 'flex';
topBar.style.justifyContent = 'space-between';
topBar.style.alignItems = 'center';
topBar.style.padding = '12px 14px 10px 14px';
topBar.style.borderBottom = '1px solid rgba(255, 255, 255, 0.05)';
const title = document.createElement('h3');
title.style.color = '#f1f5f9';
title.style.margin = '0';
title.style.fontSize = '12px';
title.style.fontWeight = '600';
title.style.letterSpacing = '0.5px';
title.style.display = 'inline-flex';
title.style.alignItems = 'center';
title.style.gap = '8px';
title.innerHTML = `<span style="color:#818cf8;">${SIDEBAR_SVG.MIC}</span><span>${t('audioSidebarTitle')}</span>`;
const refreshBtn = document.createElement('button');
refreshBtn.type = 'button';
refreshBtn.innerHTML = SIDEBAR_SVG.REFRESH;
refreshBtn.title = t('audioRefresh');
refreshBtn.style.background = 'transparent';
refreshBtn.style.border = 'none';
refreshBtn.style.color = '#94a3b8';
refreshBtn.style.cursor = 'pointer';
refreshBtn.style.padding = '4px';
refreshBtn.style.borderRadius = '4px';
refreshBtn.style.display = 'inline-flex';
refreshBtn.style.alignItems = 'center';
refreshBtn.style.justifyContent = 'center';
refreshBtn.onclick = () => renderAudioSidebar(owner);
topBar.appendChild(title);
topBar.appendChild(refreshBtn);
return topBar;
}
function createNavItem(label, iconSvg, count, isSelected, onClick) {
const item = document.createElement('div');
item.className = `anomalous-audio-nav-item${isSelected ? ' active' : ''}`;
const left = document.createElement('div');
left.style.display = 'flex';
left.style.alignItems = 'center';
left.style.gap = '8px';
left.innerHTML = `<span style="opacity:0.85;display:inline-flex;align-items:center;">${iconSvg}</span><span>${label}</span>`;
item.appendChild(left);
if (count !== undefined && count !== null) {
const badge = document.createElement('span');
badge.style.fontSize = '10px';
badge.style.color = isSelected ? '#818cf8' : '#64748b';
badge.style.background = isSelected ? 'rgba(99, 102, 241, 0.2)' : 'rgba(255, 255, 255, 0.05)';
badge.style.padding = '1px 6px';
badge.style.borderRadius = '8px';
badge.textContent = count;
item.appendChild(badge);
}
item.onclick = onClick;
return item;
}
function createSectionLabel(text) {
const sec = document.createElement('div');
sec.style.padding = '12px 14px 4px 14px';
sec.style.fontSize = '10px';
sec.style.fontWeight = '700';
sec.style.letterSpacing = '1px';
sec.style.textTransform = 'uppercase';
sec.style.color = '#64748b';
sec.textContent = text;
return sec;
}
export async function renderAudioSidebar(owner) {
if (!owner || !owner.sidebar) return;
owner.sidebar.innerHTML = '';
owner.sidebar.appendChild(renderSidebarHeader(owner));
const listContainer = document.createElement('div');
listContainer.style.overflowY = 'auto';
listContainer.style.flex = '1';
listContainer.style.padding = '6px 0';
try {
const resp = await fetch('/anomalous/audio_voices');
const data = await resp.json();
const chars = data.characters || [];
// 1. All Voices Item
const allItem = createNavItem(
t('audioAllVoices'), SIDEBAR_SVG.ALL_VOICES, data.total_slices || 0,
activeFilter.type === 'all',
() => {
activeFilter = { type: 'all', value: null };
renderAudioSidebar(owner);
owner.switchAudioTab?.('presets', activeFilter);
}
);
listContainer.appendChild(allItem);
// 2. Character Categories
listContainer.appendChild(createSectionLabel(t('audioSectionCharacters')));
chars.forEach(c => {
const isSel = activeFilter.type === 'character' && activeFilter.value === c.character;
const cItem = createNavItem(
c.character, SIDEBAR_SVG.USER, c.total_slices, isSel,
() => {
activeFilter = { type: 'character', value: c.character };
renderAudioSidebar(owner);
owner.switchAudioTab?.('presets', activeFilter);
}
);
listContainer.appendChild(cItem);
});
// 3. Output History Entry
listContainer.appendChild(createSectionLabel(t('audioSectionHistory')));
const histItem = createNavItem(
t('audioGalleryTab'), SIDEBAR_SVG.DISC, null,
activeFilter.type === 'gallery',
() => {
activeFilter = { type: 'gallery', value: null };
renderAudioSidebar(owner);
owner.switchAudioTab?.('gallery', activeFilter);
}
);
listContainer.appendChild(histItem);
} catch (e) {
console.error('Failed to render audio sidebar:', e);
}
owner.sidebar.appendChild(listContainer);
}