Use the parent clip last frame and new-segment join for extensions, not the stitched file's first frame. Show this account's library storage in Settings.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-28 10:54:46 -05:00
co-authored by Cursor
parent 231c59a7a7
commit 0af3cddb14
16 changed files with 571 additions and 70 deletions
+57 -20
View File
@@ -101,28 +101,64 @@ export function resolveGlobalLocks(locks: string, firstPrompt: string) {
: { locks: '', prompt: firstPrompt }
}
function extractState(locks: string) {
const match = String(locks || '').match(/(?:^|\n)\s*State:\s*(.+?)(?=\n\s*[A-Za-z][A-Za-z0-9 _-]{0,22}\s*:|\n\s*\[|$)/is)
return String(match?.[1] || '').replace(/\s+/g, ' ').trim()
}
export function permanenceLabels(refs: PermanenceRef[] | null | undefined) {
return [...new Set(normalizePermanenceRefs(refs).map(item => item.label).filter(Boolean))]
}
const PERSON_WORD = /\b(man|men|woman|women|person|people|boy|girl|guy|lady|male|female|human|child|kid|figure|character|actor|actress)\b/i
const MAIN_SUBJECT_ALIAS = /^(subject|woman|women|she|her|female)$/i
export function extraPersonLabels(refs: PermanenceRef[] | null | undefined) {
return permanenceLabels(refs).filter(label => {
if (MAIN_SUBJECT_ALIAS.test(label)) return false
return PERSON_WORD.test(label) || /^extras?$/i.test(label)
})
}
export function shouldForceLastFrameI2V(refs?: PermanenceRef[] | null, _locks = '') {
return extraPersonLabels(refs).length > 0
}
export function allowIdentityRefs(
requested: boolean,
familyRefs?: PermanenceRef[] | null,
locks = '',
shotRefs?: PermanenceRef[][] | null
) {
if (!requested) return false
return !shouldForceLastFrameI2V(mergePermanenceRefs(familyRefs, (shotRefs || []).flat()), locks)
}
function permanenceLine(refs: PermanenceRef[]) {
const labels = permanenceLabels(refs)
if (!labels.length) return ''
return `Permanence: ${labels.join(', ')} stay the same.`
}
function inheritLine(locks: string, refs: PermanenceRef[]) {
const parts = ['[INHERITS] Global locks.']
function inheritLine(refs: PermanenceRef[]) {
const labels = permanenceLabels(refs)
if (labels.length) parts.push(`Same ${labels.join(', ')} as locked.`)
const state = extractState(locks)
if (state) parts.push(`State: ${state}`)
return parts.join(' ')
if (labels.length) return `[INHERITS] Same ${labels.join(', ')} as now`
return '[INHERITS] Same as now'
}
export function stripLockHeader(prompt: string) {
let src = String(prompt || '').replace(/\r\n/g, '\n').trim()
while (/^\[(GLOBAL LOCKS|INHERITS)\]/i.test(src)) {
if (/^\[GLOBAL LOCKS\]/i.test(src)) {
const config = src.search(/\n\[SHOT CONFIGURATION\]/i)
if (config >= 0) {
src = src.slice(config).trim()
break
}
}
const split = src.search(/\n\s*\n/)
src = (split >= 0 ? src.slice(split) : src.replace(/^\s*\[[^\]]+\][^\n]*/, '')).trim()
if (/^\[GLOBAL LOCKS\]/i.test(src)) {
src = src.replace(/^\s*\[GLOBAL LOCKS\]\s*\n?/, '').trim()
break
}
}
return src
}
export function applyGlobalLocks(params: {
@@ -133,20 +169,21 @@ export function applyGlobalLocks(params: {
}) {
const beat = String(params.prompt || '').trim()
if (!beat) return beat
if (looksAlreadyInherited(beat)) return beat
const refs = normalizePermanenceRefs(params.refs)
const locks = String(params.locks || '').trim()
const blank = isBlankGlobalLocks(locks)
if (blank && !refs.length) return beat
if (params.shotIndex <= 0) {
const chunks = [
blank ? '' : locks,
permanenceLine(refs),
beat
].filter(Boolean)
return chunks.join('\n\n')
if (looksAlreadyInherited(beat)) {
const line = permanenceLine(refs)
if (line && !/Permanence:/i.test(beat)) return `${beat}\n\n${line}`
return beat
}
if (blank && !refs.length) return beat
return [blank ? '' : locks, permanenceLine(refs), beat].filter(Boolean).join('\n\n')
}
return `${inheritLine(blank ? '' : locks, refs)}\n\n${beat}`
const body = stripLockHeader(beat)
if (blank && !refs.length) return body
return `${inheritLine(refs)}\n\n${body}`
}
export function composeShotPrompt(params: {