Add running target indicator to command palette

Fetch /picture-targets/batch/states alongside entity data and show a
small green glowing dot next to targets that are currently processing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 23:49:26 +03:00
parent 32a54b7d3c
commit cb779e10d3
2 changed files with 23 additions and 8 deletions

View File

@@ -489,6 +489,15 @@ h2 {
color: var(--text-secondary); color: var(--text-secondary);
} }
.cp-running {
width: 6px;
height: 6px;
border-radius: 50%;
background: #4caf50;
flex-shrink: 0;
box-shadow: 0 0 4px #4caf50;
}
.cp-loading, .cp-loading,
.cp-empty { .cp-empty {
padding: 24px 16px; padding: 24px 16px;

View File

@@ -31,7 +31,7 @@ function _mapEntities(data, mapFn) {
return []; return [];
} }
function _buildItems(results) { function _buildItems(results, states = {}) {
const [devices, targets, css, automations, capTempl, ppTempl, patTempl, audioSrc, valSrc, streams, scenePresets] = results; const [devices, targets, css, automations, capTempl, ppTempl, patTempl, audioSrc, valSrc, streams, scenePresets] = results;
const items = []; const items = [];
@@ -41,15 +41,16 @@ function _buildItems(results) {
})); }));
_mapEntities(targets, tgt => { _mapEntities(targets, tgt => {
const running = !!states[tgt.id]?.processing;
if (tgt.target_type === 'key_colors') { if (tgt.target_type === 'key_colors') {
items.push({ items.push({
name: tgt.name, detail: 'key_colors', group: 'kc_targets', icon: getTargetTypeIcon('key_colors'), name: tgt.name, detail: 'key_colors', group: 'kc_targets', icon: getTargetTypeIcon('key_colors'),
nav: ['targets', 'key_colors', 'kc-targets', 'data-kc-target-id', tgt.id], nav: ['targets', 'key_colors', 'kc-targets', 'data-kc-target-id', tgt.id], running,
}); });
} else { } else {
items.push({ items.push({
name: tgt.name, detail: tgt.target_type, group: 'targets', icon: ICON_TARGET, name: tgt.name, detail: tgt.target_type, group: 'targets', icon: ICON_TARGET,
nav: ['targets', 'led', 'led-targets', 'data-target-id', tgt.id], nav: ['targets', 'led', 'led-targets', 'data-target-id', tgt.id], running,
}); });
} }
}); });
@@ -124,14 +125,18 @@ const _responseKeys = [
]; ];
async function _fetchAllEntities() { async function _fetchAllEntities() {
const results = await Promise.all( const [statesData, ...results] = await Promise.all([
_responseKeys.map(([ep, key]) => fetchWithAuth('/picture-targets/batch/states', { retry: false, timeout: 5000 })
.then(r => r.ok ? r.json() : {})
.then(data => data.states || {})
.catch(() => ({})),
..._responseKeys.map(([ep, key]) =>
fetchWithAuth(ep, { retry: false, timeout: 5000 }) fetchWithAuth(ep, { retry: false, timeout: 5000 })
.then(r => r.ok ? r.json() : {}) .then(r => r.ok ? r.json() : {})
.then(data => data[key] || []) .then(data => data[key] || [])
.catch(() => [])) .catch(() => [])),
); ]);
return _buildItems(results); return _buildItems(results, statesData);
} }
// ─── Group ordering ─── // ─── Group ordering ───
@@ -197,6 +202,7 @@ function _render() {
html += `<div class="cp-result${active}" data-cp-idx="${idx}"${colorStyle}>` + html += `<div class="cp-result${active}" data-cp-idx="${idx}"${colorStyle}>` +
`<span class="cp-icon">${item.icon}</span>` + `<span class="cp-icon">${item.icon}</span>` +
`<span class="cp-name">${_escHtml(item.name)}</span>` + `<span class="cp-name">${_escHtml(item.name)}</span>` +
(item.running ? '<span class="cp-running"></span>' : '') +
(item.detail ? `<span class="cp-detail">${_escHtml(item.detail)}</span>` : '') + (item.detail ? `<span class="cp-detail">${_escHtml(item.detail)}</span>` : '') +
`</div>`; `</div>`;
idx++; idx++;