Move Scenes into Automations tab, smaller Capture button, scene crosslinks
- Merge Scenes tab into Automations tab as a second CardSection below automations - Make dashboard Capture button match Stop All sizing - Dashboard scene cards navigate to automations tab on click (crosslink) - Add scene steps to automations tutorial - Fix tour.tgt.devices to say "LED controllers" instead of "WLED controllers" - Update command palette and navigation for new scene location Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,22 +1,19 @@
|
||||
/**
|
||||
* Scene Presets — capture, activate, edit, delete system state snapshots.
|
||||
* Renders as a dedicated tab and also provides dashboard section rendering.
|
||||
* Rendered as a CardSection inside the Automations tab, plus dashboard compact cards.
|
||||
*/
|
||||
|
||||
import { apiKey } from '../core/state.js';
|
||||
import { fetchWithAuth, escapeHtml } from '../core/api.js';
|
||||
import { t } from '../core/i18n.js';
|
||||
import { showToast, showConfirm } from '../core/ui.js';
|
||||
import { Modal } from '../core/modal.js';
|
||||
import { CardSection } from '../core/card-sections.js';
|
||||
import { updateTabBadge } from './tabs.js';
|
||||
import {
|
||||
ICON_SCENE, ICON_CAPTURE, ICON_START, ICON_EDIT, ICON_REFRESH, ICON_TARGET, ICON_SETTINGS,
|
||||
} from '../core/icons.js';
|
||||
|
||||
let _presetsCache = [];
|
||||
let _editingId = null;
|
||||
let _scenesLoading = false;
|
||||
|
||||
class ScenePresetEditorModal extends Modal {
|
||||
constructor() { super('scene-preset-editor-modal'); }
|
||||
@@ -30,59 +27,14 @@ class ScenePresetEditorModal extends Modal {
|
||||
}
|
||||
const scenePresetModal = new ScenePresetEditorModal();
|
||||
|
||||
const csScenes = new CardSection('scenes', {
|
||||
export const csScenes = new CardSection('scenes', {
|
||||
titleKey: 'scenes.title',
|
||||
gridClass: 'devices-grid',
|
||||
addCardOnclick: "openScenePresetCapture()",
|
||||
keyAttr: 'data-scene-id',
|
||||
});
|
||||
|
||||
// Re-render scenes when language changes (only if tab is active)
|
||||
document.addEventListener('languageChanged', () => {
|
||||
if (apiKey && (localStorage.getItem('activeTab') || 'dashboard') === 'scenes') loadScenes();
|
||||
});
|
||||
|
||||
// ===== Tab rendering =====
|
||||
|
||||
export async function loadScenes() {
|
||||
if (_scenesLoading) return;
|
||||
_scenesLoading = true;
|
||||
|
||||
try {
|
||||
const resp = await fetchWithAuth('/scene-presets');
|
||||
if (!resp.ok) { _scenesLoading = false; return; }
|
||||
const data = await resp.json();
|
||||
_presetsCache = data.presets || [];
|
||||
} catch {
|
||||
_scenesLoading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const container = document.getElementById('scenes-content');
|
||||
const items = csScenes.applySortOrder(_presetsCache.map(p => ({ key: p.id, html: _createSceneCard(p) })));
|
||||
|
||||
updateTabBadge('scenes', _presetsCache.length);
|
||||
|
||||
if (csScenes.isMounted()) {
|
||||
csScenes.reconcile(items);
|
||||
} else {
|
||||
const toolbar = `<div class="stream-tab-bar"><span class="cs-expand-collapse-group"><button class="btn-expand-collapse" onclick="expandAllSceneSections()" title="${t('section.expand_all')}">⊞</button><button class="btn-expand-collapse" onclick="collapseAllSceneSections()" title="${t('section.collapse_all')}">⊟</button></span></div>`;
|
||||
container.innerHTML = toolbar + csScenes.render(items);
|
||||
csScenes.bind();
|
||||
}
|
||||
|
||||
_scenesLoading = false;
|
||||
}
|
||||
|
||||
export function expandAllSceneSections() {
|
||||
CardSection.expandAll([csScenes]);
|
||||
}
|
||||
|
||||
export function collapseAllSceneSections() {
|
||||
CardSection.collapseAll([csScenes]);
|
||||
}
|
||||
|
||||
function _createSceneCard(preset) {
|
||||
export function createSceneCard(preset) {
|
||||
const targetCount = (preset.targets || []).length;
|
||||
const deviceCount = (preset.devices || []).length;
|
||||
const automationCount = (preset.automations || []).length;
|
||||
@@ -133,7 +85,7 @@ export async function loadScenePresets() {
|
||||
export function renderScenePresetsSection(presets) {
|
||||
if (!presets || presets.length === 0) return '';
|
||||
|
||||
const captureBtn = `<button class="btn btn-sm btn-primary" onclick="event.stopPropagation(); openScenePresetCapture()" title="${t('scenes.capture')}">${ICON_CAPTURE} ${t('scenes.capture')}</button>`;
|
||||
const captureBtn = `<button class="btn btn-sm btn-primary dashboard-stop-all" onclick="event.stopPropagation(); openScenePresetCapture()" title="${t('scenes.capture')}">${ICON_CAPTURE} ${t('scenes.capture')}</button>`;
|
||||
const cards = presets.map(p => _renderDashboardPresetCard(p)).join('');
|
||||
|
||||
return { headerExtra: captureBtn, content: `<div class="dashboard-autostart-grid">${cards}</div>` };
|
||||
@@ -151,8 +103,8 @@ function _renderDashboardPresetCard(preset) {
|
||||
automationCount > 0 ? `${automationCount} ${t('scenes.automations_count')}` : null,
|
||||
].filter(Boolean).join(' \u00b7 ');
|
||||
|
||||
return `<div class="dashboard-target dashboard-scene-preset" data-scene-id="${preset.id}" style="${borderStyle}">
|
||||
<div class="dashboard-target-info" onclick="activateScenePreset('${preset.id}')">
|
||||
return `<div class="dashboard-target dashboard-scene-preset dashboard-card-link" data-scene-id="${preset.id}" style="${borderStyle}" onclick="if(!event.target.closest('button')){navigateToCard('automations',null,'scenes','data-scene-id','${preset.id}')}">
|
||||
<div class="dashboard-target-info">
|
||||
<span class="dashboard-target-icon">${ICON_SCENE}</span>
|
||||
<div>
|
||||
<div class="dashboard-target-name">${escapeHtml(preset.name)}</div>
|
||||
@@ -327,9 +279,9 @@ export async function deleteScenePreset(presetId) {
|
||||
// ===== Helpers =====
|
||||
|
||||
function _reloadScenesTab() {
|
||||
// Reload the scenes tab if it's active
|
||||
if ((localStorage.getItem('activeTab') || 'dashboard') === 'scenes') {
|
||||
loadScenes();
|
||||
// Reload automations tab (which includes scenes section)
|
||||
if ((localStorage.getItem('activeTab') || 'dashboard') === 'automations') {
|
||||
if (typeof window.loadAutomations === 'function') window.loadAutomations();
|
||||
}
|
||||
// Also refresh dashboard (scene presets section)
|
||||
if (typeof window.loadDashboard === 'function') window.loadDashboard(true);
|
||||
|
||||
Reference in New Issue
Block a user