feat: generate Swift design tokens from tokens.json and add SstUI base
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -264,29 +264,29 @@ $('#rad').innerHTML = Object.entries(T.radius).map(([n, r]) => `<div><i style="b
|
||||
const VU = T.meter;
|
||||
const vuMin = VU['vu.min'].value, vuMax = VU['vu.max'].value, arc = VU['vu.arcDegrees'].value;
|
||||
const W = VU['vu.width'].value, H = VU['vu.height'].value;
|
||||
const pivot = { x: W / 2, y: H + 22 };
|
||||
const pivot = { x: W / 2, y: H + VU['vu.pivotOffset'].value };
|
||||
const frac = v => Math.max(0, Math.min(1.03, (v - vuMin) / (vuMax - vuMin)));
|
||||
const ang = f => (-arc / 2 + arc * f) * Math.PI / 180;
|
||||
const pt = (r, a) => [pivot.x + r * Math.sin(a), pivot.y - r * Math.cos(a)];
|
||||
const f2 = n => n.toFixed(2);
|
||||
|
||||
function meterSvg() {
|
||||
const R = 142;
|
||||
const R = VU['vu.scaleRadius'].value;
|
||||
let s = `<svg class="vu" viewBox="0 0 ${W} ${H}" role="img" aria-label="VU-meter" style="aspect-ratio:${W}/${H}">`;
|
||||
s += `<rect width="${W}" height="${H}" fill="var(--c-surface-meterFace)"/>`;
|
||||
const redFrom = frac(0);
|
||||
const arcPath = (r0, f0, f1) => { const [x0, y0] = pt(r0, ang(f0)), [x1, y1] = pt(r0, ang(f1)); return `M${f2(x0)} ${f2(y0)} A${r0} ${r0} 0 0 1 ${f2(x1)} ${f2(y1)}`; };
|
||||
s += `<path d="${arcPath(R, 0, redFrom)}" stroke="var(--c-ink-onMeter)" stroke-width="1" fill="none"/>`;
|
||||
s += `<path d="${arcPath(R - 2, redFrom, 1)}" stroke="var(--c-accent-record)" stroke-width="5" fill="none"/>`;
|
||||
s += `<path d="${arcPath(R - 2, redFrom, 1)}" stroke="var(--c-accent-record)" stroke-width="${VU['vu.redBandWidth'].value}" fill="none"/>`;
|
||||
for (let v = vuMin; v <= vuMax; v++) {
|
||||
const major = [-20, -10, -7, -5, -3, 0, 3].includes(v), a = ang(frac(v));
|
||||
const [x0, y0] = pt(R, a), [x1, y1] = pt(R - (major ? 11 : 6), a);
|
||||
const [x0, y0] = pt(R, a), [x1, y1] = pt(R - (major ? VU['vu.tickMajor'].value : VU['vu.tickMinor'].value), a);
|
||||
s += `<line x1="${f2(x0)}" y1="${f2(y0)}" x2="${f2(x1)}" y2="${f2(y1)}" stroke="${v > 0 ? 'var(--c-accent-record)' : 'var(--c-ink-onMeter)'}" stroke-width="${major ? 1.4 : 1}"/>`;
|
||||
if (major) { const [tx, ty] = pt(R - 24, a); s += `<text x="${f2(tx)}" y="${f2(ty + 3)}" text-anchor="middle" font-family="${fam('chrome')}" font-weight="500" font-size="9" letter-spacing=".5" fill="${v > 0 ? 'var(--c-accent-record)' : 'var(--c-ink-onMeter)'}">${v > 0 ? '+' + v : v}</text>`; }
|
||||
if (major) { const [tx, ty] = pt(R - VU['vu.labelInset'].value, a); s += `<text x="${f2(tx)}" y="${f2(ty + 3)}" text-anchor="middle" font-family="${fam('chrome')}" font-weight="500" font-size="9" letter-spacing=".5" fill="${v > 0 ? 'var(--c-accent-record)' : 'var(--c-ink-onMeter)'}">${v > 0 ? '+' + v : v}</text>`; }
|
||||
}
|
||||
s += `<text x="${W / 2}" y="${H - 30}" text-anchor="middle" font-family="${fam('chrome')}" font-weight="500" font-size="15" letter-spacing="3" fill="var(--c-ink-onMeter)">VU</text>`;
|
||||
s += `<text x="14" y="${H - 10}" font-family="${fam('chrome')}" font-weight="500" font-size="8" letter-spacing="1.2" fill="var(--c-ink-onMeter)" opacity=".75">SST · FIELD RECORDER</text>`;
|
||||
s += `<line class="needle" x1="${pivot.x}" y1="${pivot.y}" x2="${pivot.x}" y2="${pivot.y - 146}" stroke="var(--c-ink-onMeter)" stroke-width="${VU['vu.needleWidth'].value}" stroke-linecap="butt"/>`;
|
||||
s += `<line class="needle" x1="${pivot.x}" y1="${pivot.y}" x2="${pivot.x}" y2="${pivot.y - VU['vu.needleLength'].value}" stroke="var(--c-ink-onMeter)" stroke-width="${VU['vu.needleWidth'].value}" stroke-linecap="butt"/>`;
|
||||
s += `<circle cx="${W - 18}" cy="18" r="5" class="peak" fill="var(--c-accent-recordUnlit)" stroke="var(--c-ink-onMeter)" stroke-width=".8"/><text x="${W - 18}" y="34" text-anchor="middle" font-family="${fam('chrome')}" font-size="7" font-weight="500" letter-spacing=".8" fill="var(--c-ink-onMeter)">PEAK</text>`;
|
||||
s += `</svg>`;
|
||||
return s;
|
||||
@@ -304,9 +304,9 @@ function digit(ch, ox, lit) {
|
||||
return `<g transform="translate(${ox},0)">` + Object.entries(P).map(([k, pts]) => `<polygon points="${pts}" fill="${lit && on.includes(k) ? 'var(--c-level-amber)' : 'var(--c-display-segmentUnlit)'}"/>`).join('') + `</g>`;
|
||||
}
|
||||
function counterSvg(text) {
|
||||
const adv = DW + 6; let x = 0, out = '';
|
||||
const adv = SEG.advance.value; let x = 0, out = '';
|
||||
for (const ch of text) {
|
||||
if (ch === ':' || ch === '.') { out += `<rect x="${x + 2}" y="${ch === ':' ? DH * .28 : DH - 5}" width="4" height="4" fill="var(--c-level-amber)"/>` + (ch === ':' ? `<rect x="${x + 2}" y="${DH * .64}" width="4" height="4" fill="var(--c-level-amber)"/>` : ''); x += 10; }
|
||||
if (ch === ':' || ch === '.') { out += `<rect x="${x + 2}" y="${ch === ':' ? DH * .28 : DH - 5}" width="4" height="4" fill="var(--c-level-amber)"/>` + (ch === ':' ? `<rect x="${x + 2}" y="${DH * .64}" width="4" height="4" fill="var(--c-level-amber)"/>` : ''); x += SEG.colonWidth.value; }
|
||||
else { out += digit(ch, x, true); x += adv; }
|
||||
}
|
||||
return `<svg width="${x}" height="${DH}" viewBox="0 0 ${x} ${DH}" role="img" aria-label="Teller ${text}">${out}</svg>`;
|
||||
@@ -350,7 +350,7 @@ function frame(now) {
|
||||
const a = wn * wn * (tgt - r.x) - 2 * zeta * wn * r.v;
|
||||
r.v += a * dt; r.x += r.v * dt;
|
||||
const ang_ = ang(Math.max(-0.02, Math.min(1.05, r.x)));
|
||||
const [nx, ny] = pt(146, ang_);
|
||||
const [nx, ny] = pt(VU['vu.needleLength'].value, ang_);
|
||||
r.needle.setAttribute('x2', f2(nx)); r.needle.setAttribute('y2', f2(ny));
|
||||
const vuNow = vuMin + (vuMax - vuMin) * r.x;
|
||||
if (vuNow > 0 && r.running) r.peakUntil = now + VU['peak.holdMs'].value;
|
||||
|
||||
Reference in New Issue
Block a user