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:
Alexander Brown
2026-01-08 14:29:02 -08:00
committed by GitHub
parent 0ca27f3d9b
commit 405e756d4c
7 changed files with 413 additions and 65 deletions

View File

@@ -17,6 +17,7 @@
<GlobalToast />
<RerouteMigrationToast />
<ModelImportProgressDialog />
<UnloadWindowConfirmDialog v-if="!isElectron()" />
<MenuHamburger />
</template>
@@ -49,6 +50,7 @@ import { useErrorHandling } from '@/composables/useErrorHandling'
import { useProgressFavicon } from '@/composables/useProgressFavicon'
import { SERVER_CONFIG_ITEMS } from '@/constants/serverConfig'
import { i18n, loadLocale } from '@/i18n'
import ModelImportProgressDialog from '@/platform/assets/components/ModelImportProgressDialog.vue'
import { isCloud } from '@/platform/distribution/types'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useTelemetry } from '@/platform/telemetry'
@@ -60,7 +62,6 @@ import { api } from '@/scripts/api'
import { app } from '@/scripts/app'
import { setupAutoQueueHandler } from '@/services/autoQueueService'
import { useKeybindingService } from '@/services/keybindingService'
import { useAssetDownloadStore } from '@/stores/assetDownloadStore'
import { useAssetsStore } from '@/stores/assetsStore'
import { useCommandStore } from '@/stores/commandStore'
import { useExecutionStore } from '@/stores/executionStore'
@@ -88,7 +89,6 @@ const { t } = useI18n()
const toast = useToast()
const settingStore = useSettingStore()
const executionStore = useExecutionStore()
const assetDownloadStore = useAssetDownloadStore()
const colorPaletteStore = useColorPaletteStore()
const queueStore = useQueueStore()
const assetsStore = useAssetsStore()
@@ -256,7 +256,6 @@ onMounted(() => {
api.addEventListener('reconnecting', onReconnecting)
api.addEventListener('reconnected', onReconnected)
executionStore.bindExecutionEvents()
assetDownloadStore.setup()
try {
init()
@@ -273,7 +272,6 @@ onBeforeUnmount(() => {
api.removeEventListener('reconnecting', onReconnecting)
api.removeEventListener('reconnected', onReconnected)
executionStore.unbindExecutionEvents()
assetDownloadStore.teardown()
// Clean up page visibility listener
if (visibilityListener) {