Show Settings LoRA aliases in picker lists instead of filenames.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -31,10 +31,10 @@
|
|||||||
>
|
>
|
||||||
<span class="block leading-none" aria-hidden="true">⋮⋮</span>
|
<span class="block leading-none" aria-hidden="true">⋮⋮</span>
|
||||||
</button>
|
</button>
|
||||||
<span class="min-w-0 truncate text-zinc-200">{{ loraDisplayName(item.name) }}</span>
|
<span class="min-w-0 truncate text-zinc-200">{{ labelFor(item.name) }}</span>
|
||||||
<LoraTriggerTip
|
<LoraTriggerTip
|
||||||
:text="triggerFor(item.name)"
|
:text="triggerFor(item.name)"
|
||||||
:aria-label="'Activation for ' + loraDisplayName(item.name)"
|
:aria-label="'Activation for ' + labelFor(item.name)"
|
||||||
@insert="emit('insert-trigger', $event)"
|
@insert="emit('insert-trigger', $event)"
|
||||||
@save="saveTrigger(item.name, $event)"
|
@save="saveTrigger(item.name, $event)"
|
||||||
/>
|
/>
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
v-for="name in availableNames"
|
v-for="name in availableNames"
|
||||||
:key="name"
|
:key="name"
|
||||||
:value="name"
|
:value="name"
|
||||||
>{{ loraDisplayName(name) }}</option>
|
>{{ labelFor(name) }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -103,7 +103,7 @@ import {
|
|||||||
LORA_STRENGTH_MIN,
|
LORA_STRENGTH_MIN,
|
||||||
LORA_STRENGTH_STEP,
|
LORA_STRENGTH_STEP,
|
||||||
clampLoraStrength,
|
clampLoraStrength,
|
||||||
loraDisplayName,
|
loraFriendlyName,
|
||||||
loraIdentityKey,
|
loraIdentityKey,
|
||||||
loraTriggerFor,
|
loraTriggerFor,
|
||||||
makeLoraStackItem,
|
makeLoraStackItem,
|
||||||
@@ -120,6 +120,7 @@ const props = withDefaults(defineProps<{
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
clipStrength?: boolean
|
clipStrength?: boolean
|
||||||
triggers?: Record<string, string>
|
triggers?: Record<string, string>
|
||||||
|
aliases?: Record<string, string>
|
||||||
}>(), {
|
}>(), {
|
||||||
label: 'LoRA',
|
label: 'LoRA',
|
||||||
emptyLabel: 'None',
|
emptyLabel: 'None',
|
||||||
@@ -128,7 +129,8 @@ const props = withDefaults(defineProps<{
|
|||||||
compact: false,
|
compact: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
clipStrength: true,
|
clipStrength: true,
|
||||||
triggers: () => ({})
|
triggers: () => ({}),
|
||||||
|
aliases: () => ({})
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -143,7 +145,10 @@ const dragOverIndex = ref<number | null>(null)
|
|||||||
|
|
||||||
const availableNames = computed(() => {
|
const availableNames = computed(() => {
|
||||||
const taken = new Set(model.value.map(item => loraIdentityKey(item.name)))
|
const taken = new Set(model.value.map(item => loraIdentityKey(item.name)))
|
||||||
return props.names.filter(name => !taken.has(loraIdentityKey(name)))
|
return props.names
|
||||||
|
.filter(name => !taken.has(loraIdentityKey(name)))
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => labelFor(a).localeCompare(labelFor(b), undefined, { sensitivity: 'base' }))
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectPlaceholder = computed(() => {
|
const selectPlaceholder = computed(() => {
|
||||||
@@ -220,6 +225,10 @@ function triggerFor(name: string) {
|
|||||||
return loraTriggerFor(name, props.triggers)
|
return loraTriggerFor(name, props.triggers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function labelFor(name: string) {
|
||||||
|
return loraFriendlyName(name, props.aliases)
|
||||||
|
}
|
||||||
|
|
||||||
function saveTrigger(name: string, trigger: string) {
|
function saveTrigger(name: string, trigger: string) {
|
||||||
emit('update-trigger', name, trigger)
|
emit('update-trigger', name, trigger)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,8 +202,11 @@
|
|||||||
class="flex flex-col gap-2 rounded-2xl border border-white/10 bg-zinc-950/50 px-4 py-3 sm:flex-row sm:items-center"
|
class="flex flex-col gap-2 rounded-2xl border border-white/10 bg-zinc-950/50 px-4 py-3 sm:flex-row sm:items-center"
|
||||||
>
|
>
|
||||||
<div class="min-w-0 flex-1">
|
<div class="min-w-0 flex-1">
|
||||||
<p class="truncate text-sm text-zinc-200">{{ row.file }}</p>
|
<p class="truncate text-sm text-zinc-200">{{ row.draft || row.file }}</p>
|
||||||
<p class="text-[10px] uppercase tracking-wide text-zinc-600">{{ row.kind === 'both' ? 'Image · Video' : row.kind }}</p>
|
<p class="truncate text-[10px] uppercase tracking-wide text-zinc-600">
|
||||||
|
{{ row.kind === 'both' ? 'Image · Video' : row.kind }}
|
||||||
|
<span v-if="row.draft" class="normal-case tracking-normal text-zinc-500"> · {{ row.file }}</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex w-full flex-col gap-2 sm:w-56">
|
<div class="flex w-full flex-col gap-2 sm:w-56">
|
||||||
<input
|
<input
|
||||||
@@ -445,7 +448,7 @@ const filteredLoraRows = computed(() => {
|
|||||||
if (!q) return true
|
if (!q) return true
|
||||||
const hay = `${row.file} ${row.draft} ${row.trigger} ${row.key} ${row.placeholder}`.toLowerCase()
|
const hay = `${row.file} ${row.draft} ${row.trigger} ${row.key} ${row.placeholder}`.toLowerCase()
|
||||||
return hay.includes(q)
|
return hay.includes(q)
|
||||||
})
|
}).sort((a, b) => (a.draft || a.file).localeCompare(b.draft || b.file, undefined, { sensitivity: 'base' }))
|
||||||
})
|
})
|
||||||
const loginUser = ref('')
|
const loginUser = ref('')
|
||||||
const loginCurrent = ref('')
|
const loginCurrent = ref('')
|
||||||
|
|||||||
@@ -458,6 +458,7 @@
|
|||||||
:model-value="shotLoraStacks[shot.n] || []"
|
:model-value="shotLoraStacks[shot.n] || []"
|
||||||
:names="videoLoraOptions"
|
:names="videoLoraOptions"
|
||||||
:triggers="loraTriggers"
|
:triggers="loraTriggers"
|
||||||
|
:aliases="loraAliases"
|
||||||
label="LoRA"
|
label="LoRA"
|
||||||
empty-label="Default"
|
empty-label="Default"
|
||||||
add-label="Add LoRA"
|
add-label="Add LoRA"
|
||||||
@@ -523,6 +524,7 @@
|
|||||||
:model-value="item.loraStack || []"
|
:model-value="item.loraStack || []"
|
||||||
:names="videoLoraOptions"
|
:names="videoLoraOptions"
|
||||||
:triggers="loraTriggers"
|
:triggers="loraTriggers"
|
||||||
|
:aliases="loraAliases"
|
||||||
label="LoRA"
|
label="LoRA"
|
||||||
empty-label="Default"
|
empty-label="Default"
|
||||||
add-label="Add LoRA"
|
add-label="Add LoRA"
|
||||||
@@ -749,6 +751,7 @@
|
|||||||
v-model="videoLoraStack"
|
v-model="videoLoraStack"
|
||||||
:names="videoLoraOptions"
|
:names="videoLoraOptions"
|
||||||
:triggers="loraTriggers"
|
:triggers="loraTriggers"
|
||||||
|
:aliases="loraAliases"
|
||||||
label="LoRA"
|
label="LoRA"
|
||||||
empty-label="None"
|
empty-label="None"
|
||||||
compact
|
compact
|
||||||
@@ -811,6 +814,7 @@
|
|||||||
v-model="imageV2LoraStack"
|
v-model="imageV2LoraStack"
|
||||||
:names="imageV2LoraOptions"
|
:names="imageV2LoraOptions"
|
||||||
:triggers="loraTriggers"
|
:triggers="loraTriggers"
|
||||||
|
:aliases="loraAliases"
|
||||||
label="LoRA"
|
label="LoRA"
|
||||||
empty-label="Add LoRA"
|
empty-label="Add LoRA"
|
||||||
missing-label="Wake GPU to load LoRAs"
|
missing-label="Wake GPU to load LoRAs"
|
||||||
@@ -935,6 +939,7 @@
|
|||||||
v-model="imageLoraStack"
|
v-model="imageLoraStack"
|
||||||
:names="imageLoraOptions"
|
:names="imageLoraOptions"
|
||||||
:triggers="loraTriggers"
|
:triggers="loraTriggers"
|
||||||
|
:aliases="loraAliases"
|
||||||
label="LoRA"
|
label="LoRA"
|
||||||
empty-label="None"
|
empty-label="None"
|
||||||
compact
|
compact
|
||||||
@@ -1401,6 +1406,7 @@
|
|||||||
v-model="extendLoraStack"
|
v-model="extendLoraStack"
|
||||||
:names="videoLoraOptions"
|
:names="videoLoraOptions"
|
||||||
:triggers="loraTriggers"
|
:triggers="loraTriggers"
|
||||||
|
:aliases="loraAliases"
|
||||||
label="LoRA"
|
label="LoRA"
|
||||||
empty-label="Same as clip / none"
|
empty-label="Same as clip / none"
|
||||||
compact
|
compact
|
||||||
|
|||||||
+7
-3
@@ -194,6 +194,7 @@
|
|||||||
:model-value="readLoraStack(segment)"
|
:model-value="readLoraStack(segment)"
|
||||||
:names="videoLoraOptions"
|
:names="videoLoraOptions"
|
||||||
:triggers="loraTriggers"
|
:triggers="loraTriggers"
|
||||||
|
:aliases="loraAliases"
|
||||||
label="LoRA"
|
label="LoRA"
|
||||||
empty-label="Default"
|
empty-label="Default"
|
||||||
add-label="Add LoRA"
|
add-label="Add LoRA"
|
||||||
@@ -225,7 +226,7 @@
|
|||||||
>
|
>
|
||||||
<p class="text-sm font-medium text-zinc-100">{{ job.kind === 'edit' ? `Pass ${index + 1}` : `Shot ${index + 1}` }} <span class="font-normal text-zinc-500">· queued with this job</span></p>
|
<p class="text-sm font-medium text-zinc-100">{{ job.kind === 'edit' ? `Pass ${index + 1}` : `Shot ${index + 1}` }} <span class="font-normal text-zinc-500">· queued with this job</span></p>
|
||||||
<p class="mt-1 max-h-24 overflow-y-auto whitespace-pre-wrap text-xs text-zinc-400 [overflow-wrap:anywhere]">{{ shot.prompt }}</p>
|
<p class="mt-1 max-h-24 overflow-y-auto whitespace-pre-wrap text-xs text-zinc-400 [overflow-wrap:anywhere]">{{ shot.prompt }}</p>
|
||||||
<p v-if="loraStackLabel(readLoraStack(shot))" class="mt-1 text-[11px] text-zinc-500">{{ loraStackLabel(readLoraStack(shot)) }}</p>
|
<p v-if="loraStackLabel(readLoraStack(shot), loraAliases)" class="mt-1 text-[11px] text-zinc-500">{{ loraStackLabel(readLoraStack(shot), loraAliases) }}</p>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</section>
|
</section>
|
||||||
@@ -339,6 +340,7 @@
|
|||||||
:model-value="readLoraStack(segment)"
|
:model-value="readLoraStack(segment)"
|
||||||
:names="videoLoraOptions"
|
:names="videoLoraOptions"
|
||||||
:triggers="loraTriggers"
|
:triggers="loraTriggers"
|
||||||
|
:aliases="loraAliases"
|
||||||
label="LoRA"
|
label="LoRA"
|
||||||
empty-label="Default"
|
empty-label="Default"
|
||||||
add-label="Add LoRA"
|
add-label="Add LoRA"
|
||||||
@@ -465,6 +467,7 @@ type PendingRemove = {
|
|||||||
|
|
||||||
const videoLoras = ref<string[]>([])
|
const videoLoras = ref<string[]>([])
|
||||||
const loraTriggers = ref<Record<string, string>>({})
|
const loraTriggers = ref<Record<string, string>>({})
|
||||||
|
const loraAliases = ref<Record<string, string>>({})
|
||||||
const jobs = ref<StudioJobRow[]>([])
|
const jobs = ref<StudioJobRow[]>([])
|
||||||
const orphans = ref<Queue[]>([])
|
const orphans = ref<Queue[]>([])
|
||||||
const waitingCount = ref(0)
|
const waitingCount = ref(0)
|
||||||
@@ -640,7 +643,7 @@ async function saveLoraTrigger(name: string, trigger: string) {
|
|||||||
|
|
||||||
function segmentLoraLabel(queue: Queue, segment: Segment) {
|
function segmentLoraLabel(queue: Queue, segment: Segment) {
|
||||||
const stack = readLoraStack(segment).length ? readLoraStack(segment) : readLoraStack(queue)
|
const stack = readLoraStack(segment).length ? readLoraStack(segment) : readLoraStack(queue)
|
||||||
return loraStackLabel(stack)
|
return loraStackLabel(stack, loraAliases.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
function segmentClass(queue: Queue, segment: Segment) {
|
function segmentClass(queue: Queue, segment: Segment) {
|
||||||
@@ -1040,8 +1043,9 @@ onMounted(async () => {
|
|||||||
void $fetch<{ video?: string[] }>('/api/loras').then((listed) => {
|
void $fetch<{ video?: string[] }>('/api/loras').then((listed) => {
|
||||||
videoLoras.value = listed.video || []
|
videoLoras.value = listed.video || []
|
||||||
}).catch(() => null)
|
}).catch(() => null)
|
||||||
void $fetch<{ loraTriggers?: Record<string, string> }>('/api/library?light=1').then((library) => {
|
void $fetch<{ loraTriggers?: Record<string, string>; loraAliases?: Record<string, string> }>('/api/library?light=1').then((library) => {
|
||||||
if (library.loraTriggers) loraTriggers.value = library.loraTriggers
|
if (library.loraTriggers) loraTriggers.value = library.loraTriggers
|
||||||
|
if (library.loraAliases) loraAliases.value = library.loraAliases
|
||||||
}).catch(() => null)
|
}).catch(() => null)
|
||||||
window.addEventListener('keydown', onRemoveKey)
|
window.addEventListener('keydown', onRemoveKey)
|
||||||
await loadQueues().catch((err: any) => {
|
await loadQueues().catch((err: any) => {
|
||||||
|
|||||||
+11
-2
@@ -67,6 +67,15 @@ export function loraDisplayName(name: string) {
|
|||||||
return loraBasename(name).replace(/\.(safetensors|sft|ckpt|pt|bin)$/i, '')
|
return loraBasename(name).replace(/\.(safetensors|sft|ckpt|pt|bin)$/i, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function loraAliasFor(name: string, aliases: Record<string, string> = {}) {
|
||||||
|
const id = loraIdentityKey(name)
|
||||||
|
return String(aliases[id] || aliases[loraBasename(name).toLowerCase()] || '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loraFriendlyName(name: string, aliases: Record<string, string> = {}) {
|
||||||
|
return loraAliasFor(name, aliases) || loraDisplayName(name)
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizeLoraName(raw: unknown) {
|
export function normalizeLoraName(raw: unknown) {
|
||||||
const value = String(raw || '').trim().replace(/\\/g, '/')
|
const value = String(raw || '').trim().replace(/\\/g, '/')
|
||||||
if (!value) return ''
|
if (!value) return ''
|
||||||
@@ -175,8 +184,8 @@ export function primaryLoraName(stack?: LoraStackItem[] | null) {
|
|||||||
return stack?.[0]?.name || ''
|
return stack?.[0]?.name || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loraStackLabel(stack?: LoraStackItem[] | null) {
|
export function loraStackLabel(stack?: LoraStackItem[] | null, aliases: Record<string, string> = {}) {
|
||||||
return (stack || []).map(item => loraDisplayName(item.name)).join(' · ')
|
return (stack || []).map(item => loraFriendlyName(item.name, aliases)).join(' · ')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function persistLoraFields(stack?: unknown) {
|
export function persistLoraFields(stack?: unknown) {
|
||||||
|
|||||||
Reference in New Issue
Block a user