Add sidecar Qwen VL prompt recommend, fix queued shot bursts, and leave unused MiniMax identity slots empty.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-27 17:04:43 -05:00
co-authored by Cursor
parent 2bb30395d8
commit df5ac24622
23 changed files with 1068 additions and 25 deletions
+126 -2
View File
@@ -5,7 +5,7 @@
<div class="flex items-start justify-between gap-3">
<div>
<h2 class="font-display text-2xl font-bold">Settings</h2>
<p class="mt-1 text-sm text-zinc-400">Library folders, passwords, and image-preview defaults for this account.</p>
<p class="mt-1 text-sm text-zinc-400">Library folders, passwords, and prompt-helper instruction sets for this account.</p>
</div>
<button type="button" class="rounded-full border border-white/10 px-3 py-1 text-sm text-zinc-400 hover:text-white" @click="emit('close')">Close</button>
</div>
@@ -109,6 +109,68 @@
<button type="submit" class="rounded-xl bg-amber-400 px-4 py-2 text-sm font-semibold text-zinc-950">Add folder</button>
</form>
</section>
<section class="mt-8 space-y-3">
<div>
<h3 class="text-sm font-semibold uppercase tracking-wider text-zinc-500">Prompt helper</h3>
<p class="mt-1 text-sm text-zinc-400">
Instruction sets sent to Sidecar Qwen VL with your still and draft. Use these later for multi-shot video scripts.
</p>
</div>
<div
v-for="preset in promptPresets"
:key="preset.id"
class="rounded-2xl border border-white/10 bg-zinc-950/50 p-4 space-y-2"
>
<div class="flex flex-wrap items-center justify-between gap-2">
<p class="text-sm font-medium text-zinc-200">
{{ preset.name }}
<span v-if="preset.builtin" class="ml-2 text-[10px] uppercase tracking-wide text-zinc-500">Built-in</span>
</p>
<button
v-if="!preset.builtin"
type="button"
class="text-xs text-red-300 hover:text-red-200"
@click="removePreset(preset.id)"
>
Delete
</button>
</div>
<p v-if="preset.description" class="text-xs text-zinc-500">{{ preset.description }}</p>
<template v-if="!preset.builtin">
<input
v-model="preset.name"
class="w-full rounded-xl border border-white/10 bg-zinc-950 px-3 py-2 text-sm"
placeholder="Name"
@change="savePreset(preset)"
>
<textarea
v-model="preset.systemPrompt"
rows="5"
class="w-full rounded-xl border border-white/10 bg-zinc-950 px-3 py-2 text-sm"
placeholder="Instruction set…"
@change="savePreset(preset)"
/>
</template>
</div>
<form class="space-y-2 rounded-2xl border border-dashed border-white/15 p-4" @submit.prevent="addPreset">
<p class="text-xs font-medium uppercase tracking-wide text-zinc-500">New instruction set</p>
<input
v-model="newPresetName"
class="w-full rounded-xl border border-white/10 bg-zinc-950 px-3 py-2 text-sm"
placeholder="Name, e.g. Multi-shot tavern script"
>
<textarea
v-model="newPresetPrompt"
rows="4"
class="w-full rounded-xl border border-white/10 bg-zinc-950 px-3 py-2 text-sm"
placeholder="Tell Qwen VL how to rewrite the draft. You can add shot-script rules here later."
/>
<button type="submit" class="rounded-xl bg-amber-400 px-4 py-2 text-sm font-semibold text-zinc-950">Add instruction set</button>
</form>
</section>
</div>
</div>
</Teleport>
@@ -138,6 +200,9 @@ const emit = defineEmits<{
}>()
const newName = ref('')
const newPresetName = ref('')
const newPresetPrompt = ref('')
const promptPresets = ref<Array<{ id: string; name: string; description?: string; builtin?: boolean; systemPrompt?: string }>>([])
const loginUser = ref(props.username || '')
const loginCurrent = ref('')
const loginNext = ref('')
@@ -147,7 +212,10 @@ function onKey(event: KeyboardEvent) {
if (event.key === 'Escape') emit('close')
}
onMounted(() => window.addEventListener('keydown', onKey))
onMounted(() => {
window.addEventListener('keydown', onKey)
void loadPresets()
})
onBeforeUnmount(() => window.removeEventListener('keydown', onKey))
watch(() => props.folders, (folders) => {
@@ -199,6 +267,62 @@ async function addFolder() {
}
}
interface PromptPresetRow {
id: string
name: string
description?: string
builtin?: boolean
systemPrompt?: string
}
async function loadPresets() {
const data = await $fetch<{ presets?: PromptPresetRow[] }>('/api/prompt/presets').catch(() => ({ presets: [] }))
promptPresets.value = data.presets || []
}
async function addPreset() {
const name = newPresetName.value.trim()
const systemPrompt = newPresetPrompt.value.trim()
if (!name || !systemPrompt) {
emit('error', 'Name and instruction text are required')
return
}
try {
const data = await $fetch<{ presets?: PromptPresetRow[] }>('/api/prompt/presets', {
method: 'POST',
body: { name, systemPrompt }
})
promptPresets.value = data.presets || []
newPresetName.value = ''
newPresetPrompt.value = ''
} catch (error: any) {
emit('error', error?.data?.statusMessage || 'Could not save instruction set')
}
}
async function savePreset(preset: PromptPresetRow) {
if (preset.builtin) return
try {
const data = await $fetch<{ presets?: PromptPresetRow[] }>(`/api/prompt/presets/${preset.id}`, {
method: 'PATCH',
body: { name: preset.name, systemPrompt: preset.systemPrompt, description: preset.description }
})
promptPresets.value = data.presets || promptPresets.value
} catch (error: any) {
emit('error', error?.data?.statusMessage || 'Could not update instruction set')
}
}
async function removePreset(id: string) {
if (!confirm('Delete this instruction set?')) return
try {
const data = await $fetch<{ presets?: PromptPresetRow[] }>(`/api/prompt/presets/${id}`, { method: 'DELETE' })
promptPresets.value = data.presets || []
} catch (error: any) {
emit('error', error?.data?.statusMessage || 'Could not delete instruction set')
}
}
async function rename(folder: SettingsFolder) {
const name = drafts[folder.id]?.name?.trim()
if (!name || name === folder.name) return