Add crosslink navigation for picture source and audio source on CSS cards
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -575,7 +575,7 @@ function _resetAudioState() {
|
|||||||
|
|
||||||
/* ── Card ─────────────────────────────────────────────────────── */
|
/* ── Card ─────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
export function createColorStripCard(source, pictureSourceMap) {
|
export function createColorStripCard(source, pictureSourceMap, audioSourceMap) {
|
||||||
const isStatic = source.source_type === 'static';
|
const isStatic = source.source_type === 'static';
|
||||||
const isGradient = source.source_type === 'gradient';
|
const isGradient = source.source_type === 'gradient';
|
||||||
const isColorCycle = source.source_type === 'color_cycle';
|
const isColorCycle = source.source_type === 'color_cycle';
|
||||||
@@ -665,7 +665,12 @@ export function createColorStripCard(source, pictureSourceMap) {
|
|||||||
<span class="stream-card-prop">🎵 ${escapeHtml(vizLabel)}</span>
|
<span class="stream-card-prop">🎵 ${escapeHtml(vizLabel)}</span>
|
||||||
${audioPaletteLabel ? `<span class="stream-card-prop" title="${t('color_strip.audio.palette')}">🎨 ${escapeHtml(audioPaletteLabel)}</span>` : ''}
|
${audioPaletteLabel ? `<span class="stream-card-prop" title="${t('color_strip.audio.palette')}">🎨 ${escapeHtml(audioPaletteLabel)}</span>` : ''}
|
||||||
<span class="stream-card-prop" title="${t('color_strip.audio.sensitivity')}">📶 ${sensitivityVal}</span>
|
<span class="stream-card-prop" title="${t('color_strip.audio.sensitivity')}">📶 ${sensitivityVal}</span>
|
||||||
${source.audio_source_id ? `<span class="stream-card-prop" title="${t('color_strip.audio.source')}">🔊</span>` : ''}
|
${source.audio_source_id ? (() => {
|
||||||
|
const as = audioSourceMap && audioSourceMap[source.audio_source_id];
|
||||||
|
const asName = as ? as.name : source.audio_source_id;
|
||||||
|
const asSection = as && as.source_type === 'mono' ? 'audio-mono' : 'audio-multi';
|
||||||
|
return `<span class="stream-card-prop${as ? ' stream-card-link' : ''}" title="${t('color_strip.audio.source')}"${as ? ` onclick="event.stopPropagation(); navigateToCard('streams','audio','${asSection}','data-id','${source.audio_source_id}')"` : ''}>🔊 ${escapeHtml(asName)}</span>`;
|
||||||
|
})() : ''}
|
||||||
${source.mirror ? `<span class="stream-card-prop">🪞</span>` : ''}
|
${source.mirror ? `<span class="stream-card-prop">🪞</span>` : ''}
|
||||||
`;
|
`;
|
||||||
} else if (isApiInput) {
|
} else if (isApiInput) {
|
||||||
@@ -678,14 +683,18 @@ export function createColorStripCard(source, pictureSourceMap) {
|
|||||||
<span class="stream-card-prop" title="${t('color_strip.api_input.timeout')}">⏱️ ${timeoutVal}s</span>
|
<span class="stream-card-prop" title="${t('color_strip.api_input.timeout')}">⏱️ ${timeoutVal}s</span>
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
const srcName = (pictureSourceMap && pictureSourceMap[source.picture_source_id])
|
const ps = pictureSourceMap && pictureSourceMap[source.picture_source_id];
|
||||||
? pictureSourceMap[source.picture_source_id].name
|
const srcName = ps ? ps.name : source.picture_source_id || '—';
|
||||||
: source.picture_source_id || '—';
|
|
||||||
const cal = source.calibration || {};
|
const cal = source.calibration || {};
|
||||||
const calLeds = (cal.leds_top || 0) + (cal.leds_right || 0) + (cal.leds_bottom || 0) + (cal.leds_left || 0);
|
const calLeds = (cal.leds_top || 0) + (cal.leds_right || 0) + (cal.leds_bottom || 0) + (cal.leds_left || 0);
|
||||||
const ledCount = (source.led_count > 0) ? source.led_count : calLeds;
|
const ledCount = (source.led_count > 0) ? source.led_count : calLeds;
|
||||||
|
let psSubTab = 'raw', psSection = 'raw-streams';
|
||||||
|
if (ps) {
|
||||||
|
if (ps.stream_type === 'static_image') { psSubTab = 'static_image'; psSection = 'static-streams'; }
|
||||||
|
else if (ps.stream_type === 'processed') { psSubTab = 'processed'; psSection = 'proc-streams'; }
|
||||||
|
}
|
||||||
propsHtml = `
|
propsHtml = `
|
||||||
<span class="stream-card-prop stream-card-prop-full" title="${t('color_strip.picture_source')}">📺 ${escapeHtml(srcName)}</span>
|
<span class="stream-card-prop stream-card-prop-full${ps ? ' stream-card-link' : ''}" title="${t('color_strip.picture_source')}"${ps ? ` onclick="event.stopPropagation(); navigateToCard('streams','${psSubTab}','${psSection}','data-stream-id','${source.picture_source_id}')"` : ''}>📺 ${escapeHtml(srcName)}</span>
|
||||||
${ledCount ? `<span class="stream-card-prop" title="${t('color_strip.leds')}">💡 ${ledCount}</span>` : ''}
|
${ledCount ? `<span class="stream-card-prop" title="${t('color_strip.leds')}">💡 ${ledCount}</span>` : ''}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -430,13 +430,14 @@ export async function loadTargetsTab() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch devices, targets, CSS sources, picture sources, pattern templates, and value sources in parallel
|
// Fetch devices, targets, CSS sources, picture sources, pattern templates, and value sources in parallel
|
||||||
const [devicesResp, targetsResp, cssResp, psResp, patResp, vsResp] = await Promise.all([
|
const [devicesResp, targetsResp, cssResp, psResp, patResp, vsResp, asResp] = await Promise.all([
|
||||||
fetchWithAuth('/devices'),
|
fetchWithAuth('/devices'),
|
||||||
fetchWithAuth('/picture-targets'),
|
fetchWithAuth('/picture-targets'),
|
||||||
fetchWithAuth('/color-strip-sources').catch(() => null),
|
fetchWithAuth('/color-strip-sources').catch(() => null),
|
||||||
fetchWithAuth('/picture-sources').catch(() => null),
|
fetchWithAuth('/picture-sources').catch(() => null),
|
||||||
fetchWithAuth('/pattern-templates').catch(() => null),
|
fetchWithAuth('/pattern-templates').catch(() => null),
|
||||||
fetchWithAuth('/value-sources').catch(() => null),
|
fetchWithAuth('/value-sources').catch(() => null),
|
||||||
|
fetchWithAuth('/audio-sources').catch(() => null),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const devicesData = await devicesResp.json();
|
const devicesData = await devicesResp.json();
|
||||||
@@ -471,6 +472,12 @@ export async function loadTargetsTab() {
|
|||||||
(vsData.sources || []).forEach(s => { valueSourceMap[s.id] = s; });
|
(vsData.sources || []).forEach(s => { valueSourceMap[s.id] = s; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let audioSourceMap = {};
|
||||||
|
if (asResp && asResp.ok) {
|
||||||
|
const asData = await asResp.json();
|
||||||
|
(asData.sources || []).forEach(s => { audioSourceMap[s.id] = s; });
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch all device states, target states, and target metrics in batch
|
// Fetch all device states, target states, and target metrics in batch
|
||||||
const [batchDevStatesResp, batchTgtStatesResp, batchTgtMetricsResp] = await Promise.all([
|
const [batchDevStatesResp, batchTgtStatesResp, batchTgtMetricsResp] = await Promise.all([
|
||||||
fetchWithAuth('/devices/batch/states'),
|
fetchWithAuth('/devices/batch/states'),
|
||||||
@@ -530,7 +537,7 @@ export async function loadTargetsTab() {
|
|||||||
|
|
||||||
// Build items arrays for each section
|
// Build items arrays for each section
|
||||||
const deviceItems = ledDevices.map(d => ({ key: d.id, html: createDeviceCard(d) }));
|
const deviceItems = ledDevices.map(d => ({ key: d.id, html: createDeviceCard(d) }));
|
||||||
const cssItems = Object.values(colorStripSourceMap).map(s => ({ key: s.id, html: createColorStripCard(s, pictureSourceMap) }));
|
const cssItems = Object.values(colorStripSourceMap).map(s => ({ key: s.id, html: createColorStripCard(s, pictureSourceMap, audioSourceMap) }));
|
||||||
const ledTargetItems = ledTargets.map(t => ({ key: t.id, html: createTargetCard(t, deviceMap, colorStripSourceMap, valueSourceMap) }));
|
const ledTargetItems = ledTargets.map(t => ({ key: t.id, html: createTargetCard(t, deviceMap, colorStripSourceMap, valueSourceMap) }));
|
||||||
const kcTargetItems = kcTargets.map(t => ({ key: t.id, html: createKCTargetCard(t, pictureSourceMap, patternTemplateMap, valueSourceMap) }));
|
const kcTargetItems = kcTargets.map(t => ({ key: t.id, html: createKCTargetCard(t, pictureSourceMap, patternTemplateMap, valueSourceMap) }));
|
||||||
const patternItems = patternTemplates.map(pt => ({ key: pt.id, html: createPatternTemplateCard(pt) }));
|
const patternItems = patternTemplates.map(pt => ({ key: pt.id, html: createPatternTemplateCard(pt) }));
|
||||||
|
|||||||
Reference in New Issue
Block a user