mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
checkpoint
This commit is contained in:
110
templates/base.html
Normal file
110
templates/base.html
Normal file
@@ -0,0 +1,110 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>{% block title %}LoRA Manager{% endblock %}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="/loras_static/css/style.css">
|
||||
{% block page_css %}{% endblock %}
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/loras_static/images/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/loras_static/images/favicon-16x16.png">
|
||||
<link rel="manifest" href="/loras_static/images/site.webmanifest">
|
||||
|
||||
<!-- 预加载关键资源 -->
|
||||
<link rel="preload" href="/loras_static/css/style.css" as="style">
|
||||
{% block preload %}{% endblock %}
|
||||
|
||||
<!-- 优化字体加载 -->
|
||||
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/webfonts/fa-solid-900.woff2"
|
||||
as="font" type="font/woff2" crossorigin>
|
||||
|
||||
<!-- 添加性能监控 -->
|
||||
<script>
|
||||
performance.mark('page-start');
|
||||
window.addEventListener('load', () => {
|
||||
performance.mark('page-end');
|
||||
performance.measure('page-load', 'page-start', 'page-end');
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- 添加安全相关的 meta 标签 -->
|
||||
<meta http-equiv="Cross-Origin-Opener-Policy" content="same-origin">
|
||||
<meta http-equiv="Cross-Origin-Embedder-Policy" content="require-corp">
|
||||
|
||||
<!-- 添加资源加载策略 -->
|
||||
<link rel="preconnect" href="https://civitai.com">
|
||||
<link rel="preconnect" href="https://cdnjs.cloudflare.com">
|
||||
|
||||
<script>
|
||||
// 计算滚动条宽度并设置CSS变量
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const scrollDiv = document.createElement('div');
|
||||
scrollDiv.style.cssText = 'width:100px;height:100px;overflow:scroll;position:absolute;top:-9999px;';
|
||||
document.body.appendChild(scrollDiv);
|
||||
const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
||||
document.body.removeChild(scrollDiv);
|
||||
document.documentElement.style.setProperty('--scrollbar-width', scrollbarWidth + 'px');
|
||||
});
|
||||
</script>
|
||||
{% block head_scripts %}{% endblock %}
|
||||
</head>
|
||||
|
||||
<body data-page="{% block page_id %}base{% endblock %}">
|
||||
{% include 'components/header.html' %}
|
||||
|
||||
<div class="page-content">
|
||||
{% include 'components/modals.html' %}
|
||||
{% include 'components/loading.html' %}
|
||||
{% include 'components/context_menu.html' %}
|
||||
{% block additional_components %}{% endblock %}
|
||||
|
||||
<div class="container">
|
||||
{% if is_initializing %}
|
||||
<div class="initialization-notice">
|
||||
<div class="notice-content">
|
||||
<div class="loading-spinner"></div>
|
||||
<h2>{% block init_title %}Initializing{% endblock %}</h2>
|
||||
<p>{% block init_message %}Scanning and building cache. This may take a few minutes...{% endblock %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% block content %}{% endblock %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% block overlay %}{% endblock %}
|
||||
</div>
|
||||
|
||||
{% block main_script %}{% endblock %}
|
||||
|
||||
{% if is_initializing %}
|
||||
<script>
|
||||
// 检查初始化状态并设置自动刷新
|
||||
async function checkInitStatus() {
|
||||
try {
|
||||
const response = await fetch('{% block init_check_url %}/api/loras?page=1&page_size=1{% endblock %}');
|
||||
if (response.ok) {
|
||||
// 如果成功获取数据,说明初始化完成,刷新页面
|
||||
window.location.reload();
|
||||
} else {
|
||||
// 如果还未完成,继续轮询
|
||||
setTimeout(checkInitStatus, 2000); // 每2秒检查一次
|
||||
}
|
||||
} catch (error) {
|
||||
// 如果出错,继续轮询
|
||||
setTimeout(checkInitStatus, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// 启动状态检查
|
||||
checkInitStatus();
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{% block additional_scripts %}{% endblock %}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,104 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>LoRA Manager</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="/loras_static/css/style.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/loras_static/images/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/loras_static/images/favicon-16x16.png">
|
||||
<link rel="manifest" href="/loras_static/images/site.webmanifest">
|
||||
|
||||
<!-- 预加载关键资源 -->
|
||||
<link rel="preload" href="/loras_static/css/style.css" as="style">
|
||||
<link rel="preload" href="/loras_static/js/main.js" as="script" crossorigin="anonymous">
|
||||
|
||||
<!-- 优化字体加载 -->
|
||||
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/webfonts/fa-solid-900.woff2" as="font" type="font/woff2" crossorigin>
|
||||
|
||||
<!-- 添加性能监控 -->
|
||||
<script>
|
||||
performance.mark('page-start');
|
||||
window.addEventListener('load', () => {
|
||||
performance.mark('page-end');
|
||||
performance.measure('page-load', 'page-start', 'page-end');
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- 添加安全相关的 meta 标签 -->
|
||||
<meta http-equiv="Cross-Origin-Opener-Policy" content="same-origin">
|
||||
<meta http-equiv="Cross-Origin-Embedder-Policy" content="require-corp">
|
||||
|
||||
<!-- 添加资源加载策略 -->
|
||||
<link rel="preconnect" href="https://civitai.com">
|
||||
<link rel="preconnect" href="https://cdnjs.cloudflare.com">
|
||||
{% extends "base.html" %}
|
||||
|
||||
<script>
|
||||
// 计算滚动条宽度并设置CSS变量
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const scrollDiv = document.createElement('div');
|
||||
scrollDiv.style.cssText = 'width:100px;height:100px;overflow:scroll;position:absolute;top:-9999px;';
|
||||
document.body.appendChild(scrollDiv);
|
||||
const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
||||
document.body.removeChild(scrollDiv);
|
||||
document.documentElement.style.setProperty('--scrollbar-width', scrollbarWidth + 'px');
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body data-page="loras">
|
||||
{% include 'components/header.html' %}
|
||||
{% block title %}LoRA Manager{% endblock %}
|
||||
{% block page_id %}loras{% endblock %}
|
||||
|
||||
<div class="page-content">
|
||||
{% include 'components/modals.html' %}
|
||||
{% include 'components/loading.html' %}
|
||||
{% include 'components/context_menu.html' %}
|
||||
{% block preload %}
|
||||
<link rel="preload" href="/loras_static/js/loras.js" as="script" crossorigin="anonymous">
|
||||
{% endblock %}
|
||||
|
||||
<div class="container">
|
||||
{% if is_initializing %}
|
||||
<div class="initialization-notice">
|
||||
<div class="notice-content">
|
||||
<div class="loading-spinner"></div>
|
||||
<h2>Initializing LoRA Manager</h2>
|
||||
<p>Scanning and building LoRA cache. This may take a few minutes...</p>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% include 'components/controls.html' %}
|
||||
<!-- Lora卡片容器 -->
|
||||
<div class="card-grid" id="loraGrid">
|
||||
<!-- Cards will be dynamically inserted here -->
|
||||
</div>
|
||||
<!-- Bulk operations panel will be inserted here by JavaScript -->
|
||||
{% endif %}
|
||||
</div>
|
||||
{% block init_title %}Initializing LoRA Manager{% endblock %}
|
||||
{% block init_message %}Scanning and building LoRA cache. This may take a few minutes...{% endblock %}
|
||||
{% block init_check_url %}/api/loras?page=1&page_size=1{% endblock %}
|
||||
|
||||
<!-- Add after the container div -->
|
||||
<div class="bulk-mode-overlay"></div>
|
||||
{% block content %}
|
||||
{% include 'components/controls.html' %}
|
||||
<!-- Lora卡片容器 -->
|
||||
<div class="card-grid" id="loraGrid">
|
||||
<!-- Cards will be dynamically inserted here -->
|
||||
</div>
|
||||
<!-- Bulk operations panel will be inserted here by JavaScript -->
|
||||
{% endblock %}
|
||||
|
||||
<script type="module" src="/loras_static/js/main.js"></script>
|
||||
{% if is_initializing %}
|
||||
<script>
|
||||
// 检查初始化状态并设置自动刷新
|
||||
async function checkInitStatus() {
|
||||
try {
|
||||
const response = await fetch('/api/loras?page=1&page_size=1');
|
||||
if (response.ok) {
|
||||
// 如果成功获取数据,说明初始化完成,刷新页面
|
||||
window.location.reload();
|
||||
} else {
|
||||
// 如果还未完成,继续轮询
|
||||
setTimeout(checkInitStatus, 2000); // 每2秒检查一次
|
||||
}
|
||||
} catch (error) {
|
||||
// 如果出错,继续轮询
|
||||
setTimeout(checkInitStatus, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// 启动状态检查
|
||||
checkInitStatus();
|
||||
</script>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
{% block overlay %}
|
||||
<div class="bulk-mode-overlay"></div>
|
||||
{% endblock %}
|
||||
|
||||
{% block main_script %}
|
||||
<script type="module" src="/loras_static/js/loras.js"></script>
|
||||
{% endblock %}
|
||||
@@ -1,153 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>LoRA Recipes</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="/loras_static/css/style.css">
|
||||
<link rel="stylesheet" href="/loras_static/css/components/recipe-card.css">
|
||||
<link rel="stylesheet" href="/loras_static/css/components/recipe-modal.css">
|
||||
<link rel="stylesheet" href="/loras_static/css/components/import-modal.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/loras_static/images/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/loras_static/images/favicon-16x16.png">
|
||||
<link rel="manifest" href="/loras_static/images/site.webmanifest">
|
||||
|
||||
<!-- Preload critical resources -->
|
||||
<link rel="preload" href="/loras_static/css/style.css" as="style">
|
||||
<link rel="preload" href="/loras_static/js/recipes.js" as="script" crossorigin="anonymous">
|
||||
|
||||
<!-- Optimize font loading -->
|
||||
<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/webfonts/fa-solid-900.woff2" as="font" type="font/woff2" crossorigin>
|
||||
|
||||
<!-- Performance monitoring -->
|
||||
<script>
|
||||
performance.mark('page-start');
|
||||
window.addEventListener('load', () => {
|
||||
performance.mark('page-end');
|
||||
performance.measure('page-load', 'page-start', 'page-end');
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Security meta tags -->
|
||||
<meta http-equiv="Cross-Origin-Opener-Policy" content="same-origin">
|
||||
<meta http-equiv="Cross-Origin-Embedder-Policy" content="require-corp">
|
||||
|
||||
<!-- Resource loading strategy -->
|
||||
<link rel="preconnect" href="https://cdnjs.cloudflare.com">
|
||||
</head>
|
||||
<body data-page="recipes">
|
||||
{% include 'components/header.html' %}
|
||||
{% extends "base.html" %}
|
||||
|
||||
<div class="page-content">
|
||||
{% include 'components/modals.html' %}
|
||||
{% include 'components/loading.html' %}
|
||||
{% include 'components/context_menu.html' %}
|
||||
{% include 'components/import_modal.html' %}
|
||||
{% include 'components/recipe_modal.html' %}
|
||||
{% block title %}LoRA Recipes{% endblock %}
|
||||
{% block page_id %}recipes{% endblock %}
|
||||
|
||||
<div class="container">
|
||||
{% if is_initializing %}
|
||||
<div class="initialization-notice">
|
||||
<div class="notice-content">
|
||||
<div class="loading-spinner"></div>
|
||||
<h2>Initializing Recipe Manager</h2>
|
||||
<p>Scanning and building recipe cache. This may take a few moments...</p>
|
||||
</div>
|
||||
{% block page_css %}
|
||||
<link rel="stylesheet" href="/loras_static/css/components/recipe-card.css">
|
||||
<link rel="stylesheet" href="/loras_static/css/components/recipe-modal.css">
|
||||
<link rel="stylesheet" href="/loras_static/css/components/import-modal.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block preload %}
|
||||
<link rel="preload" href="/loras_static/js/recipes.js" as="script" crossorigin="anonymous">
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_components %}
|
||||
{% include 'components/import_modal.html' %}
|
||||
{% include 'components/recipe_modal.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block init_title %}Initializing Recipe Manager{% endblock %}
|
||||
{% block init_message %}Scanning and building recipe cache. This may take a few moments...{% endblock %}
|
||||
{% block init_check_url %}/api/recipes?page=1&page_size=1{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Recipe controls -->
|
||||
<div class="controls">
|
||||
<div class="action-buttons">
|
||||
<div title="Import recipes" class="control-group">
|
||||
<button onclick="importRecipes()"><i class="fas fa-file-import"></i> Import</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<!-- Recipe controls - can reuse structure from loras controls -->
|
||||
<div class="controls">
|
||||
<div class="action-buttons">
|
||||
<div title="Import recipes" class="control-group">
|
||||
<button onclick="importRecipes()"><i class="fas fa-file-import"></i> Import</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recipe grid - similar to lora grid -->
|
||||
<div class="card-grid recipe-grid" id="recipeGrid">
|
||||
{% if recipes and recipes|length > 0 %}
|
||||
{% for recipe in recipes %}
|
||||
<div class="recipe-card" data-file-path="{{ recipe.file_path }}" data-title="{{ recipe.title }}" data-created="{{ recipe.created_date }}">
|
||||
<div class="recipe-indicator" title="Recipe">R</div>
|
||||
<div class="card-preview">
|
||||
<img src="{{ recipe.file_url }}" alt="{{ recipe.title }}">
|
||||
<div class="card-header">
|
||||
<div class="base-model-wrapper">
|
||||
{% if recipe.base_model %}
|
||||
<span class="base-model-label" title="{{ recipe.base_model }}">
|
||||
{{ recipe.base_model }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<i class="fas fa-share-alt" title="Share Recipe"></i>
|
||||
<i class="fas fa-copy" title="Copy Recipe"></i>
|
||||
<i class="fas fa-trash" title="Delete Recipe"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="model-info">
|
||||
<span class="model-name">{{ recipe.title }}</span>
|
||||
</div>
|
||||
<div class="lora-count" title="Number of LoRAs in this recipe">
|
||||
<i class="fas fa-layer-group"></i> {{ recipe.loras|length }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="placeholder-message">
|
||||
<p>No recipes found</p>
|
||||
<p>Add recipe images to your recipes folder to see them here.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recipe grid -->
|
||||
<div class="card-grid recipe-grid" id="recipeGrid">
|
||||
{% if recipes and recipes|length > 0 %}
|
||||
{% for recipe in recipes %}
|
||||
<div class="recipe-card" data-file-path="{{ recipe.file_path }}" data-title="{{ recipe.title }}" data-created="{{ recipe.created_date }}">
|
||||
<div class="recipe-indicator" title="Recipe">R</div>
|
||||
<div class="card-preview">
|
||||
<img src="{{ recipe.file_url }}" alt="{{ recipe.title }}">
|
||||
<div class="card-header">
|
||||
<div class="base-model-wrapper">
|
||||
{% if recipe.base_model %}
|
||||
<span class="base-model-label" title="{{ recipe.base_model }}">
|
||||
{{ recipe.base_model }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<i class="fas fa-share-alt" title="Share Recipe"></i>
|
||||
<i class="fas fa-copy" title="Copy Recipe"></i>
|
||||
<i class="fas fa-trash" title="Delete Recipe"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="model-info">
|
||||
<span class="model-name">{{ recipe.title }}</span>
|
||||
</div>
|
||||
<div class="lora-count" title="Number of LoRAs in this recipe">
|
||||
<i class="fas fa-layer-group"></i> {{ recipe.loras|length }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="placeholder-message">
|
||||
<p>No recipes found</p>
|
||||
<p>Add recipe images to your recipes folder to see them here.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<script type="module" src="/loras_static/js/recipes.js"></script>
|
||||
{% if is_initializing %}
|
||||
<script>
|
||||
// Check initialization status and set auto-refresh
|
||||
async function checkInitStatus() {
|
||||
try {
|
||||
const response = await fetch('/api/recipes?page=1&page_size=1');
|
||||
if (response.ok) {
|
||||
// If data successfully retrieved, initialization is complete, refresh the page
|
||||
window.location.reload();
|
||||
} else {
|
||||
// If not yet complete, continue polling
|
||||
setTimeout(checkInitStatus, 2000); // Check every 2 seconds
|
||||
}
|
||||
} catch (error) {
|
||||
// If error, continue polling
|
||||
setTimeout(checkInitStatus, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// Start status checking
|
||||
checkInitStatus();
|
||||
</script>
|
||||
{% endif %}
|
||||
{% block main_script %}
|
||||
<script type="module" src="/loras_static/js/recipes.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
<script>
|
||||
// Refresh recipes
|
||||
function refreshRecipes() {
|
||||
// Will be implemented in recipes.js
|
||||
window.location.reload();
|
||||
{% block additional_scripts %}
|
||||
<script>
|
||||
// Refresh recipes
|
||||
function refreshRecipes() {
|
||||
// Will be implemented in recipes.js
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
// Import recipes
|
||||
function importRecipes() {
|
||||
// Show import modal
|
||||
const importModal = document.getElementById('importModal');
|
||||
if (importModal) {
|
||||
importModal.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// Import recipes
|
||||
function importRecipes() {
|
||||
// Show import modal
|
||||
const importModal = document.getElementById('importModal');
|
||||
if (importModal) {
|
||||
importModal.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user