const state={runId:null,bucketName:'space-factory-runs',bucketSource:null,bucketReady:false,poll:null,activeLinks:{},selectedRunDetail:null,runStatusFilter:'all',runSearch:'',runPage:0,runsPerPage:5,runsCache:[],runsLastLoaded:0,lastRunsRefreshFromPoll:0,lastJobLogPoll:0,pollErrors:0,busy:false,user:null,currentView:'onepage'}; const TERMINAL_STATUSES=new Set(['done','success','failed','full_inference_success','full_inference_candidate_health_passed','manual_hardware_required','generated_needs_manual_hardware','technical_blocker','health_only','repair_success','repair_failed','succeeded','waiting_manual_action','blocked','stale','cancelled','canceled']); const DEFAULT_TIMELINE=[{label:'Bootstrap',status:'pending'},{label:'Dependencies',status:'pending'},{label:'Auth',status:'pending'},{label:'Model analysis',status:'pending'},{label:'Workspace',status:'pending'},{label:'Node/npm',status:'pending'},{label:'Pi install',status:'pending'},{label:'Pi config',status:'pending'},{label:'Pi run',status:'pending'},{label:'Pi verification',status:'pending'},{label:'Metadata sanitize',status:'pending'},{label:'Requirements sanitize',status:'pending'},{label:'Hardware strategy',status:'pending'},{label:'Create with hardware',status:'pending'},{label:'Create Space',status:'pending'},{label:'Repair pass',status:'pending'},{label:'Upload files',status:'pending'},{label:'Space runtime',status:'pending'},{label:'Space logs',status:'pending'},{label:'API validation',status:'pending'},{label:'Live wait',status:'pending'},{label:'Generation smoke',status:'pending'},{label:'Inference gate',status:'pending'},{label:'Report',status:'pending'},{label:'Done',status:'pending'},{label:'Failure',status:'pending'}]; function saveActiveRun(){try{localStorage.setItem('asf.activeRun',JSON.stringify({runId:state.runId,bucketName:state.bucketName,links:state.activeLinks||{}}))}catch(_){}} function clearSavedRun(){try{localStorage.removeItem('asf.activeRun')}catch(_){}} function artifactUrl(runId,bucketSource){if(!runId||!bucketSource)return'';return `https://huggingface.co/buckets/${bucketSource}/tree/runs/${runId}`} function ownerFromBucketSource(bucket){return bucket?String(bucket).split('/')[0]:''} function normalizeLinks(links={}){const out={...links};const bucket=out.bucket_source||state.bucketSource;const run=out.run_id||state.runId;if(!out.artifacts_url&&bucket&&run)out.artifacts_url=artifactUrl(run,bucket);if(!out.job_url){const jobId=out.job_id||out.jobId;const owner=out.job_owner||out.created_by||out.owner||out.username||state.user?.username||ownerFromBucketSource(bucket);if(jobId&&owner)out.job_url=`https://huggingface.co/jobs/${owner}/${jobId}`}return out} function runPassedSpaceCreation(payload={}){ const events=payload.events||payload.view?.activity||payload.detail?.events||[]; const summary=payload.summary||payload.detail?.summary||{}; const stateObj=payload.state||payload.detail?.state||{}; const success=String(payload.status||summary.status||stateObj.status||'').toLowerCase(); if(success.includes('success')||success.includes('succeed')||success==='done'||success==='completed')return true; const passedSteps=new Set(['create_space','upload_files','space_runtime','space_logs','api_validation','live_wait','generation_smoke','inference_gate','report_write','done']); return Array.isArray(events)&&events.some(e=>passedSteps.has(String(e.step||''))&&String(e.status||'').toLowerCase()!=='started'); } function setGeneratedSpaceLinksEnabled(links={},enabled=false){ const space=enabled?links.target_space_url:''; const settings=enabled?(links.target_space_settings_url||links.target_space_url&&links.target_space_url+'/settings'):''; for(const id of ['openSpace','selectedOpenSpace','launchOpenSpace'])setLink(id,space); for(const id of ['openSettings','selectedOpenSettings','manualOpenSettings','launchOpenSettings'])setLink(id,settings); } function setQuickLinks(links={},context={}){links=normalizeLinks(links||{});state.activeLinks=links;for(const id of ['openJob','selectedOpenJob','launchOpenJob'])setLink(id,links.job_url);const spaceReady=runPassedSpaceCreation(context);setGeneratedSpaceLinksEnabled(links,spaceReady);for(const id of ['openArtifacts','selectedOpenArtifacts','launchOpenArtifacts'])setLink(id,links.artifacts_url);renderManualAction({links:{...links,target_space_url:spaceReady?links.target_space_url:'',target_space_settings_url:spaceReady?links.target_space_settings_url:''}})} let operationTimer=null;function toastDuration(type='info'){if(type==='loading')return 12000;if(type==='error')return 7000;if(type==='warning'||type==='warn')return 6000;return 3500} function setOperation(message,type='info',opts={}){const el=$('operationStatus');if(!el)return;if(operationTimer){clearTimeout(operationTimer);operationTimer=null}if(!message){el.hidden=true;el.textContent='';el.className='operation-status floating-toast';return}el.hidden=false;el.className='operation-status floating-toast '+type;el.textContent=message;const duration=opts.duration??toastDuration(type);if(duration>0)operationTimer=setTimeout(()=>{setOperation('')},duration)} function showMessage(msg,isError=false){const type=isError===true?'error':(typeof isError==='string'?isError:'info');setOperation(msg,type);const el=$('eventsPanel');if(el&&!state.runId)el.textContent=(isError?'Error: ':'')+msg} function setBusy(isBusy,label='Working…'){state.busy=Boolean(isBusy);if(!state.busy&&$('operationStatus')?.className.includes('loading'))setOperation('');document.querySelectorAll('[data-action-button]').forEach(btn=>{btn.disabled=state.busy||btn.dataset.requiresBucket==='true'&&!state.bucketReady});if($('launchBuild'))$('launchBuild').disabled=state.busy||!state.bucketReady;if($('launchValidate'))$('launchValidate').disabled=state.busy;if(state.busy)setOperation(label,'loading')} function setButtonBusy(id,isBusy,busyLabel){const btn=$(id);if(!btn)return;if(isBusy){btn.dataset.oldText=btn.textContent;btn.textContent=busyLabel||'Working…';btn.disabled=true;btn.classList.add('is-busy')}else{if(btn.dataset.oldText)btn.textContent=btn.dataset.oldText;btn.disabled=state.busy||btn.dataset.requiresBucket==='true'&&!state.bucketReady;btn.classList.remove('is-busy')}} function setBucketReady(ok,message){state.bucketReady=Boolean(ok);const btn=$('launchBuild');if(btn)btn.disabled=state.busy||!state.bucketReady;setText('buildGateText',state.bucketReady?'Bucket ready. You can launch builds.':(message||'Create your private run bucket before launching builds.'));setText('bucketInlineStatus',state.bucketReady?'Bucket ready':'Not ready');for(const id of ['bucketInlineDot','sidebarBucketDot']){const dot=$(id);if(dot)dot.className='status-dot '+(state.bucketReady?'green':'amber-dot')}const inline=$('bucketInlineStatus');if(inline)inline.className='badge '+(state.bucketReady?'success':'warn');document.querySelectorAll('[data-requires-bucket="true"]').forEach(btn=>btn.disabled=state.busy||!state.bucketReady)} function effectivePreferredHardware(){const preferred=formValue('preferredHardware')||'zero-a10g';const fallback=formValue('fallbackHardware')||'a10g-large';const tryZero=$('tryZeroGpu')?.checked??true;if(tryZero)return preferred;if(preferred==='zero-a10g'||preferred.startsWith('zero'))return fallback;return preferred} function hardwarePlanLabel(){const tryZero=$('tryZeroGpu')?.checked??true;const preferred=effectivePreferredHardware();const fallback=formValue('fallbackHardware')||'a10g-large';const allow=$('allowFallback')?.checked??true;if(tryZero)return `ZeroGPU → ${allow?fallback:'no fixed fallback'}`;return `${preferred}${allow&&fallback!==preferred?' → '+fallback+' fallback':''}`} function validateBuildForm(){if(!state.bucketReady)return'Create or check your private run bucket before launching a build.';if(!formValue('modelId'))return'Enter a model card URL or model ID.';if(!formValue('targetSpace'))return'Enter a fresh target Space name.';if(!formValue('piModel'))return'Enter the Pi model to use.';return''} function validateValidationForm(){if(!formValue('validateSpace'))return'Enter the target Space ID to validate.';try{JSON.parse(formValue('testArgs')||'[]');JSON.parse(formValue('testKwargs')||'{}')}catch(e){return`Invalid JSON payload: ${e.message}`}return''} function setBuildModeNew(){const panel=$('buildModePanel');if(panel){panel.classList.remove('inspecting');panel.innerHTML='Preparing a new build

This form launches new builds. Existing runs can be inspected in the Runs Explorer on the right.

'}const launch=$('launchResultPanel');if(launch)launch.hidden=true;const status=$('progressStatus');if(status){status.textContent='Idle';status.className='badge neutral'}setText('progressTitle','No run selected')} function setBuildModeInspecting(runId){const panel=$('buildModePanel');if(panel){panel.classList.add('inspecting');panel.innerHTML=`Inspecting selected run

You are viewing run ${escapeHtml(runId||'')}. The build form is unchanged and the active run panel is now tracking it.

`}} function resetRunDetails(){ const cancel=$('cancelRun');if(cancel)cancel.disabled=true; if(typeof elapsedClock!=='undefined')elapsedClock.isRunning=false;if(typeof resetProgressStability==='function')resetProgressStability(null); if(typeof activityFeedState!=='undefined'){activityFeedState.lastItems=[];activityFeedState.runId=null} for(const [id,value] of [ ['detailRunId','—'],['detailJobStatus','—'],['detailGateStatus','—'],['detailSpace','—'],['detailHardware','—'],['detailTraces','—'], ['homeRunDetailStatus','No run'],['selectedRunId','—'],['selectedModelId','—'],['selectedModelShort','—'],['selectedTargetSpace','—'], ['selectedHardware','—'],['selectedMode','—'],['runDetailStatus','—'],['progressTitle','No run selected'],['progressStatus','Idle'], ['currentStep','—'],['lastEvent','Select a run from the explorer or launch a new build.'],['elapsed','—'],['lastPolled','—'], ['progressPercent','0%'] ])setText(id,value); const status=$('progressStatus');if(status)status.className='badge neutral'; const fill=$('progressFill');if(fill){fill.style.width='0%';fill.classList.remove('is-running','progress-bump')} setQuickLinks({}); const full=$('reportPreview');if(full)full.textContent='No report selected.'; const manual=$('manualActionPanel');if(manual)manual.hidden=true; const activity=$('activityFeed');if(activity){activity.dataset.signature='';activity.classList.remove('show-all');activity.innerHTML='
No activity yet.
'} const events=$('eventsPanel');if(events)events.textContent='No events yet.'; const artifacts=$('artifactList');if(artifacts)artifacts.textContent='No artifacts indexed yet.'; const blockers=$('selectedBlockers');if(blockers){blockers.hidden=true;blockers.innerHTML=''} const del=$('deleteSelectedRun');if(del)del.disabled=true; renderTimeline(DEFAULT_TIMELINE,'idle'); } function prepareNewBuild(){if(state.poll){clearInterval(state.poll);state.poll=null}state.runId=null;state.selectedRunDetail=null;state.activeLinks={};state.pollErrors=0;clearSavedRun();document.querySelectorAll('.run-row').forEach(r=>r.classList.remove('selected'));setBuildModeNew();resetRunDetails();renderTimeline(DEFAULT_TIMELINE);setText('progressPercent','0%');const fill=$('progressFill');if(fill)fill.style.width='0%';setText('currentStep','Ready');setText('lastEvent','Choose a model card and launch a build.');setText('elapsed','—');if(typeof elapsedClock!=='undefined')elapsedClock.isRunning=false;setText('lastPolled','—');setText('hardwarePlan',hardwarePlanLabel());renderMetrics({});showMessage('New build mode ready.','info')} function setActiveRun(result){state.runId=result.run_id;if(Array.isArray(state.runsCache)&&typeof sortRunsNewestFirst==='function'){state.runsCache=sortRunsNewestFirst([{...result,run_id:result.run_id,status:result.status||'running',created_at:result.created_at||new Date().toISOString()},...state.runsCache.filter(r=>r.run_id!==result.run_id)]);renderRunsFromCache();}if(result.bucket_source)state.bucketSource=result.bucket_source;setText('detailRunId',result.run_id);setText('detailJobStatus',result.status||'RUNNING');setText('detailGateStatus',result.kind||'running');setText('detailSpace',result.target_space||'—');setText('detailHardware',result.selected_hardware||result.preferred_space_hardware||'—');setText('homeRunDetailStatus',result.status||'Running');setBuildModeInspecting(result.run_id);setQuickLinks({...result.links,bucket_source:result.bucket_source,run_id:result.run_id,job_id:result.job_id,job_owner:state.user?.username,target_space_url:result.target_space_url},result);saveActiveRun();startPolling(result.run_id)} function startPolling(runId){state.runId=runId;state.pollErrors=0;state.lastJobLogPoll=0;if(typeof resetProgressStability==='function')resetProgressStability(runId);if(state.poll)clearInterval(state.poll);saveActiveRun();refreshProgress();state.poll=setInterval(refreshProgress,2000)} async function bootstrapRunRecovery(){return restoreActiveRun()} async function restoreActiveRun(){try{const resumable=await apiGet(`/api/runs/resumable?bucket_name=${encodeURIComponent(state.bucketName)}`);if(resumable?.run?.run_id){state.runId=resumable.run.run_id;if(resumable.bucket_source)state.bucketSource=resumable.bucket_source;setBuildModeInspecting(state.runId);if(resumable.view)renderRunViewModel(resumable.view,{progress:0,events:[]});setQuickLinks(resumable.view?.links||{},resumable.view||{});startPolling(state.runId);return true}}catch(e){console.warn('resumable lookup failed',e)}try{const raw=localStorage.getItem('asf.activeRun');if(!raw)return false;const saved=JSON.parse(raw);if(saved?.bucketName)state.bucketName=saved.bucketName;if(saved?.runId){state.runId=saved.runId;setText('detailRunId',saved.runId);setQuickLinks(saved.links||{},{});setBuildModeInspecting(saved.runId);startPolling(saved.runId);return true}}catch(_){}return false} async function loadAppInfo(){setOperation('Checking sign-in and run storage…','loading');try{const me=await apiGet('/api/me');state.user=me;setText('userChip','@'+me.username);const userChip=$('userChip');if(userChip)userChip.href=`https://huggingface.co/${me.username}`;const logout=$('logoutLink');if(logout){logout.hidden=false;logout.href=me.logout_url||'/logout'}state.bucketSource=me.username+'/'+state.bucketName;setText('bucketInlineLabel',state.bucketSource);renderAuthWarnings(me);await checkBucket(false);await loadRuns(true);clearSavedRun();if(!state.bucketReady)setOperation('Create your private run bucket before launching builds.','warning');else setOperation('')}catch(e){state.user=null;setText('userChip','Sign in');const userChip=$('userChip');if(userChip)userChip.href='/oauth/huggingface/login';const logout=$('logoutLink');if(logout)logout.hidden=true;renderSignedOut();setBucketReady(false,'Sign in with Hugging Face first.');showMessage('Please sign in with Hugging Face, then refresh this page.',true)}} function renderSignedOut(){const auth=$('authPanel');if(!auth)return;auth.hidden=false;auth.className='auth-panel warning';auth.innerHTML='Sign in required

Use Hugging Face OAuth before launching Jobs or reading your Bucket.

Sign in with Hugging Face ↗'} function renderAuthWarnings(me){const auth=$('authPanel');if(!auth)return;const missing=me.missing_scopes||[];const warnings=me.warnings||[];if(!missing.length&&!warnings.length){auth.hidden=true;auth.innerHTML='';return}auth.hidden=false;auth.className='auth-panel warning';auth.innerHTML=`OAuth session warning

${escapeHtml([...warnings,missing.length?'Missing scopes: '+missing.join(', '):''].filter(Boolean).join(' • '))}

Refresh sign-in ↗`} async function checkBucket(noisy=true){setButtonBusy('checkBucket',true,'Checking…');try{const data=await apiGet(`/api/bucket/status?bucket_name=${encodeURIComponent(state.bucketName)}`);const source=data.bucket_source||state.bucketName;state.bucketSource=source;setText('bucketInlineLabel',source);setBucketReady(Boolean(data.ok),data.error||'Create your private run bucket before launching builds.');if(noisy)showMessage(data.ok?`Bucket ready: ${source}`:`Bucket not ready: ${data.error||'not found'}`,!data.ok);return data}catch(e){setBucketReady(false,e.message);if(noisy)showMessage(e.message,true);throw e}finally{setButtonBusy('checkBucket',false)}} async function createBucket(){setButtonBusy('createBucket',true,'Creating…');try{const data=await apiPost('/api/bucket/create',{bucket_name:state.bucketName});const source=data.bucket_source||state.bucketName;state.bucketSource=source;setText('bucketInlineLabel',source);setBucketReady(Boolean(data.ok),data.error||'Could not create bucket.');showMessage(data.ok?`Created/ready: ${source}`:`Could not create bucket: ${data.error}`,!data.ok);if(data.ok)loadRuns(true)}catch(e){setBucketReady(false,e.message);showMessage(e.message,true)}finally{setButtonBusy('createBucket',false)}} function renderManualAction(p={}){const panel=$('manualActionPanel');if(!panel)return;const view=p.view||{};const sm=view.status_model||{};const status=String(p.status||p.state?.status||p.inference_gate?.status||sm.global_status||'');const manual=Boolean(sm.requires_manual_action||p.manual_hardware_required||p.inference_gate?.manual_hardware_required||p.hardware_strategy?.manual_action_required||status.includes('manual_hardware_required')||status.includes('generated_needs_manual_hardware')||status.includes('waiting_manual_action'));panel.hidden=!manual;if(!manual)return;setText('manualActionTitle','Manual hardware required');setText('manualActionText','Automatic hardware selection was not available. Open Space settings, choose the recommended GPU, then run Space Test.');setLink('manualOpenSettings',(p.links||view.links||state.activeLinks||{}).target_space_settings_url)} function prepareValidationFromActiveRun(){const detail=state.selectedRunDetail;const summary=detail?.summary||{};const target=summary.target_space||$('detailSpace')?.textContent||'';if(target&&target!=='—')setInputValue('validateSpace',target);const expected=summary.expected_output_type||detail?.generation_smoke?.expected_output_type||formValue('buildExpectedOutput')||'image';setInputValue('expectedOutput',expected);setInputValue('validateApi','/generate');switchView('validate');showMessage(`Prepared validation form for ${target||'selected Space'}.`,'info')} async function launchBuild(){const formError=validateBuildForm();if(formError){showMessage(formError,true);return}setBusy(true,'Launching build Job…');setButtonBusy('launchBuild',true,'Launching…');try{setText('hardwarePlan',hardwarePlanLabel());const payload={bucket_name:state.bucketName,run_id:formValue('runId')||undefined,model_id:formValue('modelId'),target_space_name:formValue('targetSpace'),pi_model:formValue('piModel'),implementation_mode:formValue('implementationMode'),preferred_space_hardware:effectivePreferredHardware(),fallback_space_hardware:formValue('fallbackHardware'),allow_fixed_gpu_fallback:$('allowFallback')?.checked??true,expected_output_type:formValue('buildExpectedOutput')};const result=await apiPost('/api/build',payload);setActiveRun(result);showMessage(`Build launched: ${result.run_id}`,'success');loadRuns(true)}catch(e){showMessage(e.message,true)}finally{setBusy(false);setButtonBusy('launchBuild',false)}} async function launchValidate(){const formError=validateValidationForm();if(formError){showMessage(formError,true);return}setBusy(true,'Launching validation Job…');setButtonBusy('launchValidate',true,'Launching…');try{const payload={bucket_name:state.bucketName,target_space_id:formValue('validateSpace'),api_name:formValue('validateApi')||'/generate',expected_output_type:formValue('expectedOutput')||'image',test_args_json:formValue('testArgs')||'[]',test_kwargs_json:formValue('testKwargs')||'{}',live_timeout_seconds:Number(formValue('validateTimeout')||1800)};const result=await apiPost('/api/validate',payload);setActiveRun(result);showMessage(`Validation launched: ${result.run_id}`,'success');loadRuns(true)}catch(e){showMessage(e.message,true)}finally{setBusy(false);setButtonBusy('launchValidate',false)}} async function refreshRunReportPreview(runId){ if(!runId)return; try{ const detail=await apiGet(`/api/runs/${encodeURIComponent(runId)}?bucket_name=${encodeURIComponent(state.bucketName)}`); if(detail.bucket_source)state.bucketSource=detail.bucket_source; const reportText=(detail.report||'No report available.').slice(0,8000); const report=$('reportPreview'); if(report)report.textContent=reportText; state.selectedRunDetail=detail; if(typeof renderSelectedLinks==='function')renderSelectedLinks({...detail.summary,bucket_source:detail.bucket_source},detail); if(typeof renderBlockers==='function')renderBlockers(detail); renderMetrics({...detail,summary:detail.summary||{}}); }catch(e){ console.warn('report refresh failed',e); } } async function refreshProgress(){if(!state.runId||state.progressPollInFlight)return;state.progressPollInFlight=true;try{const now=Date.now();const includeLogs=!state.lastJobLogPoll||now-state.lastJobLogPoll>8000;if(includeLogs)state.lastJobLogPoll=now;const p=await apiGet(`/api/runs/${encodeURIComponent(state.runId)}/progress?bucket_name=${encodeURIComponent(state.bucketName)}${includeLogs?'&include_job_logs=1':''}`);state.pollErrors=0;if(p.bucket_source)state.bucketSource=p.bucket_source;renderProgress(p);renderEvents(p.events||[]);renderMetrics(p);if(p.view){setText('detailRunId',p.view.run_id||p.run_id);setText('selectedModelId',p.view.header?.model||p.summary?.model_id||'—')}else{setText('detailRunId',p.run_id||state.runId)}if(p.state){setText('detailSpace',p.state.target_space||p.summary?.target_space||'—');setText('detailHardware',p.state.selected_hardware||p.summary?.selected_hardware||'—')}setText('detailJobStatus',p.status||p.state?.status||p.view?.header?.raw_status||'running');setText('detailGateStatus',p.inference_gate?.status||p.summary?.gate_status||'—');setText('detailTraces',Array.isArray(p.events)?String(p.events.length)+' events':'—');setText('homeRunDetailStatus',badgeLabel(p.status||p.state?.status||p.view?.header?.status||'running'));setQuickLinks({...p.links,...p.view?.links,bucket_source:p.bucket_source,run_id:p.run_id},{...p,events:p.events||[]});saveActiveRun();const status=String(p.view?.header?.status||p.status||p.state?.status||'').toLowerCase();if(TERMINAL_STATUSES.has(status)&&state.poll){clearInterval(state.poll);state.poll=null;if(state.lastReportRefreshRunId!==state.runId){state.lastReportRefreshRunId=state.runId;refreshRunReportPreview(state.runId)}setOperation(`Run finished with status: ${badgeLabel(status)}`,status.includes('success')||status.includes('succeed')?'success':status.includes('failed')||status.includes('blocker')?'error':'warning')}if(now-(state.lastRunsRefreshFromPoll||0)>15000){state.lastRunsRefreshFromPoll=now;loadRuns(false)}}catch(e){state.pollErrors+=1;console.warn(e);setOperation(`Progress polling failed ${state.pollErrors} time${state.pollErrors===1?'':'s'}. Retrying automatically…`,'warning')}finally{state.progressPollInFlight=false}} function renderMetrics(p){const root=$('metricsList');if(!root)return;const smoke=p.generation_smoke||{};const gate=p.inference_gate||{};const health=(gate.implementation_signals&&gate.implementation_signals.health_passed)||smoke.health_passed;const smokeOk=smoke.ok||smoke.status==='success';const latency=smoke.latency_seconds||p.summary?.latency_seconds;const duration=smoke.recommended_zerogpu_duration_seconds||smoke.recommended_duration_seconds;root.innerHTML=`
  • /health${health?'passed':'pending'}
  • /generate smoke test${smokeOk?'passed':(smoke.status||'pending')}
  • Latency${latency?fmtSeconds(Math.round(latency)):'—'}
  • Recommended ZeroGPU duration${duration?fmtSeconds(Math.round(duration)):'—'}
  • `} function renderEvents(events){const root=$('eventsPanel');if(!root)return;root.textContent=(events||[]).slice(-12).map(e=>`${e.ts||''} ${e.step||''} ${e.status||''} ${e.message||''}`).join('\n')||'No events yet. The run may still be scheduling.'} async function cancelActiveRun(){ if(!state.runId){showMessage('Select a running run before cancelling.', 'warning');return} if(!confirm(`Cancel Job for run ${state.runId}?`))return; setButtonBusy('cancelRun',true,'Cancelling…'); try{ const result=await apiPost(`/api/runs/${encodeURIComponent(state.runId)}/cancel?bucket_name=${encodeURIComponent(state.bucketName)}`,{}); showMessage(`Cancelled Job ${result.job_id||''}`,'success'); if(state.poll){clearInterval(state.poll);state.poll=null} await refreshProgress(); await loadRuns(true); }catch(e){ showMessage(e.message,true); }finally{ setButtonBusy('cancelRun',false); } } async function resumeLatestRun(){setButtonBusy('resumeLatestRun',true,'Resuming…');try{await loadRuns(true);const data=await apiGet(`/api/runs/resumable?bucket_name=${encodeURIComponent(state.bucketName)}`);if(!data.run){showMessage('No latest run found in this bucket.','warning');return}state.runId=data.run.run_id;if(data.bucket_source)state.bucketSource=data.bucket_source;setText('detailRunId',state.runId);setBuildModeInspecting(state.runId);setQuickLinks(data.view?.links||{},data.view||{});if(data.view)renderRunViewModel(data.view,{run_id:state.runId,progress:data.view.header?.status==='succeeded'?100:0,events:data.view.activity||[]});startPolling(state.runId);showMessage(`Resumed ${state.runId}`,'success')}catch(e){showMessage(e.message,true)}finally{setButtonBusy('resumeLatestRun',false)}} function switchView(view){state.currentView=view;document.querySelectorAll('.nav-item').forEach(b=>b.classList.toggle('active',b.dataset.view===view));document.querySelectorAll('[data-section]').forEach(s=>s.hidden=s.dataset.section!==view);setOperation('');if(view==='progress')loadRuns(false);const targetMap={build:'newBuildCard',progress:'runsExplorerPanel',validate:'spaceTestSection'};const target=$(targetMap[view]||'');if(target)target.scrollIntoView({behavior:'smooth',block:'start'})} function bindNavigation(){document.querySelectorAll('.nav-item').forEach(btn=>btn.addEventListener('click',()=>switchView(btn.dataset.view||'build')));document.querySelectorAll('[data-view-target]').forEach(btn=>btn.addEventListener('click',()=>switchView(btn.dataset.viewTarget||'build')))} function bindRunFilters(){document.querySelectorAll('.filter[data-status]').forEach(btn=>btn.addEventListener('click',()=>{state.runStatusFilter=btn.dataset.status||'all';state.runPage=0;renderRunsFromCache()}));for(const id of ['runSearch','runSearchFull'])$(id)?.addEventListener('input',e=>{state.runSearch=e.target.value||'';state.runPage=0;for(const other of ['runSearch','runSearchFull'])if($(other)&&$(other)!==e.target)$(other).value=state.runSearch;clearTimeout(state.runSearchTimer);state.runSearchTimer=setTimeout(renderRunsFromCache,80)})} function bindActions(){$('launchBuild')?.setAttribute('data-requires-bucket','true');$('launchBuild')?.setAttribute('data-action-button','true');$('launchValidate')?.setAttribute('data-action-button','true');$('launchBuild')?.addEventListener('click',launchBuild);$('launchValidate')?.addEventListener('click',launchValidate);$('prepareNewBuild')?.addEventListener('click',prepareNewBuild);$('manualGoValidate')?.addEventListener('click',prepareValidationFromActiveRun);$('selectedValidateBtn')?.addEventListener('click',prepareValidationFromActiveRun);$('checkBucket')?.addEventListener('click',()=>checkBucket(true));$('createBucket')?.addEventListener('click',createBucket);$('refreshRuns')?.addEventListener('click',()=>loadRuns(true));$('refreshRunsHome')?.addEventListener('click',()=>loadRuns(true));$('resumeLatestRun')?.addEventListener('click',resumeLatestRun);$('cancelRun')?.addEventListener('click',cancelActiveRun);$('resumeLatestRunFull')?.addEventListener('click',resumeLatestRun);['tryZeroGpu','preferredHardware','fallbackHardware','allowFallback'].forEach(id=>$(id)?.addEventListener('change',()=>setText('hardwarePlan',hardwarePlanLabel())))} bindNavigation();bindActions();bindRunFilters();setText('hardwarePlan',hardwarePlanLabel());setBuildModeNew();renderTimeline(DEFAULT_TIMELINE);loadAppInfo(); // Pass 47 intentionally removed automatic switchView('progress') after launch and run selection.