diff --git a/server/src/wled_controller/static/css/base.css b/server/src/wled_controller/static/css/base.css index cc4bf32..7b5a545 100644 --- a/server/src/wled_controller/static/css/base.css +++ b/server/src/wled_controller/static/css/base.css @@ -49,7 +49,7 @@ --z-log-overlay: 2100; --z-confirm: 2500; --z-command-palette: 3000; - --z-toast: 3000; + --z-toast: 3500; --z-overlay-spinner: 9999; --z-lightbox: 10000; --z-connection: 10000; diff --git a/server/src/wled_controller/static/js/core/command-palette.ts b/server/src/wled_controller/static/js/core/command-palette.ts index a595b06..fa803af 100644 --- a/server/src/wled_controller/static/js/core/command-palette.ts +++ b/server/src/wled_controller/static/js/core/command-palette.ts @@ -57,26 +57,31 @@ function _buildItems(results: any[], states: any = {}) { nav: ['targets', 'led-targets', 'led-targets', 'data-target-id', tgt.id], running, }); } - // Action items: start or stop - if (running) { - items.push({ - name: tgt.name, detail: t('search.action.stop'), group: 'actions', icon: '■', - action: async () => { - const resp = await fetchWithAuth(`/output-targets/${tgt.id}/stop`, { method: 'POST' }); - if (resp.ok) { showToast(t('device.stopped'), 'success'); } - else { const err = await resp.json().catch(() => ({})); const d = err.detail || err.message || ''; const ds = Array.isArray(d) ? d.map(x => x.msg || x).join('; ') : String(d); showToast(ds || t('target.error.stop_failed'), 'error'); } - }, - }); - } else { - items.push({ - name: tgt.name, detail: t('search.action.start'), group: 'actions', icon: '▶', - action: async () => { - const resp = await fetchWithAuth(`/output-targets/${tgt.id}/start`, { method: 'POST' }); - if (resp.ok) { showToast(t('device.started'), 'success'); } - else { const err = await resp.json().catch(() => ({})); const d = err.detail || err.message || ''; const ds = Array.isArray(d) ? d.map(x => x.msg || x).join('; ') : String(d); showToast(ds || t('target.error.start_failed'), 'error'); } - }, - }); - } + // Action item: toggle start/stop + const actionItem: any = { + name: tgt.name, group: 'actions', + detail: running ? t('search.action.stop') : t('search.action.start'), + icon: running ? '■' : '▶', + _running: running, _targetId: tgt.id, + action: async () => { + const isRunning = actionItem._running; + const endpoint = isRunning ? 'stop' : 'start'; + const resp = await fetchWithAuth(`/output-targets/${tgt.id}/${endpoint}`, { method: 'POST' }); + if (resp.ok) { + showToast(t(isRunning ? 'device.stopped' : 'device.started'), 'success'); + actionItem._running = !isRunning; + actionItem.detail = !isRunning ? t('search.action.stop') : t('search.action.start'); + actionItem.icon = !isRunning ? '■' : '▶'; + _render(); + } else { + const err = await resp.json().catch(() => ({})); + const d = err.detail || err.message || ''; + const ds = Array.isArray(d) ? d.map(x => x.msg || x).join('; ') : String(d); + showToast(ds || t(`target.error.${endpoint}_failed`), 'error'); + } + }, + }; + items.push(actionItem); }); _mapEntities(css, c => items.push({ @@ -89,25 +94,28 @@ function _buildItems(results: any[], states: any = {}) { name: a.name, detail: a.enabled ? 'enabled' : '', group: 'automations', icon: ICON_AUTOMATION, nav: ['automations', null, 'automations', 'data-automation-id', a.id], }); - if (a.enabled) { - items.push({ - name: a.name, detail: t('search.action.disable'), group: 'actions', icon: ICON_AUTOMATION, - action: async () => { - const resp = await fetchWithAuth(`/automations/${a.id}/disable`, { method: 'POST' }); - if (resp.ok) { showToast(t('search.action.disable') + ': ' + a.name, 'success'); } - else { const err = await resp.json().catch(() => ({})); const d = err.detail || err.message || ''; const ds = Array.isArray(d) ? d.map(x => x.msg || x).join('; ') : String(d); showToast(ds || (t('search.action.disable') + ' failed'), 'error'); } - }, - }); - } else { - items.push({ - name: a.name, detail: t('search.action.enable'), group: 'actions', icon: ICON_AUTOMATION, - action: async () => { - const resp = await fetchWithAuth(`/automations/${a.id}/enable`, { method: 'POST' }); - if (resp.ok) { showToast(t('search.action.enable') + ': ' + a.name, 'success'); } - else { const err = await resp.json().catch(() => ({})); const d = err.detail || err.message || ''; const ds = Array.isArray(d) ? d.map(x => x.msg || x).join('; ') : String(d); showToast(ds || (t('search.action.enable') + ' failed'), 'error'); } - }, - }); - } + const autoItem: any = { + name: a.name, group: 'actions', icon: ICON_AUTOMATION, + detail: a.enabled ? t('search.action.disable') : t('search.action.enable'), + _enabled: a.enabled, + action: async () => { + const isEnabled = autoItem._enabled; + const endpoint = isEnabled ? 'disable' : 'enable'; + const resp = await fetchWithAuth(`/automations/${a.id}/${endpoint}`, { method: 'POST' }); + if (resp.ok) { + showToast(t('search.action.' + endpoint) + ': ' + a.name, 'success'); + autoItem._enabled = !isEnabled; + autoItem.detail = !isEnabled ? t('search.action.disable') : t('search.action.enable'); + _render(); + } else { + const err = await resp.json().catch(() => ({})); + const d = err.detail || err.message || ''; + const ds = Array.isArray(d) ? d.map(x => x.msg || x).join('; ') : String(d); + showToast(ds || (t('search.action.' + endpoint) + ' failed'), 'error'); + } + }, + }; + items.push(autoItem); }); _mapEntities(capTempl, ct => items.push({ @@ -378,13 +386,13 @@ function _onClick(e: Event) { function _selectCurrent() { if (_selectedIdx < 0 || _selectedIdx >= _filtered.length) return; const item = _filtered[_selectedIdx]; - closeCommandPalette(); if (item.action) { item.action().catch(err => { if (!err.isAuth) showToast(err.message || 'Action failed', 'error'); }); return; } + closeCommandPalette(); // If graph tab is active, navigate to graph node instead of card const graphTabActive = document.querySelector('.tab-btn[data-tab="graph"].active'); if (graphTabActive) { diff --git a/server/src/wled_controller/static/js/features/automations.ts b/server/src/wled_controller/static/js/features/automations.ts index 8c8389b..a5bf828 100644 --- a/server/src/wled_controller/static/js/features/automations.ts +++ b/server/src/wled_controller/static/js/features/automations.ts @@ -610,7 +610,7 @@ function addAutomationConditionRow(condition: any) { - +
`;