feat(ui): add View on Hugging Face button, plumb hf_url through full cache pipeline

This commit is contained in:
Will Miao
2026-07-01 08:38:16 +08:00
parent 8348a0cef8
commit 8b344ea39f
19 changed files with 73 additions and 4 deletions

View File

@@ -105,6 +105,7 @@
"removeFromFavorites": "Aus Favoriten entfernen", "removeFromFavorites": "Aus Favoriten entfernen",
"viewOnCivitai": "Auf Civitai anzeigen", "viewOnCivitai": "Auf Civitai anzeigen",
"notAvailableFromCivitai": "Nicht auf Civitai verfügbar", "notAvailableFromCivitai": "Nicht auf Civitai verfügbar",
"viewOnHuggingFace": "Auf Hugging Face ansehen",
"sendToWorkflow": "An ComfyUI senden (Klick: Anhängen, Shift+Klick: Ersetzen)", "sendToWorkflow": "An ComfyUI senden (Klick: Anhängen, Shift+Klick: Ersetzen)",
"copyLoRASyntax": "LoRA-Syntax kopieren", "copyLoRASyntax": "LoRA-Syntax kopieren",
"checkpointNameCopied": "Checkpoint-Name kopiert", "checkpointNameCopied": "Checkpoint-Name kopiert",
@@ -1319,6 +1320,8 @@
"editVersionName": "Versionsname bearbeiten", "editVersionName": "Versionsname bearbeiten",
"viewOnCivitai": "Auf Civitai anzeigen", "viewOnCivitai": "Auf Civitai anzeigen",
"viewOnCivitaiText": "Auf Civitai anzeigen", "viewOnCivitaiText": "Auf Civitai anzeigen",
"viewOnHuggingFace": "Auf Hugging Face ansehen",
"viewOnHuggingFaceText": "Auf Hugging Face ansehen",
"viewCreatorProfile": "Ersteller-Profil anzeigen", "viewCreatorProfile": "Ersteller-Profil anzeigen",
"openFileLocation": "Dateispeicherort öffnen", "openFileLocation": "Dateispeicherort öffnen",
"sendToWorkflow": "An ComfyUI senden", "sendToWorkflow": "An ComfyUI senden",

View File

@@ -105,6 +105,7 @@
"removeFromFavorites": "Remove from favorites", "removeFromFavorites": "Remove from favorites",
"viewOnCivitai": "View on Civitai", "viewOnCivitai": "View on Civitai",
"notAvailableFromCivitai": "Not available from Civitai", "notAvailableFromCivitai": "Not available from Civitai",
"viewOnHuggingFace": "View on Hugging Face",
"sendToWorkflow": "Send to ComfyUI (Click: Append, Shift+Click: Replace)", "sendToWorkflow": "Send to ComfyUI (Click: Append, Shift+Click: Replace)",
"copyLoRASyntax": "Copy LoRA Syntax", "copyLoRASyntax": "Copy LoRA Syntax",
"checkpointNameCopied": "Checkpoint name copied", "checkpointNameCopied": "Checkpoint name copied",
@@ -1319,6 +1320,8 @@
"editVersionName": "Edit version name", "editVersionName": "Edit version name",
"viewOnCivitai": "View on Civitai", "viewOnCivitai": "View on Civitai",
"viewOnCivitaiText": "View on Civitai", "viewOnCivitaiText": "View on Civitai",
"viewOnHuggingFace": "View on Hugging Face",
"viewOnHuggingFaceText": "View on Hugging Face",
"viewCreatorProfile": "View Creator Profile", "viewCreatorProfile": "View Creator Profile",
"openFileLocation": "Open File Location", "openFileLocation": "Open File Location",
"sendToWorkflow": "Send to ComfyUI", "sendToWorkflow": "Send to ComfyUI",

View File

@@ -105,6 +105,7 @@
"removeFromFavorites": "Eliminar de favoritos", "removeFromFavorites": "Eliminar de favoritos",
"viewOnCivitai": "Ver en Civitai", "viewOnCivitai": "Ver en Civitai",
"notAvailableFromCivitai": "No disponible en Civitai", "notAvailableFromCivitai": "No disponible en Civitai",
"viewOnHuggingFace": "Ver en Hugging Face",
"sendToWorkflow": "Enviar a ComfyUI (Clic: Añadir, Shift+Clic: Reemplazar)", "sendToWorkflow": "Enviar a ComfyUI (Clic: Añadir, Shift+Clic: Reemplazar)",
"copyLoRASyntax": "Copiar sintaxis de LoRA", "copyLoRASyntax": "Copiar sintaxis de LoRA",
"checkpointNameCopied": "Nombre del checkpoint copiado", "checkpointNameCopied": "Nombre del checkpoint copiado",
@@ -1319,6 +1320,8 @@
"editVersionName": "Editar nombre de versión", "editVersionName": "Editar nombre de versión",
"viewOnCivitai": "Ver en Civitai", "viewOnCivitai": "Ver en Civitai",
"viewOnCivitaiText": "Ver en Civitai", "viewOnCivitaiText": "Ver en Civitai",
"viewOnHuggingFace": "Ver en Hugging Face",
"viewOnHuggingFaceText": "Ver en Hugging Face",
"viewCreatorProfile": "Ver perfil del creador", "viewCreatorProfile": "Ver perfil del creador",
"openFileLocation": "Abrir ubicación del archivo", "openFileLocation": "Abrir ubicación del archivo",
"sendToWorkflow": "Enviar a ComfyUI", "sendToWorkflow": "Enviar a ComfyUI",

View File

@@ -105,6 +105,7 @@
"removeFromFavorites": "Retirer des favoris", "removeFromFavorites": "Retirer des favoris",
"viewOnCivitai": "Voir sur Civitai", "viewOnCivitai": "Voir sur Civitai",
"notAvailableFromCivitai": "Non disponible sur Civitai", "notAvailableFromCivitai": "Non disponible sur Civitai",
"viewOnHuggingFace": "Voir sur Hugging Face",
"sendToWorkflow": "Envoyer vers ComfyUI (Clic: Ajouter, Maj+Clic: Remplacer)", "sendToWorkflow": "Envoyer vers ComfyUI (Clic: Ajouter, Maj+Clic: Remplacer)",
"copyLoRASyntax": "Copier la syntaxe LoRA", "copyLoRASyntax": "Copier la syntaxe LoRA",
"checkpointNameCopied": "Nom du checkpoint copié", "checkpointNameCopied": "Nom du checkpoint copié",
@@ -1319,6 +1320,8 @@
"editVersionName": "Modifier le nom de la version", "editVersionName": "Modifier le nom de la version",
"viewOnCivitai": "Voir sur Civitai", "viewOnCivitai": "Voir sur Civitai",
"viewOnCivitaiText": "Voir sur Civitai", "viewOnCivitaiText": "Voir sur Civitai",
"viewOnHuggingFace": "Voir sur Hugging Face",
"viewOnHuggingFaceText": "Voir sur Hugging Face",
"viewCreatorProfile": "Voir le profil du créateur", "viewCreatorProfile": "Voir le profil du créateur",
"openFileLocation": "Ouvrir l'emplacement du fichier", "openFileLocation": "Ouvrir l'emplacement du fichier",
"sendToWorkflow": "Envoyer vers ComfyUI", "sendToWorkflow": "Envoyer vers ComfyUI",

View File

@@ -105,6 +105,7 @@
"removeFromFavorites": "הסר מהמועדפים", "removeFromFavorites": "הסר מהמועדפים",
"viewOnCivitai": "הצג ב-Civitai", "viewOnCivitai": "הצג ב-Civitai",
"notAvailableFromCivitai": "לא זמין מ-Civitai", "notAvailableFromCivitai": "לא זמין מ-Civitai",
"viewOnHuggingFace": "צפייה ב-Hugging Face",
"sendToWorkflow": "שלח ל-ComfyUI (לחיצה: הוסף, Shift+לחיצה: החלף)", "sendToWorkflow": "שלח ל-ComfyUI (לחיצה: הוסף, Shift+לחיצה: החלף)",
"copyLoRASyntax": "העתק תחביר LoRA", "copyLoRASyntax": "העתק תחביר LoRA",
"checkpointNameCopied": "שם Checkpoint הועתק", "checkpointNameCopied": "שם Checkpoint הועתק",
@@ -1319,6 +1320,8 @@
"editVersionName": "ערוך שם גרסה", "editVersionName": "ערוך שם גרסה",
"viewOnCivitai": "הצג ב-Civitai", "viewOnCivitai": "הצג ב-Civitai",
"viewOnCivitaiText": "הצג ב-Civitai", "viewOnCivitaiText": "הצג ב-Civitai",
"viewOnHuggingFace": "צפייה ב-Hugging Face",
"viewOnHuggingFaceText": "צפייה ב-Hugging Face",
"viewCreatorProfile": "הצג פרופיל יוצר", "viewCreatorProfile": "הצג פרופיל יוצר",
"openFileLocation": "פתח מיקום קובץ", "openFileLocation": "פתח מיקום קובץ",
"sendToWorkflow": "שלח ל-ComfyUI", "sendToWorkflow": "שלח ל-ComfyUI",

View File

@@ -105,6 +105,7 @@
"removeFromFavorites": "お気に入りから削除", "removeFromFavorites": "お気に入りから削除",
"viewOnCivitai": "Civitaiで表示", "viewOnCivitai": "Civitaiで表示",
"notAvailableFromCivitai": "Civitaiでは利用できません", "notAvailableFromCivitai": "Civitaiでは利用できません",
"viewOnHuggingFace": "Hugging Face で見る",
"sendToWorkflow": "ComfyUIに送信クリック追加、Shift+クリック:置換)", "sendToWorkflow": "ComfyUIに送信クリック追加、Shift+クリック:置換)",
"copyLoRASyntax": "LoRA構文をコピー", "copyLoRASyntax": "LoRA構文をコピー",
"checkpointNameCopied": "checkpointの名前をコピーしました", "checkpointNameCopied": "checkpointの名前をコピーしました",
@@ -1319,6 +1320,8 @@
"editVersionName": "バージョン名を編集", "editVersionName": "バージョン名を編集",
"viewOnCivitai": "Civitaiで表示", "viewOnCivitai": "Civitaiで表示",
"viewOnCivitaiText": "Civitaiで表示", "viewOnCivitaiText": "Civitaiで表示",
"viewOnHuggingFace": "Hugging Face で見る",
"viewOnHuggingFaceText": "Hugging Face で見る",
"viewCreatorProfile": "作成者プロフィールを表示", "viewCreatorProfile": "作成者プロフィールを表示",
"openFileLocation": "ファイルの場所を開く", "openFileLocation": "ファイルの場所を開く",
"sendToWorkflow": "ComfyUI に送信", "sendToWorkflow": "ComfyUI に送信",

View File

@@ -105,6 +105,7 @@
"removeFromFavorites": "즐겨찾기에서 제거", "removeFromFavorites": "즐겨찾기에서 제거",
"viewOnCivitai": "Civitai에서 보기", "viewOnCivitai": "Civitai에서 보기",
"notAvailableFromCivitai": "Civitai에서 사용할 수 없음", "notAvailableFromCivitai": "Civitai에서 사용할 수 없음",
"viewOnHuggingFace": "Hugging Face에서 보기",
"sendToWorkflow": "ComfyUI로 전송 (클릭: 추가, Shift+클릭: 교체)", "sendToWorkflow": "ComfyUI로 전송 (클릭: 추가, Shift+클릭: 교체)",
"copyLoRASyntax": "LoRA 문법 복사", "copyLoRASyntax": "LoRA 문법 복사",
"checkpointNameCopied": "Checkpoint 이름 복사됨", "checkpointNameCopied": "Checkpoint 이름 복사됨",
@@ -1319,6 +1320,8 @@
"editVersionName": "버전명 편집", "editVersionName": "버전명 편집",
"viewOnCivitai": "Civitai에서 보기", "viewOnCivitai": "Civitai에서 보기",
"viewOnCivitaiText": "Civitai에서 보기", "viewOnCivitaiText": "Civitai에서 보기",
"viewOnHuggingFace": "Hugging Face에서 보기",
"viewOnHuggingFaceText": "Hugging Face에서 보기",
"viewCreatorProfile": "제작자 프로필 보기", "viewCreatorProfile": "제작자 프로필 보기",
"openFileLocation": "파일 위치 열기", "openFileLocation": "파일 위치 열기",
"sendToWorkflow": "ComfyUI로 보내기", "sendToWorkflow": "ComfyUI로 보내기",

View File

@@ -105,6 +105,7 @@
"removeFromFavorites": "Удалить из избранного", "removeFromFavorites": "Удалить из избранного",
"viewOnCivitai": "Посмотреть на Civitai", "viewOnCivitai": "Посмотреть на Civitai",
"notAvailableFromCivitai": "Недоступно на Civitai", "notAvailableFromCivitai": "Недоступно на Civitai",
"viewOnHuggingFace": "Открыть Hugging Face",
"sendToWorkflow": "Отправить в ComfyUI (Клик: Добавить, Shift+Клик: Заменить)", "sendToWorkflow": "Отправить в ComfyUI (Клик: Добавить, Shift+Клик: Заменить)",
"copyLoRASyntax": "Копировать синтаксис LoRA", "copyLoRASyntax": "Копировать синтаксис LoRA",
"checkpointNameCopied": "Имя checkpoint скопировано", "checkpointNameCopied": "Имя checkpoint скопировано",
@@ -1319,6 +1320,8 @@
"editVersionName": "Редактировать название версии", "editVersionName": "Редактировать название версии",
"viewOnCivitai": "Посмотреть на Civitai", "viewOnCivitai": "Посмотреть на Civitai",
"viewOnCivitaiText": "Посмотреть на Civitai", "viewOnCivitaiText": "Посмотреть на Civitai",
"viewOnHuggingFace": "Открыть Hugging Face",
"viewOnHuggingFaceText": "Открыть Hugging Face",
"viewCreatorProfile": "Посмотреть профиль создателя", "viewCreatorProfile": "Посмотреть профиль создателя",
"openFileLocation": "Открыть расположение файла", "openFileLocation": "Открыть расположение файла",
"sendToWorkflow": "Отправить в ComfyUI", "sendToWorkflow": "Отправить в ComfyUI",

View File

@@ -105,6 +105,7 @@
"removeFromFavorites": "从收藏移除", "removeFromFavorites": "从收藏移除",
"viewOnCivitai": "在 Civitai 查看", "viewOnCivitai": "在 Civitai 查看",
"notAvailableFromCivitai": "Civitai 上不可用", "notAvailableFromCivitai": "Civitai 上不可用",
"viewOnHuggingFace": "在 Hugging Face 查看",
"sendToWorkflow": "发送到 ComfyUI点击追加Shift+点击:替换)", "sendToWorkflow": "发送到 ComfyUI点击追加Shift+点击:替换)",
"copyLoRASyntax": "复制 LoRA 语法", "copyLoRASyntax": "复制 LoRA 语法",
"checkpointNameCopied": "检查点名称已复制", "checkpointNameCopied": "检查点名称已复制",
@@ -1319,6 +1320,8 @@
"editVersionName": "编辑版本名称", "editVersionName": "编辑版本名称",
"viewOnCivitai": "在 Civitai 查看", "viewOnCivitai": "在 Civitai 查看",
"viewOnCivitaiText": "在 Civitai 查看", "viewOnCivitaiText": "在 Civitai 查看",
"viewOnHuggingFace": "在 Hugging Face 查看",
"viewOnHuggingFaceText": "在 Hugging Face 查看",
"viewCreatorProfile": "查看创作者主页", "viewCreatorProfile": "查看创作者主页",
"openFileLocation": "打开文件位置", "openFileLocation": "打开文件位置",
"sendToWorkflow": "发送到 ComfyUI", "sendToWorkflow": "发送到 ComfyUI",

View File

@@ -105,6 +105,7 @@
"removeFromFavorites": "移除收藏", "removeFromFavorites": "移除收藏",
"viewOnCivitai": "在 Civitai 查看", "viewOnCivitai": "在 Civitai 查看",
"notAvailableFromCivitai": "Civitai 不提供", "notAvailableFromCivitai": "Civitai 不提供",
"viewOnHuggingFace": "在 Hugging Face 查看",
"sendToWorkflow": "傳送到 ComfyUI點擊附加Shift+點擊:取代)", "sendToWorkflow": "傳送到 ComfyUI點擊附加Shift+點擊:取代)",
"copyLoRASyntax": "複製 LoRA 語法", "copyLoRASyntax": "複製 LoRA 語法",
"checkpointNameCopied": "Checkpoint 名稱已複製", "checkpointNameCopied": "Checkpoint 名稱已複製",
@@ -1319,6 +1320,8 @@
"editVersionName": "編輯版本名稱", "editVersionName": "編輯版本名稱",
"viewOnCivitai": "在 Civitai 查看", "viewOnCivitai": "在 Civitai 查看",
"viewOnCivitaiText": "在 Civitai 查看", "viewOnCivitaiText": "在 Civitai 查看",
"viewOnHuggingFace": "在 Hugging Face 查看",
"viewOnHuggingFaceText": "在 Hugging Face 查看",
"viewCreatorProfile": "查看創作者個人檔案", "viewCreatorProfile": "查看創作者個人檔案",
"openFileLocation": "開啟檔案位置", "openFileLocation": "開啟檔案位置",
"sendToWorkflow": "傳送到 ComfyUI", "sendToWorkflow": "傳送到 ComfyUI",

View File

@@ -112,7 +112,7 @@ async def _save_hf_metadata(dest_path: str, repo: str, model_root: str) -> None:
# 2. Overlay HF-specific fields # 2. Overlay HF-specific fields
metadata._unknown_fields["hf_url"] = hf_url metadata._unknown_fields["hf_url"] = hf_url
metadata.from_civitai = True # leave default, don't interfere with CivitAI fetch metadata.from_civitai = False # HF models are not from CivitAI
# 3. Save metadata atomically # 3. Save metadata atomically
await MetadataManager.save_metadata(dest_path, metadata) await MetadataManager.save_metadata(dest_path, metadata)

View File

@@ -66,6 +66,7 @@ class CheckpointService(BaseModelService):
"civitai": self.filter_civitai_data(checkpoint_data.get("civitai", {}), minimal=True), "civitai": self.filter_civitai_data(checkpoint_data.get("civitai", {}), minimal=True),
"auto_tags": checkpoint_data.get("auto_tags") or extract_auto_tags(checkpoint_data), "auto_tags": checkpoint_data.get("auto_tags") or extract_auto_tags(checkpoint_data),
"version_count": checkpoint_data.get("version_count"), "version_count": checkpoint_data.get("version_count"),
"hf_url": checkpoint_data.get("hf_url", ""),
} }
def find_duplicate_hashes(self) -> Dict: def find_duplicate_hashes(self) -> Dict:

View File

@@ -66,6 +66,7 @@ class EmbeddingService(BaseModelService):
"civitai": self.filter_civitai_data(embedding_data.get("civitai", {}), minimal=True), "civitai": self.filter_civitai_data(embedding_data.get("civitai", {}), minimal=True),
"auto_tags": embedding_data.get("auto_tags") or extract_auto_tags(embedding_data), "auto_tags": embedding_data.get("auto_tags") or extract_auto_tags(embedding_data),
"version_count": embedding_data.get("version_count"), "version_count": embedding_data.get("version_count"),
"hf_url": embedding_data.get("hf_url", ""),
} }
def find_duplicate_hashes(self) -> Dict: def find_duplicate_hashes(self) -> Dict:

View File

@@ -78,6 +78,7 @@ class LoraService(BaseModelService):
), ),
"auto_tags": lora_data.get("auto_tags") or extract_auto_tags(lora_data), "auto_tags": lora_data.get("auto_tags") or extract_auto_tags(lora_data),
"version_count": lora_data.get("version_count"), "version_count": lora_data.get("version_count"),
"hf_url": lora_data.get("hf_url", ""),
} }
async def _apply_specific_filters(self, data: List[Dict], **kwargs) -> List[Dict]: async def _apply_specific_filters(self, data: List[Dict], **kwargs) -> List[Dict]:

View File

@@ -248,6 +248,7 @@ class ModelScanner:
'civitai': civitai_slim, 'civitai': civitai_slim,
'civitai_deleted': bool(get_value('civitai_deleted', False)), 'civitai_deleted': bool(get_value('civitai_deleted', False)),
'skip_metadata_refresh': bool(get_value('skip_metadata_refresh', False)), 'skip_metadata_refresh': bool(get_value('skip_metadata_refresh', False)),
'hf_url': get_value('hf_url', '') or '',
} }
license_source: Dict[str, Any] = {} license_source: Dict[str, Any] = {}

View File

@@ -57,6 +57,7 @@ class PersistentModelCache:
"db_checked", "db_checked",
"last_checked_at", "last_checked_at",
"hash_status", "hash_status",
"hf_url",
) )
_MODEL_UPDATE_COLUMNS: Tuple[str, ...] = _MODEL_COLUMNS[2:] _MODEL_UPDATE_COLUMNS: Tuple[str, ...] = _MODEL_COLUMNS[2:]
_instances: Dict[str, "PersistentModelCache"] = {} _instances: Dict[str, "PersistentModelCache"] = {}
@@ -188,6 +189,7 @@ class PersistentModelCache:
"skip_metadata_refresh": bool(row["skip_metadata_refresh"]), "skip_metadata_refresh": bool(row["skip_metadata_refresh"]),
"license_flags": int(license_value), "license_flags": int(license_value),
"hash_status": row["hash_status"] or "completed", "hash_status": row["hash_status"] or "completed",
"hf_url": row["hf_url"] or "",
} }
raw_data.append(item) raw_data.append(item)
@@ -452,6 +454,7 @@ class PersistentModelCache:
db_checked INTEGER, db_checked INTEGER,
last_checked_at REAL, last_checked_at REAL,
hash_status TEXT, hash_status TEXT,
hf_url TEXT DEFAULT '',
PRIMARY KEY (model_type, file_path) PRIMARY KEY (model_type, file_path)
); );
@@ -500,6 +503,7 @@ class PersistentModelCache:
# Persisting without explicit flags should assume CivitAI's documented defaults (0b111001 == 57). # Persisting without explicit flags should assume CivitAI's documented defaults (0b111001 == 57).
"license_flags": f"INTEGER DEFAULT {DEFAULT_LICENSE_FLAGS}", "license_flags": f"INTEGER DEFAULT {DEFAULT_LICENSE_FLAGS}",
"hash_status": "TEXT DEFAULT 'completed'", "hash_status": "TEXT DEFAULT 'completed'",
"hf_url": "TEXT DEFAULT ''",
} }
for column, definition in required_columns.items(): for column, definition in required_columns.items():
@@ -575,6 +579,7 @@ class PersistentModelCache:
1 if item.get("db_checked") else 0, 1 if item.get("db_checked") else 0,
float(item.get("last_checked_at") or 0.0), float(item.get("last_checked_at") or 0.0),
item.get("hash_status", "completed"), item.get("hash_status", "completed"),
item.get("hf_url") or "",
) )
def _insert_model_sql(self) -> str: def _insert_model_sql(self) -> str:

View File

@@ -1,4 +1,4 @@
import { showToast, openCivitai, copyToClipboard, copyLoraSyntax, sendLoraToWorkflow, sendEmbeddingToWorkflow, openExampleImagesFolder, buildLoraSyntax, sendModelPathToWorkflow } from '../../utils/uiHelpers.js'; import { showToast, openCivitai, openHuggingFace, copyToClipboard, copyLoraSyntax, sendLoraToWorkflow, sendEmbeddingToWorkflow, openExampleImagesFolder, buildLoraSyntax, sendModelPathToWorkflow } from '../../utils/uiHelpers.js';
import { state, getCurrentPageState } from '../../state/index.js'; import { state, getCurrentPageState } from '../../state/index.js';
import { showModelModal } from './ModelModal.js'; import { showModelModal } from './ModelModal.js';
import { toggleShowcase } from './showcase/ShowcaseView.js'; import { toggleShowcase } from './showcase/ShowcaseView.js';
@@ -66,6 +66,8 @@ function handleModelCardEvent_internal(event, modelType) {
event.stopPropagation(); event.stopPropagation();
if (card.dataset.from_civitai === 'true') { if (card.dataset.from_civitai === 'true') {
openCivitai(card.dataset.filepath); openCivitai(card.dataset.filepath);
} else if (card.dataset.hf_url) {
openHuggingFace(card.dataset.hf_url);
} }
return true; // Stop propagation return true; // Stop propagation
} }
@@ -313,6 +315,7 @@ async function showModelModalFromCard(card, modelType) {
modified: card.dataset.modified, modified: card.dataset.modified,
file_size: parseInt(card.dataset.file_size || '0'), file_size: parseInt(card.dataset.file_size || '0'),
from_civitai: card.dataset.from_civitai === 'true', from_civitai: card.dataset.from_civitai === 'true',
hf_url: card.dataset.hf_url || '',
base_model: card.dataset.base_model, base_model: card.dataset.base_model,
notes: card.dataset.notes || '', notes: card.dataset.notes || '',
favorite: card.dataset.favorite === 'true', favorite: card.dataset.favorite === 'true',
@@ -401,6 +404,7 @@ function showExampleAccessModal(card, modelType) {
modified: card.dataset.modified, modified: card.dataset.modified,
file_size: card.dataset.file_size, file_size: card.dataset.file_size,
from_civitai: card.dataset.from_civitai === 'true', from_civitai: card.dataset.from_civitai === 'true',
hf_url: card.dataset.hf_url || '',
base_model: card.dataset.base_model, base_model: card.dataset.base_model,
notes: card.dataset.notes, notes: card.dataset.notes,
favorite: card.dataset.favorite === 'true', favorite: card.dataset.favorite === 'true',
@@ -467,6 +471,7 @@ export function createModelCard(model, modelType) {
card.dataset.base_model = model.base_model || 'Unknown'; card.dataset.base_model = model.base_model || 'Unknown';
card.dataset.favorite = model.favorite ? 'true' : 'false'; card.dataset.favorite = model.favorite ? 'true' : 'false';
card.dataset.exclude = model.exclude ? 'true' : 'false'; card.dataset.exclude = model.exclude ? 'true' : 'false';
card.dataset.hf_url = model.hf_url || '';
const hasUpdateAvailable = Boolean(model.update_available); const hasUpdateAvailable = Boolean(model.update_available);
card.dataset.update_available = hasUpdateAvailable ? 'true' : 'false'; card.dataset.update_available = hasUpdateAvailable ? 'true' : 'false';
card.dataset.skip_metadata_refresh = model.skip_metadata_refresh ? 'true' : 'false'; card.dataset.skip_metadata_refresh = model.skip_metadata_refresh ? 'true' : 'false';
@@ -578,7 +583,10 @@ export function createModelCard(model, modelType) {
translate('modelCard.actions.addToFavorites', {}, 'Add to favorites'); translate('modelCard.actions.addToFavorites', {}, 'Add to favorites');
const globeTitle = model.from_civitai ? const globeTitle = model.from_civitai ?
translate('modelCard.actions.viewOnCivitai', {}, 'View on Civitai') : translate('modelCard.actions.viewOnCivitai', {}, 'View on Civitai') :
translate('modelCard.actions.notAvailableFromCivitai', {}, 'Not available from Civitai'); model.hf_url ?
translate('modelCard.actions.viewOnHuggingFace', {}, 'View on Hugging Face') :
translate('modelCard.actions.notAvailableFromCivitai', {}, 'Not available from Civitai');
const globeEnabled = model.from_civitai || !!model.hf_url;
let sendTitle; let sendTitle;
let copyTitle; let copyTitle;
if (modelType === MODEL_TYPES.LORA) { if (modelType === MODEL_TYPES.LORA) {
@@ -603,7 +611,7 @@ export function createModelCard(model, modelType) {
</i> </i>
<i class="fas fa-globe" <i class="fas fa-globe"
title="${globeTitle}" title="${globeTitle}"
${!model.from_civitai ? 'style="opacity: 0.5; cursor: not-allowed"' : ''}> ${!globeEnabled ? 'style="opacity: 0.5; cursor: not-allowed"' : ''}>
</i> </i>
<i class="fas fa-paper-plane" <i class="fas fa-paper-plane"
title="${sendTitle}"> title="${sendTitle}">

View File

@@ -360,6 +360,11 @@ export async function showModelModal(model, modelType) {
const viewOnCivitaiAction = modelWithFullData.from_civitai ? ` const viewOnCivitaiAction = modelWithFullData.from_civitai ? `
<div class="civitai-view" title="${translate('modals.model.actions.viewOnCivitai', {}, 'View on Civitai')}" data-action="view-civitai" data-filepath="${escapedFilePathAttr}"> <div class="civitai-view" title="${translate('modals.model.actions.viewOnCivitai', {}, 'View on Civitai')}" data-action="view-civitai" data-filepath="${escapedFilePathAttr}">
<i class="fas fa-globe"></i> ${translate('modals.model.actions.viewOnCivitaiText', {}, 'View on Civitai')} <i class="fas fa-globe"></i> ${translate('modals.model.actions.viewOnCivitaiText', {}, 'View on Civitai')}
</div>`.trim() : '';
const escapedHfUrl = modelWithFullData.hf_url ? escapeAttribute(modelWithFullData.hf_url) : '';
const viewOnHuggingFaceAction = escapedHfUrl ? `
<div class="civitai-view" title="${translate('modals.model.actions.viewOnHuggingFace', {}, 'View on Hugging Face')}" data-action="view-huggingface" data-hf-url="${escapedHfUrl}">
<i class="fas fa-globe"></i> ${translate('modals.model.actions.viewOnHuggingFaceText', {}, 'View on Hugging Face')}
</div>`.trim() : ''; </div>`.trim() : '';
const creatorInfoAction = modelWithFullData.civitai?.creator ? ` const creatorInfoAction = modelWithFullData.civitai?.creator ? `
<div class="creator-info" data-username="${modelWithFullData.civitai.creator.username}" data-action="view-creator" title="${translate('modals.model.actions.viewCreatorProfile', {}, 'View Creator Profile')}"> <div class="creator-info" data-username="${modelWithFullData.civitai.creator.username}" data-action="view-creator" title="${translate('modals.model.actions.viewCreatorProfile', {}, 'View Creator Profile')}">
@@ -377,6 +382,9 @@ export async function showModelModal(model, modelType) {
if (viewOnCivitaiAction) { if (viewOnCivitaiAction) {
creatorActionItems.push(indentMarkup(viewOnCivitaiAction, 24)); creatorActionItems.push(indentMarkup(viewOnCivitaiAction, 24));
} }
if (viewOnHuggingFaceAction) {
creatorActionItems.push(indentMarkup(viewOnHuggingFaceAction, 24));
}
if (creatorInfoAction) { if (creatorInfoAction) {
creatorActionItems.push(indentMarkup(creatorInfoAction, 24)); creatorActionItems.push(indentMarkup(creatorInfoAction, 24));
} }
@@ -869,6 +877,11 @@ function setupEventHandlers(filePath, modelType) {
case 'view-civitai': case 'view-civitai':
openCivitai(target.dataset.filepath); openCivitai(target.dataset.filepath);
break; break;
case 'view-huggingface':
if (target.dataset.hfUrl) {
window.open(target.dataset.hfUrl, '_blank', 'noopener,noreferrer');
}
break;
case 'view-creator': case 'view-creator':
const username = target.dataset.username; const username = target.dataset.username;
if (username) { if (username) {

View File

@@ -319,6 +319,15 @@ export function openCivitai(filePath) {
openCivitaiByMetadata(civitaiId, versionId, modelName); openCivitaiByMetadata(civitaiId, versionId, modelName);
} }
/**
* Open a Hugging Face model page in a new tab
* @param {string} hfUrl - The Hugging Face URL
*/
export function openHuggingFace(hfUrl) {
if (!hfUrl) return;
window.open(hfUrl, '_blank', 'noopener,noreferrer');
}
/** /**
* Dynamically positions the search options panel and filter panel * Dynamically positions the search options panel and filter panel
* based on the current layout and folder tags container height * based on the current layout and folder tags container height