From fe6df41b287bc45a8f65853bf59f6f9297978d9a Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 12 Jul 2026 00:46:58 -0500 Subject: feat(gallery): add R19 waveform region editor A sampler-style edit screen as inline SVG: a monochrome LCD with the sample drawn as bars around a dotted centerline, S and E flags marking the selected region, the selection drawn bright against dim surroundings, and the header and status-bar chrome of the source device. Dragging moves whichever flag is nearer the pointer, and the flags cannot cross. Region selection on a waveform is a new interaction for the kit. The existing waveform strip is display-only. I verified it in headless Chrome: each flag moves independently by proximity, the selection re-brightens, no console exceptions. --- docs/prototypes/panel-widget-gallery.html | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'docs/prototypes/panel-widget-gallery.html') diff --git a/docs/prototypes/panel-widget-gallery.html b/docs/prototypes/panel-widget-gallery.html index 252a368..30478bc 100644 --- a/docs/prototypes/panel-widget-gallery.html +++ b/docs/prototypes/panel-widget-gallery.html @@ -718,6 +718,9 @@ card(C,'R16','Entry keypad', card(C,'R18','Thumb-slide attenuator pair', ``, 'a lit column of numbers, a thumb tab. The scale glows inside the rail; the cream tab rides beside it and the nearest figure lights. Drag either channel. After a compressor blend/mix pair.'); +card(C,'R19','Waveform region editor', + ``, + 'pick a region on the wave. The monochrome LCD shows the sample; drag near either flag to move START or END, and the selection draws bright. After a sampler edit screen.'); /* ============ METERS & GAUGES ============ */ const M=$('meters'); @@ -1672,6 +1675,44 @@ function setThsl(i,v){v=Math.max(0,Math.min(100,Math.round(v)));THSL[i].v=v; setRd('R18',`${THSL[0].name} ${THSL[0].v} · ${THSL[1].name} ${THSL[1].v}`);} setThsl(0,74);setThsl(1,92); +/* R19 waveform region editor: monochrome LCD, draggable S/E flags */ +let wrS=22,wrE=76,wrBars=[],wrFlagS=null,wrFlagE=null; +(function(){const s=$('wregion'),x0=10,x1=180,yMid=48,N=85; + svgEl(s,'rect',{x:1,y:1,width:188,height:98,rx:6,fill:'#0b0c0b',stroke:'#2a2c2a','stroke-width':2}); + svgEl(s,'text',{x:12,y:13,'font-size':7,'font-family':'var(--mono)',fill:'#e8eae8'}).textContent='C1:START'; + svgEl(s,'text',{x:78,y:13,'font-size':7,'font-family':'var(--mono)',fill:'#e8eae8'}).textContent='C2:OFF'; + svgEl(s,'text',{x:136,y:13,'font-size':7,'font-family':'var(--mono)',fill:'#e8eae8'}).textContent='C3:END'; + svgEl(s,'line',{x1:x0,y1:yMid,x2:x1,y2:yMid,stroke:'#3a3c3a','stroke-width':.6,'stroke-dasharray':'1.5 2'}); + for(let i=0;i{const g=svgEl(s,'g',{}); + svgEl(g,'line',{x1:0,y1:18,x2:0,y2:78,stroke:'#f2f4f2','stroke-width':1}); + svgEl(g,'rect',{x:lbl==='S'?0:-8,y:70,width:8,height:8,fill:'#f2f4f2'}); + svgEl(g,'text',{x:lbl==='S'?4:-4,y:76.5,'text-anchor':'middle','font-size':6.5,'font-weight':700, + 'font-family':'var(--mono)',fill:'#0b0c0b'}).textContent=lbl;return g;}; + wrFlagS=flag('S');wrFlagE=flag('E'); + svgEl(s,'rect',{x:10,y:84,width:78,height:11,fill:'#e8eae8'}); + svgEl(s,'text',{x:14,y:92.5,'font-size':6.5,'font-family':'var(--mono)',fill:'#0b0c0b'}).textContent='ENC:ZOOM(1x)'; + svgEl(s,'text',{x:96,y:92.5,'font-size':6.5,'font-family':'var(--mono)',fill:'#e8eae8'}).textContent='A-3 M:[S]'; + svgEl(s,'rect',{x:148,y:84,width:32,height:11,fill:'#e8eae8'}); + svgEl(s,'text',{x:164,y:92.5,'text-anchor':'middle','font-size':6.5,'font-weight':700, + 'font-family':'var(--mono)',fill:'#0b0c0b'}).textContent='MENU'; + s.style.cursor='ew-resize'; + dragX(s,pct=>{ + // move whichever flag is nearer the pointer + if(Math.abs(pct-wrS)<=Math.abs(pct-wrE))setWregion(Math.min(pct,wrE-4),wrE); + else setWregion(wrS,Math.max(pct,wrS+4));});})(); +function setWregion(sv,ev){wrS=Math.max(0,Math.min(96,sv));wrE=Math.max(4,Math.min(100,ev)); + const x0=10,x1=180,xOf=p=>x0+p/100*(x1-x0); + wrFlagS.setAttribute('transform',`translate(${xOf(wrS)},0)`); + wrFlagE.setAttribute('transform',`translate(${xOf(wrE)},0)`); + wrBars.forEach((b,i)=>{const p=i/(wrBars.length-1)*100; + b.setAttribute('fill',(p>=wrS&&p<=wrE)?'#f2f4f2':'#5a5c5a');}); + setRd('R19',`S ${Math.round(wrS)}% · E ${Math.round(wrE)}%`);} +setWregion(22,76); + /* ============ LIVE SIGNAL LOOPS ============ */ buildBars($('vuL'),16);buildBars($('vuR'),16);$('mini').innerHTML=''; (function(){const eq=$('eq');for(let b=0;b<11;b++){const band=document.createElement('span');band.className='band'; -- cgit v1.2.3