77a757cee6
- Fix dashboard 500 when deps disconnected — _gather_state() now returns
full defaults instead of bare {'connected': False}
- Phase 2: FX param schemas aligned with DSP implementations
- Phase 7: Pipeline routing, preset manager improvements
- Factory presets: cleanup and normalization
Fixes dashboard crash on disconnected state (dogfood issue #1)
425 lines
11 KiB
HTML
425 lines
11 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Pi Multi-FX Pedal — UI Template</title>
|
||
<style>
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||
background: #1a1a2e;
|
||
color: #e0e0e0;
|
||
padding: 40px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
h1 { font-size: 18px; font-weight: 600; color: #888; margin-bottom: 24px; letter-spacing: 2px; text-transform: uppercase; }
|
||
.pedal-frame {
|
||
width: 800px;
|
||
background: #16213e;
|
||
border: 2px solid #0f3460;
|
||
border-radius: 16px;
|
||
padding: 24px;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
|
||
position: relative;
|
||
}
|
||
.pedal-frame::before {
|
||
content: "480 × 320 (touchscreen)";
|
||
position: absolute;
|
||
top: -28px;
|
||
right: 0;
|
||
font-size: 11px;
|
||
color: #555;
|
||
letter-spacing: 1px;
|
||
}
|
||
|
||
/* ── Top bar ── */
|
||
.topbar {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 0 0 16px 0;
|
||
border-bottom: 1px solid #0f3460;
|
||
margin-bottom: 16px;
|
||
}
|
||
.topbar .preset-name {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
}
|
||
.topbar .preset-bank {
|
||
font-size: 13px;
|
||
color: #888;
|
||
margin-top: 2px;
|
||
}
|
||
.topbar .status-area {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
.topbar .cpu-badge {
|
||
background: #0f3460;
|
||
padding: 4px 10px;
|
||
border-radius: 6px;
|
||
font-size: 12px;
|
||
color: #53d769;
|
||
}
|
||
.topbar .bypass-btn {
|
||
background: #e94560;
|
||
color: #fff;
|
||
padding: 6px 16px;
|
||
border-radius: 8px;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
border: none;
|
||
}
|
||
.topbar .bypass-btn.off {
|
||
background: #2d4059;
|
||
color: #888;
|
||
}
|
||
|
||
/* ── VU Meters ── */
|
||
.vu-section {
|
||
display: flex;
|
||
gap: 16px;
|
||
margin-bottom: 20px;
|
||
padding: 12px 16px;
|
||
background: #0a1628;
|
||
border-radius: 10px;
|
||
}
|
||
.vu-meter {
|
||
flex: 1;
|
||
}
|
||
.vu-meter label {
|
||
font-size: 11px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1px;
|
||
color: #666;
|
||
display: block;
|
||
margin-bottom: 4px;
|
||
}
|
||
.vu-bar {
|
||
height: 8px;
|
||
background: #1a1a2e;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
.vu-fill {
|
||
height: 100%;
|
||
border-radius: 4px;
|
||
width: 65%;
|
||
}
|
||
.vu-fill.left { background: linear-gradient(90deg, #53d769, #e9d34b, #e94560); width: 72%; }
|
||
.vu-fill.right { background: linear-gradient(90deg, #53d769, #e9d34b, #e94560); width: 58%; }
|
||
.vu-clip { color: #e94560; font-size: 11px; margin-left: 4px; }
|
||
|
||
/* ── FX Chain ── */
|
||
.fx-chain {
|
||
display: flex;
|
||
gap: 6px;
|
||
margin-bottom: 20px;
|
||
flex-wrap: wrap;
|
||
padding: 8px 0;
|
||
}
|
||
.fx-block {
|
||
background: #0f3460;
|
||
border: 1px solid #1a4a7a;
|
||
border-radius: 8px;
|
||
padding: 8px 12px;
|
||
font-size: 11px;
|
||
text-align: center;
|
||
min-width: 60px;
|
||
cursor: pointer;
|
||
transition: all 0.15s;
|
||
}
|
||
.fx-block:hover { background: #1a4a7a; border-color: #53d769; }
|
||
.fx-block .icon { font-size: 18px; display: block; margin-bottom: 2px; }
|
||
.fx-block .name { color: #aaa; font-size: 10px; }
|
||
.fx-block.active { border-color: #53d769; }
|
||
.fx-block.bypassed { opacity: 0.4; }
|
||
.fx-chain .arrow { color: #333; font-size: 18px; align-self: center; padding: 0 2px; }
|
||
|
||
/* ── Main content grid ── */
|
||
.main-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 220px;
|
||
gap: 16px;
|
||
}
|
||
|
||
/* ── Knob panel ── */
|
||
.knob-panel {
|
||
background: #0a1628;
|
||
border-radius: 10px;
|
||
padding: 16px;
|
||
}
|
||
.knob-panel .header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 14px;
|
||
}
|
||
.knob-panel .header h3 { font-size: 14px; font-weight: 600; color: #ccc; }
|
||
.knob-panel .header .type { font-size: 11px; color: #53d769; }
|
||
.knob-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 12px;
|
||
}
|
||
.knob {
|
||
text-align: center;
|
||
}
|
||
.knob .circle {
|
||
width: 48px;
|
||
height: 48px;
|
||
border: 3px solid #0f3460;
|
||
border-radius: 50%;
|
||
margin: 0 auto 6px;
|
||
position: relative;
|
||
background: #16213e;
|
||
cursor: grab;
|
||
}
|
||
.knob .circle::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: 4px;
|
||
left: 50%;
|
||
width: 2px;
|
||
height: 14px;
|
||
background: #e94560;
|
||
transform: translateX(-50%) rotate(45deg);
|
||
border-radius: 1px;
|
||
}
|
||
.knob label { font-size: 10px; color: #888; display: block; }
|
||
.knob .value { font-size: 12px; color: #fff; font-weight: 600; }
|
||
|
||
/* ── Side panel (presets / browser) ── */
|
||
.side-panel {
|
||
background: #0a1628;
|
||
border-radius: 10px;
|
||
padding: 12px;
|
||
}
|
||
.side-panel .tabs {
|
||
display: flex;
|
||
gap: 4px;
|
||
margin-bottom: 10px;
|
||
}
|
||
.side-panel .tabs button {
|
||
flex: 1;
|
||
background: #0f3460;
|
||
border: none;
|
||
color: #888;
|
||
padding: 6px;
|
||
border-radius: 6px;
|
||
font-size: 11px;
|
||
cursor: pointer;
|
||
}
|
||
.side-panel .tabs button.active { background: #1a4a7a; color: #fff; }
|
||
.side-panel .preset-list { }
|
||
.preset-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 8px 6px;
|
||
border-bottom: 1px solid #0f3460;
|
||
cursor: pointer;
|
||
}
|
||
.preset-item:hover { background: #0f3460; border-radius: 4px; }
|
||
.preset-item .p-name { font-size: 12px; }
|
||
.preset-item .p-num { color: #555; font-size: 10px; }
|
||
.preset-item.active { color: #53d769; }
|
||
.preset-item.bank-header { color: #555; font-size: 10px; letter-spacing: 1px; text-transform: uppercase; }
|
||
|
||
/* ── Bottom nav ── */
|
||
.bottom-nav {
|
||
display: flex;
|
||
gap: 4px;
|
||
margin-top: 16px;
|
||
padding-top: 12px;
|
||
border-top: 1px solid #0f3460;
|
||
}
|
||
.bottom-nav button {
|
||
flex: 1;
|
||
background: none;
|
||
border: 1px solid #0f3460;
|
||
color: #888;
|
||
padding: 8px;
|
||
border-radius: 8px;
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
transition: all 0.15s;
|
||
text-align: center;
|
||
}
|
||
.bottom-nav button:hover { border-color: #53d769; color: #fff; }
|
||
.bottom-nav button.active { background: #0f3460; color: #53d769; border-color: #53d769; }
|
||
|
||
/* ── Tag overlay ── */
|
||
.tag {
|
||
position: absolute;
|
||
font-size: 10px;
|
||
color: #53d76955;
|
||
letter-spacing: 1px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h1>🎛️ Figma Template — Pi Multi-FX Pedal UI</h1>
|
||
|
||
<div class="pedal-frame">
|
||
|
||
<!-- TOP BAR -->
|
||
<div class="topbar">
|
||
<div>
|
||
<div class="preset-name">Crunch Rhythm</div>
|
||
<div class="preset-bank">Bank 1 · Preset 2 of 4</div>
|
||
</div>
|
||
<div class="status-area">
|
||
<div class="cpu-badge">CPU 34%</div>
|
||
<button class="bypass-btn off">⏸ BYPASS</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- VU METERS -->
|
||
<div class="vu-section">
|
||
<div class="vu-meter">
|
||
<label>Input</label>
|
||
<div class="vu-bar"><div class="vu-fill left"></div></div>
|
||
</div>
|
||
<div class="vu-meter">
|
||
<label>Output</label>
|
||
<div class="vu-bar"><div class="vu-fill right"></div></div>
|
||
</div>
|
||
<div class="vu-meter" style="flex: 0 0 40px;">
|
||
<label>dB</label>
|
||
<div style="font-size:22px;font-weight:700;color:#e0e0e0;">-12</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- FX CHAIN -->
|
||
<div class="fx-chain">
|
||
<div class="fx-block active"><span class="icon">🚪</span><span class="name">Gate</span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="fx-block active"><span class="icon">📦</span><span class="name">Comp</span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="fx-block active"><span class="icon">🎸</span><span class="name">OD</span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="fx-block active" style="border-color:#53d769;"><span class="icon">🧠</span><span class="name">NAM</span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="fx-block active"><span class="icon">📡</span><span class="name">IR</span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="fx-block bypassed"><span class="icon">〰️</span><span class="name">Chorus</span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="fx-block active"><span class="icon">⏳</span><span class="name">Delay</span></div>
|
||
<div class="arrow">→</div>
|
||
<div class="fx-block active"><span class="icon">🌊</span><span class="name">Reverb</span></div>
|
||
</div>
|
||
|
||
<!-- MAIN GRID -->
|
||
<div class="main-grid">
|
||
<!-- KNOB PANEL -->
|
||
<div class="knob-panel">
|
||
<div class="header">
|
||
<h3>NAM Amp</h3>
|
||
<span class="type">feather-lstm · 1.2ms</span>
|
||
</div>
|
||
<div class="knob-grid">
|
||
<div class="knob">
|
||
<div class="circle"></div>
|
||
<label>Gain</label>
|
||
<div class="value">68</div>
|
||
</div>
|
||
<div class="knob">
|
||
<div class="circle"></div>
|
||
<label>Tone</label>
|
||
<div class="value">52</div>
|
||
</div>
|
||
<div class="knob">
|
||
<div class="circle"></div>
|
||
<label>Level</label>
|
||
<div class="value">75</div>
|
||
</div>
|
||
<div class="knob">
|
||
<div class="circle"></div>
|
||
<label>Gate</label>
|
||
<div class="value">30</div>
|
||
</div>
|
||
<div class="knob">
|
||
<div class="circle"></div>
|
||
<label>Bass</label>
|
||
<div class="value">45</div>
|
||
</div>
|
||
<div class="knob">
|
||
<div class="circle"></div>
|
||
<label>Mid</label>
|
||
<div class="value">60</div>
|
||
</div>
|
||
<div class="knob">
|
||
<div class="circle"></div>
|
||
<label>Treble</label>
|
||
<div class="value">55</div>
|
||
</div>
|
||
<div class="knob">
|
||
<div class="circle"></div>
|
||
<label>Presence</label>
|
||
<div class="value">40</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SIDE PANEL -->
|
||
<div class="side-panel">
|
||
<div class="tabs">
|
||
<button class="active">Presets</button>
|
||
<button>Models</button>
|
||
<button>IRs</button>
|
||
</div>
|
||
<div class="preset-list">
|
||
<div class="preset-item bank-header">Bank 1 — Classics</div>
|
||
<div class="preset-item active">
|
||
<span class="p-name">✦ Crunch Rhythm</span>
|
||
<span class="p-num">1-2</span>
|
||
</div>
|
||
<div class="preset-item">
|
||
<span class="p-name">Lead Solo</span>
|
||
<span class="p-num">1-3</span>
|
||
</div>
|
||
<div class="preset-item">
|
||
<span class="p-name">Clean Jazz</span>
|
||
<span class="p-num">1-4</span>
|
||
</div>
|
||
<div class="preset-item bank-header">Bank 2 — FX Bench</div>
|
||
<div class="preset-item">
|
||
<span class="p-name">Overdrive</span>
|
||
<span class="p-num">2-1</span>
|
||
</div>
|
||
<div class="preset-item">
|
||
<span class="p-name">Ambient Wash</span>
|
||
<span class="p-num">2-2</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- BOTTOM NAV -->
|
||
<div class="bottom-nav">
|
||
<button class="active">🎛️ Rig</button>
|
||
<button>🔧 FX</button>
|
||
<button>📋 Presets</button>
|
||
<button>🔍 Browse</button>
|
||
<button>⚙️ Settings</button>
|
||
</div>
|
||
</div>
|
||
|
||
<p style="margin-top:32px;color:#555;font-size:12px;text-align:center;max-width:600px;">
|
||
Dark theme · 800×500 mockup (scales to 480×320 touchscreen).<br>
|
||
Sections: Top bar, VU meters, FX chain, knob panel, presets sidebar, bottom nav.<br>
|
||
Trace over the labeled blocks in Figma — swap icons, fonts, spacing to taste.
|
||
</p>
|
||
|
||
</body>
|
||
</html>
|