aboutsummaryrefslogtreecommitdiff
path: root/docs/prototypes/panel-widget-gallery.html
diff options
context:
space:
mode:
Diffstat (limited to 'docs/prototypes/panel-widget-gallery.html')
-rw-r--r--docs/prototypes/panel-widget-gallery.html188
1 files changed, 12 insertions, 176 deletions
diff --git a/docs/prototypes/panel-widget-gallery.html b/docs/prototypes/panel-widget-gallery.html
index f81b60b..2eae646 100644
--- a/docs/prototypes/panel-widget-gallery.html
+++ b/docs/prototypes/panel-widget-gallery.html
@@ -1868,22 +1868,22 @@ card(I,'R31','Radar sweep',
(st,rd)=>GW.radarSweep(st,{onChange:(v,t)=>rd(t)}),
'<b>the rotating scan.</b> A sweep beam circles the bearing ring, contacts bloom as it passes and fade after. Click to mark the current bearing. After a radar PPI scope.');
card(I,'R35','Day-date disc calendar',
- `<svg id="daydate" class="rsvg press" viewBox="0 0 130 130" width="130" height="130"></svg>`,
+ (st,rd)=>GW.dayDateCal(st,{onChange:(v,t)=>rd(t)}),
'<b>two coaxial discs, one index.</b> The outer ring carries the date, the inner disc the weekday; both read where the yellow hand points. Starts on today; click to roll midnight forward and watch the discs step. After a watch day-date movement.');
card(I,'R36','LED dot matrix',
- `<svg id="dotmx" class="rsvg press" viewBox="0 0 108 108" width="108" height="108"></svg>`,
+ (st,rd)=>GW.dotMatrix(st,{onChange:(v,t)=>rd(t)}),
'<b>a bitmap, one red dot at a time.</b> 64 discrete LEDs behind a tinted window — segments draw digits, but a matrix draws anything. Click dots to paint the pattern; the readout counts what is lit. After the K4816 pattern generator faceplate.');
card(I,'R45','Flip-disc tile array',
- `<svg id="flipdisc" class="rsvg press" viewBox="0 0 120 92" width="120" height="92"></svg>`,
+ (st,rd)=>GW.flipDisc(st,{onChange:(v,t)=>rd(t)}),
'<b>the mechanical pixel.</b> Each disc flips between its yellow face and its black back and then STAYS — no power, no glow, state that persists. Click a disc and watch it flip. The dot matrix glows; this one clacks. After transit flip-dot signs.');
card(I,'R46','Dekatron counting ring',
- `<svg id="dekatron" class="rsvg press" viewBox="0 0 120 120" width="120" height="120"></svg>`,
+ (st,rd)=>GW.dekatron(st,{onChange:(v,t)=>rd(t)}),
'<b>a count as a position, and the counting is visible.</b> Each click is a pulse; the neon glow steps one cathode around the ring and carries on wrap. Nixies show the digit — the Dekatron shows the arithmetic. After glow-transfer counter tubes.');
card(I,'R47','Landing gear indicator',
- `<svg id="gearind" class="rsvg press" viewBox="0 0 140 110" width="140" height="110"></svg>`,
+ (st,rd)=>GW.gearIndicator(st,{onChange:(v,t)=>rd(t)}),
'<b>three greens or nothing.</b> A spatial lamp panel: nose and mains each get a light, green only when down and locked; the amber transit pulse means neither state. Click the wheel lever to cycle the gear. After a cockpit gear panel.');
card(I,'R52','Blinkenlights front panel',
- `<svg id="blinken" class="rsvg press" viewBox="0 0 170 100" width="170" height="100"></svg>`,
+ (st,rd)=>GW.blinkenlights(st,{onChange:(v,t)=>rd(t)}),
'<b>the machine thinking, out loud.</b> Address and data lamps ripple with the live computation; the switch register below is yours — flip bits and watch the pattern change. DIP switches hold config; this shows work happening. After a PDP-11/70 front panel.');
/* ============ CONTROL WIRING ============ */
@@ -1994,179 +1994,15 @@ if(!reduced){
document.querySelectorAll('.reel.spin').forEach(r=>r.style.animationPlayState='paused');
}
-/* R35 day-date disc calendar: coaxial printed discs rotate so today reads under the fixed hand */
-const DD_DAYS=['SUN','MON','TUE','WED','THU','FRI','SAT'];
-let ddDate=1,ddDay=0,ddDateG=null,ddDayG=null;
-(function(){const s=$('daydate'),cx=65,cy=65,HAND=135;
- const defs=svgEl(s,'defs',{});
- const fc=svgEl(defs,'radialGradient',{id:'ddFace',cx:'46%',cy:'40%',r:'75%'});
- svgEl(fc,'stop',{offset:'0','stop-color':'#efe9da'});
- svgEl(fc,'stop',{offset:'1','stop-color':'#cfc8b6'});
- // movement plate + white calendar disc
- svgEl(s,'circle',{cx,cy,r:62,fill:'#17140f',stroke:'#060505','stroke-width':2});
- for(let k=0;k<5;k++){const [x,y]=polar(cx,cy,58,20+k*72);
- svgEl(s,'circle',{cx:x,cy:y,r:1.8,fill:'#060505'});}
- svgEl(s,'circle',{cx,cy,r:54,fill:'url(#ddFace)',stroke:'#8f897b','stroke-width':.8});
- // date ring: 31 radial numerals on its own group
- ddDateG=svgEl(s,'g',{});
- for(let d=1;d<=31;d++){const a=(d-1)*(360/31);
- const g=svgEl(ddDateG,'g',{transform:`rotate(${a},${cx},${cy})`});
- svgEl(g,'text',{x:cx,y:cy-46,'text-anchor':'middle','font-size':7.5,'font-weight':700,
- 'font-family':'var(--mono)',fill:'#14110e'}).textContent=String(d);}
- // inner weekday disc: 7 names twice around, separated by a hairline ring
- svgEl(s,'circle',{cx,cy,r:38,fill:'none',stroke:'#8f897b','stroke-width':.7});
- ddDayG=svgEl(s,'g',{});
- for(let i=0;i<14;i++){const a=i*(360/14);
- const g=svgEl(ddDayG,'g',{transform:`rotate(${a},${cx},${cy})`});
- svgEl(g,'text',{x:cx,y:cy-28,'text-anchor':'middle','font-size':6.8,'font-weight':700,
- 'font-family':'var(--mono)',fill:'#14110e'}).textContent=DD_DAYS[i%7];}
- // fixed yellow hand from hub toward the read index, and the hub
- const [hx,hy]=polar(cx,cy,44,HAND);
- svgEl(s,'line',{x1:cx,y1:cy,x2:hx,y2:hy,stroke:'#e8cf4a','stroke-width':3,'stroke-linecap':'round'});
- svgEl(s,'line',{x1:cx,y1:cy,x2:hx,y2:hy,stroke:'rgba(0,0,0,.25)','stroke-width':.8});
- svgEl(s,'circle',{cx,cy,r:6,fill:'#b8b2a4',stroke:'#5c574c','stroke-width':1.2});
- svgEl(s,'circle',{cx,cy,r:2.4,fill:'#7a7466'});
- s.style.cursor='pointer';
- s.addEventListener('click',()=>{setDdcal(ddDate%31+1,(ddDay+1)%7);});})();
-function setDdcal(d,w){ddDate=d;ddDay=w;const HAND=135;
- ddDateG.setAttribute('transform',`rotate(${(HAND-(d-1)*(360/31)).toFixed(2)},65,65)`);
- ddDayG.setAttribute('transform',`rotate(${(HAND-w*(360/14)).toFixed(2)},65,65)`);
- setRd('R35',DD_DAYS[w]+' '+d);}
-(function(){const now=new Date();setDdcal(now.getDate(),now.getDay());})();
-
-/* R36 LED dot matrix: 8x8 paintable bitmap, starts on the reference K */
-(function(){const s=$('dotmx'),N=8,STEP=11,X0=10.5,Y0=10.5;
- const K8=['10000100','10001000','10010000','11100000','10100000','10010000','10001000','10000100'];
- svgEl(s,'rect',{x:2,y:2,width:104,height:104,rx:8,fill:'#17140f',stroke:'#060505','stroke-width':1.5});
- svgEl(s,'rect',{x:6,y:6,width:96,height:96,rx:5,fill:'#0d0505',stroke:'#241010','stroke-width':1});
- const dots=[];let lit=0;
- const paint=(d,on)=>{d.on=on;
- d.el.setAttribute('fill',on?'#ff4a30':'#2a1210');
- d.el.setAttribute('filter',on?'drop-shadow(0 0 3.5px rgba(255,74,48,.85))':'none');};
- for(let r=0;r<N;r++)for(let c=0;c<N;c++){
- const el=svgEl(s,'circle',{cx:X0+c*STEP+STEP/2,cy:Y0+r*STEP+STEP/2,r:4.1,fill:'#2a1210'});
- el.style.cursor='pointer';
- const d={el,on:false};dots.push(d);
- paint(d,K8[r][c]==='1');if(d.on)lit++;
- el.addEventListener('click',()=>{paint(d,!d.on);lit+=d.on?1:-1;
- setRd('R36',lit+' / 64 lit');});}
- setRd('R36',lit+' / 64 lit');})();
+/* R35, R36 extracted to widgets.js (GW.*) */
+
/* R43, R44 extracted to widgets.js (GW.*) */
-/* R45 flip-disc: bistable mechanical pixels; scaleX flip with color swap at the midpoint */
-(function(){const s=$('flipdisc'),COLS=7,ROWS=5,STEP=14,X0=13,Y0=13;
- const GLYPH=['0000100','0000010','1111111','0000010','0000100']; // arrow right
- svgEl(s,'rect',{x:2,y:2,width:116,height:88,rx:7,fill:'#17140f',stroke:'#060505','stroke-width':1.5});
- let lit=0;
- const discs=[];
- for(let r=0;r<ROWS;r++)for(let c=0;c<COLS;c++){
- const el=svgEl(s,'circle',{cx:X0+c*STEP+STEP/2-2,cy:Y0+r*STEP+STEP/2-2,r:5.6,stroke:'#060505','stroke-width':.8});
- el.style.transformBox='fill-box';el.style.transformOrigin='center';el.style.transition='transform .11s ease-in';
- el.style.cursor='pointer';
- const d={el,on:GLYPH[r][c]==='1'};
- el.setAttribute('fill',d.on?'#e3d44f':'#141210');if(d.on)lit++;
- el.addEventListener('click',()=>{d.on=!d.on;lit+=d.on?1:-1;
- if(matchMedia('(prefers-reduced-motion: reduce)').matches){el.setAttribute('fill',d.on?'#e3d44f':'#141210');}
- else{el.style.transform='scaleX(0)';
- setTimeout(()=>{el.setAttribute('fill',d.on?'#e3d44f':'#141210');el.style.transform='scaleX(1)';},115);}
- setRd('R45',lit+' discs showing');});
- discs.push(d);}
- setRd('R45',lit+' discs showing');})();
-
-/* R46 dekatron: click pulses the neon glow one cathode around; wrap = carry */
-let dkPos=0,dkCarry=0;
-(function(){const s=$('dekatron'),cx=60,cy=60;
- svgEl(s,'circle',{cx,cy,r:52,fill:'#171310',stroke:'#060505','stroke-width':2});
- svgEl(s,'circle',{cx,cy,r:46,fill:'#0d0a08',stroke:'#2a221c','stroke-width':1});
- svgEl(s,'ellipse',{cx:cx-14,cy:cy-18,rx:20,ry:11,fill:'rgba(255,255,255,.05)',transform:`rotate(-24,${cx-14},${cy-18})`});
- const dots=[];
- for(let i=0;i<10;i++){const [x,y]=polar(cx,cy,34,i*36);
- svgEl(s,'text',{x:polar(cx,cy,43,i*36)[0],y:polar(cx,cy,43,i*36)[1]+2,'text-anchor':'middle','font-size':5.2,
- 'font-family':'var(--mono)',fill:'var(--steel)'}).textContent=String(i);
- dots.push(svgEl(s,'circle',{cx:x,cy:y,r:3.4,fill:'#3a2a20'}));}
- const carry=svgEl(s,'circle',{cx,cy:cy-6,r:3,fill:'#2a1c14'});
- svgEl(s,'text',{x:cx,y:cy+8,'text-anchor':'middle','font-size':4.6,'letter-spacing':'.12em',
- 'font-family':'var(--mono)',fill:'var(--steel)'}).textContent='CARRY';
- const draw=()=>{dots.forEach((d,i)=>{const on=i===dkPos;
- d.setAttribute('fill',on?'#ff9a4c':'#3a2a20');
- d.setAttribute('filter',on?'drop-shadow(0 0 5px rgba(255,154,76,.9))':'none');});
- setRd('R46','COUNT '+dkPos+(dkCarry?' · CARRY x'+dkCarry:''));};
- s.style.cursor='pointer';
- s.addEventListener('click',()=>{dkPos=(dkPos+1)%10;
- if(dkPos===0){dkCarry++;carry.setAttribute('fill','#ff9a4c');
- carry.setAttribute('filter','drop-shadow(0 0 4px rgba(255,154,76,.9))');
- setTimeout(()=>{carry.setAttribute('fill','#2a1c14');carry.removeAttribute('filter');},220);}
- draw();});
- draw();})();
-
-/* R47 landing gear: three greens or nothing; lever cycles down -> transit -> up */
-(function(){const s=$('gearind');
- svgEl(s,'rect',{x:2,y:2,width:136,height:106,rx:9,fill:'#1c1916',stroke:'#060505','stroke-width':1.5});
- const POS=[['NOSE',48,28],['L',24,74],['R',72,74]];
- const lamps={};
- POS.forEach(([n,x,y])=>{
- svgEl(s,'circle',{cx:x,cy:y,r:9,fill:'#0d0b09',stroke:'#2c2820','stroke-width':1.5});
- lamps[n]=svgEl(s,'circle',{cx:x,cy:y,r:6.2,fill:'#16240f'});
- svgEl(s,'text',{x,y:y+18,'text-anchor':'middle','font-size':5.2,'letter-spacing':'.08em',
- 'font-family':'var(--mono)',fill:'var(--steel)'}).textContent=n;});
- // gear lever: slot + wheel-shaped knob
- svgEl(s,'rect',{x:106,y:22,width:8,height:66,rx:4,fill:'#0d0b09',stroke:'#2c2820','stroke-width':1});
- const lever=svgEl(s,'g',{});
- svgEl(lever,'rect',{x:107.5,y:36,width:5,height:26,fill:'#8f8a7e'});
- svgEl(lever,'circle',{cx:110,cy:32,r:8,fill:'#26221c',stroke:'#4a463e','stroke-width':2.4});
- svgEl(lever,'circle',{cx:110,cy:32,r:3,fill:'#4a463e'});
- svgEl(s,'text',{x:110,y:100,'text-anchor':'middle','font-size':5.2,'letter-spacing':'.08em',
- 'font-family':'var(--mono)',fill:'var(--steel)'}).textContent='GEAR';
- let state='down',busy=false;
- const paint=()=>{const c=state==='down'?'var(--pass)':state==='transit'?'var(--amber-warn)':'#16240f';
- for(const n of Object.keys(lamps)){lamps[n].setAttribute('fill',c);
- lamps[n].setAttribute('filter',state==='down'?'drop-shadow(0 0 4px rgba(116,147,47,.8))':
- state==='transit'?'drop-shadow(0 0 4px rgba(var(--glow-lo),.7))':'none');
- lamps[n].style.animation=state==='transit'?'pulse var(--pulse-rate) ease-in-out infinite':'none';}
- lever.setAttribute('transform',state==='up'?'translate(0,-8)':state==='transit'?'translate(0,-4)':'');
- setRd('R47',state==='down'?'GEAR DOWN · 3 GREEN':state==='up'?'GEAR UP':'IN TRANSIT');};
- s.style.cursor='pointer';
- s.addEventListener('click',()=>{if(busy)return;
- const target=state==='down'?'up':'down';
- if(matchMedia('(prefers-reduced-motion: reduce)').matches){state=target;paint();return;}
- busy=true;state='transit';paint();
- setTimeout(()=>{state=target;busy=false;paint();},1500);});
- paint();})();
-
-/* R52 blinkenlights: live register lamps driven by a pseudo-PC that folds in the switch register */
-(function(){const s=$('blinken');
- svgEl(s,'rect',{x:2,y:2,width:166,height:96,rx:8,fill:'#3a1420',stroke:'#060505','stroke-width':1.5});
- svgEl(s,'rect',{x:8,y:8,width:154,height:62,rx:5,fill:'#14090e'});
- const mkRow=(y,lbl)=>{const row=[];
- svgEl(s,'text',{x:12,y:y+2.6,'font-size':4.6,'letter-spacing':'.08em','font-family':'var(--mono)',
- fill:'#b08a96'}).textContent=lbl;
- for(let i=0;i<12;i++)row.push(svgEl(s,'circle',{cx:46+i*10,cy:y,r:3,fill:'#2a1210'}));
- return row;};
- const addr=mkRow(24,'ADDR'),data=mkRow(46,'DATA');
- const paint=(row,val)=>row.forEach((d,i)=>{const on=(val>>(11-i))&1;
- d.setAttribute('fill',on?'#ffb43a':'#2a1210');
- d.setAttribute('filter',on?'drop-shadow(0 0 3px rgba(255,180,58,.8))':'none');});
- // switch register: 8 mini toggles
- let sr=0b10100101;
- const toggles=[];
- for(let i=0;i<8;i++){const x=46+i*14;
- svgEl(s,'rect',{x:x-3,y:76,width:6,height:16,rx:3,fill:'#0d0b09',stroke:'#4a2432','stroke-width':1});
- const t=svgEl(s,'circle',{cx:x,cy:80,r:4,fill:'#8f8a7e',stroke:'#3c382f','stroke-width':1});
- t.style.cursor='pointer';
- const hit=svgEl(s,'rect',{x:x-7,y:72,width:14,height:24,fill:'transparent'});
- hit.style.cursor='pointer';
- const flip=()=>{sr^=(1<<(7-i));draw();};
- hit.addEventListener('click',flip);
- toggles.push(t);}
- svgEl(s,'text',{x:12,y:83,'font-size':4.6,'letter-spacing':'.08em','font-family':'var(--mono)',
- fill:'#b08a96'}).textContent='SR';
- let pc=0o1234;
- const draw=()=>{toggles.forEach((t,i)=>t.setAttribute('cy',((sr>>(7-i))&1)?76:84));
- setRd('R52','SR '+sr.toString(8).padStart(3,'0')+' (octal)');};
- const tick=()=>{pc=((pc*5+sr+1)&0xFFF);paint(addr,pc);paint(data,(pc^(pc<<3)^sr)&0xFFF);};
- draw();tick();
- if(!matchMedia('(prefers-reduced-motion: reduce)').matches)setInterval(tick,250);})();
+/* R45-R47, R52 extracted to widgets.js (GW.*) */
+
+
+
/* R53-R56 extracted to widgets.js (GW.*) */