Replace collapsible stream groups with sub-tabs navigation

- Replace expandable/collapsible groups with tab bar (Screen Capture, Static Image, Processed)
- Persist active stream tab in localStorage
- Shorten tab labels by removing "Streams" suffix
- Remove type badge from cards (redundant with tab separation)
- Add count badge on each tab with active highlight

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 20:28:30 +03:00
parent 705179f73f
commit 7d0b6f2583
4 changed files with 79 additions and 102 deletions

View File

@@ -3087,58 +3087,23 @@ async function loadPictureStreams() {
}
}
function toggleStreamGroup(groupKey) {
const stored = JSON.parse(localStorage.getItem('streamGroupState') || '{}');
stored[groupKey] = !stored[groupKey];
localStorage.setItem('streamGroupState', JSON.stringify(stored));
renderPictureStreamsList(_cachedStreams);
}
function isStreamGroupCollapsed(groupKey) {
const stored = JSON.parse(localStorage.getItem('streamGroupState') || '{}');
return !!stored[groupKey];
}
function renderStreamGroup(groupKey, icon, titleKey, count, addType, addLabelKey, cardsHtml) {
const collapsed = isStreamGroupCollapsed(groupKey);
const chevron = collapsed ? '&#x25B6;' : '&#x25BC;';
return `<div class="stream-group${collapsed ? ' collapsed' : ''}">
<div class="stream-group-header" onclick="toggleStreamGroup('${groupKey}')">
<span class="stream-group-chevron">${chevron}</span>
<span class="stream-group-icon">${icon}</span>
<span class="stream-group-title">${t(titleKey)}</span>
<span class="stream-group-count">${count}</span>
</div>
<div class="templates-grid">
${cardsHtml}
<div class="template-card add-template-card" onclick="showAddStreamModal('${addType}')">
<div class="add-template-icon">+</div>
<div class="add-template-label">${t(addLabelKey)}</div>
</div>
</div>
</div>`;
function switchStreamTab(tabKey) {
document.querySelectorAll('.stream-tab-btn').forEach(btn =>
btn.classList.toggle('active', btn.dataset.streamTab === tabKey)
);
document.querySelectorAll('.stream-tab-panel').forEach(panel =>
panel.classList.toggle('active', panel.id === `stream-tab-${tabKey}`)
);
localStorage.setItem('activeStreamTab', tabKey);
}
function renderPictureStreamsList(streams) {
const container = document.getElementById('streams-list');
if (streams.length === 0) {
container.innerHTML =
renderStreamGroup('raw', '🖥️', 'streams.group.raw', 0, 'raw', 'streams.add.raw', '') +
renderStreamGroup('static_image', '🖼️', 'streams.group.static_image', 0, 'static_image', 'streams.add.static_image', '') +
renderStreamGroup('processed', '🎨', 'streams.group.processed', 0, 'processed', 'streams.add.processed', '');
return;
}
const activeTab = localStorage.getItem('activeStreamTab') || 'raw';
const renderCard = (stream) => {
const typeIcons = { raw: '🖥️', processed: '🎨', static_image: '🖼️' };
const typeIcon = typeIcons[stream.stream_type] || '📺';
const typeBadges = {
raw: `<span class="badge badge-raw">${t('streams.type.raw')}</span>`,
processed: `<span class="badge badge-processed">${t('streams.type.processed')}</span>`,
static_image: `<span class="badge badge-processed">${t('streams.type.static_image')}</span>`,
};
const typeBadge = typeBadges[stream.stream_type] || '';
let detailsHtml = '';
if (stream.stream_type === 'raw') {
@@ -3178,7 +3143,6 @@ function renderPictureStreamsList(streams) {
<div class="template-name">
${typeIcon} ${escapeHtml(stream.name)}
</div>
${typeBadge}
</div>
${detailsHtml}
${stream.description ? `<div class="template-config" style="opacity:0.7;">${escapeHtml(stream.description)}</div>` : ''}
@@ -3198,10 +3162,32 @@ function renderPictureStreamsList(streams) {
const processedStreams = streams.filter(s => s.stream_type === 'processed');
const staticImageStreams = streams.filter(s => s.stream_type === 'static_image');
container.innerHTML =
renderStreamGroup('raw', '🖥️', 'streams.group.raw', rawStreams.length, 'raw', 'streams.add.raw', rawStreams.map(renderCard).join('')) +
renderStreamGroup('static_image', '🖼️', 'streams.group.static_image', staticImageStreams.length, 'static_image', 'streams.add.static_image', staticImageStreams.map(renderCard).join('')) +
renderStreamGroup('processed', '🎨', 'streams.group.processed', processedStreams.length, 'processed', 'streams.add.processed', processedStreams.map(renderCard).join(''));
const addCard = (type, labelKey) => `
<div class="template-card add-template-card" onclick="showAddStreamModal('${type}')">
<div class="add-template-icon">+</div>
<div class="add-template-label">${t(labelKey)}</div>
</div>`;
const tabs = [
{ key: 'raw', icon: '🖥️', titleKey: 'streams.group.raw', streams: rawStreams, addLabelKey: 'streams.add.raw' },
{ key: 'static_image', icon: '🖼️', titleKey: 'streams.group.static_image', streams: staticImageStreams, addLabelKey: 'streams.add.static_image' },
{ key: 'processed', icon: '🎨', titleKey: 'streams.group.processed', streams: processedStreams, addLabelKey: 'streams.add.processed' },
];
const tabBar = `<div class="stream-tab-bar">${tabs.map(tab =>
`<button class="stream-tab-btn${tab.key === activeTab ? ' active' : ''}" data-stream-tab="${tab.key}" onclick="switchStreamTab('${tab.key}')">${tab.icon} ${t(tab.titleKey)} <span class="stream-tab-count">${tab.streams.length}</span></button>`
).join('')}</div>`;
const panels = tabs.map(tab =>
`<div class="stream-tab-panel${tab.key === activeTab ? ' active' : ''}" id="stream-tab-${tab.key}">
<div class="templates-grid">
${tab.streams.map(renderCard).join('')}
${addCard(tab.key, tab.addLabelKey)}
</div>
</div>`
).join('');
container.innerHTML = tabBar + panels;
}
function onStreamTypeChange() {