[feat] Add HuggingFace model import support (#7540)

## Summary
Adds HuggingFace as a model import source alongside CivitAI, with
improved UX for model type selection and UTF-8 filename support.

## Changes
- **Import Sources**: Implemented extensible import source handler
pattern supporting both CivitAI and HuggingFace
- **UTF-8 Support**: Decode URL-encoded filenames to properly display
international characters (e.g., Chinese)
- **UX**: Sort model types alphabetically for easier selection
- **Feature Flag**: Added `huggingfaceModelImportEnabled` flag for
gradual rollout
- **i18n**: Use proper template parameters for localized error messages

## Technical Details
- Created `ImportSourceHandler` interface for extensibility
- Refactored existing CivitAI logic into handler pattern
- Added URL validation per source
- Filename decoding handles malformed URLs gracefully

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7540-feat-Add-HuggingFace-model-import-support-2cb6d73d3650818f966cca89244e8c36)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Luke Mino-Altherr
2025-12-22 14:34:49 -05:00
committed by GitHub
parent 176c8e110b
commit 47884c623e
16 changed files with 383 additions and 55 deletions

View File

@@ -0,0 +1,82 @@
<template>
<div class="flex flex-col gap-6 text-sm text-muted-foreground">
<div class="flex flex-col gap-2">
<p class="m-0">
{{ $t('assetBrowser.uploadModelDescription1') }}
</p>
<ul class="list-disc space-y-1 pl-5 mt-0">
<li>
<i18n-t keypath="assetBrowser.uploadModelDescription2" tag="span">
<template #link>
<a
href="https://civitai.com/models"
target="_blank"
class="text-muted-foreground"
>
{{ $t('assetBrowser.uploadModelDescription2Link') }}
</a>
</template>
</i18n-t>
</li>
<li>
<i18n-t keypath="assetBrowser.uploadModelDescription3" tag="span">
<template #size>
<span class="font-bold italic">{{
$t('assetBrowser.maxFileSizeValue')
}}</span>
</template>
</i18n-t>
</li>
</ul>
</div>
<div class="flex flex-col gap-2">
<i18n-t keypath="assetBrowser.civitaiLinkLabel" tag="label" class="mb-0">
<template #download>
<span class="font-bold italic">{{
$t('assetBrowser.civitaiLinkLabelDownload')
}}</span>
</template>
</i18n-t>
<InputText
v-model="url"
autofocus
:placeholder="$t('assetBrowser.civitaiLinkPlaceholder')"
class="w-full bg-secondary-background border-0 p-4"
data-attr="upload-model-step1-url-input"
/>
<p v-if="error" class="text-xs text-error">
{{ error }}
</p>
<i18n-t
v-else
keypath="assetBrowser.civitaiLinkExample"
tag="p"
class="text-xs"
>
<template #example>
<strong>{{ $t('assetBrowser.civitaiLinkExampleStrong') }}</strong>
</template>
<template #link>
<a
href="https://civitai.com/models/10706/luisap-z-image-and-qwen-pixel-art-refiner?modelVersionId=2225295"
target="_blank"
class="text-muted-foreground"
>
{{ $t('assetBrowser.civitaiLinkExampleUrl') }}
</a>
</template>
</i18n-t>
</div>
</div>
</template>
<script setup lang="ts">
import InputText from 'primevue/inputtext'
defineProps<{
error?: string
}>()
const url = defineModel<string>({ required: true })
</script>