aboutsummaryrefslogtreecommitdiff
path: root/docs/prototypes/panel-widget-gallery.html
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-12 00:34:45 -0500
committerCraig Jennings <c@cjennings.net>2026-07-12 00:34:45 -0500
commit77d9db01ff383a486ae4dde80e2bf3fc31fb9413 (patch)
treef174523eea01b1afbed57b70976675faebf54600 /docs/prototypes/panel-widget-gallery.html
parentc35b6264208c0f5fa0b0014bb51b4aeaa8ce7d21 (diff)
downloadarchsetup-77d9db01ff383a486ae4dde80e2bf3fc31fb9413.tar.gz
archsetup-77d9db01ff383a486ae4dde80e2bf3fc31fb9413.zip
feat(gallery): add R16 entry keypad, a composed instrument
R16 is the first composed card: a display window, an indicator lamp pod, and a 4x3 key grid working as one instrument. Digits fill the amber display (dim dashes for empty positions), the top lamp lights while the buffer holds anything, the amber check key confirms and the amber cross key clears with a lamp flash. The keycaps are worn gray with per-key numeral jitter, after an industrial keypad prop (reference filed in the catalogue's working directory). I verified the flow in headless Chrome: digits append, confirm reports the code and empties the buffer, clear flashes the lamp, no console exceptions.
Diffstat (limited to 'docs/prototypes/panel-widget-gallery.html')
-rw-r--r--docs/prototypes/panel-widget-gallery.html45
1 files changed, 45 insertions, 0 deletions
diff --git a/docs/prototypes/panel-widget-gallery.html b/docs/prototypes/panel-widget-gallery.html
index 1ecfbaf..04cd0cf 100644
--- a/docs/prototypes/panel-widget-gallery.html
+++ b/docs/prototypes/panel-widget-gallery.html
@@ -712,6 +712,9 @@ card(C,'R14','Spade-pointer tuning knob',
card(C,'R15','Multi-band dial',
`<svg id="mband" class="rsvg" viewBox="0 0 190 110" width="190" height="110"></svg>`,
'<b>four scales, one needle.</b> Nested band arcs on a cream face; the active ring reads bold. Drag the left dial to tune; click the bandspread dial to switch bands. After a shortwave receiver dial pair.');
+card(C,'R16','Entry keypad',
+ `<svg id="keypad" class="rsvg" viewBox="0 0 150 190" width="150" height="190"></svg>`,
+ '<b>a composed entry instrument.</b> Worn keycaps feed the amber display; the lamp watches the buffer; the amber keys confirm and clear. Click the keys. After an industrial keypad prop.');
/* ============ METERS & GAUGES ============ */
const M=$('meters');
@@ -1554,6 +1557,48 @@ function setMband(v,b){mbandV=Math.max(0,Math.min(100,v));mbandB=b;
setRd('R15',`B${b+1} · ${mc.toFixed(2)} Mc`);}
setMband(45,2);
+/* R16 entry keypad: worn keys feed the amber display; lamps watch state */
+let kpBuf='',kpDisp=null,kpLampTop=null,kpLampBot=null;
+(function(){const s=$('keypad');
+ const defs=svgEl(s,'defs',{});
+ const kg=svgEl(defs,'linearGradient',{id:'kpKey',x1:0,y1:0,x2:0,y2:1});
+ svgEl(kg,'stop',{offset:'0','stop-color':'#dbd8cf'});
+ svgEl(kg,'stop',{offset:'1','stop-color':'#a29d92'});
+ const ag=svgEl(defs,'linearGradient',{id:'kpAmber',x1:0,y1:0,x2:0,y2:1});
+ svgEl(ag,'stop',{offset:'0','stop-color':'var(--amber-grad-top)'});
+ svgEl(ag,'stop',{offset:'1','stop-color':'var(--amber-grad-mid)'});
+ // lamp pod
+ const lamp=cy=>{const l=svgEl(s,'circle',{cx:13,cy,r:6.5,fill:'#3a0f0a',stroke:'#14100e','stroke-width':1.5});return l;};
+ kpLampTop=lamp(20);kpLampBot=lamp(38);
+ // display window
+ svgEl(s,'rect',{x:26,y:8,width:116,height:32,rx:5,fill:'#0a0806',stroke:'#2c261d','stroke-width':2});
+ kpDisp=svgEl(s,'text',{x:84,y:31,'text-anchor':'middle','font-size':16,'letter-spacing':'.22em',
+ 'font-family':'var(--mono)',fill:'var(--gold-hi)'});
+ // key plate
+ svgEl(s,'rect',{x:24,y:48,width:118,height:138,rx:8,fill:'#1a1714',stroke:'#0a0908','stroke-width':1.5});
+ const KEYS=[['1','2','3'],['4','5','6'],['7','8','9'],['✓','0','✗']];
+ const jitter=[-2,1,-1,2,0,-2,1,-1,2,0,-1,1];
+ KEYS.forEach((row,r)=>row.forEach((k,c)=>{
+ const x=47+c*36,y=68+r*32,amber=(k==='✓'||k==='✗');
+ const g=svgEl(s,'g',{});g.style.cursor='pointer';g.style.transition='transform .07s';
+ svgEl(g,'rect',{x:x-14,y:y-11,width:28,height:24,rx:4,fill:amber?'url(#kpAmber)':'url(#kpKey)',
+ stroke:'#4e4a42','stroke-width':1,'stroke-opacity':.8});
+ svgEl(g,'text',{x,y:y+6,'text-anchor':'middle','font-size':13,'font-weight':700,
+ 'font-family':'var(--mono)',fill:k==='✓'?'#3f5a1f':k==='✗'?'#7a2a1a':'#2b2721',
+ transform:`rotate(${jitter[r*3+c]},${x},${y})`}).textContent=k;
+ g.addEventListener('click',()=>{g.style.transform='translateY(1.5px)';
+ setTimeout(()=>g.style.transform='',80);kpPress(k);});}));
+ })();
+function kpRender(){kpDisp.textContent=kpBuf.padEnd(6,'–');
+ kpLampTop.setAttribute('fill',kpBuf?'var(--jewel-r)':'#3a0f0a');}
+function kpPress(k){
+ if(k==='✓'){setRd('R16',kpBuf?'OK · '+kpBuf:'empty');kpBuf='';kpRender();return;}
+ if(k==='✗'){kpBuf='';kpRender();setRd('R16','cleared');
+ kpLampBot.setAttribute('fill','var(--jewel-r)');
+ setTimeout(()=>kpLampBot.setAttribute('fill','#3a0f0a'),350);return;}
+ if(kpBuf.length<6){kpBuf+=k;kpRender();setRd('R16',kpBuf);}}
+kpRender();setRd('R16','enter a code');
+
/* ============ 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';