Restore pre/post text when using clips as input or extending.

Extend no longer blanks wrappers when a part omitted them, Use as input inherits family pre/post, and non-image stills stay on Video instead of being forced into Image mode.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-09-04 08:04:11 -05:00
co-authored by Cursor
parent f41ac7a826
commit 20f373b172
+64 -18
View File
@@ -2261,7 +2261,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { composePromptParts, formatPromptParts, restorePromptParts } from '~/utils/promptParts' import { composePromptParts, formatPromptParts, resolvePromptWrappers, restorePromptParts } from '~/utils/promptParts'
import { parseEditPasses, looksLikeShotScript } from '~/utils/parseRecommend' import { parseEditPasses, looksLikeShotScript } from '~/utils/parseRecommend'
import { parseImageBatchScript, parseShotScript } from '~/utils/parseShots' import { parseImageBatchScript, parseShotScript } from '~/utils/parseShots'
import type { DirectorScript } from '~/utils/directorScripts' import type { DirectorScript } from '~/utils/directorScripts'
@@ -4325,16 +4325,39 @@ function insertKeepPrompt(item: KeepPrompt) {
} }
function applyPromptRestore(saved: string, stored?: { prompt?: string; promptPre?: string; promptPost?: string } | null) { function applyPromptRestore(saved: string, stored?: { prompt?: string; promptPre?: string; promptPost?: string } | null) {
const parts = restorePromptParts(saved, { const wrappers = resolvePromptWrappers(stored || undefined)
pre: stored?.promptPre, const mid = String(saved || stored?.prompt || '').trim()
prompt: saved, const parts = restorePromptParts(mid, {
post: stored?.promptPost pre: wrappers.promptPre || stored?.promptPre,
prompt: mid,
post: wrappers.promptPost || stored?.promptPost
}) })
promptPre.value = parts.pre promptPre.value = parts.pre
prompt.value = parts.prompt prompt.value = parts.prompt
promptPost.value = parts.post promptPost.value = parts.post
} }
function promptPartsForClip(
clip: LibraryClip | null | undefined,
midFallback = ''
) {
const family = clip ? familyPartsFor(clip) : []
const wrappers = resolvePromptWrappers(
clip || undefined,
family[0],
{
promptPre: promptPre.value,
promptPost: promptPost.value
}
)
const mid = String(clip?.prompt || midFallback || '').trim()
return restorePromptParts(mid, {
pre: wrappers.promptPre,
prompt: mid,
post: wrappers.promptPost
})
}
function queuedExtensionFrom( function queuedExtensionFrom(
item: { prompt?: string; promptPre?: string; promptPost?: string; duration?: number; loraName?: string; loraStack?: LoraStackItem[] }, item: { prompt?: string; promptPre?: string; promptPost?: string; duration?: number; loraName?: string; loraStack?: LoraStackItem[] },
duration: number, duration: number,
@@ -5533,6 +5556,7 @@ async function restoreStudioPayload(kind: string, payload: {
refinementDenoise?: number refinementDenoise?: number
promptPre?: string promptPre?: string
promptPost?: string promptPost?: string
promptMid?: string
globalLocks?: string globalLocks?: string
permanenceRefs?: PermanenceRef[] permanenceRefs?: PermanenceRef[]
shotPermanenceRefs?: PermanenceRef[][] shotPermanenceRefs?: PermanenceRef[][]
@@ -5568,9 +5592,15 @@ async function restoreStudioPayload(kind: string, payload: {
|| Object.prototype.hasOwnProperty.call(item, 'promptPost') || Object.prototype.hasOwnProperty.call(item, 'promptPost')
) )
applyVideoWorkflow(payload.workflow) applyVideoWorkflow(payload.workflow)
const mid = String(payload.promptMid || '').trim() || String(payload.prompt || '').trim()
applyPromptRestore(queuedAsCards || !extensions.length applyPromptRestore(queuedAsCards || !extensions.length
? (payload.prompt || '') ? mid
: [payload.prompt.trim(), ...extensions.map((item, index) => `shot ${index + 2}\n${item.prompt.trim()}`)].join('\n\n'), payload) : [mid, ...extensions.map((item, index) => `shot ${index + 2}\n${String(item.prompt || '').trim()}`)].join('\n\n'), {
...payload,
prompt: mid,
promptPre: payload.promptPre,
promptPost: payload.promptPost
})
clipName.value = payload.name || '' clipName.value = payload.name || ''
seedInput.value = String(payload.seed || '') seedInput.value = String(payload.seed || '')
restoreAdvanced({ restoreAdvanced({
@@ -5944,7 +5974,13 @@ async function rerun(item: LibraryClip, collection = false) {
const parts = familyPartsFor(item) const parts = familyPartsFor(item)
const target = collection ? parts[0] || item : item const target = collection ? parts[0] || item : item
const restoreAll = collection && parts.length > 1 const restoreAll = collection && parts.length > 1
applyPromptRestore(restoreAll ? familyScript(parts) : target.prompt, target) const ordered = chronologicalParts(parts)
const wrappers = resolvePromptWrappers(target, ordered[0])
applyPromptRestore(restoreAll ? familyScript(parts) : target.prompt, {
prompt: restoreAll ? familyScript(parts) : target.prompt,
promptPre: wrappers.promptPre || target.promptPre,
promptPost: wrappers.promptPost || target.promptPost
})
clipName.value = restoreAll clipName.value = restoreAll
? (familyBaseName(collectionTitle({ head: target, parts })) || target.name || '') ? (familyBaseName(collectionTitle({ head: target, parts })) || target.name || '')
: (target.name || '') : (target.name || '')
@@ -5959,7 +5995,6 @@ async function rerun(item: LibraryClip, collection = false) {
if (typeof target.sound === 'boolean') withSound.value = target.sound if (typeof target.sound === 'boolean') withSound.value = target.sound
shotScriptMode.value = restoreAll shotScriptMode.value = restoreAll
identityRefs.value.forEach((_, index) => clearIdentityRef(index)) identityRefs.value.forEach((_, index) => clearIdentityRef(index))
const ordered = chronologicalParts(parts)
globalLocks.value = ordered.find(part => part.globalLocks)?.globalLocks || target.globalLocks || '' globalLocks.value = ordered.find(part => part.globalLocks)?.globalLocks || target.globalLocks || ''
familyPermanenceRefs.value = ordered[0]?.permanenceRefs || target.permanenceRefs || [] familyPermanenceRefs.value = ordered[0]?.permanenceRefs || target.permanenceRefs || []
const familyIds = new Set(familyPermanenceRefs.value.map(ref => ref.stillId)) const familyIds = new Set(familyPermanenceRefs.value.map(ref => ref.stillId))
@@ -5974,11 +6009,18 @@ async function rerun(item: LibraryClip, collection = false) {
restoreAll ? ordered : undefined restoreAll ? ordered : undefined
) )
extensionQueue.value = restoreAll extensionQueue.value = restoreAll
? parts.slice(1).map(part => queuedExtensionFrom( ? parts.slice(1).map(part => {
part, const shotWrappers = resolvePromptWrappers(part, ordered[0], target)
return queuedExtensionFrom(
{
...part,
promptPre: shotWrappers.promptPre || part.promptPre,
promptPost: shotWrappers.promptPost || part.promptPost
},
clipGenerateDuration(part), clipGenerateDuration(part),
loraStacksEqual(readLoraStack(part), readLoraStack(ordered[0] || target)) ? [] : readLoraStack(part) loraStacksEqual(readLoraStack(part), readLoraStack(ordered[0] || target)) ? [] : readLoraStack(part)
)) )
})
: [] : []
try { try {
const initialTextToVideo = textToVideo.value && !target.parentClipId && !(target.chainIndex) const initialTextToVideo = textToVideo.value && !target.parentClipId && !(target.chainIndex)
@@ -6144,13 +6186,11 @@ async function openExtend(item?: LibraryClip) {
} }
currentClipId.value = id currentClipId.value = id
extendClipId.value = id extendClipId.value = id
const clip = clips.value.find(entry => entry.id === id) const clip = clips.value.find(entry => entry.id === id) || item || null
if (clip) ensureFamilyExpanded(clip) if (clip) ensureFamilyExpanded(clip)
const parts = restorePromptParts(clip?.prompt || prompt.value, { // Prefer clip wrappers, then family head, then the main form — never blank pre/post
pre: clip ? (clip.promptPre || '') : promptPre.value, // just because this part did not re-store them.
prompt: clip?.prompt || prompt.value, const parts = promptPartsForClip(clip, prompt.value)
post: clip ? (clip.promptPost || '') : promptPost.value
})
extendPromptPre.value = parts.pre extendPromptPre.value = parts.pre
extendPrompt.value = parts.prompt extendPrompt.value = parts.prompt
extendPromptPost.value = parts.post extendPromptPost.value = parts.post
@@ -6297,6 +6337,12 @@ function applyStillSettings(still: LibraryStill) {
browseFolderId.value = still.folderId browseFolderId.value = still.folderId
rememberInputSeed(settings?.seed) rememberInputSeed(settings?.seed)
applyPromptRestore(still.prompt || '', still) applyPromptRestore(still.prompt || '', still)
// Non-image stills are video start frames — keep Video mode and only restore prompt parts.
if (!dest) {
studioMode.value = 'video'
outputFocus.value = 'video'
return
}
if (dest === 'imagev2') { if (dest === 'imagev2') {
studioMode.value = 'editv2' studioMode.value = 'editv2'
outputFocus.value = 'edit' outputFocus.value = 'edit'