From 2c7f75a7b90eaeabf3863203d05f884bfc5cee4b Mon Sep 17 00:00:00 2001 From: John Wallace Date: Thu, 15 Oct 2026 12:04:33 -0400 Subject: [PATCH] Mock Sources table with collapsible inspector and retained overview --- CLAUDE.md | 7 ++ review-feedback.js | 2 +- scripts/export-review-feedback.mjs | 2 +- site-assets/reviews/index.html | 2 +- site-assets/reviews/sources-table/index.html | 9 +++ site-assets/reviews/sources-table/table.css | 1 + site-assets/reviews/sources-table/table.js | 71 +++++++++++++++++++ .../reviews/sources-workspace/index.html | 2 +- site-assets/reviews/workflows/index.html | 2 +- tests/review-feedback.test.mjs | 1 + 10 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 site-assets/reviews/sources-table/index.html create mode 100644 site-assets/reviews/sources-table/table.css create mode 100644 site-assets/reviews/sources-table/table.js diff --git a/CLAUDE.md b/CLAUDE.md index db9f65f..8345bd6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1719,3 +1719,10 @@ unmocked destinations and unresolved acceptance. Source rows retain format/fps, category filtering and quick MV inclusion across ten stable slots. Source selection/MV inclusion must not become an alternate Scene Take workflow. The color sample illustrates visual picking and draft/apply, not Windows rendering. + +The alternative `/reviews/sources-table/` keeps the table as the default and +opens a collapsible inspector for the same selected input. It is a simulated +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. diff --git a/review-feedback.js b/review-feedback.js index ec4debe..62cfe26 100644 --- a/review-feedback.js +++ b/review-feedback.js @@ -1,5 +1,5 @@ const AREAS = new Set(['Overall workflow','Source list and identity','Layout and compact view','Picture controls and live edits','Audio controls','Lower-third text','Live versus draft behavior','Health and status clarity']); -const REVIEWS = new Map([['sources-workspace-v1',AREAS],['workflow-review-v1',new Set(['Overall workflow','Source list and identity','Lower-third designer','Grade curves and scope ROI','Preservation requirements'])],['operator-uiux-v1',new Set(['Overall workflow','Starter scenes','Lower-third designer','Shared sliders and resets','Grade curves and scope ROI','Settings and Health','Source list and identity'])]]); +const REVIEWS = new Map([['sources-table-v1',AREAS],['sources-workspace-v1',AREAS],['workflow-review-v1',new Set(['Overall workflow','Source list and identity','Lower-third designer','Grade curves and scope ROI','Preservation requirements'])],['operator-uiux-v1',new Set(['Overall workflow','Starter scenes','Lower-third designer','Shared sliders and resets','Grade curves and scope ROI','Settings and Health','Source list and identity'])]]); const VERDICTS = new Set(['changes','approve','question']); const SCENARIOS = new Set(['populated','offline','returns','unassigned']); const response = (value,status=200)=>new Response(JSON.stringify(value),{status,headers:{'content-type':'application/json; charset=utf-8','cache-control':'no-store','x-content-type-options':'nosniff','x-robots-tag':'noindex, nofollow'}}); diff --git a/scripts/export-review-feedback.mjs b/scripts/export-review-feedback.mjs index 6c69970..f3c31de 100644 --- a/scripts/export-review-feedback.mjs +++ b/scripts/export-review-feedback.mjs @@ -12,7 +12,7 @@ try{ if(!namespace||!/^[a-f0-9]{32}$/.test(namespace.id))throw Error('Missing storage'); const keys=JSON.parse(wrangler(['kv','key','list','--namespace-id',namespace.id,'--prefix','feedback:','--remote'])); const notes=[]; - for(const key of keys){if(!/^feedback:(sources-workspace-v1|operator-uiux-v1|workflow-review-v1):[a-f0-9-]{36}$/.test(key.name))continue;notes.push(JSON.parse(wrangler(['kv','key','get',key.name,'--namespace-id',namespace.id,'--remote','--text'])));} + for(const key of keys){if(!/^feedback:(sources-workspace-v1|sources-table-v1|operator-uiux-v1|workflow-review-v1):[a-f0-9-]{36}$/.test(key.name))continue;notes.push(JSON.parse(wrangler(['kv','key','get',key.name,'--namespace-id',namespace.id,'--remote','--text'])));} const secret=randomBytes(32),iv=randomBytes(12),cipher=createCipheriv('aes-256-gcm',secret,iv); const encrypted=Buffer.concat([cipher.update(JSON.stringify({exportedAt:new Date().toISOString(),notes}),'utf8'),cipher.final()]); const wrapped=publicEncrypt({key:recipient,oaepHash:'sha256',padding:constants.RSA_PKCS1_OAEP_PADDING},secret); diff --git a/site-assets/reviews/index.html b/site-assets/reviews/index.html index fc11e40..e58eb52 100644 --- a/site-assets/reviews/index.html +++ b/site-assets/reviews/index.html @@ -1,4 +1,4 @@ CoreVideo review room
CoreVideo

Owner review · October 15, 2026

Review the next operator experience.

Review from any browser, then leave feedback directly on the page. These pages show design proposals and development captures. They do not control your installed app.

START HERE · REVISED AFTER REVIEW

One workflow at a time, with a preservation checklist

Compare today’s task with the proposed change. See what stays, what moves, what is incomplete, and what must be regression-tested. Sources revision 2 restores format/fps, categories and quick MV inclusion; the color walkthrough makes visual picking and Apply/Cancel clear.

Start the guided workflow review →

Reference pages

The items below are supporting material, not a request to approve a whole redesign at once.

INTERACTIVE DESIGN PROPOSAL

Sources workspace

A new workflow to critique before implementation.

  • Stable inputs and selected-source inspector
  • Source, picture and audio controls
  • Disconnect/reconnect scenarios
  • Compact layout and clear Preview/Program actions
Try Sources and leave feedback →
ACTUAL DEVELOPMENT APP CAPTURES

Operator controls batch

Review the controls built in the October 15 candidate.

  • Solo, Two people, Screen + presenter starters
  • Lower-third designer and saved looks
  • Slider units, exact entry and Reset
  • Grading curves, scopes-only ROI and Settings
Review controls and leave feedback →

Earlier Scene Builder review gallery ↗ · October 13 captures, retained as a reference.

Unlisted review area · No account required · Feedback is private; these pages are accessible to anyone with the link. Review approval does not replace installed/live-meeting validation.
+
CoreVideo

Owner review · October 15, 2026

Review the next operator experience.

Review from any browser, then leave feedback directly on the page. These pages show design proposals and development captures. They do not control your installed app.

START HERE · REVISED AFTER REVIEW

One workflow at a time, with a preservation checklist

Compare today’s task with the proposed change. See what stays, what moves, what is incomplete, and what must be regression-tested. Sources revision 2 restores format/fps, categories and quick MV inclusion; the color walkthrough makes visual picking and Apply/Cancel clear.

Start the guided workflow review →
NEW ALTERNATIVE · NEEDS REVIEW

Sources: table first, inspector on demand

Keep the information-dense overview. Compare the same populated sources with the inspector closed or open. Quick MV/ISO, format/FPS, category filtering and secondary-column preferences are demonstrated.

Try table + inspector →

Reference pages

The items below are supporting material, not a request to approve a whole redesign at once.

INTERACTIVE DESIGN PROPOSAL

Sources workspace

A new workflow to critique before implementation.

  • Stable inputs and selected-source inspector
  • Source, picture and audio controls
  • Disconnect/reconnect scenarios
  • Compact layout and clear Preview/Program actions
Try Sources and leave feedback →
ACTUAL DEVELOPMENT APP CAPTURES

Operator controls batch

Review the controls built in the October 15 candidate.

  • Solo, Two people, Screen + presenter starters
  • Lower-third designer and saved looks
  • Slider units, exact entry and Reset
  • Grading curves, scopes-only ROI and Settings
Review controls and leave feedback →

Earlier Scene Builder review gallery ↗ · October 13 captures, retained as a reference.

Unlisted review area · No account required · Feedback is private; these pages are accessible to anyone with the link. Review approval does not replace installed/live-meeting validation.
diff --git a/site-assets/reviews/sources-table/index.html b/site-assets/reviews/sources-table/index.html new file mode 100644 index 0000000..1d5e164 --- /dev/null +++ b/site-assets/reviews/sources-table/index.html @@ -0,0 +1,9 @@ +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.

+
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.
+ +

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.
diff --git a/site-assets/reviews/sources-table/table.css b/site-assets/reviews/sources-table/table.css new file mode 100644 index 0000000..8615d55 --- /dev/null +++ b/site-assets/reviews/sources-table/table.css @@ -0,0 +1 @@ +@font-face{font-family:Space;src:url('/assets/fonts/SpaceGrotesk-var.woff2')}*{box-sizing:border-box}:root{font-family:Segoe UI,system-ui,sans-serif;color-scheme:dark;background:#0e151a;color:#e7f0f3}body{margin:0}a{color:#8ae0ca}header{padding:20px 32px;border-bottom:1px solid #2c3c46;display:flex;justify-content:space-between;gap:20px}main{max-width:1680px;margin:auto;padding:32px}h1{font:600 clamp(29px,3.1vw,46px)/1.15 Space,Segoe UI,sans-serif;letter-spacing:-.035em;margin:12px 0 20px}h2{font-size:22px}h3{font-size:17px}p{line-height:1.65;color:#acbfca}.eyebrow{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:#8ae0ca}.intro{display:grid;grid-template-columns:1.3fr 1fr;gap:60px;align-items:center}.intro>div p{max-width:690px}.intro aside{border-left:2px solid #54746e;padding-left:22px;font-size:14px}.intro aside p{margin:10px 0}.review-actions-bar{display:flex;align-items:center;justify-content:space-between;gap:30px;margin:28px 0}.review-actions-bar p{max-width:560px;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}button,input,select{font:inherit}button,summary{cursor:pointer}button{background:#20313b;color:#e7f0f3;border:1px solid #47606e;border-radius:6px;padding:9px 12px;font-size:12px}button.primary{background:#8ae0ca;color:#10261e;border-color:#8ae0ca}button:hover{border-color:#8ae0ca}button:disabled{opacity:.45;cursor:default}.app{border:1px solid #3b505c;border-radius:12px;overflow:hidden;background:#121e26}.app-header{display:flex;align-items:center;gap:20px;background:#20303a;padding:15px 18px;font-size:13px}.sim{font-size:10px;color:#8ae0ca;border:1px solid #4a7267;border-radius:4px;padding:3px 6px}.app-header>span:last-child{margin-left:auto}.workflow{display:flex;gap:30px;padding:14px 18px;background:#17262f;font-size:12px;border-bottom:1px solid #314652}.workflow strong{color:#8ae0ca}.workflow span{color:#9cb2bf}.tools{display:flex;align-items:end;gap:12px;padding:16px 18px;flex-wrap:wrap}.tools>label{font-size:11px;color:#a5bdca}.tools input,.tools select,.inspector input:not([type=checkbox]):not([type=range]),.inspector select{display:block;background:#0f191f;border:1px solid #45606f;border-radius:5px;color:#e7f0f3;padding:9px;margin-top:5px;width:100%;min-width:0}.tools label:first-child{min-width:180px;flex:1}.tools summary{padding:10px;border:1px solid #47606e;border-radius:5px;font-size:12px}.columns,.quality{position:relative}.columns fieldset,.quality>div{position:absolute;z-index:4;right:0;top:40px;width:280px;max-width:85vw;background:#21343e;border:1px solid #688b9d;border-radius:7px;padding:15px;box-shadow:0 10px 30px #0008}.columns fieldset label{display:block;margin:10px 0}.columns p,.quality p{font-size:12px}.quality>div label{display:block;margin-bottom:12px;font-size:12px}.summary{padding:0 18px 14px;display:flex;justify-content:space-between;gap:10px;font-size:11px;color:#a5bdca}.summary strong{color:#e7f0f3}.workspace{display:grid;grid-template-columns:minmax(0,1fr);gap:0}.workspace.inspector-open{grid-template-columns:minmax(0,1fr) 318px}.table-area{min-width:0;padding:0 18px 14px}table{width:100%;table-layout:fixed;border-collapse:collapse;font-size:13px}th{text-align:left;background:#243741;color:#adcad7;font-size:10px;text-transform:uppercase;letter-spacing:.06em;padding:11px 7px}td{padding:10px 7px;border-bottom:1px solid #2b414e;vertical-align:middle}tbody tr[data-row]{height:66px;cursor:pointer}tbody tr[data-row]:hover{background:#1b303b}tbody tr.selected{background:#254039}tbody tr.selected td:first-child{box-shadow:inset 3px 0 #8ae0ca}.number-col{width:4%}.name-col{width:24%}.type-col{width:8%}.format-col{width:13%}.fps-col{width:6%}.signal-col{width:12%}.audio-col{width:13%}.mv-col{width:5%}.iso-col{width:7%}.inspect-col{width:8%}.identity{display:flex;align-items:center;gap:9px;min-width:0}.thumb{flex:0 0 46px;height:30px;background:linear-gradient(135deg,#456778,#564b73);border-radius:4px;display:grid;place-items:center;color:#adc7c3}.thumb.offline{background:#2d3235;color:#d5a865}.thumb.empty{background:#1b2b34;color:#8395a0}.identity>span:last-child{min-width:0}.source-name{display:block;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.source-note{font-size:10px;color:#9bb1bd;display:block;margin-top:4px;overflow-wrap:anywhere}.signal{font-size:10px;border:1px solid #3a7665;color:#9ee6ca;border-radius:4px;padding:4px 5px;display:inline-block}.signal.offline{color:#ffcd86;border-color:#936c39}.observed-format{white-space:nowrap;font-variant-numeric:tabular-nums;font-size:12px}.meter{height:5px;border-radius:2px;background:#293b46;margin-bottom:5px;overflow:hidden}.meter span{display:block;height:100%;background:#71cda7}.audio-label{font-size:10px;color:#b7cbd5}.iso-label{display:block;font-size:9px;color:#a9c2cc;margin-top:4px}.iso-label.writing{color:#ffb5a9}input[type=checkbox]{accent-color:#8ae0ca;width:15px;height:15px;cursor:pointer;vertical-align:middle}abbr{text-decoration:none}.inspect-button{padding:7px;font-size:11px}.row-more td{background:#1a2d37;padding:14px 18px}.row-grid{display:flex;gap:22px;flex-wrap:wrap;font-size:12px;line-height:1.8}.row-grid span{color:#aac2ce}.row-grid strong{color:#edf4f7;display:block}.table-note{font-size:11px;margin:14px 0 0}.status-bar{border-top:1px solid #324b58;background:#1a2b34;padding:13px 18px;min-height:48px;font-size:12px;color:#b9d3df}.inspector{border-left:1px solid #3f5b68;background:#1b2d37;padding:16px;min-width:0}.inspector-heading{display:flex;justify-content:space-between;align-items:start;gap:10px}.inspector h2{font-size:19px;margin:6px 0 16px;overflow-wrap:anywhere}.picture{aspect-ratio:16/9;background:linear-gradient(125deg,#294958,#48445c);border-radius:6px;position:relative;overflow:hidden;text-align:center;padding:10px}.picture>span{font-size:9px;display:block}.portrait{font-size:88px;line-height:1.1;color:#98b9b5}.picture strong{font-size:12px;display:block}.detail-stats{font-size:11px;padding:12px 0;color:#c3d8e0;line-height:1.8}.tabs{display:flex;gap:4px;margin:0 0 16px}.tabs button{flex:1}.tabs button[aria-selected=true]{background:#8ae0ca;color:#10261e}.inspector label:not(.check){display:block;font-size:12px;margin:12px 0}.inspector .check{display:block;margin:14px 0;font-size:12px}.inspector p{font-size:12px}.inspector details{border-top:1px solid #47606e;padding:14px 0}.inspector summary{font-size:12px}.hint{font-size:11px!important}.warning{background:#503e24;padding:12px;border-radius:6px}.inspector output{float:right}.inspector input[type=range]{width:100%;accent-color:#8ae0ca;margin-top:16px}.exact{display:flex;gap:10px;align-items:center}.exact label{flex:1}.catalog-panel,.decisions{margin:32px 0;padding:24px;background:#17262f;border:1px solid #3d5663;border-radius:10px}.catalog-grid,.decisions>div{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:26px}.catalog-grid{grid-template-columns:repeat(4,minmax(0,1fr))}.decisions p,.catalog-panel p{font-size:13px}footer{font-size:12px;color:#9ab4c1;padding:24px 0}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}[hidden]{display:none!important}:focus-visible{outline:2px solid #8ae0ca;outline-offset:3px}.hide-type .type-col,.hide-signal .signal-col,.hide-audio .audio-col{display:none}@media(max-width:1400px){.workspace.inspector-open{grid-template-columns:minmax(0,1fr)}.inspector{border-left:0;border-top:1px solid #47606e;max-width:none;display:grid;grid-template-columns:280px minmax(0,1fr);gap:0 26px}.inspector-heading{grid-column:1/-1}.inspector .picture{grid-column:1;grid-row:2/4}.inspector .detail-stats{grid-column:1;grid-row:4}.inspector .tabs,.inspector section{grid-column:2}.name-col{width:23%}.thumb{flex-basis:34px}.intro{gap:30px}}@media(max-width:900px){.type-col,.audio-col{display:none}.name-col{width:30%}.format-col{width:17%}.fps-col{width:7%}.signal-col{width:15%}.mv-col{width:7%}.iso-col{width:8%}.inspect-col{width:12%}.number-col{width:4%}.summary{flex-wrap:wrap}.intro{grid-template-columns:1fr}.review-actions-bar{display:block}.workflow{gap:15px;flex-wrap:wrap}.catalog-grid,.decisions>div{grid-template-columns:1fr 1fr}.inspector{display:block}.inspector .picture{max-width:440px}}@media(max-width:600px){main{padding:22px 12px}header{padding:16px 12px}.tools{gap:9px;padding:12px}.tools label:first-child{min-width:150px}.tools>label:nth-child(2){flex:1;min-width:100px}.summary{padding:0 10px 12px;font-size:10px}.table-area{padding:0 6px 12px}.signal-col,.thumb{display:none}.number-col{width:6%}.name-col{width:25%}.format-col{width:19%}.fps-col{width:9%}.mv-col{width:9%}.iso-col{width:12%}.inspect-col{width:20%}th,td{padding:10px 4px}th{font-size:9px;letter-spacing:0}td{font-size:10px}.source-name{font-size:11px}.source-note{font-size:9px}.observed-format{font-size:9px;white-space:normal;overflow-wrap:anywhere}.inspect-button{padding:6px 4px;font-size:10px}.iso-label{font-size:8px}.catalog-grid,.decisions>div{grid-template-columns:1fr}.workflow{font-size:10px;padding:12px}.app-header{gap:10px;font-size:11px}.app-header .sim{font-size:8px}.columns fieldset{left:0;right:auto}.quality>div{left:-70px;right:auto}.review-feedback{padding:16px!important}.inspector{padding:14px}.row-grid{gap:10px 22px}} diff --git a/site-assets/reviews/sources-table/table.js b/site-assets/reviews/sources-table/table.js new file mode 100644 index 0000000..1f695e5 --- /dev/null +++ b/site-assets/reviews/sources-table/table.js @@ -0,0 +1,71 @@ +(()=>{ +const $=id=>document.getElementById(id); +const escape=value=>String(value).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); +const sources=[ + {id:'host',name:'Alex Morgan',type:'Zoom',format:'1920 × 1080',fps:'59.94',audio:'Source audio',level:-18,online:true}, + {id:'alice',name:'Alice Chen',type:'Zoom',format:'1280 × 720',fps:'30',audio:'Source audio',level:-22,online:true}, + {id:'share',name:'Presentation',type:'Screen',format:'2560 × 1440',fps:'15',audio:'No paired audio',online:true}, + {id:'camera',name:'Studio camera',type:'Camera',format:'1920 × 1080',fps:'60',audio:'Studio microphone',level:-16,online:true}, + {id:'clip',name:'Opening film',type:'Media',format:'1920 × 1080',fps:'29.97',audio:'File audio',level:-20,online:true}, + {id:'bob',name:'Bob Rivera',type:'Zoom',format:'1280 × 720',fps:'30',audio:'Source audio',level:-24,online:false}, + {id:'srt',name:'Remote SRT',type:'SRT',format:'1920 × 1080',fps:'50',audio:'Embedded audio',level:-21,online:true}, + {id:'rtmp',name:'RTMP guest',type:'RTMP',format:'1280 × 720',fps:'30',audio:'Embedded audio',level:-20,online:true}, + {id:'browser',name:'Web graphic',type:'Browser',format:'1920 × 1080',fps:'30',audio:'No paired audio',online:true} +]; +const inputs=Array.from({length:10},(_,i)=>({id:i+1,source:sources[i]?.id&&i<7?sources[i].id:null,mv:i<4,iso:i<2,writing:i===0})); +const looks=Object.fromEntries(sources.map(s=>[s.id,{name:s.name,opacity:100,ltName:s.name,ltSecondary:s.type==='Zoom'?'Guest':'',draftName:s.name,draftSecondary:s.type==='Zoom'?'Guest':''}])); +looks.host.ltSecondary=looks.host.draftSecondary='Host'; +let selected=2,opened=false,activeTab='source',pending=null; +const expanded=new Set(); +const input=()=>inputs.find(i=>i.id===selected); +const source=i=>sources.find(s=>s.id===(i||input()).source); +const name=i=>i.source?looks[i.source].name:'Unassigned'; +function status(text){$('status').textContent=text;} +function saveColumns(){const hidden=[...document.querySelectorAll('[data-column]')].filter(c=>!c.checked).map(c=>c.dataset.column);for(const c of document.querySelectorAll('[data-column]'))$('workspace').classList.toggle('hide-'+c.dataset.column,!c.checked);try{localStorage.setItem('corevideo-table-columns-v1',JSON.stringify(hidden));}catch{}} +try{const hidden=JSON.parse(localStorage.getItem('corevideo-table-columns-v1')||'[]');for(const c of document.querySelectorAll('[data-column]'))c.checked=!hidden.includes(c.dataset.column);}catch{} +document.querySelectorAll('[data-column]').forEach(c=>c.addEventListener('change',saveColumns));saveColumns(); +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(''); + $('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; + $('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.`; + const types=['Zoom','Camera','Screen','Media','SRT','RTMP','Browser'];$('assignment').innerHTML=''+types.map(type=>`${sources.filter(s=>s.type===type).map(s=>``).join('')}`).join('');$('assignment').value=i.source||''; + $('assignment').disabled=i.writing;$('display-name').value=look?.name||'';$('display-name').disabled=!s; + $('inspector-mv').checked=i.mv;$('inspector-mv').disabled=!s;$('inspector-iso').checked=i.iso;$('inspector-iso').disabled=!s||i.writing; + $('lt-name').value=look?.draftName||'';$('lt-secondary').value=look?.draftSecondary||'';for(const id of ['lt-name','lt-secondary','apply-text','cancel-text'])$(id).disabled=!s; + $('text-status').textContent='';$('recovery').textContent=i.writing?'Simulated writer active. Reassignment and disarming are disabled here; recording stop belongs in recording controls.':!s?'Choose a source. Other numbered inputs will not move.':!s.online?'Reconnect this same source or deliberately replace it. No automatic replacement and no false live FPS.':'Source is advancing in this simulation. Source connection settings remain separate from Multiview inclusion.'; + $('audio-detail').textContent=s?`${s.audio} · ${s.online&&s.level!=null?s.level+' dBFS (simulated)':'No advancing audio measurement'}`:'No source assigned.'; + $('opacity').value=$('opacity-exact').value=look?.opacity??100;$('opacity-readout').textContent=(look?.opacity??100)+'%';$('opacity-error').textContent='';$('opacity').disabled=$('opacity-exact').disabled=$('opacity-reset').disabled=!s; + document.querySelector('.portrait').style.opacity=look?look.opacity/100:0;tab(activeTab); +} +function render(){renderRows();renderInspector();} +function inspect(id){selected=id;opened=true;render();status(`Inspecting Input ${String(id).padStart(2,'0')} · ${name(input())}. Assignments, MV and scene routes are unchanged.`);} +$('rows').addEventListener('click',event=>{if(event.target.closest('input'))return;const more=event.target.closest('[data-more]');if(more){const id=Number(more.dataset.more);expanded.has(id)?expanded.delete(id):expanded.add(id);renderRows();return;}const row=event.target.closest('[data-row]');if(row)inspect(Number(row.dataset.row));}); +$('rows').addEventListener('change',event=>{const mv=event.target.dataset.mv,iso=event.target.dataset.iso;if(!mv&&!iso)return;const i=inputs.find(i=>i.id===Number(mv||iso));if(mv)i.mv=event.target.checked;else if(!i.writing)i.iso=event.target.checked;render();status(mv?`Input ${i.id} ${i.mv?'included in':'removed from'} Multiview. Binding and scene routes unchanged.`:`Input ${i.id} ISO ${i.iso?'armed; writer remains idle':'disarmed'}. No recording starts or stops.`);}); +$('search').addEventListener('input',renderRows);$('category').addEventListener('change',renderRows); +$('overview').addEventListener('click',()=>{opened=false;$('search').value='';$('category').value='all';render();status('Full table. Same show, assignments and selected input.');}); +$('with-inspector').addEventListener('click',()=>{inspect(selected);}); +$('toggle-inspector').addEventListener('click',()=>{opened=!opened;renderInspector();status(opened?'Inspector open for the selected input. Table values retained.':'Inspector closed. Full table width restored.');});$('close-inspector').addEventListener('click',()=>{opened=false;renderInspector();$('toggle-inspector').focus();}); +for(const id of ['disconnect','reconnect'])$(id).addEventListener('click',()=>{sources.find(s=>s.id==='alice').online=id==='reconnect';render();status(id==='disconnect'?'Alice offline. Input 02, name, MV inclusion and ISO arm state retained; FPS no longer reports live video.':'Alice returned to Input 02. Other assignments and settings retained.');}); +document.querySelectorAll('[data-tab]').forEach(button=>{button.addEventListener('click',()=>tab(button.dataset.tab));button.addEventListener('keydown',event=>{if(!['ArrowLeft','ArrowRight','Home','End'].includes(event.key))return;event.preventDefault();const tabs=[...document.querySelectorAll('[data-tab]')],index=event.key==='Home'?0:event.key==='End'?2:(tabs.indexOf(button)+(event.key==='ArrowRight'?1:2))%3;tab(tabs[index].dataset.tab);tabs[index].focus();});}); +function assign(value){input().source=value||null;if(!value){input().mv=false;input().iso=false;}render();status('Input binding explicitly changed. Other inputs did not shift. Existing scene routes still reference this input.');} +$('assignment').addEventListener('change',()=>{if(selected===1||selected===2){pending=$('assignment').value;$('assignment-confirm').hidden=false;}else assign($('assignment').value);});$('confirm-assignment').addEventListener('click',()=>{if(pending!==null)assign(pending);});$('cancel-assignment').addEventListener('click',()=>{pending=null;$('assignment-confirm').hidden=true;$('assignment').value=input().source||'';}); +$('display-name').addEventListener('change',()=>{if(!source())return;looks[input().source].name=$('display-name').value.trim()||source().name;render();status('Display name updated in the mockup. Source binding and lower-third text unchanged.');}); +for(const id of ['inspector-mv','inspector-iso'])$(id).addEventListener('change',()=>{input()[id==='inspector-mv'?'mv':'iso']=$(id).checked;render();status('Source inclusion/arm state changed in the mockup. No scene Take or recording start.');}); +for(const id of ['lt-name','lt-secondary'])$(id).addEventListener('input',()=>{if(source())looks[input().source][id==='lt-name'?'draftName':'draftSecondary']=$(id).value;}); +$('apply-text').addEventListener('click',()=>{const look=looks[input().source];if(!look)return;look.ltName=look.draftName;look.ltSecondary=look.draftSecondary;$('text-status').textContent='Text applied in simulation. Source assignment and shared style unchanged.';});$('cancel-text').addEventListener('click',()=>{const look=looks[input().source];if(!look)return;look.draftName=look.ltName;look.draftSecondary=look.ltSecondary;renderInspector();$('text-status').textContent='Draft discarded; applied text retained.';}); +function opacity(value){if(!source())return;if(!Number.isFinite(value)||value<0||value>100){$('opacity-error').textContent='Use 0–100%. The previous opacity is retained.';return;}looks[input().source].opacity=value;$('opacity').value=$('opacity-exact').value=value;$('opacity-readout').textContent=value+'%';$('opacity-error').textContent='';document.querySelector('.portrait').style.opacity=value/100;} +$('opacity').addEventListener('input',()=>opacity(Number($('opacity').value)));$('opacity-exact').addEventListener('change',()=>opacity($('opacity-exact').value.trim()===''?NaN:Number($('opacity-exact').value)));$('opacity-reset').addEventListener('click',()=>opacity(100)); +$('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;}); +render(); + +})(); diff --git a/site-assets/reviews/sources-workspace/index.html b/site-assets/reviews/sources-workspace/index.html index 8566274..b81975e 100644 --- a/site-assets/reviews/sources-workspace/index.html +++ b/site-assets/reviews/sources-workspace/index.html @@ -1,7 +1,7 @@ Sources workspace · CoreVideo design review
▦ CoreVideoPROREVISED PROPOSAL · 15 OCT 2026
-
REVISION 2 · NEEDS REVIEW

One place for every source.

A stable input list, a generous source preview, and controls that explain what changes. No sideways scrolling to find the important things.

+

New alternative: table-first overview + optional inspector →

REVISION 2 · NEEDS REVIEW

One place for every source.

A stable input list, a generous source preview, and controls that explain what changes. No sideways scrolling to find the important things.

Rearrange the controls. Preserve the workflow.

1 Assign inputs → 2 Route outputs → 3 Compose scenes. Sources assigns stable inputs and puts them in Multiview. Routing still owns ISO/output routes; Scene Builder still owns layout and Take.

Revision 2 restores at-a-glance format/fps, source categories and a quick MV toggle. Ten input slots remain. Video and numbers here are simulated, not measurements from your app.

Start the guided review and preservation checklist →
TRY A SCENARIO
▦ CoreVideo ProSIMULATED SHOW1080p · 60 fps target
StudioSourcesScenesAudioOverlaysSettings

Show inputs

Stable slots. One source per input.

Unassigning a source keeps its input slot in place. Other pictures and controls do not shift.

diff --git a/site-assets/reviews/workflows/index.html b/site-assets/reviews/workflows/index.html index 819540f..14bcd7a 100644 --- a/site-assets/reviews/workflows/index.html +++ b/site-assets/reviews/workflows/index.html @@ -1,4 +1,4 @@ -CoreVideo workflow and preservation review
← Review room

Revision 2 · Workflow review · Not approved

Keep what works. Review one task.

The earlier gallery mixed existing controls, development changes and a future Sources proposal. Start here instead. Each task names what you do today, what changes in the review, and the behavior that must survive.

This is a design review, not a feature replacement or an acceptance result.

No installed app changes were made for these pages. A missing control in a mockup is an incomplete design, not permission to remove it. Existing functionality, settings and output behavior must be preserved and tested before a redesign can replace it.

+CoreVideo workflow and preservation review

New alternative: table-first overview + optional inspector →

← Review room

Revision 2 · Workflow review · Not approved

Keep what works. Review one task.

The earlier gallery mixed existing controls, development changes and a future Sources proposal. Start here instead. Each task names what you do today, what changes in the review, and the behavior that must survive.

This is a design review, not a feature replacement or an acceptance result.

No installed app changes were made for these pages. A missing control in a mockup is an incomplete design, not permission to remove it. Existing functionality, settings and output behavior must be preserved and tested before a redesign can replace it.

Sources · Revised proposal, not implemented

Put a guest in Multiview without disturbing the scene.

TODAY · KEEP THIS WORKFLOW

Assign inputs → Route outputs → Compose scenes

  1. Choose a source category and bind a source to a numbered input.
  2. Use its In show/MV control to include it in the Studio Multiview strip.
  3. Check source resolution, frame rate, state and meters.
  4. Use Routing for output/ISO routes and Scene Builder for scene layout and Take.
PROPOSED PRESENTATION CHANGE

Stable input list + one inspector

The wide table becomes a stable list. Category, format/fps and quick MV inclusion stay visible. Selecting a row opens its detailed controls; it does not reassign the source.

Changed in revision 2: ten slots, category filtering, format/fps and per-row MV controls are shown. The extra Sources Cue/Take path from the first proposal is removed; Scene Builder keeps that responsibility.

Try these three actions

  1. Open the revised Sources proposal. Read the format/fps beneath Alice’s name; use the Zoom category filter.
  2. Toggle Input 2’s MV checkbox. Only its Multiview inclusion should change; its number and assignment should remain.
  3. Try Guest disconnects, then Guest returns. The same named input should remain; offline must not be presented as advancing video.
Open revised Sources proposal →

All numbers and video in that proposal are simulated. Hardware setup, ISO controls, Zoom quality preferences and output routing are not fully mocked. Their preservation is unresolved until their destinations and tests are reviewed below.

Compare with current development controlsCurrent development Sources table with category, format, audio and ISO columns

Actual October 15 development capture, not an installed/live meeting. The table extends beyond the capture; this is the problem being addressed.

Comment on this task ↓
diff --git a/tests/review-feedback.test.mjs b/tests/review-feedback.test.mjs index bf0307d..f9a0efb 100644 --- a/tests/review-feedback.test.mjs +++ b/tests/review-feedback.test.mjs @@ -10,6 +10,7 @@ test('save is acknowledged only after private storage, retry is idempotent',asyn test('no public reads; origin and content type are required',async()=>{const env={REVIEW_FEEDBACK:storage()};assert.equal((await handleReviewFeedback(new Request('https://corevideo.io/reviews/api/feedback'),env)).status,405);assert.equal((await handleReviewFeedback(request({}, {headers:{origin:'https://other.example'}}),env)).status,403);assert.equal((await handleReviewFeedback(request({}, {headers:{'content-type':'text/plain'}}),env)).status,415);}); test('reject invalid fields and oversized bodies without storing notes',async()=>{const kv=storage();for(const body of [{review:'other'},{submissionId:'bad'},{verdict:'anything'},{scenario:'bad'},{view:'bad'},{comment:' '},{comment:'x'.repeat(4001)},{website:'spam'}])assert.equal((await handleReviewFeedback(request(body),{REVIEW_FEEDBACK:kv})).status,400);assert.equal((await handleReviewFeedback(request({comment:'x'.repeat(25000)}),{REVIEW_FEEDBACK:kv})).status,413);assert.equal(kv.writes.length,0);}); test('missing or failing storage never reports saved',async()=>{assert.equal((await handleReviewFeedback(request(),{})).status,503);const kv=storage();kv.put=async()=>{throw Error('disk error');};const result=await handleReviewFeedback(request(),{REVIEW_FEEDBACK:kv});assert.equal(result.status,503);assert.ok(!(await result.json()).saved);}); +test('table-first alternative keeps feedback isolated from earlier Sources review',async()=>{const kv=storage(),env={REVIEW_FEEDBACK:kv},submissionId=randomUUID();for(const review of ['sources-workspace-v1','sources-table-v1'])assert.equal((await handleReviewFeedback(request({submissionId,review,area:'Layout and compact view'}),env)).status,201);assert.ok(kv.values.has('feedback:sources-table-v1:'+submissionId));assert.ok(kv.values.has('feedback:sources-workspace-v1:'+submissionId));assert.equal((await handleReviewFeedback(request({review:'sources-table-v1',area:'Unknown area'}),env)).status,400);}); test('guided workflow feedback saves requirements without widening other reviews',async()=>{const kv=storage(),env={REVIEW_FEEDBACK:kv};const result=await handleReviewFeedback(request({review:'workflow-review-v1',area:'Preservation requirements'}),env);assert.equal(result.status,201);const receipt=await result.json();assert.ok(kv.values.has('feedback:workflow-review-v1:'+receipt.id));assert.equal((await handleReviewFeedback(request({area:'Preservation requirements'}),env)).status,400);assert.equal((await handleReviewFeedback(request({review:'workflow-review-v1',area:'Unknown area'}),env)).status,400);}); test('operator review has independent areas and receipt namespace',async()=>{const kv=storage(),env={REVIEW_FEEDBACK:kv},submissionId=randomUUID();for(const area of ['Starter scenes','Lower-third designer','Shared sliders and resets','Grade curves and scope ROI','Settings and Health'])assert.equal((await handleReviewFeedback(request({review:'operator-uiux-v1',area}),env)).status,201);assert.equal((await handleReviewFeedback(request({review:'sources-workspace-v1',area:'Starter scenes'}),env)).status,400);assert.equal((await handleReviewFeedback(request({review:'operator-uiux-v1',area:'Audio controls'}),env)).status,400);assert.equal((await handleReviewFeedback(request({submissionId}),env)).status,201);assert.equal((await handleReviewFeedback(request({submissionId,review:'operator-uiux-v1'}),env)).status,201);assert.ok(kv.values.has('feedback:sources-workspace-v1:'+submissionId));assert.ok(kv.values.has('feedback:operator-uiux-v1:'+submissionId));}); test('malformed JSON is rejected and burst submissions are bounded',async()=>{const kv=storage(),env={REVIEW_FEEDBACK:kv};const bad=new Request('https://corevideo.io/reviews/api/feedback',{method:'POST',headers:{origin:'https://corevideo.io','content-type':'application/json'},body:'{'});assert.equal((await handleReviewFeedback(bad,env)).status,400);for(let n=0;n<8;n++)assert.equal((await handleReviewFeedback(request(),env)).status,201);assert.equal((await handleReviewFeedback(request(),env)).status,429);});