[feat] Add Civitai model upload wizard (#6694)

## Summary
Adds a complete model upload workflow that allows users to import models
from Civitai URLs directly into their library.

## Changes
- **Multi-step wizard**: URL input → metadata confirmation → upload
progress
- **Components**: UploadModelDialog, UploadModelUrlInput,
UploadModelConfirmation, UploadModelProgress, UploadModelDialogHeader
- **API integration**: New assetService methods for metadata retrieval
and URL-based uploads
- **Model type management**: useModelTypes composable for fetching and
formatting available model types
- **UX improvements**: Optional validation bypass for UrlInput component
- **Localization**: 26 new i18n strings for the upload workflow

## Review Focus
- Error handling for failed uploads and metadata retrieval
- Model type detection and selection logic
- Dialog state management across multi-step workflow

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6694-feat-Add-Civitai-model-upload-wizard-2ab6d73d36508193b3b1dd67c7cc5a09)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Luke Mino-Altherr
2025-11-19 20:37:22 -08:00
committed by GitHub
parent 423a2e76bc
commit acd855601c
13 changed files with 799 additions and 2 deletions

View File

@@ -0,0 +1,68 @@
<template>
<div class="flex flex-1 flex-col gap-6">
<!-- Uploading State -->
<div
v-if="status === 'uploading'"
class="flex flex-1 flex-col items-center justify-center gap-6"
>
<i
class="icon-[lucide--loader-circle] animate-spin text-6xl text-primary"
/>
<div class="text-center">
<p class="m-0 text-sm font-bold">
{{ $t('assetBrowser.uploadingModel') }}
</p>
</div>
</div>
<!-- Success State -->
<div v-else-if="status === 'success'" class="flex flex-col gap-8">
<div class="flex flex-col gap-4">
<p class="text-sm text-muted m-0 font-bold">
{{ $t('assetBrowser.modelUploaded') }}
</p>
<p class="text-sm text-muted m-0">
{{ $t('assetBrowser.findInLibrary', { type: modelType }) }}
</p>
</div>
<div class="flex flex-row items-start p-8 bg-neutral-800 rounded-lg">
<div class="flex flex-col justify-center items-start gap-1 flex-1">
<p class="text-sm m-0">
{{ metadata?.name || metadata?.filename }}
</p>
<p class="text-sm text-muted m-0">
{{ modelType }}
</p>
</div>
</div>
</div>
<!-- Error State -->
<div
v-else-if="status === 'error'"
class="flex flex-1 flex-col items-center justify-center gap-6"
>
<i class="icon-[lucide--x-circle] text-6xl text-error" />
<div class="text-center">
<p class="m-0 text-sm font-bold">
{{ $t('assetBrowser.uploadFailed') }}
</p>
<p v-if="error" class="text-sm text-muted mb-0">
{{ error }}
</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { AssetMetadata } from '@/platform/assets/schemas/assetSchema'
defineProps<{
status: 'idle' | 'uploading' | 'success' | 'error'
error?: string
metadata: AssetMetadata | null
modelType: string | undefined
}>()
</script>