Extend Prompt Builder with sequence characters, outfits, and scene toggles.

Characters live on the sequence with per-scene on/off and outfit picks; Copy scene exports only the active tab; legacy saves still open.
This commit is contained in:
Towsty
2026-10-02 08:50:31 -05:00
parent 790c33fcf0
commit 7799529788
6 changed files with 569 additions and 147 deletions
+2
View File
@@ -4,11 +4,13 @@ export default defineEventHandler(async (event) => {
const { owner } = assertLibraryOwner(event)
const body = await readBody<{
name?: string
characters?: unknown
scenes?: unknown
overwrite?: boolean
}>(event).catch(() => ({}))
const sequence = await savePromptSequence(owner, {
name: body?.name || '',
characters: body?.characters,
scenes: body?.scenes,
overwrite: body?.overwrite === true
})
+2 -1
View File
@@ -3,9 +3,10 @@ import { listPromptSequences, updatePromptSequence } from '~/server/utils/prompt
export default defineEventHandler(async (event) => {
const { owner } = assertLibraryOwner(event)
const id = String(getRouterParam(event, 'id') || '')
const body = await readBody<{ name?: string; scenes?: unknown }>(event).catch(() => ({}))
const body = await readBody<{ name?: string; characters?: unknown; scenes?: unknown }>(event).catch(() => ({}))
const sequence = await updatePromptSequence(owner, id, {
name: body?.name,
characters: body?.characters,
scenes: body?.scenes
})
return {
+26 -6
View File
@@ -4,9 +4,12 @@ import { ownerRoot } from '~/server/utils/library'
import {
normalizePromptSequence,
normalizePromptSequenceName,
normalizeCharacter,
normalizeScene,
emptyCharacter,
emptyScene,
type PromptSequence,
type PromptSequenceCharacter,
type PromptSequenceScene
} from '~/utils/promptSequences'
@@ -54,9 +57,14 @@ function mutate<T>(owner: string, fn: (sequences: PromptSequence[]) => T): Promi
return run
}
function normalizeScenesInput(raw: unknown): PromptSequenceScene[] {
if (!Array.isArray(raw) || !raw.length) return [emptyScene(1)]
return raw.map((scene, index) => normalizeScene(scene, index))
function normalizeCharactersInput(raw: unknown): PromptSequenceCharacter[] {
if (!Array.isArray(raw) || !raw.length) return [emptyCharacter()]
return raw.map(normalizeCharacter)
}
function normalizeScenesInput(raw: unknown, characters: PromptSequenceCharacter[]): PromptSequenceScene[] {
if (!Array.isArray(raw) || !raw.length) return [emptyScene(1, characters)]
return raw.map((scene, index) => normalizeScene(scene, index, characters))
}
export function listPromptSequences(owner: string) {
@@ -72,12 +80,14 @@ export function getPromptSequence(owner: string, id: string) {
export async function savePromptSequence(owner: string, input: {
name: string
characters?: unknown
scenes?: unknown
overwrite?: boolean
}) {
const name = normalizePromptSequenceName(input.name)
if (!name) throw createError({ statusCode: 400, statusMessage: 'A sequence name is required' })
const scenes = normalizeScenesInput(input.scenes)
const characters = normalizeCharactersInput(input.characters)
const scenes = normalizeScenesInput(input.scenes, characters)
const now = Date.now()
return mutate(owner, (sequences) => {
const existing = sequences.find(item => item.name.toLowerCase() === name.toLowerCase())
@@ -85,6 +95,7 @@ export async function savePromptSequence(owner: string, input: {
throw createError({ statusCode: 409, statusMessage: `Overwrite sequence “${existing.name}”?` })
}
if (existing) {
existing.characters = characters
existing.scenes = scenes
existing.updatedAt = now
return existing
@@ -92,6 +103,7 @@ export async function savePromptSequence(owner: string, input: {
const created: PromptSequence = {
id: crypto.randomUUID(),
name,
characters,
scenes,
createdAt: now,
updatedAt: now
@@ -103,6 +115,7 @@ export async function savePromptSequence(owner: string, input: {
export async function updatePromptSequence(owner: string, id: string, input: {
name?: string
characters?: unknown
scenes?: unknown
}) {
const wanted = String(id || '').trim()
@@ -117,8 +130,15 @@ export async function updatePromptSequence(owner: string, id: string, input: {
if (clash) throw createError({ statusCode: 409, statusMessage: `A sequence named “${clash.name}” already exists` })
existing.name = name
}
if (input.scenes !== undefined) {
existing.scenes = normalizeScenesInput(input.scenes)
if (input.characters !== undefined || input.scenes !== undefined) {
const characters = input.characters !== undefined
? normalizeCharactersInput(input.characters)
: existing.characters
const scenes = input.scenes !== undefined
? normalizeScenesInput(input.scenes, characters)
: existing.scenes.map((scene, index) => normalizeScene(scene, index, characters))
existing.characters = characters
existing.scenes = scenes
}
existing.updatedAt = Date.now()
return existing