mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-14 17:37:46 +00:00
v1.2.0 Side Bar & Menu rework (#189)
* Basic side tool bar skeleton + Theme toggle (#164) * Side bar skeleton * Fix grid layout * nit * Add theme toggle logic * Change primevue color theme to blue to match beta menu UI * Add litegraph canvas splitter overlay (#177) * Add vue wrapper * Splitter overlay * Move teleport to side bar comp * Toolbar placeholder * Move settings button from top menu to side bar (#178) * Reverse relationship between splitter overlay and sidebar component (#180) * Reverse relationship between splitter overlay and sidebar component * nit * Remove border on splitter * Fix canvas shift (#186) * Move queue/history display to side bar (#185) * Side bar placeholder * Pinia store for queue items * Flatten task item * Fix schema * computed * Switch running / pending order * Use class-transformer * nit * Show display status * Add tag severity style * Add execution time * nit * Rename to execution success * Add time display * Sort queue desc order * nit * Add remove item feature * Load workflow * Add confirmation popup * Add empty table placeholder * Remove beta menu UI's queue button/list * Add tests on litegraph widget text truncate (#191) * Add tests on litegraph widget text truncate * Updated screenshots * Revert port change * Remove screenshots * Update test expectations [skip ci] * Add back menu.settingsGroup for compatibility (#192) * Close side bar on menu location set as disabled (#194) * Remove placeholder side bar tabs (#196) --------- Co-authored-by: bymyself <abolkonsky.rem@gmail.com> Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
206
src/stores/queueStore.ts
Normal file
206
src/stores/queueStore.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import { api } from "@/scripts/api";
|
||||
import { app } from "@/scripts/app";
|
||||
import {
|
||||
validateTaskItem,
|
||||
TaskItem,
|
||||
TaskType,
|
||||
TaskPrompt,
|
||||
TaskStatus,
|
||||
TaskOutput,
|
||||
} from "@/types/apiTypes";
|
||||
import { plainToClass } from "class-transformer";
|
||||
import _ from "lodash";
|
||||
import { defineStore } from "pinia";
|
||||
import { toRaw } from "vue";
|
||||
|
||||
// Task type used in the API.
|
||||
export type APITaskType = "queue" | "history";
|
||||
|
||||
export enum TaskItemDisplayStatus {
|
||||
Running = "Running",
|
||||
Pending = "Pending",
|
||||
Completed = "Completed",
|
||||
Failed = "Failed",
|
||||
Cancelled = "Cancelled",
|
||||
}
|
||||
|
||||
export class TaskItemImpl {
|
||||
taskType: TaskType;
|
||||
prompt: TaskPrompt;
|
||||
status?: TaskStatus;
|
||||
outputs?: TaskOutput;
|
||||
|
||||
get apiTaskType(): APITaskType {
|
||||
switch (this.taskType) {
|
||||
case "Running":
|
||||
case "Pending":
|
||||
return "queue";
|
||||
case "History":
|
||||
return "history";
|
||||
}
|
||||
}
|
||||
|
||||
get queueIndex() {
|
||||
return this.prompt[0];
|
||||
}
|
||||
|
||||
get promptId() {
|
||||
return this.prompt[1];
|
||||
}
|
||||
|
||||
get promptInputs() {
|
||||
return this.prompt[2];
|
||||
}
|
||||
|
||||
get extraData() {
|
||||
return this.prompt[3];
|
||||
}
|
||||
|
||||
get outputsToExecute() {
|
||||
return this.prompt[4];
|
||||
}
|
||||
|
||||
get extraPngInfo() {
|
||||
return this.extraData.extra_pnginfo;
|
||||
}
|
||||
|
||||
get clientId() {
|
||||
return this.extraData.client_id;
|
||||
}
|
||||
|
||||
get workflow() {
|
||||
return this.extraPngInfo.workflow;
|
||||
}
|
||||
|
||||
get messages() {
|
||||
return this.status?.messages || [];
|
||||
}
|
||||
|
||||
get interrupted() {
|
||||
return _.some(
|
||||
this.messages,
|
||||
(message) => message[0] === "execution_interrupted"
|
||||
);
|
||||
}
|
||||
|
||||
get isHistory() {
|
||||
return this.taskType === "History";
|
||||
}
|
||||
|
||||
get isRunning() {
|
||||
return this.taskType === "Running";
|
||||
}
|
||||
|
||||
get displayStatus(): TaskItemDisplayStatus {
|
||||
switch (this.taskType) {
|
||||
case "Running":
|
||||
return TaskItemDisplayStatus.Running;
|
||||
case "Pending":
|
||||
return TaskItemDisplayStatus.Pending;
|
||||
case "History":
|
||||
switch (this.status!.status_str) {
|
||||
case "success":
|
||||
return TaskItemDisplayStatus.Completed;
|
||||
case "error":
|
||||
return this.interrupted
|
||||
? TaskItemDisplayStatus.Cancelled
|
||||
: TaskItemDisplayStatus.Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get executionStartTimestamp() {
|
||||
const message = this.messages.find(
|
||||
(message) => message[0] === "execution_start"
|
||||
);
|
||||
return message ? message[1].timestamp : undefined;
|
||||
}
|
||||
|
||||
get executionEndTimestamp() {
|
||||
const messages = this.messages.filter((message) =>
|
||||
[
|
||||
"execution_success",
|
||||
"execution_interrupted",
|
||||
"execution_error",
|
||||
].includes(message[0])
|
||||
);
|
||||
if (!messages.length) {
|
||||
return undefined;
|
||||
}
|
||||
return _.max(messages.map((message) => message[1].timestamp));
|
||||
}
|
||||
|
||||
get executionTime() {
|
||||
if (!this.executionStartTimestamp || !this.executionEndTimestamp) {
|
||||
return undefined;
|
||||
}
|
||||
return this.executionEndTimestamp - this.executionStartTimestamp;
|
||||
}
|
||||
|
||||
get executionTimeInSeconds() {
|
||||
return this.executionTime !== undefined
|
||||
? this.executionTime / 1000
|
||||
: undefined;
|
||||
}
|
||||
|
||||
public async loadWorkflow() {
|
||||
await app.loadGraphData(toRaw(this.workflow));
|
||||
if (this.outputs) {
|
||||
app.nodeOutputs = toRaw(this.outputs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface State {
|
||||
runningTasks: TaskItemImpl[];
|
||||
pendingTasks: TaskItemImpl[];
|
||||
historyTasks: TaskItemImpl[];
|
||||
}
|
||||
|
||||
export const useQueueStore = defineStore("queue", {
|
||||
state: (): State => ({
|
||||
runningTasks: [],
|
||||
pendingTasks: [],
|
||||
historyTasks: [],
|
||||
}),
|
||||
getters: {
|
||||
tasks(state) {
|
||||
return [
|
||||
...state.pendingTasks,
|
||||
...state.runningTasks,
|
||||
...state.historyTasks,
|
||||
];
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// Fetch the queue data from the API
|
||||
async update() {
|
||||
const [queue, history] = await Promise.all([
|
||||
api.getQueue(),
|
||||
api.getHistory(),
|
||||
]);
|
||||
|
||||
const toClassAll = (tasks: TaskItem[]): TaskItemImpl[] =>
|
||||
tasks
|
||||
.map((task) => validateTaskItem(task))
|
||||
.filter((result) => result.success)
|
||||
.map((result) => plainToClass(TaskItemImpl, result.data))
|
||||
// Desc order to show the latest tasks first
|
||||
.sort((a, b) => b.queueIndex - a.queueIndex);
|
||||
|
||||
this.runningTasks = toClassAll(queue.Running);
|
||||
this.pendingTasks = toClassAll(queue.Pending);
|
||||
this.historyTasks = toClassAll(history.History);
|
||||
},
|
||||
async clear() {
|
||||
await Promise.all(
|
||||
["queue", "history"].map((type) => api.clearItems(type))
|
||||
);
|
||||
await this.update();
|
||||
},
|
||||
async delete(task: TaskItemImpl) {
|
||||
await api.deleteItem(task.apiTaskType, task.promptId);
|
||||
await this.update();
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user