feat(example-images): add remote open mode support

This commit is contained in:
Will Miao
2026-04-27 14:05:21 +08:00
parent cc147a1795
commit ffe0670a27
20 changed files with 621 additions and 21 deletions

View File

@@ -538,6 +538,20 @@
"downloadLocationHelp": "Geben Sie den Ordnerpfad ein, wo Beispielbilder von Civitai gespeichert werden", "downloadLocationHelp": "Geben Sie den Ordnerpfad ein, wo Beispielbilder von Civitai gespeichert werden",
"autoDownload": "Beispielbilder automatisch herunterladen", "autoDownload": "Beispielbilder automatisch herunterladen",
"autoDownloadHelp": "Beispielbilder automatisch für Modelle herunterladen, die keine haben (erfordert gesetzten Download-Speicherort)", "autoDownloadHelp": "Beispielbilder automatisch für Modelle herunterladen, die keine haben (erfordert gesetzten Download-Speicherort)",
"openMode": "Aktion für Beispielbilder öffnen",
"openModeHelp": "Wählen Sie, ob die Aktion auf dem Server geöffnet, ein zugeordneter lokaler Pfad kopiert oder eine benutzerdefinierte URI gestartet werden soll.",
"openModeOptions": {
"system": "Auf Server öffnen",
"clipboard": "Lokalen Pfad kopieren",
"uriTemplate": "Benutzerdefinierte URI öffnen"
},
"localRoot": "Lokales Stammverzeichnis für Beispielbilder",
"localRootHelp": "Optionales lokales oder eingebundenes Stammverzeichnis, das das Beispielbild-Verzeichnis des Servers widerspiegelt. Wenn leer, wird der Serverpfad wiederverwendet.",
"localRootPlaceholder": "Beispiel: /Volumes/ComfyUI/example_images",
"uriTemplate": "URI-Vorlage öffnen",
"uriTemplateHelp": "Verwenden Sie einen benutzerdefinierten Deeplink wie eine Datei-URI oder einen Shortcuts-Link.",
"uriTemplatePlaceholder": "Beispiel: shortcuts://run-shortcut?name=Open%20Finder&input=text&text={{encoded_local_path}}",
"uriTemplatePlaceholders": "Verfügbare Platzhalter: {{local_path}}, {{encoded_local_path}}, {{relative_path}}, {{encoded_relative_path}}, {{file_uri}}, {{encoded_file_uri}}",
"optimizeImages": "Heruntergeladene Bilder optimieren", "optimizeImages": "Heruntergeladene Bilder optimieren",
"optimizeImagesHelp": "Beispielbilder optimieren, um Dateigröße zu reduzieren und Ladegeschwindigkeit zu verbessern (Metadaten bleiben erhalten)", "optimizeImagesHelp": "Beispielbilder optimieren, um Dateigröße zu reduzieren und Ladegeschwindigkeit zu verbessern (Metadaten bleiben erhalten)",
"download": "Herunterladen", "download": "Herunterladen",
@@ -1442,6 +1456,10 @@
"opened": "Beispielbilder-Ordner geöffnet", "opened": "Beispielbilder-Ordner geöffnet",
"openingFolder": "Beispielbilder-Ordner wird geöffnet", "openingFolder": "Beispielbilder-Ordner wird geöffnet",
"failedToOpen": "Fehler beim Öffnen des Beispielbilder-Ordners", "failedToOpen": "Fehler beim Öffnen des Beispielbilder-Ordners",
"copiedPath": "Pfad in Zwischenablage kopiert: {{path}}",
"clipboardFallback": "Pfad: {{path}}",
"copiedUri": "Link in Zwischenablage kopiert: {{uri}}",
"uriClipboardFallback": "Link: {{uri}}",
"setupRequired": "Beispielbilder-Speicher", "setupRequired": "Beispielbilder-Speicher",
"setupDescription": "Um benutzerdefinierte Beispielbilder hinzuzufügen, müssen Sie zuerst einen Download-Speicherort festlegen.", "setupDescription": "Um benutzerdefinierte Beispielbilder hinzuzufügen, müssen Sie zuerst einen Download-Speicherort festlegen.",
"setupUsage": "Dieser Pfad wird sowohl für heruntergeladene als auch für benutzerdefinierte Beispielbilder verwendet.", "setupUsage": "Dieser Pfad wird sowohl für heruntergeladene als auch für benutzerdefinierte Beispielbilder verwendet.",

View File

@@ -538,6 +538,20 @@
"downloadLocationHelp": "Enter the folder path where example images from Civitai will be saved", "downloadLocationHelp": "Enter the folder path where example images from Civitai will be saved",
"autoDownload": "Auto Download Example Images", "autoDownload": "Auto Download Example Images",
"autoDownloadHelp": "Automatically download example images for models that don't have them (requires download location to be set)", "autoDownloadHelp": "Automatically download example images for models that don't have them (requires download location to be set)",
"openMode": "Open Example Images Action",
"openModeHelp": "Choose whether the action opens on the server, copies a mapped local path, or launches a custom URI.",
"openModeOptions": {
"system": "Open on server",
"clipboard": "Copy local path",
"uriTemplate": "Open custom URI"
},
"localRoot": "Local Example Images Root",
"localRootHelp": "Optional local or mounted root that mirrors the server example images directory. If blank, the server path is reused.",
"localRootPlaceholder": "Example: /Volumes/ComfyUI/example_images",
"uriTemplate": "Open URI Template",
"uriTemplateHelp": "Use a custom deep link such as a file URI or a Shortcuts link.",
"uriTemplatePlaceholder": "Example: shortcuts://run-shortcut?name=Open%20Finder&input=text&text={{encoded_local_path}}",
"uriTemplatePlaceholders": "Available placeholders: {{local_path}}, {{encoded_local_path}}, {{relative_path}}, {{encoded_relative_path}}, {{file_uri}}, {{encoded_file_uri}}",
"optimizeImages": "Optimize Downloaded Images", "optimizeImages": "Optimize Downloaded Images",
"optimizeImagesHelp": "Optimize example images to reduce file size and improve loading speed (metadata will be preserved)", "optimizeImagesHelp": "Optimize example images to reduce file size and improve loading speed (metadata will be preserved)",
"download": "Download", "download": "Download",
@@ -1442,6 +1456,10 @@
"opened": "Example images folder opened", "opened": "Example images folder opened",
"openingFolder": "Opening example images folder", "openingFolder": "Opening example images folder",
"failedToOpen": "Failed to open example images folder", "failedToOpen": "Failed to open example images folder",
"copiedPath": "Path copied to clipboard: {{path}}",
"clipboardFallback": "Path: {{path}}",
"copiedUri": "Link copied to clipboard: {{uri}}",
"uriClipboardFallback": "Link: {{uri}}",
"setupRequired": "Example Images Storage", "setupRequired": "Example Images Storage",
"setupDescription": "To add custom example images, you need to set a download location first.", "setupDescription": "To add custom example images, you need to set a download location first.",
"setupUsage": "This path is used for both downloaded and custom example images.", "setupUsage": "This path is used for both downloaded and custom example images.",

View File

@@ -538,6 +538,20 @@
"downloadLocationHelp": "Introduce la ruta de la carpeta donde se guardarán las imágenes de ejemplo de Civitai", "downloadLocationHelp": "Introduce la ruta de la carpeta donde se guardarán las imágenes de ejemplo de Civitai",
"autoDownload": "Descargar automáticamente imágenes de ejemplo", "autoDownload": "Descargar automáticamente imágenes de ejemplo",
"autoDownloadHelp": "Descargar automáticamente imágenes de ejemplo para modelos que no las tengan (requiere que se establezca la ubicación de descarga)", "autoDownloadHelp": "Descargar automáticamente imágenes de ejemplo para modelos que no las tengan (requiere que se establezca la ubicación de descarga)",
"openMode": "Acción al abrir imágenes de ejemplo",
"openModeHelp": "Elige si la acción se abre en el servidor, copia una ruta local asignada o lanza una URI personalizada.",
"openModeOptions": {
"system": "Abrir en el servidor",
"clipboard": "Copiar ruta local",
"uriTemplate": "Abrir URI personalizada"
},
"localRoot": "Raíz local de imágenes de ejemplo",
"localRootHelp": "Raíz local u montada opcional que refleja el directorio de imágenes de ejemplo del servidor. Si se deja en blanco, se reutiliza la ruta del servidor.",
"localRootPlaceholder": "Ejemplo: /Volumes/ComfyUI/example_images",
"uriTemplate": "Abrir plantilla de URI",
"uriTemplateHelp": "Usa un enlace profundo personalizado, como un URI de archivo o un enlace de Shortcuts.",
"uriTemplatePlaceholder": "Ejemplo: shortcuts://run-shortcut?name=Open%20Finder&input=text&text={{encoded_local_path}}",
"uriTemplatePlaceholders": "Marcadores disponibles: {{local_path}}, {{encoded_local_path}}, {{relative_path}}, {{encoded_relative_path}}, {{file_uri}}, {{encoded_file_uri}}",
"optimizeImages": "Optimizar imágenes descargadas", "optimizeImages": "Optimizar imágenes descargadas",
"optimizeImagesHelp": "Optimizar imágenes de ejemplo para reducir el tamaño del archivo y mejorar la velocidad de carga (se preservarán los metadatos)", "optimizeImagesHelp": "Optimizar imágenes de ejemplo para reducir el tamaño del archivo y mejorar la velocidad de carga (se preservarán los metadatos)",
"download": "Descargar", "download": "Descargar",
@@ -1442,6 +1456,10 @@
"opened": "Carpeta de imágenes de ejemplo abierta", "opened": "Carpeta de imágenes de ejemplo abierta",
"openingFolder": "Abriendo carpeta de imágenes de ejemplo", "openingFolder": "Abriendo carpeta de imágenes de ejemplo",
"failedToOpen": "Error al abrir carpeta de imágenes de ejemplo", "failedToOpen": "Error al abrir carpeta de imágenes de ejemplo",
"copiedPath": "Ruta copiada al portapapeles: {{path}}",
"clipboardFallback": "Ruta: {{path}}",
"copiedUri": "Enlace copiado al portapapeles: {{uri}}",
"uriClipboardFallback": "Enlace: {{uri}}",
"setupRequired": "Almacenamiento de imágenes de ejemplo", "setupRequired": "Almacenamiento de imágenes de ejemplo",
"setupDescription": "Para agregar imágenes de ejemplo personalizadas, primero necesita establecer una ubicación de descarga.", "setupDescription": "Para agregar imágenes de ejemplo personalizadas, primero necesita establecer una ubicación de descarga.",
"setupUsage": "Esta ruta se utiliza tanto para imágenes de ejemplo descargadas como personalizadas.", "setupUsage": "Esta ruta se utiliza tanto para imágenes de ejemplo descargadas como personalizadas.",

View File

@@ -538,6 +538,20 @@
"downloadLocationHelp": "Entrez le chemin du dossier où les images d'exemple de Civitai seront sauvegardées", "downloadLocationHelp": "Entrez le chemin du dossier où les images d'exemple de Civitai seront sauvegardées",
"autoDownload": "Téléchargement automatique des images d'exemple", "autoDownload": "Téléchargement automatique des images d'exemple",
"autoDownloadHelp": "Télécharger automatiquement les images d'exemple pour les modèles qui n'en ont pas (nécessite que l'emplacement de téléchargement soit défini)", "autoDownloadHelp": "Télécharger automatiquement les images d'exemple pour les modèles qui n'en ont pas (nécessite que l'emplacement de téléchargement soit défini)",
"openMode": "Action douverture des images dexemple",
"openModeHelp": "Choisissez si laction souvre sur le serveur, copie un chemin local mappé ou lance une URI personnalisée.",
"openModeOptions": {
"system": "Ouvrir sur le serveur",
"clipboard": "Copier le chemin local",
"uriTemplate": "Ouvrir une URI personnalisée"
},
"localRoot": "Racine locale des images dexemple",
"localRootHelp": "Racine locale ou montée facultative qui reflète le répertoire des images dexemple du serveur. Si vide, le chemin du serveur est réutilisé.",
"localRootPlaceholder": "Exemple : /Volumes/ComfyUI/example_images",
"uriTemplate": "Ouvrir le modèle dURI",
"uriTemplateHelp": "Utilisez un lien profond personnalisé, tel quune URI de fichier ou un lien Shortcuts.",
"uriTemplatePlaceholder": "Exemple : shortcuts://run-shortcut?name=Open%20Finder&input=text&text={{encoded_local_path}}",
"uriTemplatePlaceholders": "Paramètres disponibles : {{local_path}}, {{encoded_local_path}}, {{relative_path}}, {{encoded_relative_path}}, {{file_uri}}, {{encoded_file_uri}}",
"optimizeImages": "Optimiser les images téléchargées", "optimizeImages": "Optimiser les images téléchargées",
"optimizeImagesHelp": "Optimiser les images d'exemple pour réduire la taille du fichier et améliorer la vitesse de chargement (les métadonnées seront préservées)", "optimizeImagesHelp": "Optimiser les images d'exemple pour réduire la taille du fichier et améliorer la vitesse de chargement (les métadonnées seront préservées)",
"download": "Télécharger", "download": "Télécharger",
@@ -1442,6 +1456,10 @@
"opened": "Dossier d'images d'exemple ouvert", "opened": "Dossier d'images d'exemple ouvert",
"openingFolder": "Ouverture du dossier d'images d'exemple", "openingFolder": "Ouverture du dossier d'images d'exemple",
"failedToOpen": "Échec de l'ouverture du dossier d'images d'exemple", "failedToOpen": "Échec de l'ouverture du dossier d'images d'exemple",
"copiedPath": "Chemin copié dans le presse-papiers : {{path}}",
"clipboardFallback": "Chemin : {{path}}",
"copiedUri": "Lien copié dans le presse-papiers : {{uri}}",
"uriClipboardFallback": "Lien : {{uri}}",
"setupRequired": "Stockage d'images d'exemple", "setupRequired": "Stockage d'images d'exemple",
"setupDescription": "Pour ajouter des images d'exemple personnalisées, vous devez d'abord définir un emplacement de téléchargement.", "setupDescription": "Pour ajouter des images d'exemple personnalisées, vous devez d'abord définir un emplacement de téléchargement.",
"setupUsage": "Ce chemin est utilisé pour les images d'exemple téléchargées et personnalisées.", "setupUsage": "Ce chemin est utilisé pour les images d'exemple téléchargées et personnalisées.",

View File

@@ -538,6 +538,20 @@
"downloadLocationHelp": "הזן את נתיב התיקייה שבו יישמרו תמונות דוגמה מ-Civitai", "downloadLocationHelp": "הזן את נתיב התיקייה שבו יישמרו תמונות דוגמה מ-Civitai",
"autoDownload": "הורדה אוטומטית של תמונות דוגמה", "autoDownload": "הורדה אוטומטית של תמונות דוגמה",
"autoDownloadHelp": "הורד אוטומטית תמונות דוגמה למודלים שאין להם (דורש הגדרת מיקום הורדה)", "autoDownloadHelp": "הורד אוטומטית תמונות דוגמה למודלים שאין להם (דורש הגדרת מיקום הורדה)",
"openMode": "פעולת פתיחת תמונות דוגמה",
"openModeHelp": "בחר אם הפעולה תיפתח בשרת, תעתיק נתיב מקומי ממופה או תפעיל URI מותאם אישית.",
"openModeOptions": {
"system": "פתח בשרת",
"clipboard": "העתק נתיב מקומי",
"uriTemplate": "פתח URI מותאם אישית"
},
"localRoot": "שורש מקומי לתמונות דוגמה",
"localRootHelp": "שורש מקומי או ממופה אופציונלי שמשקף את תיקיית תמונות הדוגמה בשרת. אם השדה ריק, ייעשה שימוש חוזר בנתיב השרת.",
"localRootPlaceholder": "דוגמה: /Volumes/ComfyUI/example_images",
"uriTemplate": "תבנית URI לפתיחה",
"uriTemplateHelp": "השתמש בקישור עומק מותאם אישית כמו URI של קובץ או קישור Shortcuts.",
"uriTemplatePlaceholder": "דוגמה: shortcuts://run-shortcut?name=Open%20Finder&input=text&text={{encoded_local_path}}",
"uriTemplatePlaceholders": "מצייני מקום זמינים: {{local_path}}, {{encoded_local_path}}, {{relative_path}}, {{encoded_relative_path}}, {{file_uri}}, {{encoded_file_uri}}",
"optimizeImages": "מטב תמונות שהורדו", "optimizeImages": "מטב תמונות שהורדו",
"optimizeImagesHelp": "מטב תמונות דוגמה כדי להקטין את גודל הקובץ ולשפר את מהירות הטעינה (מטא-דאטה תישמר)", "optimizeImagesHelp": "מטב תמונות דוגמה כדי להקטין את גודל הקובץ ולשפר את מהירות הטעינה (מטא-דאטה תישמר)",
"download": "הורד", "download": "הורד",
@@ -1442,6 +1456,10 @@
"opened": "תיקיית תמונות הדוגמה נפתחה", "opened": "תיקיית תמונות הדוגמה נפתחה",
"openingFolder": "פותח תיקיית תמונות דוגמה", "openingFolder": "פותח תיקיית תמונות דוגמה",
"failedToOpen": "פתיחת תיקיית תמונות הדוגמה נכשלה", "failedToOpen": "פתיחת תיקיית תמונות הדוגמה נכשלה",
"copiedPath": "הנתיב הועתק ללוח: {{path}}",
"clipboardFallback": "נתיב: {{path}}",
"copiedUri": "הקישור הועתק ללוח: {{uri}}",
"uriClipboardFallback": "קישור: {{uri}}",
"setupRequired": "אחסון תמונות דוגמה", "setupRequired": "אחסון תמונות דוגמה",
"setupDescription": "כדי להוסיף תמונות דוגמה מותאמות אישית, עליך קודם להגדיר מיקום הורדה.", "setupDescription": "כדי להוסיף תמונות דוגמה מותאמות אישית, עליך קודם להגדיר מיקום הורדה.",
"setupUsage": "נתיב זה משמש הן עבור תמונות דוגמה שהורדו והן עבור תמונות מותאמות אישית.", "setupUsage": "נתיב זה משמש הן עבור תמונות דוגמה שהורדו והן עבור תמונות מותאמות אישית.",

View File

@@ -538,6 +538,20 @@
"downloadLocationHelp": "Civitaiからの例画像を保存するフォルダパスを入力してください", "downloadLocationHelp": "Civitaiからの例画像を保存するフォルダパスを入力してください",
"autoDownload": "例画像の自動ダウンロード", "autoDownload": "例画像の自動ダウンロード",
"autoDownloadHelp": "例画像がないモデルの例画像を自動的にダウンロードします(ダウンロード場所の設定が必要)", "autoDownloadHelp": "例画像がないモデルの例画像を自動的にダウンロードします(ダウンロード場所の設定が必要)",
"openMode": "サンプル画像を開く動作",
"openModeHelp": "サーバー上で開くか、対応するローカルパスをコピーするか、カスタム URI を起動するかを選択します。",
"openModeOptions": {
"system": "サーバー上で開く",
"clipboard": "ローカルパスをコピー",
"uriTemplate": "カスタム URI を開く"
},
"localRoot": "ローカルのサンプル画像ルート",
"localRootHelp": "サーバーのサンプル画像ディレクトリを反映する任意のローカルまたはマウント済みルートです。空欄の場合はサーバーのパスを再利用します。",
"localRootPlaceholder": "例: /Volumes/ComfyUI/example_images",
"uriTemplate": "URI テンプレートを開く",
"uriTemplateHelp": "ファイル URI や Shortcuts リンクなどのカスタムディープリンクを使用します。",
"uriTemplatePlaceholder": "例: shortcuts://run-shortcut?name=Open%20Finder&input=text&text={{encoded_local_path}}",
"uriTemplatePlaceholders": "使用可能なプレースホルダー: {{local_path}}, {{encoded_local_path}}, {{relative_path}}, {{encoded_relative_path}}, {{file_uri}}, {{encoded_file_uri}}",
"optimizeImages": "ダウンロード画像の最適化", "optimizeImages": "ダウンロード画像の最適化",
"optimizeImagesHelp": "例画像を最適化してファイルサイズを縮小し、読み込み速度を向上させます(メタデータは保持されます)", "optimizeImagesHelp": "例画像を最適化してファイルサイズを縮小し、読み込み速度を向上させます(メタデータは保持されます)",
"download": "ダウンロード", "download": "ダウンロード",
@@ -1442,6 +1456,10 @@
"opened": "例画像フォルダが開かれました", "opened": "例画像フォルダが開かれました",
"openingFolder": "例画像フォルダを開いています", "openingFolder": "例画像フォルダを開いています",
"failedToOpen": "例画像フォルダを開くのに失敗しました", "failedToOpen": "例画像フォルダを開くのに失敗しました",
"copiedPath": "パスをクリップボードにコピーしました: {{path}}",
"clipboardFallback": "パス: {{path}}",
"copiedUri": "リンクをクリップボードにコピーしました: {{uri}}",
"uriClipboardFallback": "リンク: {{uri}}",
"setupRequired": "例画像ストレージ", "setupRequired": "例画像ストレージ",
"setupDescription": "カスタム例画像を追加するには、まずダウンロード場所を設定する必要があります。", "setupDescription": "カスタム例画像を追加するには、まずダウンロード場所を設定する必要があります。",
"setupUsage": "このパスは、ダウンロードした例画像とカスタム画像の両方に使用されます。", "setupUsage": "このパスは、ダウンロードした例画像とカスタム画像の両方に使用されます。",

View File

@@ -538,6 +538,20 @@
"downloadLocationHelp": "Civitai의 예시 이미지가 저장될 폴더 경로를 입력하세요", "downloadLocationHelp": "Civitai의 예시 이미지가 저장될 폴더 경로를 입력하세요",
"autoDownload": "예시 이미지 자동 다운로드", "autoDownload": "예시 이미지 자동 다운로드",
"autoDownloadHelp": "예시 이미지가 없는 모델의 예시 이미지를 자동으로 다운로드합니다 (다운로드 위치 설정 필요)", "autoDownloadHelp": "예시 이미지가 없는 모델의 예시 이미지를 자동으로 다운로드합니다 (다운로드 위치 설정 필요)",
"openMode": "예시 이미지 열기 동작",
"openModeHelp": "서버에서 열지, 매핑된 로컬 경로를 복사할지, 사용자 지정 URI를 실행할지 선택합니다.",
"openModeOptions": {
"system": "서버에서 열기",
"clipboard": "로컬 경로 복사",
"uriTemplate": "사용자 지정 URI 열기"
},
"localRoot": "로컬 예시 이미지 루트",
"localRootHelp": "서버 예시 이미지 디렉터리를 반영하는 선택적 로컬 또는 마운트된 루트입니다. 비워 두면 서버 경로를 재사용합니다.",
"localRootPlaceholder": "예: /Volumes/ComfyUI/example_images",
"uriTemplate": "URI 템플릿 열기",
"uriTemplateHelp": "파일 URI 또는 Shortcuts 링크 같은 사용자 지정 딥링크를 사용합니다.",
"uriTemplatePlaceholder": "예: shortcuts://run-shortcut?name=Open%20Finder&input=text&text={{encoded_local_path}}",
"uriTemplatePlaceholders": "사용 가능한 플레이스홀더: {{local_path}}, {{encoded_local_path}}, {{relative_path}}, {{encoded_relative_path}}, {{file_uri}}, {{encoded_file_uri}}",
"optimizeImages": "다운로드된 이미지 최적화", "optimizeImages": "다운로드된 이미지 최적화",
"optimizeImagesHelp": "파일 크기를 줄이고 로딩 속도를 향상시키기 위해 예시 이미지를 최적화합니다 (메타데이터는 보존됨)", "optimizeImagesHelp": "파일 크기를 줄이고 로딩 속도를 향상시키기 위해 예시 이미지를 최적화합니다 (메타데이터는 보존됨)",
"download": "다운로드", "download": "다운로드",
@@ -1442,6 +1456,10 @@
"opened": "예시 이미지 폴더가 열렸습니다", "opened": "예시 이미지 폴더가 열렸습니다",
"openingFolder": "예시 이미지 폴더를 여는 중", "openingFolder": "예시 이미지 폴더를 여는 중",
"failedToOpen": "예시 이미지 폴더 열기 실패", "failedToOpen": "예시 이미지 폴더 열기 실패",
"copiedPath": "경로를 클립보드에 복사했습니다: {{path}}",
"clipboardFallback": "경로: {{path}}",
"copiedUri": "링크를 클립보드에 복사했습니다: {{uri}}",
"uriClipboardFallback": "링크: {{uri}}",
"setupRequired": "예시 이미지 저장소", "setupRequired": "예시 이미지 저장소",
"setupDescription": "사용자 지정 예시 이미지를 추가하려면 먼저 다운로드 위치를 설정해야 합니다.", "setupDescription": "사용자 지정 예시 이미지를 추가하려면 먼저 다운로드 위치를 설정해야 합니다.",
"setupUsage": "이 경로는 다운로드한 예시 이미지와 사용자 지정 이미지 모두에 사용됩니다.", "setupUsage": "이 경로는 다운로드한 예시 이미지와 사용자 지정 이미지 모두에 사용됩니다.",

View File

@@ -538,6 +538,20 @@
"downloadLocationHelp": "Введите путь к папке, где будут сохраняться примеры изображений с Civitai", "downloadLocationHelp": "Введите путь к папке, где будут сохраняться примеры изображений с Civitai",
"autoDownload": "Автозагрузка примеров изображений", "autoDownload": "Автозагрузка примеров изображений",
"autoDownloadHelp": "Автоматически загружать примеры изображений для моделей, у которых их нет (требует настройки места загрузки)", "autoDownloadHelp": "Автоматически загружать примеры изображений для моделей, у которых их нет (требует настройки места загрузки)",
"openMode": "Действие открытия примеров изображений",
"openModeHelp": "Выберите, будет ли действие открывать папку на сервере, копировать сопоставленный локальный путь или запускать пользовательский URI.",
"openModeOptions": {
"system": "Открыть на сервере",
"clipboard": "Скопировать локальный путь",
"uriTemplate": "Открыть пользовательский URI"
},
"localRoot": "Локальный корень примеров изображений",
"localRootHelp": "Необязательный локальный или смонтированный корневой путь, отражающий каталог примеров изображений на сервере. Если оставить пустым, будет использован путь сервера.",
"localRootPlaceholder": "Пример: /Volumes/ComfyUI/example_images",
"uriTemplate": "Шаблон URI для открытия",
"uriTemplateHelp": "Используйте пользовательскую deep link-ссылку, например file URI или ссылку Shortcuts.",
"uriTemplatePlaceholder": "Пример: shortcuts://run-shortcut?name=Open%20Finder&input=text&text={{encoded_local_path}}",
"uriTemplatePlaceholders": "Доступные плейсхолдеры: {{local_path}}, {{encoded_local_path}}, {{relative_path}}, {{encoded_relative_path}}, {{file_uri}}, {{encoded_file_uri}}",
"optimizeImages": "Оптимизировать загруженные изображения", "optimizeImages": "Оптимизировать загруженные изображения",
"optimizeImagesHelp": "Оптимизировать примеры изображений для уменьшения размера файла и улучшения скорости загрузки (метаданные будут сохранены)", "optimizeImagesHelp": "Оптимизировать примеры изображений для уменьшения размера файла и улучшения скорости загрузки (метаданные будут сохранены)",
"download": "Загрузить", "download": "Загрузить",
@@ -1442,6 +1456,10 @@
"opened": "Папка с примерами изображений открыта", "opened": "Папка с примерами изображений открыта",
"openingFolder": "Открытие папки с примерами изображений", "openingFolder": "Открытие папки с примерами изображений",
"failedToOpen": "Не удалось открыть папку с примерами изображений", "failedToOpen": "Не удалось открыть папку с примерами изображений",
"copiedPath": "Путь скопирован в буфер обмена: {{path}}",
"clipboardFallback": "Путь: {{path}}",
"copiedUri": "Ссылка скопирована в буфер обмена: {{uri}}",
"uriClipboardFallback": "Ссылка: {{uri}}",
"setupRequired": "Хранилище примеров изображений", "setupRequired": "Хранилище примеров изображений",
"setupDescription": "Чтобы добавить собственные примеры изображений, сначала нужно установить место загрузки.", "setupDescription": "Чтобы добавить собственные примеры изображений, сначала нужно установить место загрузки.",
"setupUsage": "Этот путь используется как для загруженных, так и для пользовательских примеров изображений.", "setupUsage": "Этот путь используется как для загруженных, так и для пользовательских примеров изображений.",

View File

@@ -538,6 +538,20 @@
"downloadLocationHelp": "输入保存从 Civitai 下载的示例图片的文件夹路径", "downloadLocationHelp": "输入保存从 Civitai 下载的示例图片的文件夹路径",
"autoDownload": "自动下载示例图片", "autoDownload": "自动下载示例图片",
"autoDownloadHelp": "自动为没有示例图片的模型下载示例图片(需设置下载位置)", "autoDownloadHelp": "自动为没有示例图片的模型下载示例图片(需设置下载位置)",
"openMode": "打开示例图片操作",
"openModeHelp": "选择是在服务器上打开、复制映射后的本地路径,还是启动自定义 URI。",
"openModeOptions": {
"system": "在服务器上打开",
"clipboard": "复制本地路径",
"uriTemplate": "打开自定义 URI"
},
"localRoot": "本地示例图片根目录",
"localRootHelp": "可选的本地或挂载根目录,用于映射服务器上的示例图片目录。若留空,则复用服务器路径。",
"localRootPlaceholder": "例如:/Volumes/ComfyUI/example_images",
"uriTemplate": "打开 URI 模板",
"uriTemplateHelp": "使用自定义深链接,例如文件 URI 或 Shortcuts 链接。",
"uriTemplatePlaceholder": "例如shortcuts://run-shortcut?name=Open%20Finder&input=text&text={{encoded_local_path}}",
"uriTemplatePlaceholders": "可用占位符:{{local_path}}、{{encoded_local_path}}、{{relative_path}}、{{encoded_relative_path}}、{{file_uri}}、{{encoded_file_uri}}",
"optimizeImages": "优化下载图片", "optimizeImages": "优化下载图片",
"optimizeImagesHelp": "优化示例图片以减少文件大小并提升加载速度(保留元数据)", "optimizeImagesHelp": "优化示例图片以减少文件大小并提升加载速度(保留元数据)",
"download": "下载", "download": "下载",
@@ -1442,6 +1456,10 @@
"opened": "示例图片文件夹已打开", "opened": "示例图片文件夹已打开",
"openingFolder": "正在打开示例图片文件夹", "openingFolder": "正在打开示例图片文件夹",
"failedToOpen": "打开示例图片文件夹失败", "failedToOpen": "打开示例图片文件夹失败",
"copiedPath": "路径已复制到剪贴板:{{path}}",
"clipboardFallback": "路径:{{path}}",
"copiedUri": "链接已复制到剪贴板:{{uri}}",
"uriClipboardFallback": "链接:{{uri}}",
"setupRequired": "示例图片存储", "setupRequired": "示例图片存储",
"setupDescription": "要添加自定义示例图片,您需要先设置下载位置。", "setupDescription": "要添加自定义示例图片,您需要先设置下载位置。",
"setupUsage": "此路径用于存储下载的示例图片和自定义图片。", "setupUsage": "此路径用于存储下载的示例图片和自定义图片。",

View File

@@ -538,6 +538,20 @@
"downloadLocationHelp": "輸入從 Civitai 下載範例圖片要儲存的資料夾路徑", "downloadLocationHelp": "輸入從 Civitai 下載範例圖片要儲存的資料夾路徑",
"autoDownload": "自動下載範例圖片", "autoDownload": "自動下載範例圖片",
"autoDownloadHelp": "自動為沒有範例圖片的模型下載範例圖片(需設定下載位置)", "autoDownloadHelp": "自動為沒有範例圖片的模型下載範例圖片(需設定下載位置)",
"openMode": "開啟範例圖片動作",
"openModeHelp": "選擇是在伺服器上開啟、複製對應的本機路徑,或啟動自訂 URI。",
"openModeOptions": {
"system": "在伺服器上開啟",
"clipboard": "複製本機路徑",
"uriTemplate": "開啟自訂 URI"
},
"localRoot": "本機範例圖片根目錄",
"localRootHelp": "可選的本機或掛載根目錄,用於對應伺服器上的範例圖片目錄。若留白,則會重用伺服器路徑。",
"localRootPlaceholder": "例如:/Volumes/ComfyUI/example_images",
"uriTemplate": "開啟 URI 範本",
"uriTemplateHelp": "使用自訂深層連結,例如檔案 URI 或 Shortcuts 連結。",
"uriTemplatePlaceholder": "例如shortcuts://run-shortcut?name=Open%20Finder&input=text&text={{encoded_local_path}}",
"uriTemplatePlaceholders": "可用佔位符:{{local_path}}、{{encoded_local_path}}、{{relative_path}}、{{encoded_relative_path}}、{{file_uri}}、{{encoded_file_uri}}",
"optimizeImages": "最佳化下載圖片", "optimizeImages": "最佳化下載圖片",
"optimizeImagesHelp": "最佳化範例圖片以減少檔案大小並提升載入速度(會保留原有的 metadata", "optimizeImagesHelp": "最佳化範例圖片以減少檔案大小並提升載入速度(會保留原有的 metadata",
"download": "下載", "download": "下載",
@@ -1442,6 +1456,10 @@
"opened": "範例圖片資料夾已開啟", "opened": "範例圖片資料夾已開啟",
"openingFolder": "正在開啟範例圖片資料夾", "openingFolder": "正在開啟範例圖片資料夾",
"failedToOpen": "開啟範例圖片資料夾失敗", "failedToOpen": "開啟範例圖片資料夾失敗",
"copiedPath": "路徑已複製到剪貼簿:{{path}}",
"clipboardFallback": "路徑:{{path}}",
"copiedUri": "連結已複製到剪貼簿:{{uri}}",
"uriClipboardFallback": "連結:{{uri}}",
"setupRequired": "範例圖片儲存", "setupRequired": "範例圖片儲存",
"setupDescription": "要新增自訂範例圖片,您需要先設定下載位置。", "setupDescription": "要新增自訂範例圖片,您需要先設定下載位置。",
"setupUsage": "此路徑用於儲存下載的範例圖片和自訂圖片。", "setupUsage": "此路徑用於儲存下載的範例圖片和自訂圖片。",

View File

@@ -81,6 +81,9 @@ DEFAULT_SETTINGS: Dict[str, Any] = {
"folder_paths": {}, "folder_paths": {},
"extra_folder_paths": {}, "extra_folder_paths": {},
"example_images_path": "", "example_images_path": "",
"example_images_open_mode": "system",
"example_images_local_root": "",
"example_images_open_uri_template": "",
"optimize_example_images": True, "optimize_example_images": True,
"auto_download_example_images": False, "auto_download_example_images": False,
"blur_mature_content": True, "blur_mature_content": True,

View File

@@ -1,17 +1,81 @@
import logging import logging
import os import os
import sys import re
import subprocess import subprocess
import sys
from urllib.parse import quote
from aiohttp import web from aiohttp import web
from ..services.settings_manager import get_settings_manager from ..services.settings_manager import get_settings_manager
from ..utils.example_images_paths import ( from ..utils.example_images_paths import (
get_model_folder, get_model_folder,
get_model_relative_path,
) )
from ..utils.constants import SUPPORTED_MEDIA_EXTENSIONS from ..utils.constants import SUPPORTED_MEDIA_EXTENSIONS
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_WINDOWS_DRIVE_PATTERN = re.compile(r"^[A-Za-z]:/")
def _is_within_root(path: str, root: str) -> bool:
try:
return os.path.commonpath([os.path.abspath(path), os.path.abspath(root)]) == os.path.abspath(root)
except ValueError:
return False
def _join_local_example_path(local_root: str, relative_path: str) -> str:
separator = "\\" if "\\" in local_root and "/" not in local_root else "/"
normalized_root = local_root.rstrip("\\/")
normalized_relative = relative_path.replace("/", separator)
if not normalized_root:
return normalized_relative
return f"{normalized_root}{separator}{normalized_relative}"
def _build_file_uri(path: str) -> str:
normalized = path.replace("\\", "/")
if _WINDOWS_DRIVE_PATTERN.match(normalized):
return f"file:///{quote(normalized, safe='/:')}"
if normalized.startswith("/"):
return f"file://{quote(normalized, safe='/:')}"
return f"file:///{quote(normalized.lstrip('/'), safe='/:')}"
def _render_open_uri_template(template: str, local_path: str, relative_path: str) -> str:
file_uri = _build_file_uri(local_path)
replacements = {
"{{local_path}}": local_path,
"{{encoded_local_path}}": quote(local_path, safe=""),
"{{relative_path}}": relative_path,
"{{encoded_relative_path}}": quote(relative_path, safe=""),
"{{file_uri}}": file_uri,
"{{encoded_file_uri}}": quote(file_uri, safe=""),
}
rendered = template
for placeholder, value in replacements.items():
rendered = rendered.replace(placeholder, value)
return rendered
def _open_system_folder(model_folder: str) -> dict[str, object]:
if os.name == "nt": # Windows
os.startfile(model_folder)
elif os.name == "posix": # macOS and Linux
if sys.platform == "darwin": # macOS
subprocess.Popen(["open", model_folder])
else: # Linux
subprocess.Popen(["xdg-open", model_folder])
return {
"success": True,
"message": f"Opened example images folder for {model_folder}",
"path": model_folder,
}
class ExampleImagesFileManager: class ExampleImagesFileManager:
"""Manages access and operations for example image files""" """Manages access and operations for example image files"""
@@ -54,7 +118,7 @@ class ExampleImagesFileManager:
}, status=500) }, status=500)
# Path validation: ensure model_folder is under example_images_path # Path validation: ensure model_folder is under example_images_path
if not model_folder.startswith(os.path.abspath(example_images_path)): if not _is_within_root(model_folder, example_images_path):
return web.json_response({ return web.json_response({
'success': False, 'success': False,
'error': 'Invalid model folder path' 'error': 'Invalid model folder path'
@@ -66,20 +130,40 @@ class ExampleImagesFileManager:
'success': False, 'success': False,
'error': 'No example images found for this model. Download example images first.' 'error': 'No example images found for this model. Download example images first.'
}, status=404) }, status=404)
# Open folder in file explorer root_path = os.path.abspath(example_images_path)
if os.name == 'nt': # Windows relative_path = os.path.relpath(model_folder, root_path).replace("\\", "/")
os.startfile(model_folder) open_mode = settings_manager.get("example_images_open_mode") or "system"
elif os.name == 'posix': # macOS and Linux
if sys.platform == 'darwin': # macOS if open_mode == "clipboard":
subprocess.Popen(['open', model_folder]) local_root = settings_manager.get("example_images_local_root") or root_path
else: # Linux local_path = _join_local_example_path(local_root, relative_path)
subprocess.Popen(['xdg-open', model_folder]) return web.json_response({
'success': True,
return web.json_response({ 'mode': 'clipboard',
'success': True, 'path': local_path,
'message': f'Opened example images folder for model {model_hash}' 'relative_path': relative_path,
}) })
if open_mode == "uri_template":
local_root = settings_manager.get("example_images_local_root") or root_path
uri_template = settings_manager.get("example_images_open_uri_template") or ""
if not uri_template.strip():
return web.json_response({
'success': False,
'error': 'No example image open URI template configured.'
}, status=400)
local_path = _join_local_example_path(local_root, relative_path)
return web.json_response({
'success': True,
'mode': 'uri',
'path': local_path,
'relative_path': relative_path,
'uri': _render_open_uri_template(uri_template, local_path, relative_path),
})
return web.json_response(_open_system_folder(model_folder))
except Exception as e: except Exception as e:
logger.error(f"Failed to open example images folder: {e}", exc_info=True) logger.error(f"Failed to open example images folder: {e}", exc_info=True)
@@ -143,7 +227,7 @@ class ExampleImagesFileManager:
file_ext = os.path.splitext(file)[1].lower() file_ext = os.path.splitext(file)[1].lower()
if (file_ext in SUPPORTED_MEDIA_EXTENSIONS['images'] or if (file_ext in SUPPORTED_MEDIA_EXTENSIONS['images'] or
file_ext in SUPPORTED_MEDIA_EXTENSIONS['videos']): file_ext in SUPPORTED_MEDIA_EXTENSIONS['videos']):
relative_path = get_model_relative_path(model_hash) relative_path = os.path.relpath(model_folder, os.path.abspath(example_images_path)).replace("\\", "/")
files.append({ files.append({
'name': file, 'name': file,
'path': f'/example_images_static/{relative_path}/{file}', 'path': f'/example_images_static/{relative_path}/{file}',
@@ -227,4 +311,4 @@ class ExampleImagesFileManager:
return web.json_response({ return web.json_response({
'has_images': False, 'has_images': False,
'error': str(e) 'error': str(e)
}) })

View File

@@ -15,5 +15,8 @@
"C:/path/to/another/embeddings_folder" "C:/path/to/another/embeddings_folder"
] ]
}, },
"example_images_open_mode": "system",
"example_images_local_root": "",
"example_images_open_uri_template": "",
"auto_organize_exclusions": [] "auto_organize_exclusions": []
} }

View File

@@ -914,6 +914,23 @@ export class SettingsManager {
autoDownloadExampleImagesCheckbox.checked = state.global.settings.auto_download_example_images || false; autoDownloadExampleImagesCheckbox.checked = state.global.settings.auto_download_example_images || false;
} }
const exampleImagesOpenModeSelect = document.getElementById('exampleImagesOpenMode');
if (exampleImagesOpenModeSelect) {
exampleImagesOpenModeSelect.value = state.global.settings.example_images_open_mode || 'system';
}
const exampleImagesLocalRootInput = document.getElementById('exampleImagesLocalRoot');
if (exampleImagesLocalRootInput) {
exampleImagesLocalRootInput.value = state.global.settings.example_images_local_root || '';
}
const exampleImagesOpenUriTemplateInput = document.getElementById('exampleImagesOpenUriTemplate');
if (exampleImagesOpenUriTemplateInput) {
exampleImagesOpenUriTemplateInput.value = state.global.settings.example_images_open_uri_template || '';
}
this.updateExampleImagesOpenSettingsVisibility();
// Load download path templates // Load download path templates
this.loadDownloadPathTemplates(); this.loadDownloadPathTemplates();
@@ -2015,6 +2032,25 @@ export class SettingsManager {
} }
} }
updateExampleImagesOpenSettingsVisibility() {
const openMode = state.global.settings.example_images_open_mode || 'system';
const localRootSetting = document.getElementById('exampleImagesLocalRootSetting');
const uriTemplateSetting = document.getElementById('exampleImagesUriTemplateSetting');
if (localRootSetting) {
localRootSetting.style.display = openMode === 'system' ? 'none' : 'block';
}
if (uriTemplateSetting) {
uriTemplateSetting.style.display = openMode === 'uri_template' ? 'block' : 'none';
}
}
async handleExampleImagesOpenModeChange() {
await this.saveSelectSetting('exampleImagesOpenMode', 'example_images_open_mode');
this.updateExampleImagesOpenSettingsVisibility();
}
async loadMetadataArchiveSettings() { async loadMetadataArchiveSettings() {
try { try {
// Load current settings from state // Load current settings from state

View File

@@ -25,6 +25,9 @@ const DEFAULT_SETTINGS_BASE = Object.freeze({
base_model_path_mappings: {}, base_model_path_mappings: {},
download_path_templates: {}, download_path_templates: {},
example_images_path: '', example_images_path: '',
example_images_open_mode: 'system',
example_images_local_root: '',
example_images_open_uri_template: '',
optimize_example_images: true, optimize_example_images: true,
auto_download_example_images: false, auto_download_example_images: false,
blur_mature_content: true, blur_mature_content: true,

View File

@@ -64,6 +64,33 @@ export function openCivitaiUrl(url) {
return window.open(url, '_blank', 'noopener,noreferrer'); return window.open(url, '_blank', 'noopener,noreferrer');
} }
async function copyExampleImagesValue(value, successKey, fallbackKey, paramsKey = 'path') {
if (!value) {
return false;
}
const params = { [paramsKey]: value };
try {
await navigator.clipboard.writeText(value);
showToast(successKey, params, 'success');
return true;
} catch (clipboardErr) {
console.warn('Clipboard API not available:', clipboardErr);
showToast(fallbackKey, params, 'info');
return false;
}
}
function tryOpenExternalUri(uri) {
try {
const openedWindow = window.open(uri, '_blank', 'noopener,noreferrer');
return openedWindow !== null;
} catch (error) {
console.warn('Failed to open external URI:', error);
return false;
}
}
/** /**
* Utility function to copy text to clipboard with fallback for older browsers * Utility function to copy text to clipboard with fallback for older browsers
* @param {string} text - The text to copy to clipboard * @param {string} text - The text to copy to clipboard
@@ -1088,7 +1115,31 @@ export async function openExampleImagesFolder(modelHash) {
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
const message = translate('uiHelpers.exampleImages.openingFolder', {}, 'Opening example images folder'); if (result.mode === 'clipboard' && result.path) {
await copyExampleImagesValue(
result.path,
'uiHelpers.exampleImages.copiedPath',
'uiHelpers.exampleImages.clipboardFallback',
'path'
);
return true;
}
if (result.mode === 'uri' && result.uri) {
const opened = tryOpenExternalUri(result.uri);
if (!opened) {
await copyExampleImagesValue(
result.uri,
'uiHelpers.exampleImages.copiedUri',
'uiHelpers.exampleImages.uriClipboardFallback',
'uri'
);
} else {
showToast('uiHelpers.exampleImages.opened', {}, 'success');
}
return true;
}
showToast('uiHelpers.exampleImages.opened', {}, 'success'); showToast('uiHelpers.exampleImages.opened', {}, 'success');
return true; return true;
} else { } else {

View File

@@ -1081,7 +1081,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="setting-item"> <div class="setting-item">
<div class="setting-row"> <div class="setting-row">
<div class="setting-info"> <div class="setting-info">
@@ -1099,6 +1099,60 @@
</div> </div>
</div> </div>
</div> </div>
<div class="setting-item">
<div class="setting-row">
<div class="setting-info">
<label for="exampleImagesOpenMode">
{{ t('settings.exampleImages.openMode') }}
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.exampleImages.openModeHelp') }}"></i>
</label>
</div>
<div class="setting-control select-control">
<select id="exampleImagesOpenMode" onchange="settingsManager.handleExampleImagesOpenModeChange()">
<option value="system">{{ t('settings.exampleImages.openModeOptions.system') }}</option>
<option value="clipboard">{{ t('settings.exampleImages.openModeOptions.clipboard') }}</option>
<option value="uri_template">{{ t('settings.exampleImages.openModeOptions.uriTemplate') }}</option>
</select>
</div>
</div>
</div>
<div class="setting-item" id="exampleImagesLocalRootSetting" style="display: none;">
<div class="setting-row">
<div class="setting-info">
<label for="exampleImagesLocalRoot">
{{ t('settings.exampleImages.localRoot') }}
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.exampleImages.localRootHelp') }}"></i>
</label>
</div>
<div class="setting-control path-control">
<input
type="text"
id="exampleImagesLocalRoot"
placeholder="{{ t('settings.exampleImages.localRootPlaceholder') }}"
onchange="settingsManager.saveInputSetting('exampleImagesLocalRoot', 'example_images_local_root')" />
</div>
</div>
</div>
<div class="setting-item" id="exampleImagesUriTemplateSetting" style="display: none;">
<div class="setting-row">
<div class="setting-info">
<label for="exampleImagesOpenUriTemplate">
{{ t('settings.exampleImages.uriTemplate') }}
<i class="fas fa-info-circle info-icon" data-tooltip="{{ t('settings.exampleImages.uriTemplateHelp') }} {{ t('settings.exampleImages.uriTemplatePlaceholders') }}"></i>
</label>
</div>
<div class="setting-control path-control">
<input
type="text"
id="exampleImagesOpenUriTemplate"
placeholder="{{ t('settings.exampleImages.uriTemplatePlaceholder') }}"
onchange="settingsManager.saveInputSetting('exampleImagesOpenUriTemplate', 'example_images_open_uri_template')" />
</div>
</div>
</div>
</div> </div>
<!-- Auto-organize --> <!-- Auto-organize -->

View File

@@ -340,4 +340,52 @@ describe('SettingsManager library controls', () => {
expect(aria2PathSetting.style.display).toBe('none'); expect(aria2PathSetting.style.display).toBe('none');
expect(saveSpy).toHaveBeenCalledWith('downloadBackend', 'download_backend'); expect(saveSpy).toHaveBeenCalledWith('downloadBackend', 'download_backend');
}); });
it('loads example image remote-open settings and updates field visibility', async () => {
const manager = createManager();
document.body.innerHTML = `
<select id="exampleImagesOpenMode">
<option value="system">System</option>
<option value="clipboard">Clipboard</option>
<option value="uri_template">URI</option>
</select>
<div id="exampleImagesLocalRootSetting" style="display: none;"></div>
<div id="exampleImagesUriTemplateSetting" style="display: none;"></div>
<input id="exampleImagesLocalRoot" />
<input id="exampleImagesOpenUriTemplate" />
`;
vi.spyOn(manager, 'loadMetadataArchiveSettings').mockResolvedValue();
vi.spyOn(manager, 'loadBackupSettings').mockResolvedValue();
vi.spyOn(manager, 'loadLibraries').mockResolvedValue();
vi.spyOn(manager, 'loadLoraRoots').mockResolvedValue();
vi.spyOn(manager, 'loadCheckpointRoots').mockResolvedValue();
vi.spyOn(manager, 'loadUnetRoots').mockResolvedValue();
vi.spyOn(manager, 'loadEmbeddingRoots').mockResolvedValue();
state.global.settings = {
example_images_open_mode: 'uri_template',
example_images_local_root: '/Volumes/ComfyUI/examples',
example_images_open_uri_template: 'shortcuts://run-shortcut?text={{encoded_local_path}}',
};
await manager.loadSettingsToUI();
expect(document.getElementById('exampleImagesOpenMode').value).toBe('uri_template');
expect(document.getElementById('exampleImagesLocalRoot').value).toBe('/Volumes/ComfyUI/examples');
expect(document.getElementById('exampleImagesOpenUriTemplate').value)
.toBe('shortcuts://run-shortcut?text={{encoded_local_path}}');
expect(document.getElementById('exampleImagesLocalRootSetting').style.display).toBe('block');
expect(document.getElementById('exampleImagesUriTemplateSetting').style.display).toBe('block');
state.global.settings.example_images_open_mode = 'clipboard';
manager.updateExampleImagesOpenSettingsVisibility();
expect(document.getElementById('exampleImagesLocalRootSetting').style.display).toBe('block');
expect(document.getElementById('exampleImagesUriTemplateSetting').style.display).toBe('none');
state.global.settings.example_images_open_mode = 'system';
manager.updateExampleImagesOpenSettingsVisibility();
expect(document.getElementById('exampleImagesLocalRootSetting').style.display).toBe('none');
expect(document.getElementById('exampleImagesUriTemplateSetting').style.display).toBe('none');
});
}); });

View File

@@ -84,6 +84,8 @@ describe('UI helper DOM utilities', () => {
afterEach(() => { afterEach(() => {
vi.useRealTimers(); vi.useRealTimers();
delete global.fetch; delete global.fetch;
delete navigator.clipboard;
delete window.open;
}); });
it('creates toast elements and cleans them up after timeout', async () => { it('creates toast elements and cleans them up after timeout', async () => {
@@ -230,4 +232,49 @@ describe('UI helper DOM utilities', () => {
'noopener,noreferrer' 'noopener,noreferrer'
); );
}); });
it('copies mapped local example-image paths when the backend requests clipboard mode', async () => {
global.fetch = vi.fn().mockResolvedValue({
json: async () => ({
success: true,
mode: 'clipboard',
path: '/Volumes/ComfyUI/examples/demo',
}),
});
navigator.clipboard = {
writeText: vi.fn().mockResolvedValue(),
};
const { openExampleImagesFolder } = await import(UI_HELPERS_MODULE);
const result = await openExampleImagesFolder('abc123');
expect(result).toBe(true);
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('/Volumes/ComfyUI/examples/demo');
expect(global.fetch).toHaveBeenCalledWith('/api/lm/open-example-images-folder', expect.objectContaining({
method: 'POST',
}));
});
it('opens custom URIs for example-image folders when requested by the backend', async () => {
global.fetch = vi.fn().mockResolvedValue({
json: async () => ({
success: true,
mode: 'uri',
uri: 'shortcuts://run-shortcut?name=OpenFinder',
}),
});
window.open = vi.fn(() => ({}));
const { openExampleImagesFolder } = await import(UI_HELPERS_MODULE);
const result = await openExampleImagesFolder('abc123');
expect(result).toBe(true);
expect(window.open).toHaveBeenCalledWith(
'shortcuts://run-shortcut?name=OpenFinder',
'_blank',
'noopener,noreferrer'
);
});
}); });

View File

@@ -66,6 +66,97 @@ async def test_open_folder_requires_existing_model_directory(monkeypatch: pytest
assert model_hash in popen_calls[0][-1] assert model_hash in popen_calls[0][-1]
async def test_open_folder_returns_clipboard_mode_with_mapped_local_path(
monkeypatch: pytest.MonkeyPatch, tmp_path
) -> None:
settings_manager = get_settings_manager()
settings_manager.settings["example_images_path"] = str(tmp_path)
settings_manager.settings["example_images_open_mode"] = "clipboard"
settings_manager.settings["example_images_local_root"] = "/Volumes/ComfyUI/examples"
model_hash = "d" * 64
model_folder = tmp_path / "library-a" / model_hash
model_folder.mkdir(parents=True)
(model_folder / "image.png").write_text("data", encoding="utf-8")
popen_calls: list[list[str]] = []
class DummyPopen:
def __init__(self, cmd, *_args, **_kwargs):
popen_calls.append(cmd)
monkeypatch.setattr("subprocess.Popen", DummyPopen)
monkeypatch.setattr("py.utils.example_images_file_manager.get_model_folder", lambda _hash: str(model_folder))
request = JsonRequest({"model_hash": model_hash})
response = await ExampleImagesFileManager.open_folder(request)
body = json.loads(response.text)
assert response.status == 200
assert body == {
"success": True,
"mode": "clipboard",
"path": f"/Volumes/ComfyUI/examples/library-a/{model_hash}",
"relative_path": f"library-a/{model_hash}",
}
assert popen_calls == []
async def test_open_folder_returns_uri_mode_with_rendered_template(
monkeypatch: pytest.MonkeyPatch, tmp_path
) -> None:
settings_manager = get_settings_manager()
settings_manager.settings["example_images_path"] = str(tmp_path)
settings_manager.settings["example_images_open_mode"] = "uri_template"
settings_manager.settings["example_images_local_root"] = "/Volumes/ComfyUI/examples"
settings_manager.settings["example_images_open_uri_template"] = (
"shortcuts://run-shortcut?name=OpenFinder&input=text&text={{encoded_local_path}}"
)
model_hash = "e" * 64
model_folder = tmp_path / model_hash
model_folder.mkdir()
(model_folder / "image.png").write_text("data", encoding="utf-8")
popen_calls: list[list[str]] = []
class DummyPopen:
def __init__(self, cmd, *_args, **_kwargs):
popen_calls.append(cmd)
monkeypatch.setattr("subprocess.Popen", DummyPopen)
request = JsonRequest({"model_hash": model_hash})
response = await ExampleImagesFileManager.open_folder(request)
body = json.loads(response.text)
assert response.status == 200
assert body["success"] is True
assert body["mode"] == "uri"
assert body["path"] == f"/Volumes/ComfyUI/examples/{model_hash}"
assert body["relative_path"] == model_hash
assert body["uri"] == (
"shortcuts://run-shortcut?name=OpenFinder&input=text&text="
f"%2FVolumes%2FComfyUI%2Fexamples%2F{model_hash}"
)
assert popen_calls == []
async def test_open_folder_rejects_missing_uri_template(monkeypatch: pytest.MonkeyPatch, tmp_path) -> None:
settings_manager = get_settings_manager()
settings_manager.settings["example_images_path"] = str(tmp_path)
settings_manager.settings["example_images_open_mode"] = "uri_template"
model_hash = "f" * 64
model_folder = tmp_path / model_hash
model_folder.mkdir()
(model_folder / "image.png").write_text("data", encoding="utf-8")
response = await ExampleImagesFileManager.open_folder(JsonRequest({"model_hash": model_hash}))
body = json.loads(response.text)
assert response.status == 400
assert body["success"] is False
assert body["error"] == "No example image open URI template configured."
async def test_open_folder_rejects_invalid_paths(monkeypatch: pytest.MonkeyPatch, tmp_path) -> None: async def test_open_folder_rejects_invalid_paths(monkeypatch: pytest.MonkeyPatch, tmp_path) -> None:
settings_manager = get_settings_manager() settings_manager = get_settings_manager()
settings_manager.settings["example_images_path"] = str(tmp_path) settings_manager.settings["example_images_path"] = str(tmp_path)