upload() ran fitStill (ffmpeg scale+pad) on every image except hero, including the start/handoff still. Four chained extends resampled the anchor frame four times and destroyed identity even with the hero reference and lock text intact. - upload(): only fit when the still's aspect truly differs from the job canvas (needsFit); never fit hero or start regardless. - Track handoffSource (anchor|extract) and whether the start frame was fitted; surface both in job metadata and Details. - extendSelected(): re-binding an already-selected clip on task switch must not stomp the user's overridden locks/settings back to the clip's saved values (regression introduced by the last hotfix push). - Add tests/studio2-extend-fit.test.mjs: two extends at identical width/height reach Comfy with the anchor PNG byte-identical, no ffmpeg fit. Co-authored-by: Cursor <cursoragent@cursor.com>
26 lines
2.2 KiB
Vue
26 lines
2.2 KiB
Vue
<template>
|
||
<dl class="space-y-3 text-sm">
|
||
<div v-for="row in rows" :key="row.label"><dt class="font-semibold">{{ row.label }}</dt><dd class="break-words whitespace-pre-wrap text-zinc-400">{{ row.value ?? 'Not recorded' }}</dd></div>
|
||
</dl>
|
||
</template>
|
||
<script setup lang="ts">
|
||
const props=defineProps<{metadata?:any}>()
|
||
const rows=computed(()=>{const m=props.metadata||{},s=m.settings||{};const time=(v:any)=>v?new Date(v).toLocaleString():null;return [
|
||
{label:'Kind / mode',value:[m.kind,m.mode].filter(Boolean).join(' / ')},
|
||
{label:'Engine',value:m.engine},{label:'Graph ID',value:m.graphId},
|
||
{label:'Sample latent',value:m.sampleLatent},{label:'Edit denoise',value:m.sampleDenoise},
|
||
{label:'Source still',value:m.sourceStillId || m.imageAId || 'None'},
|
||
{label:'Hero reference attached',value:m.heroReferenceAttached==null?'Not recorded':m.heroReferenceAttached?'Yes':'No'},
|
||
{label:'Seed',value:s.seed},{label:'Steps',value:s.steps},{label:'CFG',value:s.cfg},
|
||
{label:'Size',value:s.width&&s.height?`${s.width} × ${s.height}`:null},{label:'Duration',value:s.duration!=null?`${s.duration}s`:null},
|
||
{label:'LoRAs',value:s.loraStack?.length?s.loraStack.map((l:any)=>`${l.name} · ${l.strengthModel}`).join('\n'):'None'},
|
||
{label:'Identity still',value:m.identityStillId||'None'},{label:'Face lock',value:m.lockFace!==false?'On':'Off'},{label:'Outfit lock',value:m.lockOutfit!==false?'On':'Off'},
|
||
{label:'Aspect',value:s.aspect||'auto'},{label:'FPS',value:s.fps||24},{label:'Hide input',value:m.hideInput?'On':'Off'},
|
||
{label:'Start frame',value:m.startFrameSource?JSON.stringify(m.startFrameSource):'None'},
|
||
{label:'Handoff source',value:m.handoffSource || 'None'},{label:'Handoff fitted (resampled)',value:m.fitted==null?'Not recorded':m.fitted?'Yes':'No'},
|
||
{label:'End still',value:m.endStillId||'None'},{label:'Guide stills',value:m.guides?.length?JSON.stringify(m.guides):'None'},
|
||
{label:'Compiled prompt',value:m.compiledPrompt},{label:'Queued',value:time(m.queuedAt)},{label:'Started',value:time(m.startedAt)},{label:'Saved',value:time(m.savedAt)},
|
||
{label:'GPU seconds',value:m.gpuSeconds},{label:'Wall seconds',value:m.wallTime},{label:'Comfy prompt ID',value:m.promptId},{label:'Host cleanup',value:m.purgeResult}
|
||
]})
|
||
</script>
|