From fad637f3502ec28a9d9984a7daf216b23e2a466d Mon Sep 17 00:00:00 2001 From: John Wallace Date: Thu, 15 Oct 2026 13:01:02 -0400 Subject: [PATCH] Connect Sources table and inspector to expanded grading review --- CLAUDE.md | 6 ++++ site-assets/reviews/sources-table/grade.css | 3 ++ site-assets/reviews/sources-table/grade.js | 35 ++++++++++++++++++++ site-assets/reviews/sources-table/index.html | 9 ++--- site-assets/reviews/sources-table/table.js | 7 ++-- 5 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 site-assets/reviews/sources-table/grade.css create mode 100644 site-assets/reviews/sources-table/grade.js diff --git a/CLAUDE.md b/CLAUDE.md index 8345bd6..19c71d8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1726,3 +1726,9 @@ design review, not desktop behavior. Source format/FPS, MV and ISO stay visible; optional columns persist in browser storage and narrow views expose secondary values through row Details. ISO arm is explicitly distinct from writer state. Its feedback is isolated under `sources-table-v1`; preserve prior proposals. + +The table mockup exposes Grade selected in the toolbar and Grade in the +inspector. Both open the source-bound expanded grade dialog, then return to +the same table state. Picture/scopes are explicitly illustrative, not sampled +video or native processing. ROI is scopes-only; the LUT/native stack remains +a preservation requirement rather than a claim of complete web simulation. diff --git a/site-assets/reviews/sources-table/grade.css b/site-assets/reviews/sources-table/grade.css new file mode 100644 index 0000000..c6cc222 --- /dev/null +++ b/site-assets/reviews/sources-table/grade.css @@ -0,0 +1,3 @@ +#grade-dialog{width:min(1450px,96vw);max-width:96vw;max-height:94vh;padding:0;background:#101b22;color:#e7f0f3;border:1px solid #587582;border-radius:12px;overflow:auto}#grade-dialog::backdrop{background:#02080dd9}.grade-header{display:flex;justify-content:space-between;gap:20px;padding:18px 22px;border-bottom:1px solid #344d59}.grade-header h2{margin:5px 0}.grade-header p{font-size:12px;margin:5px 0}.grade-header button{align-self:start}.grade-warning{background:#20372f;padding:10px 22px;font-size:12px;color:#bad5cc;line-height:1.5}.grade-workspace{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:20px;padding:20px}.grade-monitor{min-width:0}.grade-monitor-tools{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:12px}.grade-monitor-tools label{font-size:12px;margin-left:auto}.grade-stage{aspect-ratio:16/9;background:#182b38;border:1px solid #52707d;position:relative;overflow:hidden;border-radius:7px;touch-action:none;cursor:crosshair}.grade-stage>svg,#grade-art{position:absolute;inset:0;width:100%;height:100%}#grade-art{background:linear-gradient(120deg,#275469,#63536d 60%,#31444b)}.grade-art-label{position:absolute;top:12px;left:14px;font-size:10px;color:#c0d0da}.grade-subject{position:absolute;inset:13% 20% 0}.grade-subject span{display:block;position:absolute;left:36%;top:0;width:28%;height:45%;border-radius:50%;background:#aac7bd}.grade-subject i{position:absolute;left:7%;top:48%;width:86%;height:90%;border-radius:50% 50% 0 0;background:#5d908a}.grade-stage>strong{position:absolute;bottom:16px;left:18px;font-size:14px}#grade-region-guide rect,#grade-region-guide ellipse{stroke:#8ae0ca;stroke-width:3;fill:#8ae0ca20;vector-effect:non-scaling-stroke}.scope-toolbar{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin:12px 0;font-size:12px;color:#bdd4dc}.scope-toolbar select,.grade-controls select,.grade-controls input[type=number]{background:#0c151b;color:#e7f0f3;border:1px solid #4f6b79;border-radius:5px;padding:8px;font:inherit}.scope-note{font-size:11px!important;margin:10px 0;color:#a2b9c5}.grade-scopes{display:grid;grid-template-columns:1fr 1fr .7fr;gap:10px}.grade-scopes section{background:#0b141b;border:1px solid #2e4a58;border-radius:5px;padding:10px;min-width:0}.grade-scopes h3{font-size:11px;margin:0 0 12px}.grade-scopes h3 span{font-weight:400;float:right;color:#93adbc;font-size:9px}.grade-scopes svg{width:100%;height:auto;min-height:120px;display:block}.scope-grid{fill:none;stroke:#3d5361;stroke-width:1}.scope-red,.scope-green,.scope-blue{fill:none;stroke-width:2;stroke-linejoin:round}.scope-red{stroke:#ff797f}.scope-green{stroke:#87dfba}.scope-blue{stroke:#86acff}.grade-scopes text{fill:#a8c4d2;font-size:8px}.grade-controls{background:#1a2b35;padding:16px;border-radius:7px;min-width:0}.grade-controls .check{font-size:12px;display:block}.grade-controls p,.grade-controls li{font-size:12px}.grade-controls label:not(.check){display:block;margin:16px 0;font-size:12px}.grade-controls label>select{display:block;width:100%;margin-top:6px}.grade-tabs{display:flex;gap:5px;margin:18px 0}.grade-tabs button{flex:1;padding:9px 5px}.grade-tabs [aria-selected=true],.grade-monitor-tools [aria-pressed=true]{background:#8ae0ca;color:#10251e}.grade-control-row{margin-bottom:18px}.grade-control-row label{margin-bottom:7px!important}.grade-control-row output{float:right}.grade-control-row>div{display:flex;align-items:center;gap:8px}.grade-controls input[type=range]{width:100%;min-width:50px;accent-color:#8ae0ca}.grade-controls input[type=number]{width:62px;padding:6px}.grade-control-row button{padding:6px}.grade-curve{width:100%;background:#0c151b;border:1px solid #3d5967;border-radius:6px}#grade-curve-path{fill:none;stroke:#a8e9d4;stroke-width:3}#grade-curve-point{fill:#e7f0f3}.grade-controls details{margin-top:20px;border-top:1px solid #47606e;padding-top:16px}.grade-controls summary{font-size:12px}.grade-footer{display:flex;align-items:center;justify-content:end;gap:12px;padding:14px 22px;background:#1b2e38;border-top:1px solid #45616e;position:sticky;bottom:0}.grade-footer span{margin-right:auto;font-size:11px;color:#abc4d1}#grade-edit-status{color:#c1d8d0}.grade-stage:focus-visible{outline:2px solid #8ae0ca;outline-offset:3px}@media(max-width:1000px){.grade-workspace{grid-template-columns:minmax(0,1fr)}.grade-controls{display:block}.grade-scopes svg{min-height:90px}.grade-footer{flex-wrap:wrap}.grade-footer span{width:100%}}@media(max-width:600px){#grade-dialog{width:98vw;max-width:98vw;max-height:96vh}.grade-header,.grade-warning{padding:12px}.grade-workspace{padding:10px;gap:12px}.grade-header h2{font-size:18px}.grade-monitor-tools{gap:5px}.grade-monitor-tools button{font-size:10px;padding:7px}.grade-monitor-tools label{margin-left:0}.grade-scopes{grid-template-columns:1fr}.grade-scopes svg{max-height:170px}.grade-scopes h3{font-size:12px}.grade-footer{padding:12px;gap:8px}.grade-footer button{font-size:11px}} + +.grade-header{padding:12px 18px}.grade-warning{padding:8px 18px;font-size:11px}.grade-stage{max-width:min(100%,calc(35vh * 16 / 9));margin:0 auto}.expanded-picture .grade-stage{max-width:none}.grade-scopes svg{height:115px;min-height:0}.grade-monitor-tools label{margin-left:0}.grade-monitor-tools{justify-content:flex-start}@media(max-width:1000px){.grade-stage{max-width:none}.grade-scopes svg{height:115px}} diff --git a/site-assets/reviews/sources-table/grade.js b/site-assets/reviews/sources-table/grade.js new file mode 100644 index 0000000..d8d1e39 --- /dev/null +++ b/site-assets/reviews/sources-table/grade.js @@ -0,0 +1,35 @@ +(()=>{ +const $=id=>document.getElementById(id),dialog=$('grade-dialog'); +const controls=[['brightness','Brightness',50,150,100],['contrast','Contrast',50,150,100],['saturation','Saturation',0,200,100],['intensity','Overall intensity',0,100,100]]; +const neutral=()=>({brightness:100,contrast:100,saturation:100,intensity:100,bypass:false,channel:'Master',midpoints:{Master:0,Red:0,Green:0,Blue:0},preset:'neutral'}); +const applied=new Map(),regions=new Map();let target=null,draft=neutral(),roi=null,mode='move',drag=null,live=false,opener=null; +$('grade-sliders').innerHTML=controls.map(([key,label,min,max,value])=>`
`).join(''); +function saveApplied(){applied.set(target.id,structuredClone(draft));const active=!draft.bypass&&(controls.some(control=>draft[control[0]]!==control[4])||Object.values(draft.midpoints).some(v=>v!==0));window.dispatchEvent(new CustomEvent('corevideo-grade-applied',{detail:{id:target.id,active}}));} +function scopeLabel(){const tap=$('grade-scope-tap').value==='graded'?'Graded draft':'Original source';$('grade-roi-summary').textContent=roi?`${roi.shape==='circle'?'Circle':'Rectangle'} ROI · x ${(roi.x*100).toFixed(1)}% / y ${(roi.y*100).toFixed(1)}% · ${(roi.w*100).toFixed(1)} × ${(roi.h*100).toFixed(1)}%`:'Full-frame scope analysis';$('grade-scope-label').textContent=`Illustrative scopes · ${tap} · ${roi?'ROI':'Full frame'} · not live measurements`;} +function drawRegion(){const rect=$('grade-rect'),circle=$('grade-circle');rect.hidden=!roi||roi.shape!=='rectangle';circle.hidden=!roi||roi.shape!=='circle';rect.toggleAttribute('hidden',rect.hidden);circle.toggleAttribute('hidden',circle.hidden);rect.style.display=rect.hidden?'none':'block';circle.style.display=circle.hidden?'none':'block';if(roi){rect.setAttribute('x',roi.x*1000);rect.setAttribute('y',roi.y*562.5);rect.setAttribute('width',roi.w*1000);rect.setAttribute('height',roi.h*562.5);circle.setAttribute('cx',(roi.x+roi.w/2)*1000);circle.setAttribute('cy',(roi.y+roi.h/2)*562.5);circle.setAttribute('rx',roi.w*500);circle.setAttribute('ry',roi.h*281.25);}if(target)regions.set(target.id,roi?{...roi}:null);scopeLabel();} +function render(){ + for(const [key] of controls){$('grade-'+key).value=$('grade-'+key+'-exact').value=draft[key];$('grade-'+key+'-out').textContent=draft[key]+'%';} + $('grade-bypass').checked=draft.bypass;$('grade-live').checked=live;$('grade-channel').value=draft.channel;$('grade-preset').value=draft.preset;$('grade-curve-mid').value=draft.midpoints[draft.channel]; + const curveY=120-draft.midpoints[draft.channel]*2;$('grade-curve-path').setAttribute('d',`M0 240Q120 ${curveY} 240 0`);$('grade-curve-point').setAttribute('cy',120-draft.midpoints[draft.channel]);$('grade-curve-path').style.stroke={Master:'#b0f2d9',Red:'#ff797f',Green:'#87dfba',Blue:'#86acff'}[draft.channel]; + const strength=draft.intensity/100,identity=draft.bypass||$('grade-compare').checked; + $('grade-art').style.filter=identity?'none':`brightness(${1+(draft.brightness/100-1)*strength}) contrast(${1+(draft.contrast/100-1)*strength}) saturate(${1+(draft.saturation/100-1)*strength})`; + $('grade-edit-status').textContent=live?'Live-edit simulation ON. Changes update the simulated applied reference immediately.':'Draft only. Apply and return commits this simulated source grade.';drawRegion(); +} +function change(){if(live)saveApplied();render();} +window.addEventListener('corevideo-grade-open',event=>{target=event.detail;opener=document.activeElement;draft=structuredClone(applied.get(target.id)||neutral());roi=regions.get(target.id)||null;live=false;mode='move';$('grade-compare').checked=false;$('grade-expand').checked=false;dialog.classList.remove('expanded-picture');$('grade-scope-tap').value='graded';$('grade-title').textContent=`Grade · ${target.name}`;$('grade-preview-source').textContent=target.name;$('grade-source-info').textContent=`Input ${String(target.input).padStart(2,'0')} · ${target.format} / ${target.fps} fps${target.online?' · simulated source':' · source offline; layout demonstration only'}`;setMode('move');gradeTab('primaries');render();dialog.showModal();}); +function close(commit){if(commit)saveApplied();dialog.close();opener?.focus({preventScroll:true});} +$('grade-expand').addEventListener('change',()=>dialog.classList.toggle('expanded-picture',$('grade-expand').checked)); +$('grade-apply').addEventListener('click',()=>close(true));$('grade-discard').addEventListener('click',()=>close(false));$('grade-close').addEventListener('click',()=>close(false));dialog.addEventListener('cancel',event=>{event.preventDefault();close(false);}); +for(const [key,,min,max,defaultValue] of controls){function update(value){if(!Number.isFinite(value)||valuemax){$('grade-edit-status').textContent=`Enter ${min}–${max}%. The current grade is unchanged.`;return;}draft[key]=value;change();}$('grade-'+key).addEventListener('input',()=>update(Number($('grade-'+key).value)));$('grade-'+key+'-exact').addEventListener('change',()=>update($('grade-'+key+'-exact').value.trim()===''?NaN:Number($('grade-'+key+'-exact').value)));document.querySelector(`[data-grade-reset="${key}"]`).addEventListener('click',()=>update(defaultValue));} +$('grade-preset').addEventListener('change',()=>{const choice=$('grade-preset').value;draft={...neutral(),preset:choice,...(choice==='warm'?{brightness:108,contrast:112,saturation:120}:choice==='mono'?{saturation:0}:{})};change();}); +$('grade-live').addEventListener('change',()=>{live=$('grade-live').checked;if(live)saveApplied();render();});$('grade-bypass').addEventListener('change',()=>{draft.bypass=$('grade-bypass').checked;change();});$('grade-compare').addEventListener('change',render);$('grade-channel').addEventListener('change',()=>{draft.channel=$('grade-channel').value;render();});$('grade-curve-mid').addEventListener('input',()=>{draft.midpoints[draft.channel]=Number($('grade-curve-mid').value);change();});$('grade-scope-tap').addEventListener('change',scopeLabel); +function gradeTab(name){for(const button of document.querySelectorAll('[data-grade-tab]')){const on=button.dataset.gradeTab===name;button.setAttribute('aria-selected',String(on));button.tabIndex=on?0:-1;$('grade-'+button.dataset.gradeTab).hidden=!on;}} +document.querySelectorAll('[data-grade-tab]').forEach(button=>{button.addEventListener('click',()=>gradeTab(button.dataset.gradeTab));button.addEventListener('keydown',event=>{if(!['ArrowLeft','ArrowRight','Home','End'].includes(event.key))return;event.preventDefault();const tabs=[...document.querySelectorAll('[data-grade-tab]')],i=event.key==='Home'?0:event.key==='End'?2:(tabs.indexOf(button)+(event.key==='ArrowRight'?1:2))%3;gradeTab(tabs[i].dataset.gradeTab);tabs[i].focus();});}); +function setMode(value){mode=value;for(const button of document.querySelectorAll('[data-roi-mode]'))button.setAttribute('aria-pressed',String(button.dataset.roiMode===mode));} +document.querySelectorAll('[data-roi-mode]').forEach(button=>button.addEventListener('click',()=>setMode(button.dataset.roiMode)));$('grade-full-frame').addEventListener('click',()=>{roi=null;drawRegion();}); +const clamp=(n,min,max)=>Math.max(min,Math.min(max,n));function point(event){const r=$('grade-stage').getBoundingClientRect();return {x:clamp((event.clientX-r.left)/r.width,0,1),y:clamp((event.clientY-r.top)/r.height,0,1)};} +$('grade-stage').addEventListener('pointerdown',event=>{if(event.button!==0)return;const p=point(event);if(mode==='move'&&(!roi||p.xroi.x+roi.w||p.yroi.y+roi.h))return;event.preventDefault();$('grade-stage').focus();drag={start:p,prior:roi?{...roi}:null};$('grade-stage').setPointerCapture(event.pointerId);}); +$('grade-stage').addEventListener('pointermove',event=>{if(!drag)return;const p=point(event);if(mode==='move'){roi={...drag.prior,x:clamp(drag.prior.x+p.x-drag.start.x,0,1-drag.prior.w),y:clamp(drag.prior.y+p.y-drag.start.y,0,1-drag.prior.h)};}else if(mode==='circle'){const aspect=$('grade-stage').clientWidth/$('grade-stage').clientHeight,w=Math.min(Math.abs(p.x-drag.start.x),Math.abs(p.y-drag.start.y)/aspect),h=w*aspect;if(w>.005)roi={shape:mode,x:p.x{if(!roi||!['ArrowLeft','ArrowRight','ArrowUp','ArrowDown'].includes(event.key))return;event.preventDefault();const n=event.shiftKey ? .01 : .001;roi.x=clamp(roi.x+(event.key==='ArrowLeft'?-n:event.key==='ArrowRight'?n:0),0,1-roi.w);roi.y=clamp(roi.y+(event.key==='ArrowUp'?-n:event.key==='ArrowDown'?n:0),0,1-roi.h);drawRegion();}); +})(); diff --git a/site-assets/reviews/sources-table/index.html b/site-assets/reviews/sources-table/index.html index 1d5e164..33d13cc 100644 --- a/site-assets/reviews/sources-table/index.html +++ b/site-assets/reviews/sources-table/index.html @@ -1,9 +1,10 @@ -Sources table + inspector · CoreVideo review
← CoreVideo reviewsLeave feedback
Alternative proposal · Table first · Needs review

Keep the overview.
Open the detail when you need it.

The table remains the main workspace. Check every source quickly, use MV and ISO controls in the row, and open an inspector for the selected input.

+Sources table + inspector · CoreVideo review
← CoreVideo reviewsLeave feedback
Alternative proposal · Table first · Needs review

Keep the overview.
Open the detail when you need it.

The table remains the main workspace. Check every source quickly, use MV and ISO controls in the row, and open an inspector for the selected input.

Compare the same populated show

Try: scan format/FPS → toggle Input 2’s MV → inspect Alice → close inspector. Other assignments and scene routes stay put.

-
▦ CoreVideo ProSIMULATED SHOWSources
1 · Assign inputs / Multiview2 · Route outputs3 · Compose scenes / Take
Columns
Secondary overview columns

Format, FPS, MV and ISO stay visible. Column choices are saved in this browser. At narrow widths, secondary fields remain available in each row’s Details.

Zoom quality

Requested limits, not delivered signal. They do not rewrite the observed Format/FPS cells. Simulation only.

+
▦ CoreVideo ProSIMULATED SHOWSources
1 · Assign inputs / Multiview2 · Route outputs3 · Compose scenes / Take
Columns
Secondary overview columns

Format, FPS, MV and ISO stay visible. Column choices are saved in this browser. At narrow widths, secondary fields remain available in each row’s Details.

Zoom quality

Requested limits, not delivered signal. They do not rewrite the observed Format/FPS cells. Simulation only.

FORMAT / FPS = observed source signal · all demo values
#SourceTypeFormatFPSSignalAudioMVISODetails

MV inclusion does not change a scene or Take. ISO shows arm state separately from writer state. Empty and offline inputs keep their numbers and assignments.

-
Table overview. Select Inspect to open details; no scene change is implied.
+
Table overview. Select Inspect to open details; no scene change is implied.

What this changes—and what it keeps

Kept in the overview

Ten stable slots, category/type, observed format and FPS, signal, audio summary, quick MV and separate ISO state. Secondary columns are customizable; important data is not replaced by a big preview.

Opened on demand

The selected input’s larger preview, assignment, lower-third text and detailed controls. Close the inspector to recover the full-width table. Selection and filtering never reassign a source.

Same production workflow

Sources assigns inputs and MV inclusion. Routing owns output routes; Scenes owns composition and Take. Detailed grade, audio, recording and webcam-only overlays remain their existing workflows.

At narrower widths the inspector stacks below the table. Secondary columns move into row Details so Format/FPS, MV and ISO remain available without horizontal page scrolling.

Open the full preservation requirements →

Does the table + inspector strike the right balance?

No login needed. Notes are saved privately for CoreVideo for 180 days. Unsent drafts stay in this browser. Pick an area and mention the control or screenshot you mean.

-
Design alternative only · Not installed or approved · Compare collapsed and expanded views before choosing a direction.
+
Design alternative only · Not installed or approved · Compare collapsed and expanded views before choosing a direction.
PER-SOURCE GRADE · INTERACTIVE LAYOUT MOCKUP

Illustrative picture and scopes, not sampled video or native grading. Nothing is sent to your app. Returning keeps the table, selection, MV and ISO state.
ILLUSTRATIVE SOURCE PICTURE
Full-frame scope analysis

ROI affects scopes only. The grade still covers the whole source. No crop, local grade mask or outgoing ROI guide is created.

Histogram RGB · 0–255

Waveform Luma · 0–100 IRE

Vectorscope 75% targets

RMgBCyGYl

Illustrative scopes · Graded draft · Full frame · not live measurements

+ diff --git a/site-assets/reviews/sources-table/table.js b/site-assets/reviews/sources-table/table.js index 1f695e5..e4aeda7 100644 --- a/site-assets/reviews/sources-table/table.js +++ b/site-assets/reviews/sources-table/table.js @@ -27,12 +27,12 @@ document.querySelectorAll('[data-column]').forEach(c=>c.addEventListener('change function renderRows(){ const query=$('search').value.toLowerCase(),category=$('category').value; const visible=inputs.filter(i=>{const s=source(i);return (name(i)+' '+(s?.type||'')+' '+String(i.id).padStart(2,'0')).toLowerCase().includes(query)&&(category==='all'||(category==='network'?['SRT','RTMP'].includes(s?.type):s?.type===category));}); - $('rows').innerHTML=visible.map(i=>{const s=source(i),offline=s&&!s.online,state=!s?'Empty':offline?'Offline':'Advancing';return `${String(i.id).padStart(2,'0')}
${escape(name(i))}${escape(s?s.type+' · '+(offline?'Offline':i.mv?'In MV':'Not in MV'):'Choose a source')}
${s?.type||'—'}${s&&s.online?s.format:'—'}${s&&s.online?s.fps:'—'}${state}${s&&s.level!=null&&s.online?`${s.level} dBFS`:`${offline?'Unavailable':s?'No paired audio':'—'}`}${i.writing?'REC · demo':i.iso?'Armed':'Off'}
Source type${s?.type||'Unassigned'}Signal${state}Paired audio${escape(s?.audio||'None')}Audio level${s?.level!=null&&s.online?s.level+' dBFS · demo':'Unavailable'}ISO writer${i.writing?'Writing · simulated':i.iso?'Armed · writer idle':'Not armed'}${offline?`Last known format, not advancing${s.format} · ${s.fps} fps`:''}
`;}).join(''); + $('rows').innerHTML=visible.map(i=>{const s=source(i),offline=s&&!s.online,state=!s?'Empty':offline?'Offline':'Advancing';return `${String(i.id).padStart(2,'0')}
${escape(name(i))}${escape(s?s.type+' · '+(offline?'Offline':i.mv?'In MV':'Not in MV')+(looks[s.id].gradeApplied?' · Grade':''):'Choose a source')}
${s?.type||'—'}${s&&s.online?s.format:'—'}${s&&s.online?s.fps:'—'}${state}${s&&s.level!=null&&s.online?`${s.level} dBFS`:`${offline?'Unavailable':s?'No paired audio':'—'}`}${i.writing?'REC · demo':i.iso?'Armed':'Off'}
Source type${s?.type||'Unassigned'}Signal${state}Paired audio${escape(s?.audio||'None')}Audio level${s?.level!=null&&s.online?s.level+' dBFS · demo':'Unavailable'}ISO writer${i.writing?'Writing · simulated':i.iso?'Armed · writer idle':'Not armed'}${offline?`Last known format, not advancing${s.format} · ${s.fps} fps`:''}
`;}).join(''); $('no-results').hidden=visible.length>0;$('counts').textContent=`${inputs.filter(i=>i.source).length}/10 assigned · ${inputs.filter(i=>i.mv&&i.source).length} in MV · ${inputs.filter(i=>i.iso).length} ISO armed`; } function tab(name){activeTab=name;for(const button of document.querySelectorAll('[data-tab]')){const on=button.dataset.tab===name;button.setAttribute('aria-selected',String(on));button.tabIndex=on?0:-1;$('pane-'+button.dataset.tab).hidden=!on;}} function renderInspector(){ - const i=input(),s=source(),look=looks[i.source];pending=null;$('assignment-confirm').hidden=true; + const i=input(),s=source(),look=looks[i.source];$('grade-selected').disabled=$('grade-inspector').disabled=!s;$('grade-selected').title=s?'Grade '+look.name:'Assign a source to grade it';pending=null;$('assignment-confirm').hidden=true; $('inspector').hidden=!opened;$('workspace').classList.toggle('inspector-open',opened);$('toggle-inspector').setAttribute('aria-expanded',String(opened));$('toggle-inspector').textContent=opened?'Close inspector':'Open inspector'; $('inspector-slot').textContent=`INPUT ${String(selected).padStart(2,'0')} · ${s?.type||'UNASSIGNED'}`;$('inspector-name').textContent=name(i);$('preview-name').textContent=!s?'Unassigned':!s.online?'Offline · binding retained':name(i); $('detail-stats').textContent=!s?'No source selected.':s.online?`${s.format} · ${s.fps} fps · advancing (demo)`:`Offline · no advancing video. Last known ${s.format} / ${s.fps} fps.`; @@ -66,6 +66,9 @@ $('opacity').addEventListener('input',()=>opacity(Number($('opacity').value)));$ $('catalog').addEventListener('click',()=>{$('catalog-panel').hidden=!$('catalog-panel').hidden;if(!$('catalog-panel').hidden)$('catalog-panel').scrollIntoView({behavior:'auto'});});$('close-catalog').addEventListener('click',()=>{$('catalog-panel').hidden=true;$('catalog').focus();}); for(const id of ['quality-resolution','quality-fps'])$(id).addEventListener('change',()=>status('Requested Zoom quality changed in the mockup. Observed source format/FPS cells remain unchanged.')); document.addEventListener('click',event=>{for(const detail of document.querySelectorAll('.tools details[open]'))if(!detail.contains(event.target))detail.open=false;}); +function openGrade(){const s=source();if(!s)return;window.dispatchEvent(new CustomEvent('corevideo-grade-open',{detail:{id:s.id,name:looks[s.id].name,input:selected,format:s.format,fps:s.fps,online:s.online}}));} +$('grade-selected').addEventListener('click',openGrade);$('grade-inspector').addEventListener('click',openGrade); +window.addEventListener('corevideo-grade-applied',event=>{if(!looks[event.detail.id])return;looks[event.detail.id].gradeApplied=event.detail.active;renderRows();status('Simulated source grade applied. Source assignment, MV, ISO and scene routes unchanged.');}); render(); })();