From d803d57a78ec12af819c610ec698e7186e0d7945 Mon Sep 17 00:00:00 2001 From: Towsty Date: Sat, 3 Oct 2026 19:38:55 -0500 Subject: [PATCH] Fix bench draft structuredClone 500 and Prompt Builder button contrast. Draft snapshots use plain JSON of picked fields with restore try/catch that clears bad storage; Prompt Builder actions use amber primary/secondary styles with 44px tap targets. --- components/studio2/Bench.vue | 167 +++- pages/prompt-builder.vue | 1752 +++++++++++++++++----------------- 2 files changed, 1003 insertions(+), 916 deletions(-) diff --git a/components/studio2/Bench.vue b/components/studio2/Bench.vue index 3a99b0a..cef79f9 100644 --- a/components/studio2/Bench.vue +++ b/components/studio2/Bench.vue @@ -520,6 +520,58 @@ function defaultEngineLook(engine:string):EngineLook{ if(engine==='qwen21') return {steps:25,cfg:1,aspect:'auto',seed:null,seedMode:'random',loraStack:[]} return {steps:24,cfg:4,aspect:'auto',seed:null,seedMode:'random',loraStack:[]} } +/** Plain JSON snapshot only — never structuredClone Vue proxies / Files. */ +function plainJson(value:unknown, fallback:T):T{ + try{return JSON.parse(JSON.stringify(value)) as T}catch{return fallback} +} +function plainLoraStack(stack:unknown){ + if(!Array.isArray(stack)) return [] as any[] + return stack.map((item:any)=>{ + if(!item || typeof item!=='object') return null + const name=String(item.name||item.filename||item.lora||'').trim() + if(!name) return null + const out:any={name} + if(item.strength!=null && Number.isFinite(Number(item.strength))) out.strength=Number(item.strength) + if(item.strengthModel!=null && Number.isFinite(Number(item.strengthModel))) out.strengthModel=Number(item.strengthModel) + if(item.strengthClip!=null && Number.isFinite(Number(item.strengthClip))) out.strengthClip=Number(item.strengthClip) + if(item.clipStrength!=null && Number.isFinite(Number(item.clipStrength))) out.clipStrength=Number(item.clipStrength) + return out + }).filter(Boolean) +} +function plainPromptSections(sections:any){ + const src=sections&&typeof sections==='object'?sections:{} + return { + action:String(src.action||''), + camera:String(src.camera||''), + soundscape:String(src.soundscape||''), + music:String(src.music||''), + locks:String(src.locks||''), + cameraBeats:plainJson(src.cameraBeats||[], [] as any[]), + soundscapeChips:Array.isArray(src.soundscapeChips)?src.soundscapeChips.map((c:any)=>String(c||'')).filter(Boolean):[], + soundscapeLevel:String(src.soundscapeLevel||'normal'), + musicChip:String(src.musicChip||'N/A'), + dialogue:plainJson(src.dialogue||{mode:'none',line:''}, {mode:'none',line:''}) + } +} +function plainEngineSamplingMap(){ + const out:Record={} + for(const key of ['flux','krea','qwen21'] as const){ + const row=engineSampling[key]||defaultEngineLook(key) + out[key]={ + steps:Number(row.steps)||defaultEngineLook(key).steps, + cfg:Number(row.cfg)||defaultEngineLook(key).cfg, + aspect:String(row.aspect||'auto'), + seed:row.seed==null || row.seed==='' ? null : Number(row.seed), + seedMode:row.seedMode==='locked'?'locked':'random', + loraStack:plainLoraStack(row.loraStack) + } + } + return out +} +function clearDraftStores(){ + try{localStorage.removeItem(BENCH_DRAFT_KEY)}catch{/* ignore */} + try{sessionStorage.removeItem(BENCH_DRAFT_SESSION_KEY)}catch{/* ignore */} +} function readStoredNegatives(){ if(!import.meta.client)return [...defaultNegatives] try{ @@ -550,36 +602,64 @@ function writeStoredTurbo(on:boolean){ try{localStorage.setItem(TURBO_STORAGE_KEY,on?'1':'0')}catch{/* ignore */} } function benchDraftPayload(){ - if(['flux','krea','qwen21'].includes(form.engine)) rememberEngineSampling(form.engine) - return { - v:2, - mode:form.mode, - engine:form.engine, - folderId:form.folderId, - enhancePrompt:!!form.enhancePrompt, - turbo:!!form.turbo, - lockFace:!!form.lockFace, - lockOutfit:!!form.lockOutfit, - promptSections:structuredClone(form.promptSections||{}), - photos:(form.photos||[]).filter((p:any)=>p?.stillId).map((p:any)=>({stillId:p.stillId,role:p.role})), - imageStyles:structuredClone(form.imageStyles||{positive:[],negative:[]}), - settings:{ - aspect:form.settings?.aspect||'auto', - seed:form.settings?.seed ?? null, - seedMode:form.settings?.seedMode||'random', - steps:form.settings?.steps, - cfg:form.settings?.cfg, - duration:form.settings?.duration, - fps:form.settings?.fps, - loraStack:structuredClone(form.settings?.loraStack||[]) - }, - engineSampling:structuredClone(engineSampling), - batchMode:!!batchMode.value, - eyeOpen:!!eyeOpen.value, - hideInputs:!!hideInputs.value, - queueCollapsed:!!queueCollapsed.value, - selectedId:selected.value?.id||'', - music:structuredClone({tags:music.tags,lyrics:music.lyrics,duration:music.duration,seed:music.seed,name:music.name}) + try{ + if(['flux','krea','qwen21'].includes(form.engine)) rememberEngineSampling(form.engine) + const styles=form.imageStyles||{positive:[],negative:[]} + return { + v:2, + mode:String(form.mode||'generate'), + engine:String(form.engine||'flux'), + folderId:String(form.folderId||''), + enhancePrompt:!!form.enhancePrompt, + turbo:!!form.turbo, + lockFace:!!form.lockFace, + lockOutfit:!!form.lockOutfit, + promptSections:plainPromptSections(form.promptSections), + photos:(form.photos||[]) + .filter((p:any)=>p && typeof p.stillId==='string' && p.stillId) + .map((p:any)=>({stillId:String(p.stillId),role:String(p.role||'change')})), + imageStyles:{ + positive:Array.isArray(styles.positive)?styles.positive.map((s:any)=>String(s||'')).filter(Boolean):[], + negative:Array.isArray(styles.negative)?styles.negative.map((s:any)=>String(s||'')).filter(Boolean):[] + }, + settings:{ + aspect:String(form.settings?.aspect||'auto'), + seed:form.settings?.seed==null || form.settings?.seed==='' ? null : Number(form.settings.seed), + seedMode:form.settings?.seedMode==='locked'?'locked':'random', + steps:Number(form.settings?.steps)||24, + cfg:Number(form.settings?.cfg)||4, + duration:Number(form.settings?.duration)||5, + fps:Number(form.settings?.fps)||24, + loraStack:plainLoraStack(form.settings?.loraStack) + }, + engineSampling:plainEngineSamplingMap(), + batchMode:!!batchMode.value, + eyeOpen:!!eyeOpen.value, + hideInputs:!!hideInputs.value, + queueCollapsed:!!queueCollapsed.value, + selectedId:typeof selected.value?.id==='string'?selected.value.id:'', + music:{ + tags:String(music.tags||''), + lyrics:String(music.lyrics||''), + duration:Number(music.duration)||30, + seed:String(music.seed||''), + name:String(music.name||'') + } + } + }catch{ + return { + v:2, + mode:String(form.mode||'generate'), + engine:String(form.engine||'flux'), + folderId:String(form.folderId||''), + promptSections:{action:String(form.promptSections?.action||'')}, + engineSampling:plainEngineSamplingMap(), + settings:{ + aspect:String(form.settings?.aspect||'auto'), + steps:Number(form.settings?.steps)||24, + cfg:Number(form.settings?.cfg)||4 + } + } } } function writeDraftStores(payload:string){ @@ -609,18 +689,18 @@ function mergeEngineLook(row:any, fallback:EngineLook):EngineLook{ aspect:typeof row?.aspect==='string' && row.aspect ? row.aspect : fallback.aspect, seed:row?.seed==null || row?.seed==='' ? null : Number(row.seed), seedMode:row?.seedMode==='locked'?'locked':'random', - loraStack:Array.isArray(row?.loraStack)?structuredClone(row.loraStack):structuredClone(fallback.loraStack) + loraStack:plainLoraStack(row?.loraStack ?? fallback.loraStack) } } function restoreBenchDraft(){ if(!import.meta.client)return const raw=readDraftRaw() if(!raw)return - let draft:any - try{draft=JSON.parse(raw)}catch{return} - if(!draft|| (draft.v!==1 && draft.v!==2))return restoringDraft=true try{ + let draft:any + try{draft=JSON.parse(raw)}catch{clearDraftStores();return} + if(!draft|| (draft.v!==1 && draft.v!==2)){clearDraftStores();return} if(draft.engineSampling && typeof draft.engineSampling==='object'){ for(const key of ['flux','krea','qwen21'] as const){ const row=draft.engineSampling[key] @@ -636,8 +716,13 @@ function restoreBenchDraft(){ if(draft.lockFace!=null)form.lockFace=!!draft.lockFace if(draft.lockOutfit!=null)form.lockOutfit=!!draft.lockOutfit if(draft.promptSections)form.promptSections=sectionsFrom(draft.promptSections) - if(Array.isArray(draft.photos))form.photos=draft.photos.filter((p:any)=>p?.stillId).map((p:any)=>({stillId:String(p.stillId),role:String(p.role||'change')})) - if(draft.imageStyles)form.imageStyles={positive:draft.imageStyles.positive||[],negative:draft.imageStyles.negative||[]} + if(Array.isArray(draft.photos))form.photos=draft.photos + .filter((p:any)=>p && typeof p.stillId==='string' && p.stillId) + .map((p:any)=>({stillId:String(p.stillId),role:String(p.role||'change')})) + if(draft.imageStyles)form.imageStyles={ + positive:Array.isArray(draft.imageStyles.positive)?draft.imageStyles.positive.map((s:any)=>String(s||'')).filter(Boolean):[], + negative:Array.isArray(draft.imageStyles.negative)?draft.imageStyles.negative.map((s:any)=>String(s||'')).filter(Boolean):[] + } if(draft.settings){ Object.assign(form.settings,{ aspect:draft.settings.aspect||form.settings.aspect, @@ -647,7 +732,7 @@ function restoreBenchDraft(){ cfg:draft.settings.cfg ?? form.settings.cfg, duration:draft.settings.duration ?? form.settings.duration, fps:draft.settings.fps ?? form.settings.fps, - loraStack:Array.isArray(draft.settings.loraStack)?draft.settings.loraStack:form.settings.loraStack + loraStack:Array.isArray(draft.settings.loraStack)?plainLoraStack(draft.settings.loraStack):form.settings.loraStack }) } if(['flux','krea','qwen21'].includes(form.engine)) applyEngineSampling(form.engine) @@ -663,7 +748,9 @@ function restoreBenchDraft(){ if(draft.music.name!=null)music.name=String(draft.music.name) } syncPhotosBindings() - } finally { + }catch{ + clearDraftStores() + }finally{ restoringDraft=false } } @@ -746,7 +833,7 @@ function rememberEngineSampling(engine:string){ aspect:String(form.settings.aspect||fallback.aspect), seed:form.settings.seed==null || form.settings.seed==='' ? null : Number(form.settings.seed), seedMode:form.settings.seedMode==='locked'?'locked':'random', - loraStack:Array.isArray(form.settings.loraStack)?structuredClone(form.settings.loraStack):[] + loraStack:plainLoraStack(form.settings.loraStack) } } function applyEngineSampling(engine:string){ @@ -758,7 +845,7 @@ function applyEngineSampling(engine:string){ form.settings.aspect=saved.aspect || form.settings.aspect || 'auto' form.settings.seed=saved.seed form.settings.seedMode=saved.seedMode || 'random' - form.settings.loraStack=Array.isArray(saved.loraStack)?structuredClone(saved.loraStack):[] + form.settings.loraStack=plainLoraStack(saved.loraStack) } function clearPrompt(){ if(form.promptSections) form.promptSections.action='' diff --git a/pages/prompt-builder.vue b/pages/prompt-builder.vue index 62c6dfa..1809983 100644 --- a/pages/prompt-builder.vue +++ b/pages/prompt-builder.vue @@ -1,876 +1,876 @@ -