Add LoRA activation words and hide LoRAs that do not match the current engine.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -234,3 +234,98 @@ export function filterLoraStackForImageEngine(stack: unknown, engine: 'flux' | '
|
||||
export function kleinLoraOnKreaMessage(name: string) {
|
||||
return `${loraDisplayName(name)} is a Flux LoRA and cannot be used with Krea.`
|
||||
}
|
||||
|
||||
export type LoraUse = 'minimax' | 'ltx' | 'flux' | 'krea'
|
||||
|
||||
export function isSystemGraphLora(name: string) {
|
||||
const id = loraIdentityKey(name)
|
||||
if (id === loraIdentityKey(MINIMAX_TURBO_LORA)) return true
|
||||
if (/flux2-klein-9b-consistency/i.test(id)) return true
|
||||
if (/ltx-2\.3-22b-distilled/i.test(id)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
/** Filename hints. `null` means the LoRA can appear in any picker. */
|
||||
export function inferLoraUse(name: string): LoraUse[] | null {
|
||||
const id = loraIdentityKey(name)
|
||||
if (/snofs_krea|(^|[_-])krea([_-]|$)/i.test(id)) return ['krea']
|
||||
if (isKleinImageLora(name) || /flux-?2-klein|flux2-klein/i.test(id)) return ['flux']
|
||||
if (/(^|[_-])ltx([_-]|$)|ltx-2/i.test(id)) return ['ltx']
|
||||
if (/minimax|h3_fl2v|fl2v_turbo/i.test(id)) return ['minimax']
|
||||
return null
|
||||
}
|
||||
|
||||
export function loraMatchesUse(name: string, use: LoraUse) {
|
||||
if (isSystemGraphLora(name)) return false
|
||||
const inferred = inferLoraUse(name)
|
||||
if (!inferred) return true
|
||||
return inferred.includes(use)
|
||||
}
|
||||
|
||||
export function filterLorasForUse(names: string[], use: LoraUse) {
|
||||
return names.filter(name => loraMatchesUse(name, use))
|
||||
}
|
||||
|
||||
export function sanitizeLoraTrigger(raw: unknown) {
|
||||
return String(raw || '')
|
||||
.replace(/\r\n/g, '\n')
|
||||
.replace(/[\0\r]/g, '')
|
||||
.replace(/[ \t]+\n/g, '\n')
|
||||
.trim()
|
||||
.slice(0, 240)
|
||||
}
|
||||
|
||||
export function normalizeLoraTriggers(raw: unknown): Record<string, string> {
|
||||
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {}
|
||||
const out: Record<string, string> = {}
|
||||
for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {
|
||||
const id = loraIdentityKey(key) || String(key || '').trim().toLowerCase()
|
||||
const trigger = sanitizeLoraTrigger(value)
|
||||
if (!id || !trigger) continue
|
||||
out[id] = trigger
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
export function loraTriggerFor(name: string, triggers: Record<string, string> = {}) {
|
||||
const id = loraIdentityKey(name)
|
||||
return String(triggers[id] || triggers[loraBasename(name).toLowerCase()] || '').trim()
|
||||
}
|
||||
|
||||
export function insertActivationPhrase(text: string, phrase: string) {
|
||||
const src = String(text || '')
|
||||
const token = sanitizeLoraTrigger(phrase)
|
||||
if (!token) return src
|
||||
if (src.includes(token)) return src
|
||||
if (!src.trim()) return token
|
||||
const needsSpace = !/\s$/.test(src) && !/^\s/.test(token)
|
||||
return needsSpace ? `${src} ${token}` : `${src}${token}`
|
||||
}
|
||||
|
||||
export function insertActivationInShotScript(script: string, shotN: number, phrase: string) {
|
||||
const src = String(script || '')
|
||||
const token = sanitizeLoraTrigger(phrase)
|
||||
if (!token || src.includes(token)) return src
|
||||
const marker = new RegExp(`(^[ \\t]*shot\\s+${Number(shotN)}\\b[^\\n]*\\n?)`, 'im')
|
||||
const match = marker.exec(src)
|
||||
if (!match || match.index == null) {
|
||||
if (Number(shotN) === 1) {
|
||||
const nextShot = src.search(/^[ \t]*shot\s+\d+\b/m)
|
||||
if (nextShot > 0) {
|
||||
const head = src.slice(0, nextShot).replace(/\s+$/, '')
|
||||
const gap = src.slice(0, nextShot).match(/\s+$/)?.[0] || '\n\n'
|
||||
return insertActivationPhrase(head, token) + gap + src.slice(nextShot)
|
||||
}
|
||||
}
|
||||
return insertActivationPhrase(src, token)
|
||||
}
|
||||
const start = match.index + match[0].length
|
||||
const rest = src.slice(start)
|
||||
const next = rest.search(/^[ \t]*shot\s+\d+\b/m)
|
||||
const end = next < 0 ? src.length : start + next
|
||||
const block = src.slice(start, end)
|
||||
const lead = block.match(/^\s*/)?.[0] || ''
|
||||
const body = block.slice(lead.length).replace(/\s+$/, '')
|
||||
const tail = block.match(/\s+$/)?.[0] ?? (next < 0 ? '' : '\n')
|
||||
return src.slice(0, start) + lead + insertActivationPhrase(body, token) + tail + src.slice(end)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user