mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-02 22:37:32 +00:00
Add progress bars and progress text
This commit is contained in:
169
src/components/queue/QueueInlineProgressSummary.stories.ts
Normal file
169
src/components/queue/QueueInlineProgressSummary.stories.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import QueueInlineProgressSummary from './QueueInlineProgressSummary.vue'
|
||||
import { useExecutionStore } from '@/stores/executionStore'
|
||||
|
||||
type SeedOptions = {
|
||||
promptId: string
|
||||
nodes: Record<string, boolean>
|
||||
runningNodeId?: string
|
||||
runningNodeTitle?: string
|
||||
runningNodeType?: string
|
||||
currentValue?: number
|
||||
currentMax?: number
|
||||
}
|
||||
|
||||
function resetExecutionStore() {
|
||||
const exec = useExecutionStore()
|
||||
exec.activePromptId = null
|
||||
exec.queuedPrompts = {}
|
||||
exec.nodeProgressStates = {}
|
||||
exec.nodeProgressStatesByPrompt = {}
|
||||
exec._executingNodeProgress = null
|
||||
exec.lastExecutionError = null
|
||||
exec.lastNodeErrors = null
|
||||
exec.initializingPromptIds = new Set()
|
||||
exec.promptIdToWorkflowId = new Map()
|
||||
}
|
||||
|
||||
function seedExecutionState({
|
||||
promptId,
|
||||
nodes,
|
||||
runningNodeId,
|
||||
runningNodeTitle,
|
||||
runningNodeType,
|
||||
currentValue = 0,
|
||||
currentMax = 100
|
||||
}: SeedOptions) {
|
||||
resetExecutionStore()
|
||||
|
||||
const exec = useExecutionStore()
|
||||
const workflow = runningNodeId
|
||||
? ({
|
||||
changeTracker: {
|
||||
activeState: {
|
||||
nodes: Object.keys(nodes).map((id) => ({
|
||||
id,
|
||||
title:
|
||||
id === runningNodeId ? (runningNodeTitle ?? '') : `Node ${id}`,
|
||||
type: id === runningNodeId ? (runningNodeType ?? 'Node') : 'Node'
|
||||
}))
|
||||
}
|
||||
}
|
||||
} as any)
|
||||
: undefined
|
||||
|
||||
exec.activePromptId = promptId
|
||||
exec.queuedPrompts = {
|
||||
[promptId]: {
|
||||
nodes,
|
||||
...(workflow ? { workflow } : {})
|
||||
}
|
||||
} as any
|
||||
|
||||
const nodeProgress = runningNodeId
|
||||
? {
|
||||
[runningNodeId]: {
|
||||
value: currentValue,
|
||||
max: currentMax,
|
||||
state: 'running',
|
||||
node_id: runningNodeId,
|
||||
prompt_id: promptId
|
||||
}
|
||||
}
|
||||
: {}
|
||||
|
||||
exec.nodeProgressStates = nodeProgress as any
|
||||
exec.nodeProgressStatesByPrompt = runningNodeId
|
||||
? ({ [promptId]: nodeProgress } as any)
|
||||
: {}
|
||||
exec._executingNodeProgress = runningNodeId
|
||||
? ({
|
||||
value: currentValue,
|
||||
max: currentMax,
|
||||
prompt_id: promptId,
|
||||
node: runningNodeId
|
||||
} as any)
|
||||
: null
|
||||
}
|
||||
|
||||
const meta: Meta<typeof QueueInlineProgressSummary> = {
|
||||
title: 'Queue/QueueInlineProgressSummary',
|
||||
component: QueueInlineProgressSummary,
|
||||
parameters: {
|
||||
layout: 'padded',
|
||||
backgrounds: {
|
||||
default: 'light'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const RunningKSampler: Story = {
|
||||
render: () => ({
|
||||
components: { QueueInlineProgressSummary },
|
||||
setup() {
|
||||
seedExecutionState({
|
||||
promptId: 'prompt-running',
|
||||
nodes: { '1': true, '2': false, '3': false, '4': true },
|
||||
runningNodeId: '2',
|
||||
runningNodeTitle: 'KSampler',
|
||||
runningNodeType: 'KSampler',
|
||||
currentValue: 12,
|
||||
currentMax: 100
|
||||
})
|
||||
|
||||
return {}
|
||||
},
|
||||
template: `
|
||||
<div style="background: var(--color-surface-primary); width: 420px; padding: 12px;">
|
||||
<QueueInlineProgressSummary />
|
||||
</div>
|
||||
`
|
||||
})
|
||||
}
|
||||
|
||||
export const RunningWithFallbackName: Story = {
|
||||
render: () => ({
|
||||
components: { QueueInlineProgressSummary },
|
||||
setup() {
|
||||
seedExecutionState({
|
||||
promptId: 'prompt-fallback',
|
||||
nodes: { '10': true, '11': true, '12': false, '13': true },
|
||||
runningNodeId: '12',
|
||||
runningNodeTitle: '',
|
||||
runningNodeType: 'custom_node',
|
||||
currentValue: 78,
|
||||
currentMax: 100
|
||||
})
|
||||
|
||||
return {}
|
||||
},
|
||||
template: `
|
||||
<div style="background: var(--color-surface-primary); width: 420px; padding: 12px;">
|
||||
<QueueInlineProgressSummary />
|
||||
</div>
|
||||
`
|
||||
})
|
||||
}
|
||||
|
||||
export const ProgressWithoutCurrentNode: Story = {
|
||||
render: () => ({
|
||||
components: { QueueInlineProgressSummary },
|
||||
setup() {
|
||||
seedExecutionState({
|
||||
promptId: 'prompt-progress-only',
|
||||
nodes: { '21': true, '22': true, '23': true, '24': false }
|
||||
})
|
||||
|
||||
return {}
|
||||
},
|
||||
template: `
|
||||
<div style="background: var(--color-surface-primary); width: 420px; padding: 12px;">
|
||||
<QueueInlineProgressSummary />
|
||||
</div>
|
||||
`
|
||||
})
|
||||
}
|
||||
64
src/components/queue/QueueInlineProgressSummary.vue
Normal file
64
src/components/queue/QueueInlineProgressSummary.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div v-if="shouldShow" class="flex justify-end">
|
||||
<div
|
||||
class="flex items-center gap-4 whitespace-nowrap text-[0.75rem] leading-[normal] drop-shadow-[1px_1px_8px_rgba(0,0,0,0.4)]"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="flex items-center gap-1 text-base-foreground">
|
||||
<span class="font-normal">{{ totalLabel }}:</span>
|
||||
<span class="w-[5ch] shrink-0 text-right font-bold tabular-nums">
|
||||
{{ totalPercentFormatted }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1 text-muted-foreground">
|
||||
<span
|
||||
class="w-[16ch] shrink-0 truncate text-right"
|
||||
:title="currentNodeLabel"
|
||||
>
|
||||
{{ currentNodeLabel }}:
|
||||
</span>
|
||||
<span class="w-[5ch] shrink-0 text-right tabular-nums">
|
||||
{{ currentNodePercentFormatted }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useCurrentNodeName } from '@/composables/queue/useCurrentNodeName'
|
||||
import { useQueueProgress } from '@/composables/queue/useQueueProgress'
|
||||
import { useExecutionStore } from '@/stores/executionStore'
|
||||
|
||||
const props = defineProps<{
|
||||
hidden?: boolean
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
const executionStore = useExecutionStore()
|
||||
const { currentNodeName } = useCurrentNodeName()
|
||||
const {
|
||||
totalPercent,
|
||||
totalPercentFormatted,
|
||||
currentNodePercent,
|
||||
currentNodePercentFormatted
|
||||
} = useQueueProgress()
|
||||
|
||||
const totalLabel = computed<string>(() =>
|
||||
t('sideToolbar.queueProgressOverlay.inlineTotalLabel')
|
||||
)
|
||||
|
||||
const currentNodeLabel = computed<string>(() => currentNodeName.value)
|
||||
|
||||
const shouldShow = computed(
|
||||
() =>
|
||||
!props.hidden &&
|
||||
(!executionStore.isIdle ||
|
||||
totalPercent.value > 0 ||
|
||||
currentNodePercent.value > 0)
|
||||
)
|
||||
</script>
|
||||
Reference in New Issue
Block a user