diff options
Diffstat (limited to 'docs/prototypes/panel-widget-gallery.html')
| -rw-r--r-- | docs/prototypes/panel-widget-gallery.html | 156 |
1 files changed, 154 insertions, 2 deletions
diff --git a/docs/prototypes/panel-widget-gallery.html b/docs/prototypes/panel-widget-gallery.html index c56a21f..944657b 100644 --- a/docs/prototypes/panel-widget-gallery.html +++ b/docs/prototypes/panel-widget-gallery.html @@ -510,6 +510,11 @@ h2::after{content:"";height:1px;background:var(--wash);flex:1} .patch svg{position:absolute;inset:0;pointer-events:none;width:100%;height:100%} .patch svg path{fill:none;stroke:var(--gold);stroke-width:2.4;opacity:.85;stroke-linecap:round} +/* ===== reference-batch (R) widgets — SVG-first, after period hardware ===== */ +.rsvg{display:block} +.rsvg.drag{cursor:ns-resize;touch-action:none} +.rsvg.press{cursor:pointer} + @media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}} </style> </head> @@ -518,8 +523,9 @@ h2::after{content:"";height:1px;background:var(--wash);flex:1} <header class="masthead"> <div class="eyebrow">archsetup · dupre panel family · live kit</div> <h1>Widget gallery — the instrument-console kit, fully driveable</h1> - <p>Every control + display idiom in the dupre faceplate language, merged into one live console — the base kit - plus the candidate batch, ~55 widgets rendering from the same tokens the net / bt / sound panels use. + <p>Every control + display idiom in the dupre faceplate language, merged into one live console — the base kit, + the candidate batch, and the reference batch (R: modeled on period hardware), ~57 widgets rendering from + the same tokens the net / bt / sound panels use. <b>Controls</b> take input; <b>meters & gauges</b> show a live analog value; <b>indicators & readouts</b> show state or a number. Every widget is operable: drag the faders, knobs and gauges; click the switches, keys and lamps. Each card carries a live readout that tracks what you do. The trace displays run @@ -682,6 +688,15 @@ card(C,'N09','DIP-switch bank', card(C,'N10','Jog / shuttle wheel', `<span class="jog" id="jog10"><span class="shuttle" style="--sh:40deg"></span><span class="inner" id="jogi"><span class="dimple"></span></span></span>`, '<b>scrub fine, shuttle fast.</b> The edit-deck wheel. Drag to jog the inner wheel; readout shows the position.'); +card(C,'R02','Calibrated vernier dial', + `<svg id="vern" class="rsvg drag" viewBox="0 0 150 150" width="150" height="150"></svg>`, + '<b>precision on a rotating disc.</b> The big lab dial — the calibrated disc turns under a fixed hairline. Drag up/down; readout reads to a tenth. After a 1950s lab-oscillator vernier.'); +card(C,'R03','Bat-handle toggle', + `<svg id="batsw" class="rsvg press" viewBox="0 0 70 90" width="70" height="90"></svg>`, + '<b>the period power switch.</b> Chrome bat lever on a hex bushing, snaps up ON / down OFF. Click to throw it. After a lab-instrument panel toggle.'); +card(C,'R04','Bakelite fluted knob', + `<svg id="bakek" class="rsvg drag" viewBox="0 0 110 110" width="110" height="110"></svg>`, + '<b>the skirted console knob.</b> Scalloped bakelite skirt, glossy dome, amber index over a printed 0-10 scale. Drag up/down to turn. After a vintage console mixer knob.'); /* ============ METERS & GAUGES ============ */ const M=$('meters'); @@ -742,6 +757,9 @@ card(M,'N17','Correlation meter (±)', card(M,'N18','Battery-cell gauge', `<div class="batt" id="batt18"><div class="cells" id="battc"></div><span class="nub"></span></div>`, '<b>charge as discrete cells.</b> Filled cells count up, the last warn in terracotta. Drag left/right to set; readout shows %.'); +card(M,'R01','Moving-coil VU meter', + `<svg id="mcvu" class="rsvg" viewBox="0 0 150 96" width="150" height="96"></svg>`, + '<b>the classic cream-faced VU.</b> Printed dB arc, red zone past 0, ballistic needle. Runs the live signal; readout shows VU. After a vintage console meter pair.'); /* ============ INDICATORS & READOUTS ============ */ const I=$('indicators'); @@ -1024,6 +1042,138 @@ setRd('N24','click to cycle'); jacks[pending].classList.remove('sel');pending=null;draw();};}); conns=['0-5','2-7'];draw();window.addEventListener('resize',draw);})(); +/* ============ REFERENCE-BATCH (R) WIDGETS — SVG builders ============ */ +const SVGNS='http://www.w3.org/2000/svg'; +function svgEl(parent,tag,attrs){const e=document.createElementNS(SVGNS,tag); + for(const k in attrs)e.setAttribute(k,attrs[k]);parent.appendChild(e);return e;} +function polar(cx,cy,r,deg){const a=deg*Math.PI/180;return [cx+r*Math.sin(a),cy-r*Math.cos(a)];} + +/* R01 moving-coil VU: pivot below the face, authentic nonlinear dB scale */ +const VUDB=[[-20,0],[-10,.20],[-7,.31],[-5,.40],[-3,.50],[-2,.58],[-1,.66],[0,.75],[1,.84],[2,.91],[3,1]]; +const mcvu={t:.35,needle:null}; +(function(){const s=$('mcvu'),cx=75,cy=112,sweep=t=>-43+t*86; + const defs=svgEl(s,'defs',{}); + const g1=svgEl(defs,'linearGradient',{id:'vuGlass',x1:0,y1:0,x2:0,y2:1}); + svgEl(g1,'stop',{offset:'0','stop-color':'rgba(255,255,255,.13)'}); + svgEl(g1,'stop',{offset:'.35','stop-color':'rgba(255,255,255,0)'}); + const clip=svgEl(defs,'clipPath',{id:'vuFace'}); + svgEl(clip,'rect',{x:8,y:8,width:134,height:74,rx:3}); + svgEl(s,'rect',{x:.5,y:.5,width:149,height:95,rx:7,fill:'#16130f',stroke:'#060505'}); + svgEl(s,'rect',{x:8,y:8,width:134,height:74,rx:3,fill:'var(--cream)'}); + const face=svgEl(s,'g',{'clip-path':'url(#vuFace)'}); + const arcPt=t=>polar(cx,cy,58,sweep(t)); + const [ax1,ay1]=arcPt(0),[ax2,ay2]=arcPt(1); + svgEl(face,'path',{d:`M ${ax1} ${ay1} A 58 58 0 0 1 ${ax2} ${ay2}`,fill:'none',stroke:'#3a3128','stroke-width':1.2}); + const [rx1,ry1]=arcPt(.75); + svgEl(face,'path',{d:`M ${rx1} ${ry1} A 58 58 0 0 1 ${ax2} ${ay2}`,fill:'none',stroke:'var(--fail)','stroke-width':3.5}); + for(const [db,t] of VUDB){const a=sweep(t),[x1,y1]=polar(cx,cy,55,a),[x2,y2]=polar(cx,cy,63,a); + svgEl(face,'line',{x1,y1,x2,y2,stroke:db>0?'var(--fail)':'#3a3128','stroke-width':1.1});} + for(const [db,t] of VUDB){if(![-20,-10,-5,-3,0,1,2,3].includes(db))continue; + const [x,y]=polar(cx,cy,69,sweep(t)); + svgEl(face,'text',{x,y:y+2,'text-anchor':'middle','font-size':5.8,'font-family':'var(--mono)', + fill:db>0?'var(--fail)':'#3a3128'}).textContent=Math.abs(db);} + svgEl(face,'text',{x:75,y:78,'text-anchor':'middle','font-size':11,'font-weight':700, + 'font-family':'var(--mono)',fill:'#3a3128'}).textContent='VU'; + mcvu.needle=svgEl(face,'line',{x1:cx,y1:cy,x2:cx,y2:cy-62,stroke:'#1a1613','stroke-width':1.6, + transform:`rotate(${sweep(.35)},${cx},${cy})`}); + svgEl(face,'circle',{cx,cy:88,r:9,fill:'#16130f'}); + svgEl(s,'rect',{x:8,y:8,width:134,height:74,rx:3,fill:'url(#vuGlass)'}); + mcvu.sweep=sweep;})(); +function setMcVu(t){t=Math.max(0,Math.min(1.02,t));mcvu.t=t; + mcvu.needle.setAttribute('transform',`rotate(${mcvu.sweep(Math.min(1,t))},75,112)`); + let i=0;while(i<VUDB.length-2&&VUDB[i+1][1]<t)i++; + const [d1,t1]=VUDB[i],[d2,t2]=VUDB[i+1]; + const db=d1+(d2-d1)*Math.max(0,Math.min(1,(t-t1)/(t2-t1))); + setRd('R01',(db>0?'+':'')+db.toFixed(1)+' VU');} +function tickMcVu(){const a=lvl();const target=Math.min(1.02,a*1.12); + setMcVu(mcvu.t+(target-mcvu.t)*(target>mcvu.t?0.32:0.10));} + +/* R02 calibrated vernier dial: the disc turns under a fixed hairline */ +let vernV=42.0,vernDisc=null; +(function(){const s=$('vern'),cx=75,cy=75; + const defs=svgEl(s,'defs',{}); + const fg=svgEl(defs,'radialGradient',{id:'vernFace',cx:'50%',cy:'45%',r:'60%'}); + svgEl(fg,'stop',{offset:'0','stop-color':'#f7f2da'}); + svgEl(fg,'stop',{offset:'1','stop-color':'#e4dfc2'}); + const bk=svgEl(defs,'radialGradient',{id:'bakeKnob',cx:'42%',cy:'36%',r:'75%'}); + svgEl(bk,'stop',{offset:'0','stop-color':'#37332c'}); + svgEl(bk,'stop',{offset:'1','stop-color':'#060505'}); + svgEl(s,'circle',{cx,cy,r:72,fill:'#0c0b0a',stroke:'#262320'}); + vernDisc=svgEl(s,'g',{}); + svgEl(vernDisc,'circle',{cx,cy,r:66,fill:'url(#vernFace)',stroke:'#b7b29a','stroke-width':1}); + for(let u=0;u<=100;u++){const a=u*3,major=u%10===0,half=u%5===0; + const len=major?10:half?7:4.5,[x1,y1]=polar(cx,cy,63,a),[x2,y2]=polar(cx,cy,63-len,a); + svgEl(vernDisc,'line',{x1,y1,x2,y2,stroke:'#3a3128','stroke-width':major?1.3:half?1:.7}); + if(major){const g=svgEl(vernDisc,'g',{transform:`rotate(${a},${cx},${cy})`}); + svgEl(g,'text',{x:cx,y:cy-44,'text-anchor':'middle','font-size':7.5,'font-family':'var(--mono)', + fill:'#3a3128'}).textContent=u;}} + for(let k=0;k<12;k++){const [x,y]=polar(cx,cy,24,k*30); + svgEl(s,'circle',{cx:x,cy:y,r:4.8,fill:'url(#bakeKnob)'});} + svgEl(s,'circle',{cx,cy,r:24,fill:'url(#bakeKnob)',stroke:'#000','stroke-width':.6}); + svgEl(s,'circle',{cx,cy,r:16,fill:'url(#bakeKnob)'}); + svgEl(s,'ellipse',{cx:69,cy:66,rx:7,ry:4,fill:'rgba(255,255,255,.10)',transform:'rotate(-32,69,66)'}); + svgEl(s,'line',{x1:cx,y1:6,x2:cx,y2:22,stroke:'var(--fail)','stroke-width':2.2,'stroke-linecap':'round'});})(); +function setVern(v){vernV=v;vernDisc.setAttribute('transform',`rotate(${-v*3},75,75)`);setRd('R02',v.toFixed(1));} +dragDelta($('vern'),()=>vernV,setVern,{min:0,max:100,sens:0.15});setVern(42.0); + +/* R03 bat-handle toggle */ +let batOn=true,batLever=null,batLblOn=null,batLblOff=null; +(function(){const s=$('batsw'),cx=35,cy=44; + const defs=svgEl(s,'defs',{}); + const ng=svgEl(defs,'linearGradient',{id:'nutG',x1:0,y1:0,x2:0,y2:1}); + svgEl(ng,'stop',{offset:'0','stop-color':'#8a8578'}); + svgEl(ng,'stop',{offset:'1','stop-color':'#4e4a42'}); + const cg=svgEl(defs,'linearGradient',{id:'chromeG',x1:0,y1:0,x2:1,y2:0}); + svgEl(cg,'stop',{offset:'0','stop-color':'#7e7a70'}); + svgEl(cg,'stop',{offset:'.45','stop-color':'#e8e5db'}); + svgEl(cg,'stop',{offset:'1','stop-color':'#8a867c'}); + const tg=svgEl(defs,'radialGradient',{id:'tipG',cx:'40%',cy:'32%',r:'75%'}); + svgEl(tg,'stop',{offset:'0','stop-color':'#f2efe8'}); + svgEl(tg,'stop',{offset:'1','stop-color':'#7e7a70'}); + batLblOn=svgEl(s,'text',{x:cx,y:9,'text-anchor':'middle','font-size':7,'letter-spacing':'.1em', + 'font-family':'var(--mono)',fill:'var(--gold-hi)'});batLblOn.textContent='ON'; + batLblOff=svgEl(s,'text',{x:cx,y:87,'text-anchor':'middle','font-size':7,'letter-spacing':'.1em', + 'font-family':'var(--mono)',fill:'var(--dim)'});batLblOff.textContent='OFF'; + const pts=[];for(let k=0;k<6;k++){const a=(k*60+30)*Math.PI/180;pts.push((cx+17*Math.cos(a))+','+(cy+17*Math.sin(a)));} + svgEl(s,'polygon',{points:pts.join(' '),fill:'url(#nutG)',stroke:'#2b2822'}); + svgEl(s,'circle',{cx,cy,r:9,fill:'#14110e',stroke:'#000'}); + batLever=svgEl(s,'g',{}); + svgEl(batLever,'path',{d:`M ${cx-3.2} ${cy} L ${cx-1.8} 21 L ${cx+1.8} 21 L ${cx+3.2} ${cy} Z`,fill:'url(#chromeG)',stroke:'#4e4a42','stroke-width':.5}); + svgEl(batLever,'ellipse',{cx,cy:17,rx:6.5,ry:8,fill:'url(#tipG)',stroke:'#5e5a52','stroke-width':.6}); + batLever.style.transformOrigin=`${cx}px ${cy}px`;batLever.style.transition='transform .12s'; + $('batsw').onclick=()=>setBat(!batOn);})(); +function setBat(on){batOn=on;batLever.style.transform=on?'rotate(0deg)':'rotate(180deg)'; + batLblOn.setAttribute('fill',on?'var(--gold-hi)':'var(--dim)'); + batLblOff.setAttribute('fill',on?'var(--dim)':'var(--gold-hi)');setRd('R03',on?'ON':'OFF');} +setBat(true); + +/* R04 bakelite fluted knob over a printed 0-10 scale */ +let bakeV=63,bakeIdx=null; +(function(){const s=$('bakek'),cx=55,cy=54; + const defs=svgEl(s,'defs',{}); + const sk=svgEl(defs,'radialGradient',{id:'bakeSk',cx:'42%',cy:'36%',r:'80%'}); + svgEl(sk,'stop',{offset:'0','stop-color':'#312d27'}); + svgEl(sk,'stop',{offset:'1','stop-color':'#050404'}); + const dm=svgEl(defs,'radialGradient',{id:'bakeDome',cx:'40%',cy:'32%',r:'80%'}); + svgEl(dm,'stop',{offset:'0','stop-color':'#3a362f'}); + svgEl(dm,'stop',{offset:'1','stop-color':'#0a0908'}); + for(let i=0;i<=10;i++){const a=-135+i*27,major=i%5===0; + const [x1,y1]=polar(cx,cy,38,a),[x2,y2]=polar(cx,cy,major?45:43.5,a); + svgEl(s,'line',{x1,y1,x2,y2,stroke:'var(--steel)','stroke-width':major?1.5:1}); + if(major){const [x,y]=polar(cx,cy,50.5,a); + svgEl(s,'text',{x,y:y+2.6,'text-anchor':'middle','font-size':8,'font-family':'var(--mono)', + fill:'var(--steel)'}).textContent=i;}} + for(let k=0;k<18;k++){const [x,y]=polar(cx,cy,31,k*20); + svgEl(s,'circle',{cx:x,cy:y,r:3.4,fill:'url(#bakeSk)'});} + svgEl(s,'circle',{cx,cy,r:31,fill:'url(#bakeSk)',stroke:'#000','stroke-width':.6}); + bakeIdx=svgEl(s,'line',{x1:cx,y1:cy-30,x2:cx,y2:cy-21,stroke:'var(--gold-hi)','stroke-width':2.4, + 'stroke-linecap':'round'}); + svgEl(s,'circle',{cx,cy,r:19,fill:'url(#bakeDome)'}); + svgEl(s,'ellipse',{cx:49,cy:46,rx:6,ry:3.5,fill:'rgba(255,255,255,.14)',transform:'rotate(-28,49,46)'});})(); +function setBake(v){bakeV=v;bakeIdx.setAttribute('transform',`rotate(${v*2.7-135},55,54)`); + setRd('R04',(v/10).toFixed(1)+' / 10');} +dragDelta($('bakek'),()=>bakeV,setBake,{min:0,max:100,sens:0.25});setBake(63); + /* ============ LIVE SIGNAL LOOPS ============ */ buildBars($('vuL'),16);buildBars($('vuR'),16);$('mini').innerHTML='<i></i><i></i><i></i><i></i>'; (function(){const eq=$('eq');for(let b=0;b<11;b++){const band=document.createElement('span');band.className='band'; @@ -1120,10 +1270,12 @@ function paintStatic(){ $('scopep').setAttribute('points',Array.from({length:60},(_,i)=>`${i*3},${39+Math.sin(i*0.5)*18}`).join(' '));setRd('N11','Vpp 74'); $('stripp').setAttribute('points',stripHist.map((v,i)=>`${i/(stripHist.length-1)*176},${31-Math.sin(i*0.3)*16}`).join(' '));setRd('N16','50'); tickClock();renderSeven();setRd('N20','DNS');setRd('N22','scrolling');setRd('N25',String(ctrVal).padStart(6,'0')); + setMcVu(.5); } if(!reduced){ setInterval(fastTick,80); + setInterval(tickMcVu,80); setInterval(tickClock,1000);tickClock(); setInterval(tickFlap,1500); setInterval(tickVfd,32); |
