|
129 | 129 | // you — 72.7 of it meant "nearly full", which is the opposite of how it read. Raw units stay in |
130 | 130 | // the CLI and the docs; nothing in the GUI shows them. |
131 | 131 | const pct = (raw) => (raw / BUDGET) * 100; |
132 | | - // Header readout, per DSP: used and actually-left. "96.9% · 3.1% free" on the Stomp, both DSPs |
133 | | - // joined by "·" on the Floor. |
134 | | - const dspLoadLabel = $derived( |
135 | | - dspViews |
136 | | - .map((v) => `${pct(v.dsp_load).toFixed(1)}% · ${Math.max(pct(BUDGET - v.dsp_load), 0).toFixed(1)}% free`) |
137 | | - .join(" · "), |
138 | | - ); |
| 132 | + // Header readout, per DSP. Rendered as one labelled chip each rather than a joined string: on |
| 133 | + // the Floor the two used to run together as "96.9% · 3.1% free · 0.0% · 100.0% free", with the |
| 134 | + // same separator between the DSPs as inside them and nothing saying which was which. |
| 135 | + const dspUsed = (v) => Math.min(100, Math.max(0, pct(v.dsp_load))); |
| 136 | + const dspFree = (v) => Math.max(pct(BUDGET - v.dsp_load), 0); |
| 137 | + // Within a few points of the ceiling — the picker is already greying most models out by here. |
| 138 | + const dspTight = (v) => dspFree(v) < 10; |
139 | 139 |
|
140 | 140 | // Whether the selected slot is a structural node (split/mixer/input/output) rather than a normal |
141 | 141 | // block — nodes aren't swappable or deletable. |
|
740 | 740 | <span>device <b>{preset.device_model ?? "—"}</b></span> |
741 | 741 | <span>fw <b>{preset.firmware ?? "—"}</b></span> |
742 | 742 | <span>routing <b>{preset.split ? "parallel (split)" : "serial"}</b></span> |
743 | | - <span>DSP <b>{dspLoadLabel}</b></span> |
| 743 | + {#each dspViews as v (v.dsp)} |
| 744 | + <span |
| 745 | + class="dsp-chip" |
| 746 | + class:tight={dspTight(v)} |
| 747 | + title="{dspViews.length > 1 ? `DSP ${v.dsp + 1}` : 'DSP'}: {dspUsed(v).toFixed( |
| 748 | + 1, |
| 749 | + )}% of what the pedal will accept, {dspFree(v).toFixed(1)}% free" |
| 750 | + > |
| 751 | + <span class="dsp-name">{dspViews.length > 1 ? `DSP ${v.dsp + 1}` : "DSP"}</span> |
| 752 | + <span class="dsp-bar"><span class="fill" style="width:{dspUsed(v)}%"></span></span> |
| 753 | + <b>{dspUsed(v).toFixed(1)}%</b> |
| 754 | + <span class="dsp-free">{dspFree(v).toFixed(1)}% free</span> |
| 755 | + </span> |
| 756 | + {/each} |
744 | 757 | </div> |
745 | 758 | {#if preset.snapshot_names.length} |
746 | 759 | <div class="snapshots"> |
|
782 | 795 | {/if} |
783 | 796 | {#each dspViews as dspView (dspView.dsp)} |
784 | 797 | {#if dspViews.length > 1} |
785 | | - <div class="dsp-head"> |
| 798 | + <div class="dsp-head" class:tight={dspTight(dspView)}> |
786 | 799 | DSP {dspView.dsp + 1} |
| 800 | + <span class="dsp-bar" |
| 801 | + ><span class="fill" style="width:{dspUsed(dspView)}%"></span></span |
| 802 | + > |
787 | 803 | <span class="dsp-load" |
788 | | - >{pct(dspView.dsp_load).toFixed(1)}% · {Math.max( |
789 | | - pct(BUDGET - dspView.dsp_load), |
790 | | - 0, |
791 | | - ).toFixed(1)}% free</span |
| 804 | + >{dspUsed(dspView).toFixed(1)}% used · {dspFree(dspView).toFixed(1)}% free</span |
792 | 805 | > |
793 | 806 | </div> |
794 | 807 | {/if} |
|
1139 | 1152 | font-size: 12px; |
1140 | 1153 | margin-left: 6px; |
1141 | 1154 | } |
| 1155 | + /* One per DSP. The bar is what makes two of them tell apart at a glance — the numbers alone |
| 1156 | + read as one run-on figure, which is how "96.9% · 3.1% free · 0.0% · 100.0% free" happened. */ |
| 1157 | + .dsp-chip { |
| 1158 | + display: inline-flex; |
| 1159 | + align-items: center; |
| 1160 | + gap: 8px; |
| 1161 | + } |
| 1162 | + .dsp-chip .dsp-name { |
| 1163 | + font-variant-numeric: tabular-nums; |
| 1164 | + } |
| 1165 | + .dsp-bar { |
| 1166 | + width: 60px; |
| 1167 | + height: 6px; |
| 1168 | + border-radius: 3px; |
| 1169 | + background: #2a2f3a; |
| 1170 | + overflow: hidden; |
| 1171 | + } |
| 1172 | + .dsp-bar .fill { |
| 1173 | + display: block; |
| 1174 | + height: 100%; |
| 1175 | + background: #5b8dd6; |
| 1176 | + } |
| 1177 | + .dsp-chip b { |
| 1178 | + font-variant-numeric: tabular-nums; |
| 1179 | + } |
| 1180 | + .dsp-chip .dsp-free { |
| 1181 | + font-size: 12px; |
| 1182 | + color: #6d7688; |
| 1183 | + font-variant-numeric: tabular-nums; |
| 1184 | + } |
| 1185 | + /* Nearly full: the same warning colour the model picker uses on a model that won't fit. */ |
| 1186 | + .dsp-chip.tight .dsp-bar .fill { |
| 1187 | + background: #e0785f; |
| 1188 | + } |
| 1189 | + .dsp-chip.tight b { |
| 1190 | + color: #e0785f; |
| 1191 | + } |
1142 | 1192 | .hint { |
1143 | 1193 | color: #9aa3b2; |
1144 | 1194 | } |
|
1154 | 1204 | color: #9aa3b2; |
1155 | 1205 | text-transform: uppercase; |
1156 | 1206 | } |
| 1207 | + .dsp-head.tight .dsp-bar .fill { |
| 1208 | + background: #e0785f; |
| 1209 | + } |
1157 | 1210 | .dsp-head .dsp-load { |
1158 | 1211 | font-size: 11px; |
1159 | 1212 | font-weight: 600; |
|
0 commit comments