// Downloadable harness: import paths are relative to the original LuminaCut repository root. // Portfolio harness. The editor reducer and core functions below are imported unchanged. import {createInitialEditorState, editorReducer, type EditorAction} from './desktop/src/renderer/store'; import {activeSequence, createCommand, mediaToTimelineItem} from './packages/editor-core/src'; import {normalizeMediaAsset} from './packages/media-engine/src'; const root=document.getElementById('root')!; root.innerHTML=`

LUMINACUT V2 / ORIGINAL EDITOR REDUCER

An edit you can take back.

Drag Scale, then Undo. Move the playhead: it leaves the edit history alone.

Walter World courtyard, a still-image fixture for this scale studySTILL-IMAGE FIXTURE

The existing reducer runs unchanged. This small view is a portfolio harness, not the full editor. Consecutive scale changes within 900 ms share one undo step.

`; const scale=root.querySelector('#scale')!,playhead=root.querySelector('#playhead')!,undo=root.querySelector('#undo')!,redo=root.querySelector('#redo')!; function fixture(){ let s=createInitialEditorState(); const media=normalizeMediaAsset({id:'portfolio-still',name:'Courtyard still',kind:'image',sourcePath:'/assets/threshold/portfolio/walter-world/exterior.jpg',fingerprint:'portfolio-walter-courtyard',duration:3,width:1280,height:720,hasAudio:false,missing:false}); s=editorReducer(s,{type:'IMPORT_MEDIA',media:[media]}); const item=mediaToTimelineItem(media,activeSequence(s.project)); s=editorReducer(s,{type:'APPLY_COMMAND',command:createCommand(s.project,'append',{item})}); // Start history after the fixed sample is loaded, as the editor does for an opened project. return {...s,history:[],future:[],lastCoalesce:undefined,dirty:false}; } let state=fixture(); function render(){ const item=activeSequence(state.project).items[0],value=Number(item.transform.scale.value); scale.value=String(value);playhead.max=String(item.durationFrames-1);playhead.value=String(state.playheadFrame); root.querySelector('#scale-value')!.textContent=`${value}%`; root.querySelector('#frame-value')!.textContent=`Frame ${state.playheadFrame}`; (root.querySelector('#clip-image') as HTMLImageElement).style.transform=`scale(${value/100})`; undo.disabled=!state.history.length;redo.disabled=!state.future.length; root.querySelector('#history-status')!.textContent=`${state.history.length} undoable ${state.history.length===1?'edit':'edits'} ยท ${state.future.length} to redo`; } function dispatch(action:EditorAction){state=editorReducer(state,action);render();} scale.addEventListener('input',()=>{const item=activeSequence(state.project).items[0];dispatch({type:'APPLY_COMMAND',command:createCommand(state.project,'set-item',{itemId:item.id,patch:{transform:{...item.transform,scale:{...item.transform.scale,value:Number(scale.value)}}}}),coalesceKey:`${item.id}:transform:scale`});}); playhead.addEventListener('input',()=>dispatch({type:'SET_PLAYHEAD',frame:Number(playhead.value)})); undo.addEventListener('click',()=>dispatch({type:'UNDO'}));redo.addEventListener('click',()=>dispatch({type:'REDO'})); root.querySelector('#reset')!.addEventListener('click',()=>{state=fixture();render();});render();