diff --git a/src/controllers/profile.py b/src/controllers/profile.py index 7fcbf9c..9bacb87 100644 --- a/src/controllers/profile.py +++ b/src/controllers/profile.py @@ -81,11 +81,117 @@ async def apply_profile(request, session, id): async def create_profile(request): """Create a new profile.""" try: - data = request.json or {} + data = dict(request.json or {}) name = data.get("name", "") + seed_raw = data.get("seed_dj_tab", False) + if isinstance(seed_raw, str): + seed_dj_tab = seed_raw.strip().lower() in ("1", "true", "yes", "on") + else: + seed_dj_tab = bool(seed_raw) + # Request-only flag: do not persist on profile records. + data.pop("seed_dj_tab", None) profile_id = profiles.create(name) + # Avoid persisting request-only fields. + data.pop("name", None) if data: profiles.update(profile_id, data) + + # New profiles always start with a default tab pre-populated with starter presets. + default_preset_ids = [] + default_preset_defs = [ + { + "name": "on", + "pattern": "on", + "colors": ["#FFFFFF"], + "brightness": 255, + "delay": 100, + "auto": True, + }, + { + "name": "off", + "pattern": "off", + "colors": [], + "brightness": 0, + "delay": 100, + "auto": True, + }, + { + "name": "rainbow", + "pattern": "rainbow", + "colors": [], + "brightness": 255, + "delay": 100, + "auto": True, + "n1": 2, + }, + { + "name": "transition", + "pattern": "transition", + "colors": ["#FF0000", "#00FF00", "#0000FF"], + "brightness": 255, + "delay": 500, + "auto": True, + }, + ] + + for preset_data in default_preset_defs: + pid = presets.create(profile_id) + presets.update(pid, preset_data) + default_preset_ids.append(str(pid)) + + default_tab_id = tabs.create(name="default", names=["1"], presets=[default_preset_ids]) + tabs.update(default_tab_id, { + "presets_flat": default_preset_ids, + "default_preset": default_preset_ids[0] if default_preset_ids else None, + }) + + profile = profiles.read(profile_id) or {} + profile_tabs = profile.get("tabs", []) if isinstance(profile.get("tabs", []), list) else [] + profile_tabs.append(str(default_tab_id)) + + if seed_dj_tab: + # Seed a DJ-focused tab with three starter presets. + seeded_preset_ids = [] + preset_defs = [ + { + "name": "DJ Rainbow", + "pattern": "rainbow", + "colors": [], + "brightness": 220, + "delay": 60, + "n1": 12, + }, + { + "name": "DJ Single Color", + "pattern": "on", + "colors": ["#ff00ff"], + "brightness": 220, + "delay": 100, + }, + { + "name": "DJ Transition", + "pattern": "transition", + "colors": ["#ff0000", "#00ff00", "#0000ff"], + "brightness": 220, + "delay": 250, + }, + ] + + for preset_data in preset_defs: + pid = presets.create(profile_id) + presets.update(pid, preset_data) + seeded_preset_ids.append(str(pid)) + + dj_tab_id = tabs.create(name="dj", names=["dj"], presets=[seeded_preset_ids]) + tabs.update(dj_tab_id, { + "presets_flat": seeded_preset_ids, + "default_preset": seeded_preset_ids[0] if seeded_preset_ids else None, + }) + + profile_tabs.append(str(dj_tab_id)) + + profiles.update(profile_id, {"tabs": profile_tabs}) + profile_data = profiles.read(profile_id) return json.dumps({profile_id: profile_data}), 201, {'Content-Type': 'application/json'} except Exception as e: diff --git a/src/static/profiles.js b/src/static/profiles.js index 6f2a61d..fba2c77 100644 --- a/src/static/profiles.js +++ b/src/static/profiles.js @@ -4,6 +4,7 @@ document.addEventListener("DOMContentLoaded", () => { const profilesCloseButton = document.getElementById("profiles-close-btn"); const profilesList = document.getElementById("profiles-list"); const newProfileInput = document.getElementById("new-profile-name"); + const newProfileSeedDjInput = document.getElementById("new-profile-seed-dj"); const createProfileButton = document.getElementById("create-profile-btn"); if (!profilesButton || !profilesModal || !profilesList) { @@ -19,6 +20,18 @@ document.addEventListener("DOMContentLoaded", () => { profilesModal.classList.remove("active"); }; + const refreshTabsForActiveProfile = async () => { + // Clear stale current tab so tab controller falls back to first tab of applied profile. + document.cookie = "current_tab=; path=/; max-age=0"; + + if (window.tabsManager && typeof window.tabsManager.loadTabs === "function") { + await window.tabsManager.loadTabs(); + } + if (window.tabsManager && typeof window.tabsManager.loadTabsModal === "function") { + await window.tabsManager.loadTabsModal(); + } + }; + const renderProfiles = (profiles, currentProfileId) => { profilesList.innerHTML = ""; let entries = []; @@ -66,7 +79,7 @@ document.addEventListener("DOMContentLoaded", () => { throw new Error("Failed to apply profile"); } await loadProfiles(); - document.body.dispatchEvent(new Event("tabs-updated")); + await refreshTabsForActiveProfile(); } catch (error) { console.error("Apply profile failed:", error); alert("Failed to apply profile."); @@ -115,22 +128,8 @@ document.addEventListener("DOMContentLoaded", () => { headers: { Accept: "application/json" }, }); } - document.cookie = "current_tab=; path=/; max-age=0"; await loadProfiles(); - if (typeof window.loadTabs === "function") { - await window.loadTabs(); - } - if (typeof window.loadTabsModal === "function") { - await window.loadTabsModal(); - } - const tabContent = document.getElementById("tab-content"); - if (tabContent) { - tabContent.innerHTML = ` -