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.
This commit is contained in:
Towsty
2026-10-03 19:38:55 -05:00
parent a8ae61704e
commit d803d57a78
2 changed files with 1003 additions and 916 deletions
+127 -40
View File
@@ -520,6 +520,58 @@ function defaultEngineLook(engine:string):EngineLook{
if(engine==='qwen21') return {steps:25,cfg:1,aspect:'auto',seed:null,seedMode:'random',loraStack:[]} 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:[]} return {steps:24,cfg:4,aspect:'auto',seed:null,seedMode:'random',loraStack:[]}
} }
/** Plain JSON snapshot only — never structuredClone Vue proxies / Files. */
function plainJson<T>(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<string,EngineLook>={}
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(){ function readStoredNegatives(){
if(!import.meta.client)return [...defaultNegatives] if(!import.meta.client)return [...defaultNegatives]
try{ try{
@@ -550,36 +602,64 @@ function writeStoredTurbo(on:boolean){
try{localStorage.setItem(TURBO_STORAGE_KEY,on?'1':'0')}catch{/* ignore */} try{localStorage.setItem(TURBO_STORAGE_KEY,on?'1':'0')}catch{/* ignore */}
} }
function benchDraftPayload(){ function benchDraftPayload(){
if(['flux','krea','qwen21'].includes(form.engine)) rememberEngineSampling(form.engine) try{
return { if(['flux','krea','qwen21'].includes(form.engine)) rememberEngineSampling(form.engine)
v:2, const styles=form.imageStyles||{positive:[],negative:[]}
mode:form.mode, return {
engine:form.engine, v:2,
folderId:form.folderId, mode:String(form.mode||'generate'),
enhancePrompt:!!form.enhancePrompt, engine:String(form.engine||'flux'),
turbo:!!form.turbo, folderId:String(form.folderId||''),
lockFace:!!form.lockFace, enhancePrompt:!!form.enhancePrompt,
lockOutfit:!!form.lockOutfit, turbo:!!form.turbo,
promptSections:structuredClone(form.promptSections||{}), lockFace:!!form.lockFace,
photos:(form.photos||[]).filter((p:any)=>p?.stillId).map((p:any)=>({stillId:p.stillId,role:p.role})), lockOutfit:!!form.lockOutfit,
imageStyles:structuredClone(form.imageStyles||{positive:[],negative:[]}), promptSections:plainPromptSections(form.promptSections),
settings:{ photos:(form.photos||[])
aspect:form.settings?.aspect||'auto', .filter((p:any)=>p && typeof p.stillId==='string' && p.stillId)
seed:form.settings?.seed ?? null, .map((p:any)=>({stillId:String(p.stillId),role:String(p.role||'change')})),
seedMode:form.settings?.seedMode||'random', imageStyles:{
steps:form.settings?.steps, positive:Array.isArray(styles.positive)?styles.positive.map((s:any)=>String(s||'')).filter(Boolean):[],
cfg:form.settings?.cfg, negative:Array.isArray(styles.negative)?styles.negative.map((s:any)=>String(s||'')).filter(Boolean):[]
duration:form.settings?.duration, },
fps:form.settings?.fps, settings:{
loraStack:structuredClone(form.settings?.loraStack||[]) aspect:String(form.settings?.aspect||'auto'),
}, seed:form.settings?.seed==null || form.settings?.seed==='' ? null : Number(form.settings.seed),
engineSampling:structuredClone(engineSampling), seedMode:form.settings?.seedMode==='locked'?'locked':'random',
batchMode:!!batchMode.value, steps:Number(form.settings?.steps)||24,
eyeOpen:!!eyeOpen.value, cfg:Number(form.settings?.cfg)||4,
hideInputs:!!hideInputs.value, duration:Number(form.settings?.duration)||5,
queueCollapsed:!!queueCollapsed.value, fps:Number(form.settings?.fps)||24,
selectedId:selected.value?.id||'', loraStack:plainLoraStack(form.settings?.loraStack)
music:structuredClone({tags:music.tags,lyrics:music.lyrics,duration:music.duration,seed:music.seed,name:music.name}) },
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){ 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, aspect:typeof row?.aspect==='string' && row.aspect ? row.aspect : fallback.aspect,
seed:row?.seed==null || row?.seed==='' ? null : Number(row.seed), seed:row?.seed==null || row?.seed==='' ? null : Number(row.seed),
seedMode:row?.seedMode==='locked'?'locked':'random', 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(){ function restoreBenchDraft(){
if(!import.meta.client)return if(!import.meta.client)return
const raw=readDraftRaw() const raw=readDraftRaw()
if(!raw)return if(!raw)return
let draft:any
try{draft=JSON.parse(raw)}catch{return}
if(!draft|| (draft.v!==1 && draft.v!==2))return
restoringDraft=true restoringDraft=true
try{ 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'){ if(draft.engineSampling && typeof draft.engineSampling==='object'){
for(const key of ['flux','krea','qwen21'] as const){ for(const key of ['flux','krea','qwen21'] as const){
const row=draft.engineSampling[key] const row=draft.engineSampling[key]
@@ -636,8 +716,13 @@ function restoreBenchDraft(){
if(draft.lockFace!=null)form.lockFace=!!draft.lockFace if(draft.lockFace!=null)form.lockFace=!!draft.lockFace
if(draft.lockOutfit!=null)form.lockOutfit=!!draft.lockOutfit if(draft.lockOutfit!=null)form.lockOutfit=!!draft.lockOutfit
if(draft.promptSections)form.promptSections=sectionsFrom(draft.promptSections) 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(Array.isArray(draft.photos))form.photos=draft.photos
if(draft.imageStyles)form.imageStyles={positive:draft.imageStyles.positive||[],negative:draft.imageStyles.negative||[]} .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){ if(draft.settings){
Object.assign(form.settings,{ Object.assign(form.settings,{
aspect:draft.settings.aspect||form.settings.aspect, aspect:draft.settings.aspect||form.settings.aspect,
@@ -647,7 +732,7 @@ function restoreBenchDraft(){
cfg:draft.settings.cfg ?? form.settings.cfg, cfg:draft.settings.cfg ?? form.settings.cfg,
duration:draft.settings.duration ?? form.settings.duration, duration:draft.settings.duration ?? form.settings.duration,
fps:draft.settings.fps ?? form.settings.fps, 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) 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) if(draft.music.name!=null)music.name=String(draft.music.name)
} }
syncPhotosBindings() syncPhotosBindings()
} finally { }catch{
clearDraftStores()
}finally{
restoringDraft=false restoringDraft=false
} }
} }
@@ -746,7 +833,7 @@ function rememberEngineSampling(engine:string){
aspect:String(form.settings.aspect||fallback.aspect), aspect:String(form.settings.aspect||fallback.aspect),
seed:form.settings.seed==null || form.settings.seed==='' ? null : Number(form.settings.seed), seed:form.settings.seed==null || form.settings.seed==='' ? null : Number(form.settings.seed),
seedMode:form.settings.seedMode==='locked'?'locked':'random', 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){ function applyEngineSampling(engine:string){
@@ -758,7 +845,7 @@ function applyEngineSampling(engine:string){
form.settings.aspect=saved.aspect || form.settings.aspect || 'auto' form.settings.aspect=saved.aspect || form.settings.aspect || 'auto'
form.settings.seed=saved.seed form.settings.seed=saved.seed
form.settings.seedMode=saved.seedMode || 'random' form.settings.seedMode=saved.seedMode || 'random'
form.settings.loraStack=Array.isArray(saved.loraStack)?structuredClone(saved.loraStack):[] form.settings.loraStack=plainLoraStack(saved.loraStack)
} }
function clearPrompt(){ function clearPrompt(){
if(form.promptSections) form.promptSections.action='' if(form.promptSections) form.promptSections.action=''
+26 -26
View File
@@ -14,14 +14,14 @@
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
<button <button
type="button" type="button"
class="rounded-full border border-white/10 px-3 py-1.5 text-xs text-zinc-300 hover:border-amber-300/50 hover:text-white"␍ class="rounded-full bg-amber-400 px-4 min-h-11 py-2.5 text-sm font-semibold text-zinc-950 hover:bg-amber-300 disabled:cursor-not-allowed disabled:opacity-50"
@click="startNew" @click="startNew"
> >
New New
</button> </button>
<button <button
type="button" type="button"
class="rounded-full border border-white/10 px-3 py-1.5 text-xs text-zinc-300 hover:border-amber-300/50 hover:text-white disabled:cursor-not-allowed disabled:opacity-50"␍ class="rounded-full bg-amber-400 px-4 min-h-11 py-2.5 text-sm font-semibold text-zinc-950 hover:bg-amber-300 disabled:cursor-not-allowed disabled:opacity-50"
:disabled="busy || !name.trim()" :disabled="busy || !name.trim()"
@click="save" @click="save"
> >
@@ -52,7 +52,7 @@
</div> </div>
<button <button
type="button" type="button"
class="rounded-full border border-white/10 px-3 py-1 text-[11px] text-zinc-300 hover:border-amber-300/50 hover:text-white"␍ class="rounded-full bg-amber-400 px-4 min-h-11 py-2.5 text-sm font-semibold text-zinc-950 hover:bg-amber-300 disabled:cursor-not-allowed disabled:opacity-50"
@click="addCharacter" @click="addCharacter"
> >
Add character Add character
@@ -66,11 +66,11 @@
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<span class="text-[11px] uppercase tracking-wide text-zinc-500">Character {{ cIndex + 1 }}</span> <span class="text-[11px] uppercase tracking-wide text-zinc-500">Character {{ cIndex + 1 }}</span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button type="button" class="text-[11px] text-zinc-500 hover:text-amber-200" @click="clearCharacter(cIndex)">Clear</button>␍ <button type="button" class="rounded-full border border-amber-300/70 px-3 min-h-11 py-1.5 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10" @click="clearCharacter(cIndex)">Clear</button>
<button <button
v-if="characters.length > 1" v-if="characters.length > 1"
type="button" type="button"
class="text-[11px] text-zinc-500 hover:text-red-300"␍ class="rounded-full border border-amber-300/70 px-3 min-h-11 py-2 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10"
@click="removeCharacter(cIndex)" @click="removeCharacter(cIndex)"
> >
Remove Remove
@@ -94,7 +94,7 @@
<span class="text-[11px] font-medium uppercase tracking-wide text-zinc-500">Outfits</span> <span class="text-[11px] font-medium uppercase tracking-wide text-zinc-500">Outfits</span>
<button <button
type="button" type="button"
class="text-[11px] text-zinc-400 hover:text-amber-200"␍ class="rounded-full bg-amber-400 px-4 min-h-11 py-2.5 text-sm font-semibold text-zinc-950 hover:bg-amber-300 disabled:cursor-not-allowed disabled:opacity-50"
@click="addOutfit(cIndex)" @click="addOutfit(cIndex)"
> >
Add outfit Add outfit
@@ -108,7 +108,7 @@
> >
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<span class="text-[11px] text-zinc-500">Outfit {{ oIndex + 1 }}</span> <span class="text-[11px] text-zinc-500">Outfit {{ oIndex + 1 }}</span>
<button type="button" class="text-[11px] text-zinc-500 hover:text-red-300" @click="removeOutfit(cIndex, oIndex)">Remove</button>␍ <button type="button" class="rounded-full border border-amber-300/70 px-3 min-h-11 py-2 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10" @click="removeOutfit(cIndex, oIndex)">Remove</button>
</div> </div>
<input <input
v-model="outfit.name" v-model="outfit.name"
@@ -135,7 +135,7 @@
</div> </div>
<button <button
type="button" type="button"
class="rounded-full border border-white/10 px-3 py-1 text-[11px] text-zinc-300 hover:border-amber-300/50 hover:text-white"␍ class="rounded-full bg-amber-400 px-4 min-h-11 py-2.5 text-sm font-semibold text-zinc-950 hover:bg-amber-300 disabled:cursor-not-allowed disabled:opacity-50"
@click="addSet" @click="addSet"
> >
Add set Add set
@@ -150,10 +150,10 @@
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<span class="text-[11px] uppercase tracking-wide text-zinc-500">Set {{ sIndex + 1 }}</span> <span class="text-[11px] uppercase tracking-wide text-zinc-500">Set {{ sIndex + 1 }}</span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button type="button" class="text-[11px] text-zinc-500 hover:text-amber-200" @click="clearSet(sIndex)">Clear</button>␍ <button type="button" class="rounded-full border border-amber-300/70 px-3 min-h-11 py-1.5 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10" @click="clearSet(sIndex)">Clear</button>
<button <button
type="button" type="button"
class="text-[11px] text-zinc-500 hover:text-red-300"␍ class="rounded-full border border-amber-300/70 px-3 min-h-11 py-2 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10"
@click="removeSet(sIndex)" @click="removeSet(sIndex)"
> >
Remove Remove
@@ -177,7 +177,7 @@
<span class="text-[11px] font-medium uppercase tracking-wide text-zinc-500">Beats</span> <span class="text-[11px] font-medium uppercase tracking-wide text-zinc-500">Beats</span>
<button <button
type="button" type="button"
class="text-[11px] text-zinc-400 hover:text-amber-200"␍ class="rounded-full bg-amber-400 px-4 min-h-11 py-2.5 text-sm font-semibold text-zinc-950 hover:bg-amber-300 disabled:cursor-not-allowed disabled:opacity-50"
@click="addBeat(sIndex)" @click="addBeat(sIndex)"
> >
Add beat Add beat
@@ -191,8 +191,8 @@
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<span class="text-[11px] text-zinc-500">Beat {{ bIndex + 1 }}</span> <span class="text-[11px] text-zinc-500">Beat {{ bIndex + 1 }}</span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button type="button" class="text-[11px] text-zinc-500 hover:text-amber-200" @click="clearBeat(sIndex, bIndex)">Clear</button>␍ <button type="button" class="rounded-full border border-amber-300/70 px-3 min-h-11 py-1.5 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10" @click="clearBeat(sIndex, bIndex)">Clear</button>
<button type="button" class="text-[11px] text-zinc-500 hover:text-red-300" @click="removeBeat(sIndex, bIndex)">Remove</button>␍ <button type="button" class="rounded-full border border-amber-300/70 px-3 min-h-11 py-2 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10" @click="removeBeat(sIndex, bIndex)">Remove</button>
</div> </div>
</div> </div>
<input <input
@@ -235,22 +235,22 @@
type="button" type="button"
class="rounded-full px-3 py-1.5 text-xs transition" class="rounded-full px-3 py-1.5 text-xs transition"
:class="index === activeIndex :class="index === activeIndex
? 'bg-amber-400/15 text-amber-100 ring-1 ring-amber-300/40'␍ ? 'min-h-11 bg-amber-400/15 px-3 py-2 text-sm font-semibold text-amber-100 ring-1 ring-amber-300/40'
: 'text-zinc-400 hover:bg-white/5 hover:text-zinc-200'"␍ : 'min-h-11 border border-white/25 px-3 py-2 text-sm font-semibold text-zinc-300 hover:bg-white/5 hover:text-zinc-100'"
@click="activeIndex = index" @click="activeIndex = index"
> >
{{ scene.title || `Scene ${index + 1}` }} {{ scene.title || `Scene ${index + 1}` }}
</button> </button>
<button <button
type="button" type="button"
class="rounded-full border border-white/10 px-3 py-1.5 text-xs text-zinc-300 hover:border-amber-300/50 hover:text-white"␍ class="rounded-full bg-amber-400 px-4 min-h-11 py-2.5 text-sm font-semibold text-zinc-950 hover:bg-amber-300 disabled:cursor-not-allowed disabled:opacity-50"
@click="addNextScene" @click="addNextScene"
> >
Next scene Next scene
</button> </button>
<button <button
type="button" type="button"
class="rounded-full border border-white/10 px-2.5 py-1.5 text-xs text-zinc-300 hover:border-amber-300/50 hover:text-white disabled:cursor-not-allowed disabled:opacity-40"␍ class="rounded-full border border-amber-300/70 px-4 min-h-11 py-2.5 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10 disabled:cursor-not-allowed disabled:opacity-40"
title="Move scene earlier" title="Move scene earlier"
:disabled="activeIndex <= 0" :disabled="activeIndex <= 0"
@click="moveScene(activeIndex, -1)" @click="moveScene(activeIndex, -1)"
@@ -259,7 +259,7 @@
</button> </button>
<button <button
type="button" type="button"
class="rounded-full border border-white/10 px-2.5 py-1.5 text-xs text-zinc-300 hover:border-amber-300/50 hover:text-white disabled:cursor-not-allowed disabled:opacity-40"␍ class="rounded-full border border-amber-300/70 px-4 min-h-11 py-2.5 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10 disabled:cursor-not-allowed disabled:opacity-40"
title="Move scene later" title="Move scene later"
:disabled="activeIndex >= scenes.length - 1" :disabled="activeIndex >= scenes.length - 1"
@click="moveScene(activeIndex, 1)" @click="moveScene(activeIndex, 1)"
@@ -268,7 +268,7 @@
</button> </button>
<button <button
type="button" type="button"
class="rounded-full border border-white/10 px-3 py-1.5 text-xs text-zinc-300 hover:border-red-400/50 hover:text-red-200 disabled:cursor-not-allowed disabled:opacity-40"␍ class="rounded-full border border-amber-300/70 px-4 min-h-11 py-2.5 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10 disabled:cursor-not-allowed disabled:opacity-40"
:disabled="scenes.length <= 1" :disabled="scenes.length <= 1"
@click="deleteScene(activeIndex)" @click="deleteScene(activeIndex)"
> >
@@ -293,7 +293,7 @@
<div class="space-y-1"> <div class="space-y-1">
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<span class="text-sm font-medium text-zinc-300">Style</span> <span class="text-sm font-medium text-zinc-300">Style</span>
<button type="button" class="text-[11px] text-zinc-500 hover:text-amber-200" @click="activeScene.style = ''">Clear</button>␍ <button type="button" class="rounded-full border border-amber-300/70 px-3 min-h-11 py-1.5 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10" @click="activeScene.style = ''">Clear</button>
</div> </div>
<textarea <textarea
v-model="activeScene.style" v-model="activeScene.style"
@@ -306,7 +306,7 @@
<div class="space-y-1"> <div class="space-y-1">
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<span class="text-sm font-medium text-zinc-300">Settings</span> <span class="text-sm font-medium text-zinc-300">Settings</span>
<button type="button" class="text-[11px] text-zinc-500 hover:text-amber-200" @click="activeScene.settings = ''">Clear</button>␍ <button type="button" class="rounded-full border border-amber-300/70 px-3 min-h-11 py-1.5 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10" @click="activeScene.settings = ''">Clear</button>
</div> </div>
<textarea <textarea
v-model="activeScene.settings" v-model="activeScene.settings"
@@ -392,7 +392,7 @@
<div class="space-y-1"> <div class="space-y-1">
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<span class="text-sm font-medium text-zinc-300">Prompt</span> <span class="text-sm font-medium text-zinc-300">Prompt</span>
<button type="button" class="text-[11px] text-zinc-500 hover:text-amber-200" @click="activeScene.prompt = ''">Clear</button>␍ <button type="button" class="rounded-full border border-amber-300/70 px-3 min-h-11 py-1.5 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10" @click="activeScene.prompt = ''">Clear</button>
</div> </div>
<textarea <textarea
v-model="activeScene.prompt" v-model="activeScene.prompt"
@@ -412,7 +412,7 @@
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button <button
type="button" type="button"
class="rounded-full border border-white/10 px-3 py-1.5 text-xs text-zinc-300 hover:border-amber-300/50 hover:text-white disabled:opacity-50"␍ class="rounded-full bg-amber-400 px-4 min-h-11 py-2.5 text-sm font-semibold text-zinc-950 hover:bg-amber-300 disabled:cursor-not-allowed disabled:opacity-50"
:disabled="!activeSceneText" :disabled="!activeSceneText"
@click="copyScene" @click="copyScene"
> >
@@ -420,7 +420,7 @@
</button> </button>
<button <button
type="button" type="button"
class="rounded-full border border-white/10 px-3 py-1.5 text-xs text-zinc-300 hover:border-amber-300/50 hover:text-white disabled:opacity-50"␍ class="rounded-full bg-amber-400 px-4 min-h-11 py-2.5 text-sm font-semibold text-zinc-950 hover:bg-amber-300 disabled:cursor-not-allowed disabled:opacity-50"
:disabled="!previewText" :disabled="!previewText"
@click="copyPreview" @click="copyPreview"
> >
@@ -428,7 +428,7 @@
</button> </button>
<button <button
type="button" type="button"
class="rounded-full border border-white/10 px-3 py-1.5 text-xs text-zinc-300 hover:border-amber-300/50 hover:text-white"␍ class="rounded-full border border-amber-300/70 px-4 min-h-11 py-2.5 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10 disabled:cursor-not-allowed disabled:opacity-40"
@click="previewOpen = !previewOpen" @click="previewOpen = !previewOpen"
> >
{{ previewOpen ? 'Hide' : 'Show' }} {{ previewOpen ? 'Hide' : 'Show' }}
@@ -470,7 +470,7 @@
</div> </div>
<button <button
type="button" type="button"
class="shrink-0 text-xs text-zinc-500 hover:text-red-300"␍ class="rounded-full border border-amber-300/70 px-3 min-h-11 py-2 text-sm font-semibold text-amber-100 hover:border-amber-300 hover:bg-amber-400/10"
@click.stop="remove(sequence)" @click.stop="remove(sequence)"
> >
Delete Delete