import { useState, useEffect } from "react"; import api from "../api/client"; import { LANGUAGE_MASTER_LIST, getLanguageName, formatDuration, normalizeColor, } from "./melodyUtils"; const DEFAULT_NOTE_ASSIGNMENT_COLORS = [ "#67E8F9", "#5EEAD4", "#6EE7B7", "#86EFAC", "#BEF264", "#FDE68A", "#FCD34D", "#FBBF24", "#FDBA74", "#FB923C", "#F97316", "#FB7185", "#F87171", "#EF4444", "#DC2626", "#B91C1C", ]; const headingStyle = { color: "var(--text-heading)" }; const mutedStyle = { color: "var(--text-muted)" }; export default function MelodySettings() { const [settings, setSettings] = useState(null); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(""); const [success, setSuccess] = useState(""); const [langToAdd, setLangToAdd] = useState(""); const cssColorDefault = (typeof window !== "undefined" && window.getComputedStyle) ? getComputedStyle(document.documentElement).getPropertyValue("--color-default").trim() : "var(--color-default)"; const [colorToAdd, setColorToAdd] = useState(cssColorDefault); const [colorHexInput, setColorHexInput] = useState(cssColorDefault); const [durationToAdd, setDurationToAdd] = useState(""); const [colorModalBell, setColorModalBell] = useState(null); const [modalColor, setModalColor] = useState("#67E8F9"); const [modalColorInput, setModalColorInput] = useState("#67E8F9"); useEffect(() => { loadSettings(); }, []); const loadSettings = async () => { setLoading(true); try { const data = await api.get("/settings/melody"); setSettings({ ...data, note_assignment_colors: (data.note_assignment_colors || DEFAULT_NOTE_ASSIGNMENT_COLORS).slice(0, 16), }); } catch (err) { setError(err.message); } finally { setLoading(false); } }; const saveSettings = async (updated) => { setSaving(true); setError(""); setSuccess(""); try { const result = await api.put("/settings/melody", updated); setSettings(result); setSuccess("Settings saved."); setTimeout(() => setSuccess(""), 2000); } catch (err) { setError(err.message); } finally { setSaving(false); } }; const addLanguage = () => { if (!langToAdd || settings.available_languages.includes(langToAdd)) return; const updated = { ...settings, available_languages: [...settings.available_languages, langToAdd], }; setLangToAdd(""); saveSettings(updated); }; const removeLanguage = (code) => { if (settings.available_languages.length <= 1) return; const updated = { ...settings, available_languages: settings.available_languages.filter((c) => c !== code), primary_language: settings.primary_language === code ? settings.available_languages.find((c) => c !== code) : settings.primary_language, }; saveSettings(updated); }; const setPrimaryLanguage = (code) => { saveSettings({ ...settings, primary_language: code }); }; const addColor = () => { const color = colorHexInput.startsWith("#") ? colorHexInput : `#${colorHexInput}`; if (!/^#[0-9A-Fa-f]{6}$/.test(color)) return; if (settings.quick_colors.includes(color)) return; const updated = { ...settings, quick_colors: [...settings.quick_colors, color], }; saveSettings(updated); }; const removeColor = (color) => { const updated = { ...settings, quick_colors: settings.quick_colors.filter((c) => c !== color), }; saveSettings(updated); }; const addDuration = () => { const val = parseInt(durationToAdd, 10); if (isNaN(val) || val < 0) return; if (settings.duration_values.includes(val)) return; const updated = { ...settings, duration_values: [...settings.duration_values, val].sort((a, b) => a - b), }; setDurationToAdd(""); saveSettings(updated); }; const removeDuration = (val) => { const updated = { ...settings, duration_values: settings.duration_values.filter((v) => v !== val), }; saveSettings(updated); }; const openNoteColorModal = (index) => { const current = settings.note_assignment_colors?.[index] || DEFAULT_NOTE_ASSIGNMENT_COLORS[index]; setModalColor(current); setModalColorInput(current); setColorModalBell(index); }; const applyNoteColor = () => { if (colorModalBell == null) return; const candidate = modalColorInput.startsWith("#") ? modalColorInput : `#${modalColorInput}`; if (!/^#[0-9A-Fa-f]{6}$/.test(candidate)) return; const next = [...(settings.note_assignment_colors || DEFAULT_NOTE_ASSIGNMENT_COLORS)]; next[colorModalBell] = candidate; saveSettings({ ...settings, note_assignment_colors: next.slice(0, 16) }); setColorModalBell(null); }; const resetNoteColor = () => { if (colorModalBell == null) return; const fallback = DEFAULT_NOTE_ASSIGNMENT_COLORS[colorModalBell]; setModalColor(fallback); setModalColorInput(fallback); }; if (loading) { return
Colors used in Composer, Playback, and View table dots. Click a bell to customize.
Pick a custom color for this bell number.