Ship duration control, hidden previews, job resume, and desktop file purge.

The slider never left this machine, so deploy the generate UI with separate input/output hide toggles, reconnect in-progress jobs after refresh, and delete Comfy input/output files after they are saved to the library.
This commit is contained in:
2026-08-25 20:25:36 -05:00
parent 1a271c05a4
commit c5ed4d7ac4
16 changed files with 651 additions and 104 deletions
+14 -3
View File
@@ -1,3 +1,9 @@
function parseDuration(raw: string) {
const seconds = Number(raw)
if (!Number.isFinite(seconds)) return 5
return Math.min(120, Math.max(0.5, seconds))
}
function frameLength(seconds: number) {
const raw = Math.max(5, Math.round(seconds * 24))
return raw + (5 - (raw % 17) + 17) % 17
@@ -33,7 +39,7 @@ export default defineEventHandler(async (event) => {
const seed = fields.seed && fields.seed !== 'random'
? Number(fields.seed)
: Math.floor(Math.random() * 2_147_483_647)
const length = frameLength(Number(fields.duration || 5))
const length = frameLength(parseDuration(fields.duration || '5'))
const hideThumbnail = fields.hideThumbnail === 'true'
const clipName = (fields.name || '').trim().slice(0, 80)
const ownerKey = libraryOwnerKey(event)
@@ -44,7 +50,9 @@ export default defineEventHandler(async (event) => {
if (!folderId) {
throw createError({ statusCode: 400, statusMessage: 'Create a library folder before generating' })
}
assertFolderAccess(event, folderId)
const destFolder = library.folders.find(folder => folder.id === folderId)
const folderLocked = Boolean(destFolder?.protected && !destFolder.unlocked)
assertFolderExists(event, folderId)
await saveStill({
ownerKey,
folderId,
@@ -61,6 +69,7 @@ export default defineEventHandler(async (event) => {
ownerKey,
folderId,
hideThumbnail,
folderLocked,
name: clipName,
prompt,
aspect: fields.aspect || 'auto',
@@ -90,7 +99,8 @@ export default defineEventHandler(async (event) => {
steps,
turbo,
folderId,
hideThumbnail
hideThumbnail,
folderLocked
}
})
@@ -149,6 +159,7 @@ async function runGeneration(
ownerKey: job.library.ownerKey,
folderId: job.library.folderId,
hideThumbnail: job.library.hideThumbnail,
folderLocked: job.library.folderLocked,
name: job.library.name,
prompt: job.library.prompt,
aspect: job.library.aspect,
+33
View File
@@ -0,0 +1,33 @@
export default defineEventHandler((event) => {
const owner = libraryOwnerKey(event)
const live = listJobs()
.filter(job => job.library?.ownerKey === owner && (job.status === 'queued' || job.status === 'uploading' || job.status === 'running'))
.sort((a, b) => b.startedAt - a.startedAt)[0]
if (live) {
return {
jobId: live.id,
hideThumbnail: live.hideThumbnail === true,
folderLocked: live.library?.folderLocked === true,
...jobSnapshot(live)
}
}
const pending = listPendingJobs()
.filter(job => job.ownerKey === owner)
.sort((a, b) => b.startedAt - a.startedAt)[0]
if (pending) {
return {
jobId: pending.jobId,
type: 'snapshot',
status: 'running',
message: 'Reconnecting to ComfyUI...',
progress: Math.max(8, Math.min(90, Math.round((Date.now() - pending.startedAt) / 1000))),
promptId: pending.promptId,
elapsedMs: Date.now() - pending.startedAt,
hideThumbnail: pending.hideThumbnail === true,
folderLocked: pending.folderLocked === true
}
}
return { jobId: null }
})
+1 -1
View File
@@ -23,7 +23,7 @@ export default defineEventHandler(async (event) => {
if (!folderId) {
throw createError({ statusCode: 400, statusMessage: 'Create a library folder first' })
}
assertFolderAccess(event, folderId)
assertFolderExists(event, folderId)
const still = await saveStill({
ownerKey: owner,
folderId,