Pause the queue after the current clip for real, and carry global locks plus labeled permanence stills through generate and shot prompts.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<details class="rounded-2xl border border-white/10 bg-zinc-950/40 px-4 py-3">
|
||||
<summary class="cursor-pointer text-sm font-medium text-zinc-200">
|
||||
Global locks
|
||||
<span class="ml-2 font-normal text-xs text-zinc-500">Every shot inherits these. Each clip adds one beat.</span>
|
||||
</summary>
|
||||
<p class="mt-2 text-xs text-zinc-500">
|
||||
Shared standards for last-frame I2V. Later shots get a compact <span class="text-zinc-300">[INHERITS]</span> line, not this whole block again.
|
||||
</p>
|
||||
<textarea
|
||||
:value="modelValue"
|
||||
rows="10"
|
||||
class="mt-3 max-h-64 w-full overflow-y-auto rounded-xl border border-white/10 bg-zinc-950 px-3 py-2 text-sm outline-none ring-amber-300/40 placeholder:text-zinc-600 [overflow-wrap:anywhere] focus:ring-2"
|
||||
:placeholder="placeholder"
|
||||
@input="emit('update:modelValue', ($event.target as HTMLTextAreaElement).value)"
|
||||
/>
|
||||
<div class="mt-2 flex flex-wrap gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-lg border border-white/15 px-2.5 py-1 text-xs text-zinc-300 hover:border-amber-300/40 hover:text-amber-100"
|
||||
@click="insertTemplate"
|
||||
>
|
||||
Insert template
|
||||
</button>
|
||||
<button
|
||||
v-if="modelValue.trim()"
|
||||
type="button"
|
||||
class="rounded-lg border border-white/15 px-2.5 py-1 text-xs text-zinc-400 hover:text-white"
|
||||
@click="emit('update:modelValue', '')"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
</div>
|
||||
</details>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DEFAULT_GLOBAL_LOCKS } from '~/utils/globalLocks'
|
||||
|
||||
defineProps<{
|
||||
modelValue: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string]
|
||||
}>()
|
||||
|
||||
const placeholder = DEFAULT_GLOBAL_LOCKS
|
||||
|
||||
function insertTemplate() {
|
||||
emit('update:modelValue', DEFAULT_GLOBAL_LOCKS)
|
||||
}
|
||||
</script>
|
||||
@@ -172,13 +172,15 @@ const props = defineProps<{
|
||||
folderId: string
|
||||
hideInputPreview?: boolean
|
||||
heading?: string
|
||||
stillOnly?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
close: []
|
||||
updated: [payload: unknown]
|
||||
error: [message: string]
|
||||
select: [file: File, hidden: boolean]
|
||||
select: [file: File, hidden: boolean, stillId?: string]
|
||||
pick: [still: { id: string; filename: string; hideInput?: boolean }]
|
||||
}>()
|
||||
|
||||
const fileInput = ref<HTMLInputElement | null>(null)
|
||||
@@ -262,8 +264,10 @@ async function ingest(file: File) {
|
||||
body.append('folderId', current.value?.id || activeFolderId.value)
|
||||
body.append('hideInput', String(props.hideInputPreview === true))
|
||||
try {
|
||||
emit('updated', await $fetch('/api/library/stills', { method: 'POST', body }))
|
||||
emit('select', file, props.hideInputPreview === true)
|
||||
const result = await $fetch<{ still?: { id: string; filename: string; hideInput?: boolean } }>('/api/library/stills', { method: 'POST', body })
|
||||
emit('updated', result)
|
||||
if (result.still?.id) emit('pick', result.still)
|
||||
emit('select', file, props.hideInputPreview === true, result.still?.id)
|
||||
} catch (error: any) {
|
||||
emit('error', error?.data?.statusMessage || 'Could not save image')
|
||||
}
|
||||
@@ -283,10 +287,12 @@ function onDrop(event: DragEvent) {
|
||||
}
|
||||
|
||||
async function pickStill(still: PickerStill) {
|
||||
emit('pick', { id: still.id, filename: still.filename, hideInput: still.hideInput })
|
||||
if (props.stillOnly) return
|
||||
try {
|
||||
const blob = await $fetch<Blob>(`/api/library/stills/${still.id}`, { responseType: 'blob' })
|
||||
const file = new File([blob], still.filename, { type: blob.type || 'image/png' })
|
||||
emit('select', file, still.hideInput === true)
|
||||
emit('select', file, still.hideInput === true, still.id)
|
||||
} catch (error: any) {
|
||||
emit('error', error?.data?.statusMessage || 'Could not load that still')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="label" class="mb-1.5 flex items-baseline justify-between gap-2">
|
||||
<p class="text-[11px] font-medium uppercase tracking-wide text-zinc-500">{{ label }}</p>
|
||||
<p v-if="hint" class="text-[11px] text-zinc-600">{{ hint }}</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-start gap-1.5">
|
||||
<div
|
||||
v-for="(item, index) in refs"
|
||||
:key="item.stillId + '-' + index"
|
||||
class="flex w-14 flex-col items-center gap-0.5"
|
||||
>
|
||||
<span class="relative h-12 w-12 overflow-hidden rounded-lg bg-zinc-900">
|
||||
<span v-if="hidden" class="sensitive-glow block h-full w-full" />
|
||||
<img
|
||||
v-else
|
||||
:src="`/api/library/stills/${item.stillId}?thumb=1`"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="h-full w-full object-cover"
|
||||
>
|
||||
<button
|
||||
v-if="!disabled"
|
||||
type="button"
|
||||
class="absolute right-0 top-0 rounded-bl-md bg-zinc-950/80 px-1 text-[10px] leading-4 text-zinc-300 hover:text-red-200"
|
||||
title="Remove"
|
||||
@click.stop="emit('remove', index)"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
<select
|
||||
class="w-full truncate rounded border border-white/10 bg-zinc-950 px-0.5 py-0 text-[10px] text-zinc-400"
|
||||
:disabled="disabled"
|
||||
:value="item.label"
|
||||
@change="emit('updateLabel', index, ($event.target as HTMLSelectElement).value)"
|
||||
>
|
||||
<option v-for="option in labelOptions(item.label)" :key="option" :value="option">{{ option }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<button
|
||||
v-if="!disabled && refs.length < max"
|
||||
type="button"
|
||||
class="flex h-12 w-12 flex-col items-center justify-center rounded-lg border border-dashed border-white/15 text-[10px] text-zinc-500 hover:border-amber-300/40 hover:text-amber-100"
|
||||
:title="addTitle"
|
||||
@click="emit('add')"
|
||||
>
|
||||
+ Ref
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { PERMANENCE_REF_LABELS, type PermanenceRef } from '~/utils/globalLocks'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
refs: PermanenceRef[]
|
||||
label?: string
|
||||
hint?: string
|
||||
hidden?: boolean
|
||||
disabled?: boolean
|
||||
max?: number
|
||||
addTitle?: string
|
||||
}>(), {
|
||||
max: 6,
|
||||
addTitle: 'Attach a permanence still'
|
||||
})
|
||||
|
||||
defineEmits<{
|
||||
add: []
|
||||
remove: [index: number]
|
||||
updateLabel: [index: number, label: string]
|
||||
}>()
|
||||
|
||||
function labelOptions(current: string) {
|
||||
if (PERMANENCE_REF_LABELS.includes(current as typeof PERMANENCE_REF_LABELS[number])) return [...PERMANENCE_REF_LABELS]
|
||||
return [...PERMANENCE_REF_LABELS, current]
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user