Separate tree nodes into independent panels, remove graph local search, UI improvements
- Split Sources tab: raw/raw_templates, processed/proc_templates each get own panel - Split Targets tab: led-devices, led-targets, kc-targets, kc-patterns each get own panel - Remove graph local search — search button and / key open global command palette - Add graphNavigateToNode for command palette → graph node navigation - Add tree group expand/collapse animation (max-height + opacity transition) - Make tree group headers visually distinct (smaller, uppercase, left border on children) - Make CardSection collapse opt-in via collapsible flag (disabled by default) - Move filter textbox next to section title (remove margin-left: auto) - Fix notification bell button vertical centering in test preview - Fix clipboard copy on non-HTTPS with execCommand fallback - Add overlay toggle button on picture-based CSS cards - Add CSPT to graph add-entity picker and global search - Update all cross-link navigation paths for new panel keys - Add i18n keys for new tree groups and search groups (en/ru/zh) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1246,9 +1246,11 @@ export function switchStreamTab(tabKey) {
|
||||
}
|
||||
|
||||
const _streamSectionMap = {
|
||||
raw: [csRawStreams, csRawTemplates],
|
||||
raw: [csRawStreams],
|
||||
raw_templates: [csRawTemplates],
|
||||
static_image: [csStaticStreams],
|
||||
processed: [csProcStreams, csProcTemplates],
|
||||
processed: [csProcStreams],
|
||||
proc_templates: [csProcTemplates],
|
||||
css_processing: [csCSPTemplates],
|
||||
color_strip: [csColorStrips],
|
||||
audio: [csAudioMulti, csAudioMono, csAudioTemplates],
|
||||
@@ -1283,7 +1285,7 @@ function renderPictureSourcesList(streams) {
|
||||
detailsHtml = `<div class="stream-card-props">
|
||||
<span class="stream-card-prop" title="${t('streams.display')}">${ICON_MONITOR} ${stream.display_index ?? 0}</span>
|
||||
<span class="stream-card-prop" title="${t('streams.target_fps')}">${ICON_FPS} ${stream.target_fps ?? 30}</span>
|
||||
${capTmplName ? `<span class="stream-card-prop stream-card-link" title="${t('streams.capture_template')}" onclick="event.stopPropagation(); navigateToCard('streams','raw','raw-templates','data-template-id','${stream.capture_template_id}')">${ICON_TEMPLATE} ${capTmplName}</span>` : ''}
|
||||
${capTmplName ? `<span class="stream-card-prop stream-card-link" title="${t('streams.capture_template')}" onclick="event.stopPropagation(); navigateToCard('streams','raw_templates','raw-templates','data-template-id','${stream.capture_template_id}')">${ICON_TEMPLATE} ${capTmplName}</span>` : ''}
|
||||
</div>`;
|
||||
} else if (stream.stream_type === 'processed') {
|
||||
const sourceStream = _cachedStreams.find(s => s.id === stream.source_stream_id);
|
||||
@@ -1297,7 +1299,7 @@ function renderPictureSourcesList(streams) {
|
||||
}
|
||||
detailsHtml = `<div class="stream-card-props">
|
||||
<span class="stream-card-prop stream-card-link" title="${t('streams.source')}" onclick="event.stopPropagation(); navigateToCard('streams','${sourceSubTab}','${sourceSection}','data-stream-id','${stream.source_stream_id}')">${ICON_LINK_SOURCE} ${sourceName}</span>
|
||||
${ppTmplName ? `<span class="stream-card-prop stream-card-link" title="${t('streams.pp_template')}" onclick="event.stopPropagation(); navigateToCard('streams','processed','proc-templates','data-pp-template-id','${stream.postprocessing_template_id}')">${ICON_TEMPLATE} ${ppTmplName}</span>` : ''}
|
||||
${ppTmplName ? `<span class="stream-card-prop stream-card-link" title="${t('streams.pp_template')}" onclick="event.stopPropagation(); navigateToCard('streams','proc_templates','proc-templates','data-pp-template-id','${stream.postprocessing_template_id}')">${ICON_TEMPLATE} ${ppTmplName}</span>` : ''}
|
||||
</div>`;
|
||||
} else if (stream.stream_type === 'static_image') {
|
||||
const src = stream.image_source || '';
|
||||
@@ -1440,8 +1442,10 @@ function renderPictureSourcesList(streams) {
|
||||
|
||||
const tabs = [
|
||||
{ key: 'raw', icon: getPictureSourceIcon('raw'), titleKey: 'streams.group.raw', count: rawStreams.length },
|
||||
{ key: 'raw_templates', icon: ICON_CAPTURE_TEMPLATE, titleKey: 'streams.group.raw_templates', count: _cachedCaptureTemplates.length },
|
||||
{ key: 'static_image', icon: getPictureSourceIcon('static_image'), titleKey: 'streams.group.static_image', count: staticImageStreams.length },
|
||||
{ key: 'processed', icon: getPictureSourceIcon('processed'), titleKey: 'streams.group.processed', count: processedStreams.length },
|
||||
{ key: 'proc_templates', icon: ICON_PP_TEMPLATE, titleKey: 'streams.group.proc_templates', count: _cachedPPTemplates.length },
|
||||
{ key: 'css_processing', icon: ICON_CSPT, titleKey: 'streams.group.css_processing', count: csptTemplates.length },
|
||||
{ key: 'color_strip', icon: getColorStripIcon('static'), titleKey: 'streams.group.color_strip', count: colorStrips.length },
|
||||
{ key: 'audio', icon: getAudioSourceIcon('multichannel'), titleKey: 'streams.group.audio', count: _cachedAudioSources.length },
|
||||
@@ -1452,11 +1456,21 @@ function renderPictureSourcesList(streams) {
|
||||
// Build tree navigation structure
|
||||
const treeGroups = [
|
||||
{
|
||||
key: 'picture_group', icon: getPictureSourceIcon('raw'), titleKey: 'tree.group.picture',
|
||||
key: 'capture_group', icon: getPictureSourceIcon('raw'), titleKey: 'tree.group.capture',
|
||||
children: [
|
||||
{ key: 'raw', titleKey: 'streams.group.raw', icon: getPictureSourceIcon('raw'), count: rawStreams.length },
|
||||
{ key: 'static_image', titleKey: 'streams.group.static_image', icon: getPictureSourceIcon('static_image'), count: staticImageStreams.length },
|
||||
{ key: 'raw_templates', titleKey: 'streams.group.raw_templates', icon: ICON_CAPTURE_TEMPLATE, count: _cachedCaptureTemplates.length },
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'static_image', icon: getPictureSourceIcon('static_image'), titleKey: 'streams.group.static_image',
|
||||
count: staticImageStreams.length,
|
||||
},
|
||||
{
|
||||
key: 'processing_group', icon: getPictureSourceIcon('processed'), titleKey: 'tree.group.processing',
|
||||
children: [
|
||||
{ key: 'processed', titleKey: 'streams.group.processed', icon: getPictureSourceIcon('processed'), count: processedStreams.length },
|
||||
{ key: 'proc_templates', titleKey: 'streams.group.proc_templates', icon: ICON_PP_TEMPLATE, count: _cachedPPTemplates.length },
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1584,8 +1598,10 @@ function renderPictureSourcesList(streams) {
|
||||
// Incremental update: reconcile cards in-place
|
||||
_streamsTree.updateCounts({
|
||||
raw: rawStreams.length,
|
||||
raw_templates: _cachedCaptureTemplates.length,
|
||||
static_image: staticImageStreams.length,
|
||||
processed: processedStreams.length,
|
||||
proc_templates: _cachedPPTemplates.length,
|
||||
css_processing: csptTemplates.length,
|
||||
color_strip: colorStrips.length,
|
||||
audio: _cachedAudioSources.length + _cachedAudioTemplates.length,
|
||||
@@ -1608,8 +1624,10 @@ function renderPictureSourcesList(streams) {
|
||||
// First render: build full HTML
|
||||
const panels = tabs.map(tab => {
|
||||
let panelContent = '';
|
||||
if (tab.key === 'raw') panelContent = csRawStreams.render(rawStreamItems) + csRawTemplates.render(rawTemplateItems);
|
||||
else if (tab.key === 'processed') panelContent = csProcStreams.render(procStreamItems) + csProcTemplates.render(procTemplateItems);
|
||||
if (tab.key === 'raw') panelContent = csRawStreams.render(rawStreamItems);
|
||||
else if (tab.key === 'raw_templates') panelContent = csRawTemplates.render(rawTemplateItems);
|
||||
else if (tab.key === 'processed') panelContent = csProcStreams.render(procStreamItems);
|
||||
else if (tab.key === 'proc_templates') panelContent = csProcTemplates.render(procTemplateItems);
|
||||
else if (tab.key === 'css_processing') panelContent = csCSPTemplates.render(csptItems);
|
||||
else if (tab.key === 'color_strip') panelContent = csColorStrips.render(colorStripItems);
|
||||
else if (tab.key === 'audio') panelContent = csAudioMulti.render(multiItems) + csAudioMono.render(monoItems) + csAudioTemplates.render(audioTemplateItems);
|
||||
@@ -1626,9 +1644,9 @@ function renderPictureSourcesList(streams) {
|
||||
_streamsTree.setExtraHtml(`<button class="btn-expand-collapse" onclick="expandAllStreamSections()" data-i18n-title="section.expand_all" title="${t('section.expand_all')}">⊞</button><button class="btn-expand-collapse" onclick="collapseAllStreamSections()" data-i18n-title="section.collapse_all" title="${t('section.collapse_all')}">⊟</button><button class="tutorial-trigger-btn" onclick="startSourcesTutorial()" data-i18n-title="tour.restart" title="${t('tour.restart')}">${ICON_HELP}</button>`);
|
||||
_streamsTree.update(treeGroups, activeTab);
|
||||
_streamsTree.observeSections('streams-list', {
|
||||
'raw-streams': 'raw', 'raw-templates': 'raw',
|
||||
'raw-streams': 'raw', 'raw-templates': 'raw_templates',
|
||||
'static-streams': 'static_image',
|
||||
'proc-streams': 'processed', 'proc-templates': 'processed',
|
||||
'proc-streams': 'processed', 'proc-templates': 'proc_templates',
|
||||
'css-proc-templates': 'css_processing',
|
||||
'color-strips': 'color_strip',
|
||||
'audio-multi': 'audio', 'audio-mono': 'audio', 'audio-templates': 'audio',
|
||||
|
||||
Reference in New Issue
Block a user