diff --git a/src/App.jsx b/src/App.jsx index e5df120..930742e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -36,6 +36,7 @@ const API = { getAudioProfile:() => api("GET", "/api/audio/profile"), getRouting: () => api("GET", "/api/routing"), setRouting: (mode, breakpoint) => api("POST", "/api/routing", { routing_mode: mode, routing_breakpoint: breakpoint }), + saveSettings: (settings) => api("POST", "/api/settings", settings), searchModels: (q) => api("GET", `/api/tonehub/search?q=${encodeURIComponent(q)}`), searchIrs: (q) => api("GET", `/api/tonehub/search/irs?q=${encodeURIComponent(q)}`), installModel: (url, name) => api("POST", "/api/models/tonedownload/install", { download_url: url, name }), @@ -1015,6 +1016,7 @@ export default function App(){ onMasterVolume={(v)=>API.masterVolume(v).catch(()=>{})} onAudioProfile={(rate, period)=>API.setAudioProfile(rate, period).catch(()=>{})} onRoutingChange={(mode, bp)=>API.setRouting(mode, bp).catch(()=>{})} + onSaveSettings={(s)=>API.saveSettings(s).catch(()=>{})} /> )} diff --git a/src/GlobalSettings.jsx b/src/GlobalSettings.jsx index 75e326b..37b5f9d 100644 --- a/src/GlobalSettings.jsx +++ b/src/GlobalSettings.jsx @@ -6,7 +6,6 @@ const T = { green: "#3AB87A", red: "#C84040", textPrimary: "#F0EDE6", textSec: "#8888A0", textDim: "#444458", }; -// ── API calls (injected via props or fetched internally) ── async function api(method, path, body) { const opts = { method, headers: { "Content-Type": "application/json" } }; if (body) opts.body = JSON.stringify(body); @@ -15,10 +14,10 @@ async function api(method, path, body) { return res.json(); } -// ── Settings definitions ── const SETTINGS = { "Audio": [ - { key: "master_volume", label: "Master Volume", type: "slider", min: 0, max: 100, def: 72, unit: "%", icon: "🔊" }, + { key: "master_volume", label: "Master Volume", type: "slider", min: 0, max: 100, def: 72, unit: "%", icon: "🔊", + via: "volume" }, { key: "sample_rate", label: "Sample Rate", type: "select", def: "48000", options: [ { value: "22050", label: "22.05 kHz" }, @@ -26,7 +25,7 @@ const SETTINGS = { { value: "48000", label: "48 kHz" }, { value: "88200", label: "88.2 kHz" }, { value: "96000", label: "96 kHz" }, - ], icon: "📊" }, + ], icon: "📊", via: "audio_profile" }, { key: "buffer_size", label: "Buffer Size", type: "select", def: "512", options: [ { value: "64", label: "64 (1.3ms)" }, @@ -34,14 +33,14 @@ const SETTINGS = { { value: "256", label: "256 (5.3ms)" }, { value: "512", label: "512 (10.6ms)" }, { value: "1024",label: "1024 (21.3ms)" }, - ], icon: "📦" }, + ], icon: "📦", via: "audio_profile" }, { key: "input_pad", label: "Input Pad", type: "select", def: "off", options: [ { value: "off", label: "Off (0 dB)" }, { value: "-6", label: "-6 dB" }, { value: "-12", label: "-12 dB" }, { value: "-18", label: "-18 dB" }, - ], icon: "📥" }, + ], icon: "📥", via: "settings" }, { key: "input_impedance", label: "Input Impedance", type: "select", def: "1m", options: [ { value: "1m", label: "1 MΩ" }, @@ -49,14 +48,14 @@ const SETTINGS = { { value: "250k", label: "250 kΩ" }, { value: "150k", label: "150 kΩ" }, { value: "68k", label: "68 kΩ" }, - ], icon: "⚡" }, + ], icon: "⚡", via: "settings" }, ], "Routing": [ { key: "routing_mode", label: "Routing Mode", type: "select", def: "mono", options: [ { value: "mono", label: "Mono" }, { value: "4cm", label: "4CM (Stereo FX Loop)" }, - ], icon: "🔀" }, + ], icon: "🔀", via: "routing" }, { key: "routing_breakpoint", label: "FX Loop Breakpoint", type: "select", def: "7", options: [ { value: "1", label: "After block 1" }, @@ -67,7 +66,7 @@ const SETTINGS = { { value: "6", label: "After block 6" }, { value: "7", label: "After block 7" }, { value: "8", label: "After block 8" }, - ], icon: "↔" }, + ], icon: "↔", via: "routing" }, ], "Audio Interface": [ { key: "input_device", label: "Input Device", type: "select", def: "hw:0,0", @@ -75,19 +74,19 @@ const SETTINGS = { { value: "hw:0,0", label: "Default (hw:0,0)" }, { value: "hw:1,0", label: "USB Audio (hw:1,0)" }, { value: "hw:2,0", label: "HDMI Audio (hw:2,0)" }, - ], icon: "🎤" }, + ], icon: "🎤", via: "settings" }, { key: "output_device", label: "Output Device", type: "select", def: "hw:0,0", options: [ { value: "hw:0,0", label: "Default (hw:0,0)" }, { value: "hw:1,0", label: "USB Audio (hw:1,0)" }, { value: "hw:2,0", label: "HDMI Audio (hw:2,0)" }, - ], icon: "🔈" }, + ], icon: "🔈", via: "settings" }, { key: "channel_mode", label: "Channel Mode", type: "select", def: "mono", options: [ { value: "mono", label: "Mono" }, { value: "dual-mono", label: "Dual Mono" }, { value: "stereo_4cm", label: "Stereo (4CM)" }, - ], icon: "🔊" }, + ], icon: "🔊", via: "settings" }, ], "MIDI": [ { key: "midi_channel", label: "MIDI Channel", type: "select", def: "omni", @@ -96,13 +95,13 @@ const SETTINGS = { ...Array.from({ length: 16 }, (_, i) => ({ value: String(i + 1), label: `Channel ${i + 1}` })), - ], icon: "🎹" }, - { key: "midi_clock", label: "MIDI Clock", type: "toggle", def: false, icon: "⏱" }, - { key: "midi_thru", label: "MIDI Thru", type: "toggle", def: true, icon: "↔" }, + ], icon: "🎹", via: "settings" }, + { key: "midi_clock", label: "MIDI Clock", type: "toggle", def: false, via: "settings", icon: "⏱" }, + { key: "midi_thru", label: "MIDI Thru", type: "toggle", def: true, via: "settings", icon: "↔" }, ], "Display": [ - { key: "brightness", label: "Display Brightness", type: "slider", min: 1, max: 10, def: 8, unit: "", icon: "☀️" }, - { key: "auto_dim", label: "Auto Dim", type: "toggle", def: true, icon: "🌙" }, + { key: "brightness", label: "Display Brightness", type: "slider", min: 1, max: 10, def: 8, unit: "", icon: "☀️", via: "settings" }, + { key: "auto_dim", label: "Auto Dim", type: "toggle", def: true, via: "settings", icon: "🌙" }, { key: "screen_saver", label: "Screen Saver", type: "select", def: "5m", options: [ { value: "off", label: "Off" }, @@ -110,10 +109,10 @@ const SETTINGS = { { value: "5m", label: "5 min" }, { value: "15m", label: "15 min" }, { value: "30m", label: "30 min" }, - ], icon: "💤" }, + ], icon: "💤", via: "settings" }, ], "Tempo": [ - { key: "tap_tempo", label: "Tap Tempo", type: "bpm", def: 120, icon: "🔄" }, + { key: "tap_tempo", label: "Tap Tempo", type: "bpm", def: 120, via: "settings", icon: "🔄" }, { key: "tempo_division", label: "Tempo Division", type: "select", def: "quarter", options: [ { value: "half", label: "½ Note" }, @@ -122,12 +121,11 @@ const SETTINGS = { { value: "triplet", label: "⅛ Triplet" }, { value: "sixteenth", label: "¹⁄₁₆ Note" }, { value: "dotted_eighth", label: "Dotted ⅛" }, - ], icon: "🎶" }, - { key: "tap_tempo_mute", label: "Mute on Tap", type: "toggle", def: false, icon: "🔇" }, + ], icon: "🎶", via: "settings" }, + { key: "tap_tempo_mute", label: "Mute on Tap", type: "toggle", def: false, via: "settings", icon: "🔇" }, ], }; -// ── Build defaults from SETTINGS definitions ── function buildDefaults(overrides = {}) { const init = {}; for (const group of Object.values(SETTINGS)) { @@ -138,7 +136,8 @@ function buildDefaults(overrides = {}) { return { ...init, ...overrides }; } -// ── Slider Widget ── +// ── Widgets ──────────────────────────────────────────────────────────────── + function SettingsSlider({ label, value, min, max, unit, onChange }) { const trackRef = useRef(null); const dragRef = useRef(false); @@ -196,45 +195,22 @@ function SettingsSlider({ label, value, min, max, unit, onChange }) { position: "relative", height: 32, display: "flex", alignItems: "center", cursor: "pointer", touchAction: "none", }}> -