Move color strip sources from Targets tab to Sources tab
- Remove csColorStrips CardSection from targets.js, add to streams.js - Add color_strip sub-tab with tree nav entry between Picture and Audio - Update navigateToCard refs in target cards and command palette - Update tutorial steps: remove led-css from targets, add color_strip to sources - Add i18n keys for streams.group.color_strip and tour.src.color_strip Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -57,7 +57,7 @@ function _buildItems(results, states = {}) {
|
||||
|
||||
_mapEntities(css, c => items.push({
|
||||
name: c.name, detail: c.css_type || c.source_type, group: 'css', icon: getColorStripIcon(c.css_type || c.source_type),
|
||||
nav: ['targets', 'led', 'led-css', 'data-css-id', c.id],
|
||||
nav: ['streams', 'color_strip', 'color-strips', 'data-css-id', c.id],
|
||||
}));
|
||||
|
||||
_mapEntities(automations, a => items.push({
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
apiKey,
|
||||
streamsCache, ppTemplatesCache, captureTemplatesCache,
|
||||
audioSourcesCache, audioTemplatesCache, valueSourcesCache, syncClocksCache, filtersCache,
|
||||
colorStripSourcesCache,
|
||||
} from '../core/state.js';
|
||||
import { API_BASE, getHeaders, fetchWithAuth, escapeHtml } from '../core/api.js';
|
||||
import { t } from '../core/i18n.js';
|
||||
@@ -41,8 +42,9 @@ import { TreeNav } from '../core/tree-nav.js';
|
||||
import { updateSubTabHash } from './tabs.js';
|
||||
import { createValueSourceCard } from './value-sources.js';
|
||||
import { createSyncClockCard } from './sync-clocks.js';
|
||||
import { createColorStripCard } from './color-strips.js';
|
||||
import {
|
||||
getEngineIcon, getAudioEngineIcon, getPictureSourceIcon, getAudioSourceIcon,
|
||||
getEngineIcon, getAudioEngineIcon, getPictureSourceIcon, getAudioSourceIcon, getColorStripIcon,
|
||||
ICON_TEMPLATE, ICON_CLONE, ICON_EDIT, ICON_TEST, ICON_LINK_SOURCE,
|
||||
ICON_FPS, ICON_WEB, ICON_VALUE_SOURCE, ICON_CLOCK, ICON_AUDIO_LOOPBACK, ICON_AUDIO_INPUT,
|
||||
ICON_AUDIO_TEMPLATE, ICON_MONITOR, ICON_WRENCH, ICON_RADIO,
|
||||
@@ -69,6 +71,7 @@ const csAudioMulti = new CardSection('audio-multi', { titleKey: 'audio_source.gr
|
||||
const csAudioMono = new CardSection('audio-mono', { titleKey: 'audio_source.group.mono', gridClass: 'templates-grid', addCardOnclick: "showAudioSourceModal('mono')", keyAttr: 'data-id' });
|
||||
const csStaticStreams = new CardSection('static-streams', { titleKey: 'streams.group.static_image', gridClass: 'templates-grid', addCardOnclick: "showAddStreamModal('static_image')", keyAttr: 'data-stream-id' });
|
||||
const csAudioTemplates = new CardSection('audio-templates', { titleKey: 'audio_template.title', gridClass: 'templates-grid', addCardOnclick: "showAddAudioTemplateModal()", keyAttr: 'data-audio-template-id' });
|
||||
const csColorStrips = new CardSection('color-strips', { titleKey: 'targets.section.color_strips', gridClass: 'templates-grid', addCardOnclick: "showCSSEditor()", keyAttr: 'data-css-id' });
|
||||
const csValueSources = new CardSection('value-sources', { titleKey: 'value_source.group.title', gridClass: 'templates-grid', addCardOnclick: "showValueSourceModal()", keyAttr: 'data-id' });
|
||||
const csSyncClocks = new CardSection('sync-clocks', { titleKey: 'sync_clock.group.title', gridClass: 'templates-grid', addCardOnclick: "showSyncClockModal()", keyAttr: 'data-id' });
|
||||
|
||||
@@ -1175,6 +1178,7 @@ export async function loadPictureSources() {
|
||||
valueSourcesCache.fetch(),
|
||||
syncClocksCache.fetch(),
|
||||
audioTemplatesCache.fetch(),
|
||||
colorStripSourcesCache.fetch(),
|
||||
filtersCache.data.length === 0 ? filtersCache.fetch() : Promise.resolve(filtersCache.data),
|
||||
]);
|
||||
renderPictureSourcesList(streams);
|
||||
@@ -1216,6 +1220,7 @@ const _streamSectionMap = {
|
||||
raw: [csRawStreams, csRawTemplates],
|
||||
static_image: [csStaticStreams],
|
||||
processed: [csProcStreams, csProcTemplates],
|
||||
color_strip: [csColorStrips],
|
||||
audio: [csAudioMulti, csAudioMono, csAudioTemplates],
|
||||
value: [csValueSources],
|
||||
sync: [csSyncClocks],
|
||||
@@ -1367,11 +1372,18 @@ function renderPictureSourcesList(streams) {
|
||||
const multichannelSources = _cachedAudioSources.filter(s => s.source_type === 'multichannel');
|
||||
const monoSources = _cachedAudioSources.filter(s => s.source_type === 'mono');
|
||||
|
||||
// Color strip sources (maps needed for card rendering)
|
||||
const colorStrips = colorStripSourcesCache.data;
|
||||
const pictureSourceMap = {};
|
||||
streams.forEach(s => { pictureSourceMap[s.id] = s; });
|
||||
const audioSourceMap = {};
|
||||
_cachedAudioSources.forEach(s => { audioSourceMap[s.id] = s; });
|
||||
|
||||
const tabs = [
|
||||
{ key: 'raw', icon: getPictureSourceIcon('raw'), titleKey: 'streams.group.raw', count: rawStreams.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: '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 },
|
||||
{ key: 'value', icon: ICON_VALUE_SOURCE, titleKey: 'streams.group.value', count: _cachedValueSources.length },
|
||||
{ key: 'sync', icon: ICON_CLOCK, titleKey: 'streams.group.sync', count: _cachedSyncClocks.length },
|
||||
@@ -1387,6 +1399,10 @@ function renderPictureSourcesList(streams) {
|
||||
{ key: 'processed', titleKey: 'streams.group.processed', icon: getPictureSourceIcon('processed'), count: processedStreams.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 + _cachedAudioTemplates.length,
|
||||
@@ -1496,6 +1512,7 @@ function renderPictureSourcesList(streams) {
|
||||
const monoItems = csAudioMono.applySortOrder(monoSources.map(s => ({ key: s.id, html: renderAudioSourceCard(s) })));
|
||||
const audioTemplateItems = csAudioTemplates.applySortOrder(_cachedAudioTemplates.map(t => ({ key: t.id, html: renderAudioTemplateCard(t) })));
|
||||
const staticItems = csStaticStreams.applySortOrder(staticImageStreams.map(s => ({ key: s.id, html: renderStreamCard(s) })));
|
||||
const colorStripItems = csColorStrips.applySortOrder(colorStrips.map(s => ({ key: s.id, html: createColorStripCard(s, pictureSourceMap, audioSourceMap) })));
|
||||
const valueItems = csValueSources.applySortOrder(_cachedValueSources.map(s => ({ key: s.id, html: createValueSourceCard(s) })));
|
||||
const syncClockItems = csSyncClocks.applySortOrder(_cachedSyncClocks.map(s => ({ key: s.id, html: createSyncClockCard(s) })));
|
||||
|
||||
@@ -1505,6 +1522,7 @@ function renderPictureSourcesList(streams) {
|
||||
raw: rawStreams.length,
|
||||
static_image: staticImageStreams.length,
|
||||
processed: processedStreams.length,
|
||||
color_strip: colorStrips.length,
|
||||
audio: _cachedAudioSources.length + _cachedAudioTemplates.length,
|
||||
value: _cachedValueSources.length,
|
||||
sync: _cachedSyncClocks.length,
|
||||
@@ -1513,6 +1531,7 @@ function renderPictureSourcesList(streams) {
|
||||
csRawTemplates.reconcile(rawTemplateItems);
|
||||
csProcStreams.reconcile(procStreamItems);
|
||||
csProcTemplates.reconcile(procTemplateItems);
|
||||
csColorStrips.reconcile(colorStripItems);
|
||||
csAudioMulti.reconcile(multiItems);
|
||||
csAudioMono.reconcile(monoItems);
|
||||
csAudioTemplates.reconcile(audioTemplateItems);
|
||||
@@ -1525,6 +1544,7 @@ function renderPictureSourcesList(streams) {
|
||||
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);
|
||||
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);
|
||||
else if (tab.key === 'value') panelContent = csValueSources.render(valueItems);
|
||||
else if (tab.key === 'sync') panelContent = csSyncClocks.render(syncClockItems);
|
||||
@@ -1533,7 +1553,7 @@ function renderPictureSourcesList(streams) {
|
||||
}).join('');
|
||||
|
||||
container.innerHTML = panels;
|
||||
CardSection.bindAll([csRawStreams, csRawTemplates, csProcStreams, csProcTemplates, csAudioMulti, csAudioMono, csAudioTemplates, csStaticStreams, csValueSources, csSyncClocks]);
|
||||
CardSection.bindAll([csRawStreams, csRawTemplates, csProcStreams, csProcTemplates, csColorStrips, csAudioMulti, csAudioMono, csAudioTemplates, csStaticStreams, csValueSources, csSyncClocks]);
|
||||
|
||||
// Render tree sidebar with expand/collapse buttons
|
||||
_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>`);
|
||||
@@ -1542,6 +1562,7 @@ function renderPictureSourcesList(streams) {
|
||||
'raw-streams': 'raw', 'raw-templates': 'raw',
|
||||
'static-streams': 'static_image',
|
||||
'proc-streams': 'processed', 'proc-templates': 'processed',
|
||||
'color-strips': 'color_strip',
|
||||
'audio-multi': 'audio', 'audio-mono': 'audio', 'audio-templates': 'audio',
|
||||
'value-sources': 'value',
|
||||
'sync-clocks': 'sync',
|
||||
|
||||
@@ -19,7 +19,6 @@ import { Modal } from '../core/modal.js';
|
||||
import { createDeviceCard, attachDeviceListeners, fetchDeviceBrightness, enrichOpenrgbZoneBadges, _computeMaxFps, getZoneCountCache } from './devices.js';
|
||||
import { _splitOpenrgbZone } from './device-discovery.js';
|
||||
import { createKCTargetCard, patchKCTargetMetrics, connectKCWebSocket, disconnectKCWebSocket } from './kc-targets.js';
|
||||
import { createColorStripCard } from './color-strips.js';
|
||||
import {
|
||||
getValueSourceIcon, getTargetTypeIcon, getDeviceTypeIcon, getColorStripIcon,
|
||||
ICON_CLONE, ICON_EDIT, ICON_START, ICON_STOP,
|
||||
@@ -41,7 +40,6 @@ import { updateSubTabHash, updateTabBadge } from './tabs.js';
|
||||
|
||||
// ── Card section instances ──
|
||||
const csDevices = new CardSection('led-devices', { titleKey: 'targets.section.devices', gridClass: 'devices-grid', addCardOnclick: "showAddDevice()", keyAttr: 'data-device-id' });
|
||||
const csColorStrips = new CardSection('led-css', { titleKey: 'targets.section.color_strips', gridClass: 'devices-grid', addCardOnclick: "showCSSEditor()", keyAttr: 'data-css-id' });
|
||||
const csLedTargets = new CardSection('led-targets', { titleKey: 'targets.section.targets', gridClass: 'devices-grid', addCardOnclick: "showTargetEditor()", keyAttr: 'data-target-id', headerExtra: `<button class="btn btn-sm btn-danger" onclick="event.stopPropagation(); stopAllLedTargets()" data-stop-all="led" data-i18n-title="targets.stop_all.button" data-i18n-aria-label="targets.stop_all.button">${ICON_STOP}</button>` });
|
||||
const csKCTargets = new CardSection('kc-targets', { titleKey: 'targets.section.key_colors', gridClass: 'devices-grid', addCardOnclick: "showKCEditor()", keyAttr: 'data-kc-target-id', headerExtra: `<button class="btn btn-sm btn-danger" onclick="event.stopPropagation(); stopAllKCTargets()" data-stop-all="kc" data-i18n-title="targets.stop_all.button" data-i18n-aria-label="targets.stop_all.button">${ICON_STOP}</button>` });
|
||||
const csPatternTemplates = new CardSection('kc-patterns', { titleKey: 'targets.section.pattern_templates', gridClass: 'templates-grid', addCardOnclick: "showPatternTemplateEditor()", keyAttr: 'data-pattern-template-id' });
|
||||
@@ -569,7 +567,7 @@ export function expandAllTargetSections() {
|
||||
const activeSubTab = localStorage.getItem('activeTargetSubTab') || 'led';
|
||||
const sections = activeSubTab === 'key_colors'
|
||||
? [csKCTargets, csPatternTemplates]
|
||||
: [csDevices, csColorStrips, csLedTargets];
|
||||
: [csDevices, csLedTargets];
|
||||
CardSection.expandAll(sections);
|
||||
}
|
||||
|
||||
@@ -577,7 +575,7 @@ export function collapseAllTargetSections() {
|
||||
const activeSubTab = localStorage.getItem('activeTargetSubTab') || 'led';
|
||||
const sections = activeSubTab === 'key_colors'
|
||||
? [csKCTargets, csPatternTemplates]
|
||||
: [csDevices, csColorStrips, csLedTargets];
|
||||
: [csDevices, csLedTargets];
|
||||
CardSection.collapseAll(sections);
|
||||
}
|
||||
|
||||
@@ -605,7 +603,7 @@ export async function loadTargetsTab() {
|
||||
syncClocksCache.fetch().catch(() => []),
|
||||
]);
|
||||
|
||||
let colorStripSourceMap = {};
|
||||
const colorStripSourceMap = {};
|
||||
cssArr.forEach(s => { colorStripSourceMap[s.id] = s; });
|
||||
|
||||
let pictureSourceMap = {};
|
||||
@@ -669,7 +667,6 @@ export async function loadTargetsTab() {
|
||||
key: 'led_group', icon: getTargetTypeIcon('led'), titleKey: 'targets.subtab.led',
|
||||
children: [
|
||||
{ key: 'led-devices', titleKey: 'targets.section.devices', icon: getDeviceTypeIcon('wled'), count: ledDevices.length, subTab: 'led', sectionKey: 'led-devices' },
|
||||
{ key: 'led-css', titleKey: 'targets.section.color_strips', icon: getColorStripIcon('static'), count: Object.keys(colorStripSourceMap).length, subTab: 'led', sectionKey: 'led-css' },
|
||||
{ key: 'led-targets', titleKey: 'targets.section.targets', icon: getTargetTypeIcon('led'), count: ledTargets.length, subTab: 'led', sectionKey: 'led-targets' },
|
||||
]
|
||||
},
|
||||
@@ -689,7 +686,6 @@ export async function loadTargetsTab() {
|
||||
|
||||
// Build items arrays for each section (apply saved drag order)
|
||||
const deviceItems = csDevices.applySortOrder(ledDevices.map(d => ({ key: d.id, html: createDeviceCard(d) })));
|
||||
const cssItems = csColorStrips.applySortOrder(Object.values(colorStripSourceMap).map(s => ({ key: s.id, html: createColorStripCard(s, pictureSourceMap, audioSourceMap) })));
|
||||
const ledTargetItems = csLedTargets.applySortOrder(ledTargets.map(t => ({ key: t.id, html: createTargetCard(t, deviceMap, colorStripSourceMap, valueSourceMap) })));
|
||||
const kcTargetItems = csKCTargets.applySortOrder(kcTargets.map(t => ({ key: t.id, html: createKCTargetCard(t, pictureSourceMap, patternTemplateMap, valueSourceMap) })));
|
||||
const patternItems = csPatternTemplates.applySortOrder(patternTemplates.map(pt => ({ key: pt.id, html: createPatternTemplateCard(pt) })));
|
||||
@@ -701,13 +697,11 @@ export async function loadTargetsTab() {
|
||||
// ── Incremental update: reconcile cards in-place ──
|
||||
_targetsTree.updateCounts({
|
||||
'led-devices': ledDevices.length,
|
||||
'led-css': Object.keys(colorStripSourceMap).length,
|
||||
'led-targets': ledTargets.length,
|
||||
'kc-targets': kcTargets.length,
|
||||
'kc-patterns': patternTemplates.length,
|
||||
});
|
||||
csDevices.reconcile(deviceItems);
|
||||
csColorStrips.reconcile(cssItems);
|
||||
const ledResult = csLedTargets.reconcile(ledTargetItems);
|
||||
const kcResult = csKCTargets.reconcile(kcTargetItems);
|
||||
csPatternTemplates.reconcile(patternItems);
|
||||
@@ -727,7 +721,6 @@ export async function loadTargetsTab() {
|
||||
const ledPanel = `
|
||||
<div class="target-sub-tab-panel stream-tab-panel${activeSubTab === 'led' ? ' active' : ''}" id="target-sub-tab-led">
|
||||
${csDevices.render(deviceItems)}
|
||||
${csColorStrips.render(cssItems)}
|
||||
${csLedTargets.render(ledTargetItems)}
|
||||
</div>`;
|
||||
const kcPanel = `
|
||||
@@ -736,7 +729,7 @@ export async function loadTargetsTab() {
|
||||
${csPatternTemplates.render(patternItems)}
|
||||
</div>`;
|
||||
container.innerHTML = ledPanel + kcPanel;
|
||||
CardSection.bindAll([csDevices, csColorStrips, csLedTargets, csKCTargets, csPatternTemplates]);
|
||||
CardSection.bindAll([csDevices, csLedTargets, csKCTargets, csPatternTemplates]);
|
||||
|
||||
// Render tree sidebar with expand/collapse buttons
|
||||
_targetsTree.setExtraHtml(`<button class="btn-expand-collapse" onclick="expandAllTargetSections()" data-i18n-title="section.expand_all" title="${t('section.expand_all')}">⊞</button><button class="btn-expand-collapse" onclick="collapseAllTargetSections()" data-i18n-title="section.collapse_all" title="${t('section.collapse_all')}">⊟</button><button class="tutorial-trigger-btn" onclick="startTargetsTutorial()" data-i18n-title="tour.restart" title="${t('tour.restart')}">${ICON_HELP}</button>`);
|
||||
@@ -1011,7 +1004,7 @@ export function createTargetCard(target, deviceMap, colorStripSourceMap, valueSo
|
||||
<span class="stream-card-prop stream-card-link" title="${t('targets.device')}" onclick="event.stopPropagation(); navigateToCard('targets','led','led-devices','data-device-id','${target.device_id}')">${ICON_LED} ${escapeHtml(deviceName)}</span>
|
||||
<span class="stream-card-prop" title="${t('targets.fps')}">${ICON_FPS} ${target.fps || 30}</span>
|
||||
<span class="stream-card-prop" title="${t('targets.protocol')}">${_protocolBadge(device, target)}</span>
|
||||
<span class="stream-card-prop${cssId ? ' stream-card-link' : ''}" title="${t('targets.color_strip_source')}"${cssId ? ` onclick="event.stopPropagation(); navigateToCard('targets','led','led-css','data-css-id','${cssId}')"` : ''}>${ICON_FILM} ${cssSummary}</span>
|
||||
<span class="stream-card-prop${cssId ? ' stream-card-link' : ''}" title="${t('targets.color_strip_source')}"${cssId ? ` onclick="event.stopPropagation(); navigateToCard('streams','color_strip','color-strips','data-css-id','${cssId}')"` : ''}>${ICON_FILM} ${cssSummary}</span>
|
||||
${bvs ? `<span class="stream-card-prop stream-card-prop-full stream-card-link" title="${t('targets.brightness_vs')}" onclick="event.stopPropagation(); navigateToCard('streams','value','value-sources','data-id','${bvsId}')">${getValueSourceIcon(bvs.source_type)} ${escapeHtml(bvs.name)}</span>` : ''}
|
||||
${target.min_brightness_threshold > 0 ? `<span class="stream-card-prop" title="${t('targets.min_brightness_threshold')}">${ICON_SUN_DIM} <${target.min_brightness_threshold} → off</span>` : ''}
|
||||
</div>
|
||||
|
||||
@@ -46,7 +46,6 @@ const dashboardTutorialSteps = [
|
||||
const targetsTutorialSteps = [
|
||||
{ selector: '[data-tree-group="led_group"]', textKey: 'tour.tgt.led_tab', position: 'right' },
|
||||
{ selector: '[data-card-section="led-devices"]', textKey: 'tour.tgt.devices', position: 'bottom' },
|
||||
{ selector: '[data-card-section="led-css"]', textKey: 'tour.tgt.css', position: 'bottom' },
|
||||
{ selector: '[data-card-section="led-targets"]', textKey: 'tour.tgt.targets', position: 'bottom' },
|
||||
{ selector: '[data-tree-group="kc_group"]', textKey: 'tour.tgt.kc_tab', position: 'right' }
|
||||
];
|
||||
@@ -56,6 +55,7 @@ const sourcesTourSteps = [
|
||||
{ selector: '[data-card-section="raw-templates"]', textKey: 'tour.src.templates', position: 'bottom' },
|
||||
{ selector: '#streams-tree-nav [data-tree-leaf="static_image"]', textKey: 'tour.src.static', position: 'right' },
|
||||
{ selector: '#streams-tree-nav [data-tree-leaf="processed"]', textKey: 'tour.src.processed', position: 'right' },
|
||||
{ selector: '#streams-tree-nav [data-tree-leaf="color_strip"]', textKey: 'tour.src.color_strip', position: 'right' },
|
||||
{ selector: '#streams-tree-nav [data-tree-leaf="audio"]', textKey: 'tour.src.audio', position: 'right' },
|
||||
{ selector: '#streams-tree-nav [data-tree-leaf="value"]', textKey: 'tour.src.value', position: 'right' },
|
||||
{ selector: '#streams-tree-nav [data-tree-leaf="sync"]', textKey: 'tour.src.sync', position: 'right' }
|
||||
|
||||
@@ -355,6 +355,7 @@
|
||||
"tour.src.templates": "Capture Templates — reusable capture configurations (resolution, FPS, crop).",
|
||||
"tour.src.static": "Static Image — test your setup with image files instead of live capture.",
|
||||
"tour.src.processed": "Processed — apply post-processing effects like blur, brightness, or color correction.",
|
||||
"tour.src.color_strip": "Color Strips — define how screen regions map to LED segments.",
|
||||
"tour.src.audio": "Audio — analyze microphone or system audio for reactive LED effects.",
|
||||
"tour.src.value": "Value — numeric data sources used as conditions in automations.",
|
||||
"tour.src.sync": "Sync Clocks — shared timers that synchronize animations across multiple sources.",
|
||||
@@ -416,6 +417,7 @@
|
||||
"streams.description": "Sources define the capture pipeline. A raw source captures from a display using a capture template. A processed source applies postprocessing to another source. Assign sources to devices.",
|
||||
"streams.group.raw": "Screen Capture",
|
||||
"streams.group.processed": "Processed",
|
||||
"streams.group.color_strip": "Color Strips",
|
||||
"streams.group.audio": "Audio",
|
||||
"streams.section.streams": "Sources",
|
||||
"streams.add": "Add Source",
|
||||
|
||||
@@ -304,6 +304,7 @@
|
||||
"tour.src.templates": "Шаблоны захвата — переиспользуемые конфигурации (разрешение, FPS, обрезка).",
|
||||
"tour.src.static": "Статичные изображения — тестируйте настройку с файлами изображений.",
|
||||
"tour.src.processed": "Обработка — применяйте эффекты: размытие, яркость, цветокоррекция.",
|
||||
"tour.src.color_strip": "Цветовые полосы — определяют, как области экрана сопоставляются с LED-сегментами.",
|
||||
"tour.src.audio": "Аудио — анализ микрофона или системного звука для реактивных LED-эффектов.",
|
||||
"tour.src.value": "Значения — числовые источники данных для условий автоматизаций.",
|
||||
"tour.src.sync": "Синхро-часы — общие таймеры для синхронизации анимаций между несколькими источниками.",
|
||||
@@ -365,6 +366,7 @@
|
||||
"streams.description": "Источники определяют конвейер захвата. Сырой источник захватывает экран с помощью шаблона захвата. Обработанный источник применяет постобработку к другому источнику. Назначайте источники устройствам.",
|
||||
"streams.group.raw": "Захват Экрана",
|
||||
"streams.group.processed": "Обработанные",
|
||||
"streams.group.color_strip": "Цветовые Полосы",
|
||||
"streams.group.audio": "Аудио",
|
||||
"streams.section.streams": "Источники",
|
||||
"streams.add": "Добавить Источник",
|
||||
|
||||
@@ -304,6 +304,7 @@
|
||||
"tour.src.templates": "捕获模板 — 可复用的捕获配置(分辨率、FPS、裁剪)。",
|
||||
"tour.src.static": "静态图片 — 使用图片文件测试您的设置。",
|
||||
"tour.src.processed": "处理 — 应用后处理效果,如模糊、亮度或色彩校正。",
|
||||
"tour.src.color_strip": "色带 — 定义屏幕区域如何映射到 LED 段。",
|
||||
"tour.src.audio": "音频 — 分析麦克风或系统音频以实现响应式 LED 效果。",
|
||||
"tour.src.value": "数值 — 用于自动化条件的数字数据源。",
|
||||
"tour.src.sync": "同步时钟 — 在多个源之间同步动画的共享定时器。",
|
||||
@@ -365,6 +366,7 @@
|
||||
"streams.description": "源定义采集管线。原始源使用采集模板从显示器采集。处理源对另一个源应用后处理。将源分配给设备。",
|
||||
"streams.group.raw": "屏幕采集",
|
||||
"streams.group.processed": "已处理",
|
||||
"streams.group.color_strip": "色带源",
|
||||
"streams.group.audio": "音频",
|
||||
"streams.section.streams": "源",
|
||||
"streams.add": "添加源",
|
||||
|
||||
Reference in New Issue
Block a user