mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-23 16:24:06 +00:00
## Summary - Adds asynchronous model upload support with HTTP 202 responses - Implements WebSocket-based real-time download progress tracking via `asset_download` events - Creates `assetDownloadStore` for centralized download state management and toast notifications - Updates upload wizard UI to show "processing" state when downloads continue in background ## Changes - **Core**: New `assetDownloadStore` for managing async downloads with WebSocket events - **API**: Support for HTTP 202 async upload responses with task tracking - **UI**: Upload wizard now shows "processing" state and allows closing dialog during download - **Progress**: Periodic toast notifications (every 5s) during active downloads with completion/error toasts - **Schema**: Updated task statuses (`created`, `running`, `completed`, `failed`) and WebSocket message types ## Review Focus - WebSocket event handling and download state management in `assetDownloadStore` - Upload flow UX - users can now close the dialog and download continues in background - Toast notification frequency and timing - Schema alignment with backend async upload API Fixes #7748 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7746-feat-Add-async-model-upload-with-WebSocket-progress-tracking-2d36d73d3650811cb79ae06f470dcded) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitHub Action <action@github.com>
122 lines
3.2 KiB
Vue
122 lines
3.2 KiB
Vue
<template>
|
|
<div
|
|
class="upload-model-dialog flex flex-col justify-between gap-6 p-4 pt-6 border-t border-border-default"
|
|
>
|
|
<!-- Step 1: Enter URL -->
|
|
<UploadModelUrlInput
|
|
v-if="currentStep === 1 && flags.huggingfaceModelImportEnabled"
|
|
v-model="wizardData.url"
|
|
:error="uploadError"
|
|
class="flex-1"
|
|
/>
|
|
<UploadModelUrlInputCivitai
|
|
v-else-if="currentStep === 1"
|
|
v-model="wizardData.url"
|
|
:error="uploadError"
|
|
/>
|
|
|
|
<!-- Step 2: Confirm Metadata -->
|
|
<UploadModelConfirmation
|
|
v-else-if="currentStep === 2"
|
|
v-model="selectedModelType"
|
|
:metadata="wizardData.metadata"
|
|
:preview-image="wizardData.previewImage"
|
|
/>
|
|
|
|
<!-- Step 3: Upload Progress -->
|
|
<UploadModelProgress
|
|
v-else-if="currentStep === 3 && uploadStatus != null"
|
|
:result="uploadStatus"
|
|
:error="uploadError"
|
|
:metadata="wizardData.metadata"
|
|
:model-type="selectedModelType"
|
|
:preview-image="wizardData.previewImage"
|
|
/>
|
|
|
|
<!-- Navigation Footer -->
|
|
<UploadModelFooter
|
|
:current-step="currentStep"
|
|
:is-fetching-metadata="isFetchingMetadata"
|
|
:is-uploading="isUploading"
|
|
:can-fetch-metadata="canFetchMetadata"
|
|
:can-upload-model="canUploadModel"
|
|
:upload-status="uploadStatus"
|
|
@back="goToPreviousStep"
|
|
@fetch-metadata="handleFetchMetadata"
|
|
@upload="handleUploadModel"
|
|
@close="handleClose"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted } from 'vue'
|
|
|
|
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
|
import UploadModelConfirmation from '@/platform/assets/components/UploadModelConfirmation.vue'
|
|
import UploadModelFooter from '@/platform/assets/components/UploadModelFooter.vue'
|
|
import UploadModelProgress from '@/platform/assets/components/UploadModelProgress.vue'
|
|
import UploadModelUrlInput from '@/platform/assets/components/UploadModelUrlInput.vue'
|
|
import UploadModelUrlInputCivitai from '@/platform/assets/components/UploadModelUrlInputCivitai.vue'
|
|
import { useModelTypes } from '@/platform/assets/composables/useModelTypes'
|
|
import { useUploadModelWizard } from '@/platform/assets/composables/useUploadModelWizard'
|
|
import { useDialogStore } from '@/stores/dialogStore'
|
|
|
|
const { flags } = useFeatureFlags()
|
|
const dialogStore = useDialogStore()
|
|
const { modelTypes, fetchModelTypes } = useModelTypes()
|
|
|
|
const emit = defineEmits<{
|
|
'upload-success': []
|
|
}>()
|
|
|
|
const {
|
|
currentStep,
|
|
isFetchingMetadata,
|
|
isUploading,
|
|
uploadStatus,
|
|
uploadError,
|
|
wizardData,
|
|
selectedModelType,
|
|
canFetchMetadata,
|
|
canUploadModel,
|
|
fetchMetadata,
|
|
uploadModel,
|
|
goToPreviousStep
|
|
} = useUploadModelWizard(modelTypes)
|
|
|
|
async function handleFetchMetadata() {
|
|
await fetchMetadata()
|
|
}
|
|
|
|
async function handleUploadModel() {
|
|
const success = await uploadModel()
|
|
if (success) {
|
|
emit('upload-success')
|
|
}
|
|
}
|
|
|
|
function handleClose() {
|
|
dialogStore.closeDialog({ key: 'upload-model' })
|
|
}
|
|
|
|
onMounted(() => {
|
|
fetchModelTypes()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.upload-model-dialog {
|
|
width: 90vw;
|
|
max-width: 800px;
|
|
min-height: 400px;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.upload-model-dialog {
|
|
width: auto;
|
|
min-width: 600px;
|
|
}
|
|
}
|
|
</style>
|