Queue media preview (#449)

* output url

* Basic image previews

* Split out task item component

* Move task actions to context menu

* simplify

* Move spinner

* Lift context menu to tab scope

* Better tag

* Fix placeholder style

* nit

* Correctly handle cancelled

* nit

* Split out result item as separate component

* nit

* Fix center crop

* nit

* Simplify task item

* Flat list

* Show prompt id

* Make image draggable

* Disable preview for dragging

* Fix key

* Correctly handle task in expanded view

* Add preview
This commit is contained in:
Chenlei Hu
2024-08-15 23:26:38 -04:00
committed by GitHub
parent 9e3dffd7fd
commit e6d29656fa
8 changed files with 397 additions and 125 deletions

View File

@@ -1,15 +1,17 @@
import { api } from '@/scripts/api'
import { app } from '@/scripts/app'
import {
validateTaskItem,
import type {
TaskItem,
TaskType,
TaskPrompt,
TaskStatus,
StatusWsMessageStatus,
TaskOutput,
StatusWsMessageStatus
ResultItem
} from '@/types/apiTypes'
import { plainToClass } from 'class-transformer'
import { validateTaskItem } from '@/types/apiTypes'
import type { NodeId } from '@/types/comfyWorkflow'
import { instanceToPlain, plainToClass } from 'class-transformer'
import _ from 'lodash'
import { defineStore } from 'pinia'
import { toRaw } from 'vue'
@@ -25,11 +27,43 @@ export enum TaskItemDisplayStatus {
Cancelled = 'Cancelled'
}
export class ResultItemImpl {
filename: string
subfolder?: string
type: string
nodeId: NodeId
// 'audio' | 'images' | ...
mediaType: string
get url(): string {
return api.apiURL(`/view?filename=${encodeURIComponent(this.filename)}&type=${this.type}&
subfolder=${encodeURIComponent(this.subfolder || '')}&t=${+new Date()}`)
}
}
export class TaskItemImpl {
taskType: TaskType
prompt: TaskPrompt
status?: TaskStatus
outputs?: TaskOutput
outputs: TaskOutput
get flatOutputs(): ResultItemImpl[] {
if (!this.outputs) {
return []
}
return Object.entries(this.outputs).flatMap(([nodeId, nodeOutputs]) =>
Object.entries(nodeOutputs).flatMap(([mediaType, items]) =>
(items as ResultItem[]).flatMap((item: ResultItem) =>
plainToClass(ResultItemImpl, {
...item,
nodeId,
mediaType
})
)
)
)
}
get apiTaskType(): APITaskType {
switch (this.taskType) {
@@ -41,6 +75,10 @@ export class TaskItemImpl {
}
}
get key() {
return this.promptId + this.displayStatus
}
get queueIndex() {
return this.prompt[0]
}
@@ -174,6 +212,31 @@ export const useQueueStore = defineStore('queue', {
...state.historyTasks
]
},
flatTasks(): TaskItemImpl[] {
return this.tasks.flatMap((task: TaskItemImpl) => {
if (task.displayStatus !== TaskItemDisplayStatus.Completed) {
return [task]
}
return task.flatOutputs.map((output: ResultItemImpl, i: number) =>
plainToClass(TaskItemImpl, {
...instanceToPlain(task),
prompt: [
task.queueIndex,
`${task.promptId}-${i}`,
task.promptInputs,
task.extraData,
task.outputsToExecute
],
outputs: {
[output.nodeId]: {
[output.mediaType]: [instanceToPlain(output)]
}
}
})
)
})
},
lastHistoryQueueIndex(state) {
return state.historyTasks.length ? state.historyTasks[0].queueIndex : -1
}