mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-10 01:50:08 +00:00
feat: add model download progress dialog (#7897)
## Summary Add a progress dialog for model downloads that appears when downloads are active. ## Changes - Add `ModelImportProgressDialog` component for showing download progress - Add `ProgressToastItem` component for individual download job display - Add `StatusBadge` component for status indicators - Extend `assetDownloadStore` with: - `finishedDownloads` computed for completed/failed jobs - `hasDownloads` computed for dialog visibility - `clearFinishedDownloads()` to dismiss finished downloads - Dialog visibility driven by store state - Closing dialog clears finished downloads - Filter dropdown to show all/completed/failed downloads - Expandable/collapsible UI with animated transitions - Update AGENTS.md with import type convention and pluralization note ## Testing - Start a model download and verify the dialog appears - Verify expand/collapse animation works - Verify filter dropdown works - Verify closing the dialog clears finished downloads - Verify dialog hides when no downloads remain ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7897-feat-add-model-download-progress-dialog-2e26d73d36508116960eff6fbe7dc392) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
30
src/components/common/StatusBadge.vue
Normal file
30
src/components/common/StatusBadge.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
type Severity = 'default' | 'secondary' | 'warn' | 'danger' | 'contrast'
|
||||
|
||||
const { label, severity = 'default' } = defineProps<{
|
||||
label: string
|
||||
severity?: Severity
|
||||
}>()
|
||||
|
||||
function badgeClasses(sev: Severity): string {
|
||||
const baseClasses =
|
||||
'inline-flex h-3.5 items-center justify-center rounded-full px-1 text-xxxs font-semibold uppercase'
|
||||
|
||||
switch (sev) {
|
||||
case 'danger':
|
||||
return `${baseClasses} bg-destructive-background text-white`
|
||||
case 'contrast':
|
||||
return `${baseClasses} bg-base-foreground text-base-background`
|
||||
case 'warn':
|
||||
return `${baseClasses} bg-warning-background text-base-background`
|
||||
case 'secondary':
|
||||
return `${baseClasses} bg-secondary-background text-base-foreground`
|
||||
default:
|
||||
return `${baseClasses} bg-primary-background text-base-foreground`
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span :class="badgeClasses(severity)">{{ label }}</span>
|
||||
</template>
|
||||
66
src/components/toast/ProgressToastItem.vue
Normal file
66
src/components/toast/ProgressToastItem.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import StatusBadge from '@/components/common/StatusBadge.vue'
|
||||
import type { AssetDownload } from '@/stores/assetDownloadStore'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
const { job } = defineProps<{
|
||||
job: AssetDownload
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const progressPercent = computed(() => Math.round(job.progress * 100))
|
||||
const isCompleted = computed(() => job.status === 'completed')
|
||||
const isFailed = computed(() => job.status === 'failed')
|
||||
const isRunning = computed(() => job.status === 'running')
|
||||
const isPending = computed(() => job.status === 'created')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'flex items-center justify-between rounded-lg bg-modal-card-background px-4 py-3',
|
||||
isCompleted && 'opacity-50'
|
||||
)
|
||||
"
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-sm text-base-foreground">{{ job.assetName }}</span>
|
||||
<span v-if="isRunning" class="text-xs text-muted-foreground">
|
||||
{{ progressPercent }}%
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<template v-if="isFailed">
|
||||
<i
|
||||
class="icon-[lucide--circle-alert] size-4 text-destructive-background"
|
||||
/>
|
||||
<StatusBadge :label="t('progressToast.failed')" severity="danger" />
|
||||
</template>
|
||||
|
||||
<template v-else-if="isCompleted">
|
||||
<StatusBadge :label="t('progressToast.finished')" severity="contrast" />
|
||||
</template>
|
||||
|
||||
<template v-else-if="isRunning">
|
||||
<i
|
||||
class="icon-[lucide--loader-circle] size-4 animate-spin text-primary-background"
|
||||
/>
|
||||
<span class="text-xs text-primary-background">
|
||||
{{ progressPercent }}%
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template v-else-if="isPending">
|
||||
<span class="text-xs text-muted-foreground">
|
||||
{{ t('progressToast.pending') }}
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user