From aa6c6035b6028fa120b7b74edf1558d99361e44b Mon Sep 17 00:00:00 2001
From: Will Miao <13051207myq@gmail.com>
Date: Fri, 25 Apr 2025 13:17:31 +0800
Subject: [PATCH 1/2] refactor: consolidate save model metadata functionality
across APIs
---
static/js/api/checkpointApi.js | 11 +++++---
static/js/api/loraApi.js | 25 ++++++++++++++++++
.../ContextMenu/CheckpointContextMenu.js | 4 +--
.../components/ContextMenu/LoraContextMenu.js | 19 ++------------
.../checkpointModal/ModelMetadata.js | 26 +------------------
static/js/components/checkpointModal/index.js | 4 +--
.../js/components/loraModal/ModelMetadata.js | 26 +------------------
static/js/components/loraModal/PresetTags.js | 3 +--
.../js/components/loraModal/TriggerWords.js | 2 +-
static/js/components/loraModal/index.js | 4 +--
10 files changed, 45 insertions(+), 79 deletions(-)
diff --git a/static/js/api/checkpointApi.js b/static/js/api/checkpointApi.js
index cbf6a66a..a1a59c96 100644
--- a/static/js/api/checkpointApi.js
+++ b/static/js/api/checkpointApi.js
@@ -62,8 +62,13 @@ export async function refreshSingleCheckpointMetadata(filePath) {
return refreshSingleModelMetadata(filePath, 'checkpoint');
}
-// Save checkpoint metadata (similar to the Lora version)
-export async function saveCheckpointMetadata(filePath, data) {
+/**
+ * Save model metadata to the server
+ * @param {string} filePath - Path to the model file
+ * @param {Object} data - Metadata to save
+ * @returns {Promise} - Promise that resolves with the server response
+ */
+export async function saveModelMetadata(filePath, data) {
const response = await fetch('/api/checkpoints/save-metadata', {
method: 'POST',
headers: {
@@ -79,5 +84,5 @@ export async function saveCheckpointMetadata(filePath, data) {
throw new Error('Failed to save metadata');
}
- return await response.json();
+ return response.json();
}
\ No newline at end of file
diff --git a/static/js/api/loraApi.js b/static/js/api/loraApi.js
index 5e433799..8c4cb9a5 100644
--- a/static/js/api/loraApi.js
+++ b/static/js/api/loraApi.js
@@ -9,6 +9,31 @@ import {
refreshSingleModelMetadata
} from './baseModelApi.js';
+/**
+ * Save model metadata to the server
+ * @param {string} filePath - File path
+ * @param {Object} data - Data to save
+ * @returns {Promise} Promise of the save operation
+ */
+export async function saveModelMetadata(filePath, data) {
+ const response = await fetch('/api/loras/save-metadata', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ file_path: filePath,
+ ...data
+ })
+ });
+
+ if (!response.ok) {
+ throw new Error('Failed to save metadata');
+ }
+
+ return response.json();
+}
+
export async function loadMoreLoras(resetPage = false, updateFolders = false) {
return loadMoreModels({
resetPage,
diff --git a/static/js/components/ContextMenu/CheckpointContextMenu.js b/static/js/components/ContextMenu/CheckpointContextMenu.js
index 975ac986..c0975bbe 100644
--- a/static/js/components/ContextMenu/CheckpointContextMenu.js
+++ b/static/js/components/ContextMenu/CheckpointContextMenu.js
@@ -1,5 +1,5 @@
import { BaseContextMenu } from './BaseContextMenu.js';
-import { refreshSingleCheckpointMetadata, saveCheckpointMetadata } from '../../api/checkpointApi.js';
+import { refreshSingleCheckpointMetadata, saveModelMetadata } from '../../api/checkpointApi.js';
import { showToast, getNSFWLevelName } from '../../utils/uiHelpers.js';
import { NSFW_LEVELS } from '../../utils/constants.js';
import { getStorageItem } from '../../utils/storageHelpers.js';
@@ -82,7 +82,7 @@ export class CheckpointContextMenu extends BaseContextMenu {
if (!filePath) return;
try {
- await saveCheckpointMetadata(filePath, { preview_nsfw_level: level });
+ await saveModelMetadata(filePath, { preview_nsfw_level: level });
// Update card data
const card = document.querySelector(`.lora-card[data-filepath="${filePath}"]`);
diff --git a/static/js/components/ContextMenu/LoraContextMenu.js b/static/js/components/ContextMenu/LoraContextMenu.js
index 64382d0e..146c3d94 100644
--- a/static/js/components/ContextMenu/LoraContextMenu.js
+++ b/static/js/components/ContextMenu/LoraContextMenu.js
@@ -1,5 +1,5 @@
import { BaseContextMenu } from './BaseContextMenu.js';
-import { refreshSingleLoraMetadata } from '../../api/loraApi.js';
+import { refreshSingleLoraMetadata, saveModelMetadata } from '../../api/loraApi.js';
import { showToast, getNSFWLevelName } from '../../utils/uiHelpers.js';
import { NSFW_LEVELS } from '../../utils/constants.js';
import { getStorageItem } from '../../utils/storageHelpers.js';
@@ -111,22 +111,7 @@ export class LoraContextMenu extends BaseContextMenu {
}
async saveModelMetadata(filePath, data) {
- const response = await fetch('/api/loras/save-metadata', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- file_path: filePath,
- ...data
- })
- });
-
- if (!response.ok) {
- throw new Error('Failed to save metadata');
- }
-
- return await response.json();
+ return saveModelMetadata(filePath, data);
}
updateCardBlurEffect(card, level) {
diff --git a/static/js/components/checkpointModal/ModelMetadata.js b/static/js/components/checkpointModal/ModelMetadata.js
index 4bf7b2b3..18167f3b 100644
--- a/static/js/components/checkpointModal/ModelMetadata.js
+++ b/static/js/components/checkpointModal/ModelMetadata.js
@@ -5,31 +5,7 @@
import { showToast } from '../../utils/uiHelpers.js';
import { BASE_MODELS } from '../../utils/constants.js';
import { updateCheckpointCard } from '../../utils/cardUpdater.js';
-
-/**
- * Save model metadata to the server
- * @param {string} filePath - Path to the model file
- * @param {Object} data - Metadata to save
- * @returns {Promise} - Promise that resolves with the server response
- */
-export async function saveModelMetadata(filePath, data) {
- const response = await fetch('/api/checkpoints/save-metadata', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- file_path: filePath,
- ...data
- })
- });
-
- if (!response.ok) {
- throw new Error('Failed to save metadata');
- }
-
- return response.json();
-}
+import { saveModelMetadata } from '../../api/checkpointApi.js';
/**
* Set up model name editing functionality
diff --git a/static/js/components/checkpointModal/index.js b/static/js/components/checkpointModal/index.js
index 879f9218..9212d57b 100644
--- a/static/js/components/checkpointModal/index.js
+++ b/static/js/components/checkpointModal/index.js
@@ -11,9 +11,9 @@ import { setupTabSwitching, loadModelDescription } from './ModelDescription.js';
import {
setupModelNameEditing,
setupBaseModelEditing,
- setupFileNameEditing,
- saveModelMetadata
+ setupFileNameEditing
} from './ModelMetadata.js';
+import { saveModelMetadata } from '../../api/checkpointApi.js';
import { renderCompactTags, setupTagTooltip, formatFileSize } from './utils.js';
import { updateCheckpointCard } from '../../utils/cardUpdater.js';
diff --git a/static/js/components/loraModal/ModelMetadata.js b/static/js/components/loraModal/ModelMetadata.js
index eb0a3d18..81300150 100644
--- a/static/js/components/loraModal/ModelMetadata.js
+++ b/static/js/components/loraModal/ModelMetadata.js
@@ -5,31 +5,7 @@
import { showToast } from '../../utils/uiHelpers.js';
import { BASE_MODELS } from '../../utils/constants.js';
import { updateLoraCard } from '../../utils/cardUpdater.js';
-
-/**
- * 保存模型元数据到服务器
- * @param {string} filePath - 文件路径
- * @param {Object} data - 要保存的数据
- * @returns {Promise} 保存操作的Promise
- */
-export async function saveModelMetadata(filePath, data) {
- const response = await fetch('/api/loras/save-metadata', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- file_path: filePath,
- ...data
- })
- });
-
- if (!response.ok) {
- throw new Error('Failed to save metadata');
- }
-
- return response.json();
-}
+import { saveModelMetadata } from '../../api/loraApi.js';
/**
* 设置模型名称编辑功能
diff --git a/static/js/components/loraModal/PresetTags.js b/static/js/components/loraModal/PresetTags.js
index a6abab2b..4e331159 100644
--- a/static/js/components/loraModal/PresetTags.js
+++ b/static/js/components/loraModal/PresetTags.js
@@ -2,8 +2,7 @@
* PresetTags.js
* 处理LoRA模型预设参数标签相关的功能模块
*/
-import { saveModelMetadata } from './ModelMetadata.js';
-import { showToast } from '../../utils/uiHelpers.js';
+import { saveModelMetadata } from '../../api/loraApi.js';
/**
* 解析预设参数
diff --git a/static/js/components/loraModal/TriggerWords.js b/static/js/components/loraModal/TriggerWords.js
index e80c9e39..5c9004f3 100644
--- a/static/js/components/loraModal/TriggerWords.js
+++ b/static/js/components/loraModal/TriggerWords.js
@@ -3,7 +3,7 @@
* 处理LoRA模型触发词相关的功能模块
*/
import { showToast, copyToClipboard } from '../../utils/uiHelpers.js';
-import { saveModelMetadata } from './ModelMetadata.js';
+import { saveModelMetadata } from '../../api/loraApi.js';
/**
* 渲染触发词
diff --git a/static/js/components/loraModal/index.js b/static/js/components/loraModal/index.js
index 69e71675..db9b41e5 100644
--- a/static/js/components/loraModal/index.js
+++ b/static/js/components/loraModal/index.js
@@ -13,9 +13,9 @@ import { loadRecipesForLora } from './RecipeTab.js'; // Add import for recipe ta
import {
setupModelNameEditing,
setupBaseModelEditing,
- setupFileNameEditing,
- saveModelMetadata
+ setupFileNameEditing
} from './ModelMetadata.js';
+import { saveModelMetadata } from '../../api/loraApi.js';
import { renderCompactTags, setupTagTooltip, formatFileSize } from './utils.js';
import { updateLoraCard } from '../../utils/cardUpdater.js';
From 51a6374c336e312c844b7ac4fa2e6e25e7008f98 Mon Sep 17 00:00:00 2001
From: Will Miao <13051207myq@gmail.com>
Date: Fri, 25 Apr 2025 17:55:33 +0800
Subject: [PATCH 2/2] feat: add favorites filtering functionality across models
and UI components
---
py/routes/api_routes.py | 5 +-
py/routes/checkpoints_routes.py | 15 ++++-
py/services/lora_scanner.py | 11 +++-
py/utils/models.py | 1 +
static/css/components/card.css | 37 +++++++++++-
static/css/layout.css | 16 +++++
static/js/api/baseModelApi.js | 5 ++
static/js/components/CheckpointCard.js | 42 ++++++++++++-
static/js/components/LoraCard.js | 43 ++++++++++++-
.../js/components/controls/LorasControls.js | 1 -
static/js/components/controls/PageControls.js | 57 +++++++++++++++++-
static/js/state/index.js | 7 ++-
.../font-awesome/webfonts/fa-regular-400.ttf | Bin 0 -> 67860 bytes
.../webfonts/fa-regular-400.woff2 | Bin 0 -> 25392 bytes
templates/components/controls.html | 5 ++
15 files changed, 232 insertions(+), 13 deletions(-)
create mode 100644 static/vendor/font-awesome/webfonts/fa-regular-400.ttf
create mode 100644 static/vendor/font-awesome/webfonts/fa-regular-400.woff2
diff --git a/py/routes/api_routes.py b/py/routes/api_routes.py
index 06aff4a5..b235459f 100644
--- a/py/routes/api_routes.py
+++ b/py/routes/api_routes.py
@@ -125,6 +125,7 @@ class ApiRoutes:
# Get filter parameters
base_models = request.query.get('base_models', None)
tags = request.query.get('tags', None)
+ favorites_only = request.query.get('favorites_only', 'false').lower() == 'true' # New parameter
# New parameters for recipe filtering
lora_hash = request.query.get('lora_hash', None)
@@ -155,7 +156,8 @@ class ApiRoutes:
base_models=filters.get('base_model', None),
tags=filters.get('tags', None),
search_options=search_options,
- hash_filters=hash_filters
+ hash_filters=hash_filters,
+ favorites_only=favorites_only # Pass favorites_only parameter
)
# Get all available folders from cache
@@ -195,6 +197,7 @@ class ApiRoutes:
"from_civitai": lora.get("from_civitai", True),
"usage_tips": lora.get("usage_tips", ""),
"notes": lora.get("notes", ""),
+ "favorite": lora.get("favorite", False), # Include favorite status in response
"civitai": ModelRouteUtils.filter_civitai_data(lora.get("civitai", {}))
}
diff --git a/py/routes/checkpoints_routes.py b/py/routes/checkpoints_routes.py
index fa4e20ee..46752f28 100644
--- a/py/routes/checkpoints_routes.py
+++ b/py/routes/checkpoints_routes.py
@@ -69,6 +69,7 @@ class CheckpointsRoutes:
fuzzy_search = request.query.get('fuzzy_search', 'false').lower() == 'true'
base_models = request.query.getall('base_model', [])
tags = request.query.getall('tag', [])
+ favorites_only = request.query.get('favorites_only', 'false').lower() == 'true' # Add favorites_only parameter
# Process search options
search_options = {
@@ -101,7 +102,8 @@ class CheckpointsRoutes:
base_models=base_models,
tags=tags,
search_options=search_options,
- hash_filters=hash_filters
+ hash_filters=hash_filters,
+ favorites_only=favorites_only # Pass favorites_only parameter
)
# Format response items
@@ -123,7 +125,8 @@ class CheckpointsRoutes:
async def get_paginated_data(self, page, page_size, sort_by='name',
folder=None, search=None, fuzzy_search=False,
base_models=None, tags=None,
- search_options=None, hash_filters=None):
+ search_options=None, hash_filters=None,
+ favorites_only=False): # Add favorites_only parameter with default False
"""Get paginated and filtered checkpoint data"""
cache = await self.scanner.get_cached_data()
@@ -181,6 +184,13 @@ class CheckpointsRoutes:
if not cp.get('preview_nsfw_level') or cp.get('preview_nsfw_level') < NSFW_LEVELS['R']
]
+ # Apply favorites filtering if enabled
+ if favorites_only:
+ filtered_data = [
+ cp for cp in filtered_data
+ if cp.get('favorite', False) is True
+ ]
+
# Apply folder filtering
if folder is not None:
if search_options.get('recursive', False):
@@ -276,6 +286,7 @@ class CheckpointsRoutes:
"from_civitai": checkpoint.get("from_civitai", True),
"notes": checkpoint.get("notes", ""),
"model_type": checkpoint.get("model_type", "checkpoint"),
+ "favorite": checkpoint.get("favorite", False),
"civitai": ModelRouteUtils.filter_civitai_data(checkpoint.get("civitai", {}))
}
diff --git a/py/services/lora_scanner.py b/py/services/lora_scanner.py
index 9756e8f1..a333b2c3 100644
--- a/py/services/lora_scanner.py
+++ b/py/services/lora_scanner.py
@@ -122,7 +122,8 @@ class LoraScanner(ModelScanner):
async def get_paginated_data(self, page: int, page_size: int, sort_by: str = 'name',
folder: str = None, search: str = None, fuzzy_search: bool = False,
base_models: list = None, tags: list = None,
- search_options: dict = None, hash_filters: dict = None) -> Dict:
+ search_options: dict = None, hash_filters: dict = None,
+ favorites_only: bool = False) -> Dict:
"""Get paginated and filtered lora data
Args:
@@ -136,6 +137,7 @@ class LoraScanner(ModelScanner):
tags: List of tags to filter by
search_options: Dictionary with search options (filename, modelname, tags, recursive)
hash_filters: Dictionary with hash filtering options (single_hash or multiple_hashes)
+ favorites_only: Filter for favorite models only
"""
cache = await self.get_cached_data()
@@ -194,6 +196,13 @@ class LoraScanner(ModelScanner):
if not lora.get('preview_nsfw_level') or lora.get('preview_nsfw_level') < NSFW_LEVELS['R']
]
+ # Apply favorites filtering if enabled
+ if favorites_only:
+ filtered_data = [
+ lora for lora in filtered_data
+ if lora.get('favorite', False) is True
+ ]
+
# Apply folder filtering
if folder is not None:
if search_options.get('recursive', False):
diff --git a/py/utils/models.py b/py/utils/models.py
index 07e392ca..f0f7fc94 100644
--- a/py/utils/models.py
+++ b/py/utils/models.py
@@ -22,6 +22,7 @@ class BaseModelMetadata:
tags: List[str] = None # Model tags
modelDescription: str = "" # Full model description
civitai_deleted: bool = False # Whether deleted from Civitai
+ favorite: bool = False # Whether the model is a favorite
def __post_init__(self):
# Initialize empty lists to avoid mutable default parameter issue
diff --git a/static/css/components/card.css b/static/css/components/card.css
index ed424827..8c172872 100644
--- a/static/css/components/card.css
+++ b/static/css/components/card.css
@@ -192,12 +192,43 @@
margin-left: var(--space-1);
cursor: pointer;
color: white;
- transition: opacity 0.2s;
- font-size: 0.9em;
+ transition: opacity 0.2s, transform 0.15s ease;
+ font-size: 1.0em; /* Increased from 0.9em for better visibility */
+ width: 16px; /* Fixed width for consistent spacing */
+ height: 16px; /* Fixed height for larger touch target */
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+ padding: 4px; /* Add padding to increase clickable area */
+ box-sizing: content-box; /* Ensure padding adds to dimensions */
+ position: relative; /* For proper positioning */
+ margin: 0; /* Reset margin */
+}
+
+.card-actions i::before {
+ position: absolute; /* Position the icon glyph */
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%); /* Center the icon */
+}
+
+.card-actions {
+ display: flex;
+ gap: var(--space-1); /* Use gap instead of margin for spacing between icons */
+ align-items: center;
}
.card-actions i:hover {
- opacity: 0.8;
+ opacity: 0.9;
+ transform: scale(1.1);
+ background-color: rgba(255, 255, 255, 0.1);
+}
+
+/* Style for active favorites */
+.favorite-active {
+ color: #ffc107 !important; /* Gold color for favorites */
+ text-shadow: 0 0 5px rgba(255, 193, 7, 0.5);
}
/* 响应式设计 */
diff --git a/static/css/layout.css b/static/css/layout.css
index d1aa0e1d..72d8120f 100644
--- a/static/css/layout.css
+++ b/static/css/layout.css
@@ -81,6 +81,22 @@
opacity: 1;
}
+/* Controls */
+.control-group button.favorite-filter {
+ position: relative;
+ overflow: hidden;
+}
+
+.control-group button.favorite-filter.active {
+ background: var(--lora-accent);
+ color: white;
+}
+
+.control-group button.favorite-filter i {
+ margin-right: 4px;
+ color: #ffc107;
+}
+
/* Active state for buttons that can be toggled */
.control-group button.active {
background: var(--lora-accent);
diff --git a/static/js/api/baseModelApi.js b/static/js/api/baseModelApi.js
index 8471f1f8..45898601 100644
--- a/static/js/api/baseModelApi.js
+++ b/static/js/api/baseModelApi.js
@@ -45,6 +45,11 @@ export async function loadMoreModels(options = {}) {
params.append('folder', pageState.activeFolder);
}
+ // Add favorites filter parameter if enabled
+ if (pageState.showFavoritesOnly) {
+ params.append('favorites_only', 'true');
+ }
+
// Add search parameters if there's a search term
if (pageState.filters?.search) {
params.append('search', pageState.filters.search);
diff --git a/static/js/components/CheckpointCard.js b/static/js/components/CheckpointCard.js
index 96df010a..a07aee43 100644
--- a/static/js/components/CheckpointCard.js
+++ b/static/js/components/CheckpointCard.js
@@ -2,7 +2,7 @@ import { showToast, copyToClipboard } from '../utils/uiHelpers.js';
import { state } from '../state/index.js';
import { showCheckpointModal } from './checkpointModal/index.js';
import { NSFW_LEVELS } from '../utils/constants.js';
-import { replaceCheckpointPreview as apiReplaceCheckpointPreview } from '../api/checkpointApi.js';
+import { replaceCheckpointPreview as apiReplaceCheckpointPreview, saveModelMetadata } from '../api/checkpointApi.js';
export function createCheckpointCard(checkpoint) {
const card = document.createElement('div');
@@ -17,6 +17,7 @@ export function createCheckpointCard(checkpoint) {
card.dataset.from_civitai = checkpoint.from_civitai;
card.dataset.notes = checkpoint.notes || '';
card.dataset.base_model = checkpoint.base_model || 'Unknown';
+ card.dataset.favorite = checkpoint.favorite ? 'true' : 'false';
// Store metadata if available
if (checkpoint.civitai) {
@@ -65,6 +66,9 @@ export function createCheckpointCard(checkpoint) {
const isVideo = previewUrl.endsWith('.mp4');
const videoAttrs = autoplayOnHover ? 'controls muted loop' : 'controls autoplay muted loop';
+ // Get favorite status from checkpoint data
+ const isFavorite = checkpoint.favorite === true;
+
card.innerHTML = `
${isVideo ?
@@ -82,6 +86,9 @@ export function createCheckpointCard(checkpoint) {
${checkpoint.base_model}
+
+
@@ -198,6 +205,39 @@ export function createCheckpointCard(checkpoint) {
});
}
+ // Favorite button click event
+ card.querySelector('.fa-star')?.addEventListener('click', async e => {
+ e.stopPropagation();
+ const starIcon = e.currentTarget;
+ const isFavorite = starIcon.classList.contains('fas');
+ const newFavoriteState = !isFavorite;
+
+ try {
+ // Save the new favorite state to the server
+ await saveModelMetadata(card.dataset.filepath, {
+ favorite: newFavoriteState
+ });
+
+ // Update the UI
+ if (newFavoriteState) {
+ starIcon.classList.remove('far');
+ starIcon.classList.add('fas', 'favorite-active');
+ starIcon.title = 'Remove from favorites';
+ card.dataset.favorite = 'true';
+ showToast('Added to favorites', 'success');
+ } else {
+ starIcon.classList.remove('fas', 'favorite-active');
+ starIcon.classList.add('far');
+ starIcon.title = 'Add to favorites';
+ card.dataset.favorite = 'false';
+ showToast('Removed from favorites', 'success');
+ }
+ } catch (error) {
+ console.error('Failed to update favorite status:', error);
+ showToast('Failed to update favorite status', 'error');
+ }
+ });
+
// Copy button click event
card.querySelector('.fa-copy')?.addEventListener('click', async e => {
e.stopPropagation();
diff --git a/static/js/components/LoraCard.js b/static/js/components/LoraCard.js
index fb97aadb..b5add51d 100644
--- a/static/js/components/LoraCard.js
+++ b/static/js/components/LoraCard.js
@@ -3,7 +3,7 @@ import { state } from '../state/index.js';
import { showLoraModal } from './loraModal/index.js';
import { bulkManager } from '../managers/BulkManager.js';
import { NSFW_LEVELS } from '../utils/constants.js';
-import { replacePreview, deleteModel } from '../api/loraApi.js'
+import { replacePreview, deleteModel, saveModelMetadata } from '../api/loraApi.js'
export function createLoraCard(lora) {
const card = document.createElement('div');
@@ -20,6 +20,7 @@ export function createLoraCard(lora) {
card.dataset.usage_tips = lora.usage_tips;
card.dataset.notes = lora.notes;
card.dataset.meta = JSON.stringify(lora.civitai || {});
+ card.dataset.favorite = lora.favorite ? 'true' : 'false';
// Store tags and model description
if (lora.tags && Array.isArray(lora.tags)) {
@@ -65,6 +66,9 @@ export function createLoraCard(lora) {
const isVideo = previewUrl.endsWith('.mp4');
const videoAttrs = autoplayOnHover ? 'controls muted loop' : 'controls autoplay muted loop';
+ // Get favorite status from the lora data
+ const isFavorite = lora.favorite === true;
+
card.innerHTML = `
${isVideo ?
@@ -82,6 +86,9 @@ export function createLoraCard(lora) {
${lora.base_model}
+
+
@@ -135,6 +142,7 @@ export function createLoraCard(lora) {
base_model: card.dataset.base_model,
usage_tips: card.dataset.usage_tips,
notes: card.dataset.notes,
+ favorite: card.dataset.favorite === 'true',
// Parse civitai metadata from the card's dataset
civitai: (() => {
try {
@@ -198,6 +206,39 @@ export function createLoraCard(lora) {
});
}
+ // Favorite button click event
+ card.querySelector('.fa-star')?.addEventListener('click', async e => {
+ e.stopPropagation();
+ const starIcon = e.currentTarget;
+ const isFavorite = starIcon.classList.contains('fas');
+ const newFavoriteState = !isFavorite;
+
+ try {
+ // Save the new favorite state to the server
+ await saveModelMetadata(card.dataset.filepath, {
+ favorite: newFavoriteState
+ });
+
+ // Update the UI
+ if (newFavoriteState) {
+ starIcon.classList.remove('far');
+ starIcon.classList.add('fas', 'favorite-active');
+ starIcon.title = 'Remove from favorites';
+ card.dataset.favorite = 'true';
+ showToast('Added to favorites', 'success');
+ } else {
+ starIcon.classList.remove('fas', 'favorite-active');
+ starIcon.classList.add('far');
+ starIcon.title = 'Add to favorites';
+ card.dataset.favorite = 'false';
+ showToast('Removed from favorites', 'success');
+ }
+ } catch (error) {
+ console.error('Failed to update favorite status:', error);
+ showToast('Failed to update favorite status', 'error');
+ }
+ });
+
// Copy button click event
card.querySelector('.fa-copy')?.addEventListener('click', async e => {
e.stopPropagation();
diff --git a/static/js/components/controls/LorasControls.js b/static/js/components/controls/LorasControls.js
index 0a45a9e6..521a1cee 100644
--- a/static/js/components/controls/LorasControls.js
+++ b/static/js/components/controls/LorasControls.js
@@ -2,7 +2,6 @@
import { PageControls } from './PageControls.js';
import { loadMoreLoras, fetchCivitai, resetAndReload, refreshLoras } from '../../api/loraApi.js';
import { getSessionItem, removeSessionItem } from '../../utils/storageHelpers.js';
-import { showToast } from '../../utils/uiHelpers.js';
/**
* LorasControls class - Extends PageControls for LoRA-specific functionality
diff --git a/static/js/components/controls/PageControls.js b/static/js/components/controls/PageControls.js
index b30151c1..caf101d5 100644
--- a/static/js/components/controls/PageControls.js
+++ b/static/js/components/controls/PageControls.js
@@ -1,6 +1,6 @@
// PageControls.js - Manages controls for both LoRAs and Checkpoints pages
import { state, getCurrentPageState, setCurrentPageType } from '../../state/index.js';
-import { getStorageItem, setStorageItem } from '../../utils/storageHelpers.js';
+import { getStorageItem, setStorageItem, getSessionItem, setSessionItem } from '../../utils/storageHelpers.js';
import { showToast } from '../../utils/uiHelpers.js';
/**
@@ -26,6 +26,9 @@ export class PageControls {
// Initialize event listeners
this.initEventListeners();
+ // Initialize favorites filter button state
+ this.initFavoritesFilter();
+
console.log(`PageControls initialized for ${pageType} page`);
}
@@ -121,6 +124,12 @@ export class PageControls {
bulkButton.addEventListener('click', () => this.toggleBulkMode());
}
}
+
+ // Favorites filter button handler
+ const favoriteFilterBtn = document.getElementById('favoriteFilterBtn');
+ if (favoriteFilterBtn) {
+ favoriteFilterBtn.addEventListener('click', () => this.toggleFavoritesOnly());
+ }
}
/**
@@ -385,4 +394,50 @@ export class PageControls {
showToast('Failed to clear custom filter: ' + error.message, 'error');
}
}
+
+ /**
+ * Initialize the favorites filter button state
+ */
+ initFavoritesFilter() {
+ const favoriteFilterBtn = document.getElementById('favoriteFilterBtn');
+ if (favoriteFilterBtn) {
+ // Get current state from session storage with page-specific key
+ const storageKey = `show_favorites_only_${this.pageType}`;
+ const showFavoritesOnly = getSessionItem(storageKey, false);
+
+ // Update button state
+ if (showFavoritesOnly) {
+ favoriteFilterBtn.classList.add('active');
+ }
+
+ // Update app state
+ this.pageState.showFavoritesOnly = showFavoritesOnly;
+ }
+ }
+
+ /**
+ * Toggle favorites-only filter and reload models
+ */
+ async toggleFavoritesOnly() {
+ const favoriteFilterBtn = document.getElementById('favoriteFilterBtn');
+
+ // Toggle the filter state in storage
+ const storageKey = `show_favorites_only_${this.pageType}`;
+ const currentState = this.pageState.showFavoritesOnly;
+ const newState = !currentState;
+
+ // Update session storage
+ setSessionItem(storageKey, newState);
+
+ // Update state
+ this.pageState.showFavoritesOnly = newState;
+
+ // Update button appearance
+ if (favoriteFilterBtn) {
+ favoriteFilterBtn.classList.toggle('active', newState);
+ }
+
+ // Reload models with new filter
+ await this.resetAndReload(true);
+ }
}
\ No newline at end of file
diff --git a/static/js/state/index.js b/static/js/state/index.js
index a498e57b..23b41226 100644
--- a/static/js/state/index.js
+++ b/static/js/state/index.js
@@ -42,6 +42,7 @@ export const state = {
bulkMode: false,
selectedLoras: new Set(),
loraMetadataCache: new Map(),
+ showFavoritesOnly: false,
},
recipes: {
@@ -61,7 +62,8 @@ export const state = {
tags: [],
search: ''
},
- pageSize: 20
+ pageSize: 20,
+ showFavoritesOnly: false,
},
checkpoints: {
@@ -80,7 +82,8 @@ export const state = {
filters: {
baseModel: [],
tags: []
- }
+ },
+ showFavoritesOnly: false,
}
},
diff --git a/static/vendor/font-awesome/webfonts/fa-regular-400.ttf b/static/vendor/font-awesome/webfonts/fa-regular-400.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..549d68dc023ff6e31b8774d784c2cfcc231e7976
GIT binary patch
literal 67860
zcmeFa34C1Dc{hB{o#oEHFWNQINHek}+cVlmwy_zEZ43q?%;vC)1cKR>vcyTCQ5Kd2
z!Ye2tG$oA^2z3+ExFl~%7FuO%NK0BbO(`L1s%(^|U;2)O<&cDq-~a!dduK+rY;4-(
z>-YUS(sS>*+qvgF=Q+=Io^!5{LI|Ij5)P4h-PvmfFTMVfON5YpI6Lvi>n@u-?XeH<
z6T*114eh@g!43q3+bN#dUjoR
z)9!b6h@T_ByM%CzUwgwFFZ+{!z43V=+IHgHC$76}_oVo-xrnp}_h+uZ?7Azi*zAo9
z@#_HI>6*OZ#+%lD>(9R_#KD9R-l{-Z9WIyn%+4FzE)AcsDEtN<6XHk3Z`^M0559cx
zN#joWG>$z&Uq<%$fBN8)LKsuH{<3i=&n)?UN1geO`H~2#^SmyX+~t(}%5=!Fe(%A9wfCdGDqU9Z%`}#?mdC8f)b1DRzIIh7bRM2x
zQqEa*y`h|{d;#Wb<&iPUI?ep;w4FcmRdvEW>bj^sXFbpMww~ubvkDYl0w<(VtE^W0
zi9?@1+A-Re_fH+|o@2ZQTCwYdEWx9M1JI+>o?#rZU-w||1A7=2P7gY&dva
zy`o>N6&Hxh#2dsL#pU7(@g{Ml*d?wKSBq=JwcqhduH0Igs@zezvvODEy_NS>
z9;iH6`C#S4m6^)NDt}b@dgbZLT;->g7b^c&d8u-s@{7vLmFj$8K02S6PtLc@x6QZD
zXXpFoSI!U3Z=T;ef6Dx6^JmWQm_K*^qWLT4@0h=LetQ1?`3L7eHUG`|Z_Ph9|K0iT
z&Hs4*XY&Wg|{udec{~;cQ4$t@cxB+
z7w%v9z{0}|pIi8|h0ia1Vd0AlUs`x-;m;Rl7rwIa)rD^_d}rbN3qOC!c&YED3t#&9
zOMkaNvcGr#y8S2aKWYET`?u^rW&cI{Z`=Q={m<`zVR2xwxH!6a(&BlGS1!J7@t(zp
z7eBuE*~KRpXBWS+`1HYpXyq=E6)QzSobcbMHJz{0n&%(Znr}a1Yd(tB{D#$$9nitMrK7YGv&HGeqe)$Nkc}lG{&p~Ux8Lj!&Wm@yIhqdOH(VF`jT65=d
zwB}nE-?8}M;-ib7ski3Axr6_F@OuaU@!&t8UH@PIb$qg8NQlU4z$z9d(xHnKNkrli
zU-;MI13V5>15&^6zVP3MzZHHa{EhI}!hgY2;imwfQK?Uc{}}1V!tV{=6@Ifivnza=
z+Fu-ojuhUi(&g}p-18S+5pE7QVV?}g!!hiAVMj>4D+IkNG#mO<=o6tC-Whr@G#$Df
z`*#Cw43)4Q1{6YTLaRfqNTtfH<_k{>d2CfZU6Sz8XRbW@(92`6S&+!TU^Iz;g!+(N*%)iFp=Wq2l`J?`b->>+u
zb%%WJ`2UP&IP*5PlpQ7{Vf3FQ2&T#(e?If1j#
zc&+0;32DWj?8atBrP^@R!W4fj7vAk@7e6)_PP36T^jkrqwRid#ghXcO(CLv$jKF3}B+
z+yfb#6M5vi0^GS@42V^D#~LvxhQzQai7~Mr9D9Q}No*1)i!EX+bmh~Xn?o1T(2o68
z7Lfm3Z9ymY*I1Cj{#pxIkLxTT6vV$!J=jlLzS
zLDK}{pm#4?@b^aBk0Zg-M1mZG>HY0$tL=N~Rfc8WVZA$>AFLJyOz;fo8FF>WALK;9k
z{)HGtklKA-`VFY-kVq|f|j4WNCI->N~S&qf-6J}dI)Xi({Mkp_@g
z=TRntN`qGOq*s(V{{ao?_oBa51A36?Z_}XCpr?KS>(UQg6VS`W4^dVE`^^v0ZUlky
zRva2wuZk0Zvh!UPyo11JE9hec@Cs2uxd~zhd-O2^`%opVL5yIJzC=Ku7ZsF~fSxZZ
zXj=j}iKw7I5X30oMh&8j{mmM{Cq?D$fLrnGChVs)
z2$t~<4d9}pa;FCJlFD5g$V)2k)c`Dxe#J!ubP81H>uVKd6EIrSd@y3MU^%8bEJG
zWk!QI75k5A0LK=UM*)9?drrgt>l(zF*nb1?G|s;c`#BA&-G7QSK-KF7qyeh@KSTQ8
zklum)OBz)E2apDkS5|(Zfqbs=vIfWuQ2~7sAU(u9`UrtEIUm)a(g~yi#Nm8W1LTjG
z2ald7{p`fPO#}JQe7gqH$$VA=aX8a_)u7Vf
zLmHrP`(va5d4|F6Muw0a}Gv@M#dYVGlelfY!wC
zVh=nK#1!^tXb|tj9=IY~{h#MEVizL4O4CN7(f{^vD_C$N7(1Iw^DpaD9rSS)IQ
zwksA#HBjGMM0p5o$HntBh|geur3Ud?>_JP5Z^J$R8~b+vNGE99MYQW8a4f!#J^JJ#
z@$(G!d>+qJ_+0s1Z0rhe})3`6S@CP1=K<1{>v4Re&qgp6|ha^{@+(XIU@Ie
zNP$lO0n(r;b^c+bSzq4sQ3XPrjs4>a*cNjC6AH-RKcRP{zr{FDjruE*JkofvWo=@BmPCUqrnCs_u(_gEVMIJ^K$x{{zw=#{O9a
z?ALPfUlma1%Eg~4pv;wvzfwTHCl5da1PJjY_ELcm-^AWfAjB;8XeWRWDDMHM0wKPD
zy-R@*DEk4o0wJEl-m5^MugL>G1p>4!4+IqmSg`UyL;-Coc_6KTeMBAr{sBUK1AEXp
zKnS$$fmQ`V03Qe16bSKE?AsLx*xd3!Mu9-zln1&M2=TYr=M)I>Iqdrt2==1`0}9wr
z<$+ZS*dOJA)e6|}w1d0>+Q>JRe3W(7igOUPf$DNuCsiytEWLzEx%
z^NSxT(CHr|{bTF=PmunJmHsKxKSg>k_Ae;V>3>1`U##;lBK@M3M*IHaU#&FS_ZL62
z(*K6^zv=WZfpdVO!(WDw4q53Iq+5`_9s8^TA<+MRxk7;upuu0F`~dyz&yoH)(w70o
zA#Hyqe@8wkUxC-=dgEc^2aZ9<)s8PX4QI~znDgIV3D-u~<*vVUpXPp>`+2j?e1kb-
ze%sUSImh!Z&y${?d41kZ-rw0;`@Yu*uT?%kN*k(zXvjbt$|wte-rEoJ`{W<
zbbaW#@crR`kF1Q`8|{w%VayYIH2#J}TjI&&K=N(Lze%l5{V3g&{;Q_ZrcX3q+p@Z4
zf9n_8`r009d%k_JeYWGwj?ZP^mtE-D)bq36>0EdIwERq;r|+V^Cs+LLO83f%m0##j
z^k2|FJMcTJdRFaUeeLS6tr=VM{lfi4U-8!B!rvpc2Ssz$`?fU0Vc;AN28(!IX(}{g2zFdCxq=}QhziI2HzdCv2$q%0V^5*L|
ze}7Bg*0!y0JEiTE$G5H9_JvbVICbjOhfedI_WkXu4W4_^PS3*L0$kQ@^2KO+C_9udnx%9;9mOs?luZ{)U8AL9akSJ`Or=ve
z?Ka(JS235*<)eig@oRtKg%@tTahl2VRI>Ul84M+4CJ_osrmAyHwLFGj^|LDK#9jv+`q*F=yc&Dbp+t6-&j@v`(ecZZlitIXmUf
z=Srm_&)KP5eze4AvcQE-jiybrWU6ydC5`Y65i^x^gic{0&T2oCyYr(w6~005wl!SY
ziJRT+?d>A;p1o95=+q+#+Tw+$y9ADqSy{95?Yro(8{i!F>Li``ka
zlpAfvKh^|)JcE|QpLL~J983=mB^7>0(8~DJ*Ym6w{&=P|%4gM;ktg|jm$Rp{%j*sV
zQ;FuLCS$lG=yW-pon51`rlx4pZ^n~ttqF(WK$TNQd-G86kXv2hXnT7{XH(L2COcNj
zJw!yoh$U8bHZ=tUrpFzKcs-tIDi|~!ZnwuwBz<1fbjU&8Sd_l#$kH2=UcX^DU2aD*
zWqMpiY7rPwe}{YrXsC&PJc(MFvIuOHq)XM_R5dEeB?oT~g`&~wEVr^;Jt)&nO=)Ru
z#ge1fMWdlmb(ULM4!4E_fpDwd9u$p7c=f?w9h{SQfSwDav~;pYXXzr^BcCrO(Wi3x
zks58~i@lrN?r{NPYM$Uo;Us=bT_7>O1Sh#L=N=
zK=V>W4p!t|ISmi3?o~<9t{Zq5LJ8QvMnLbQr5sv*i57A23G{2Wd8rt+>pG-s7=0)Z4D5<}L-Ebytktev+>Xn;
zLMU#Th+I+q4C_abyH(!5L_4yM{m9$RoE1-C-)D_@M)M2B9q~@S3D|sKWqS|lq^EzQ
zc|u>_kOPnXJ@c(rB5s%e@7ttoY0aB*QnvMw@Us2V$oHMFVdcZ>)uFKeFWdU^tu0cO
z;n!9fU?%~~iYHs|WeLc&y75k%g4njiv?>R&t#}x5o&q_z-Tn{sjrHcF%=M04*wNG+
zlAc)7Z$!H;>`6CC*_7_NT;23Fpa2|IqAdT5_wt!YxW$?Ddt%NGb)$NK*Nv1a=YOD_
zQ}Qu*|4~k~gM;lbv<#)8RHTV)vGkNTo(N2%y9X8Z^$|yX#KFGX2$zl0x7Og`=vHg
z9`&21%*cD`4a9n5U(N5wFJ&Lr%*Z(#{`2-lBi>=JG{VZ_rTE?hX0T
zAx%cQyNr^6D2`-=q&}E&HQRmScTLmo2i(gQcSmB8z^S@7WZj*4yIXuj+I@=q^1vq}
zfk^BE;tMkNd3iEuIbqS!AYO$w0v_T@qLD%(=nL*jFcI@T|AMA~Kp;?^jCq3zd97+^
zSO6A1cEbz#Mr5pOM!rJMf~~5brb6^bNJTYX|_N=>=&)d&%UpVxH-LBtNJVCW9xO-7?QZ(HN3r;Mg-C4U1x&{{GC>&$6
zN}-zbnA`Q&@^*PU+f}AaSJ9AALKj_&W2IzY3MHIy^MiW`rPXo+J}=V&Q@xI$T$sdRUyJDrlUa~Kqc@`py6A?15}
zMl++mJ?hNF5@l20jmKQMRCn`AgS9rIzV28{ONWb($e!L&g`n9(6jAEBvj$_VPNVl!
zY6(d)Vc#LyXyP9&>2tO;wOItu6OxeN)P@I3b{*4rRCU1=I+qkX*RMB>_3L-C6x-IW
z>+Y7ayL;W*ZC1LaC0}gGjt=SbLjxHzm@R_c7J6QfU?6g6vIL~A3B{E0ZvrtvPSUv=?uDBBttYl?)W3`d&oA?Jw4JJHTy
zCf8nj(@i%?^IDsC;OGH6eUB;S3?JFEM?H+&d)qt0*-_^3^fazoOsCFl%xRWi9{lCO
z=j5G`AMmltT((%W?H{ywqgIc`#viDMnx))1x@F7g;2^gVC?Er4_zet%@Eg!+=o=Av
z!tn6u)~%z%!#IldhwxodlI&@cvz+jor-3A;`l9BPO5(9u3Lc;YUFkV$D)_nMOT@LrEPsjgf1
z;xRc66x8I7F3F*7Ngb9NHA|=TS=E#(U2C=q`$EN(PjdgHH1%;~`>^jz^C9l;LZPrz
zZBcu7_7FN+qCp?>Mn1jVn`ZW@*Dw2yS>SrG_6~9Ywk~T%!lcBed?9`txP-Fr=4?!F
zE);wp;2S;?^#`PPj1
zr#7~uIF^)gwvob2EZYRK4jETpwv{m@3j1v3OM2?`@?Cif`feBMK?Fr>_7>Qg;%#}_
zgvweOsY%W82f_$3`_Knz6a0(i(5OtYZnunoC+ZK!pZ_4c=D$3vaFwz2Fo;HIA;b%N
zAZdj)7)u$?@yQA|s?c}oW{}gWkh(w3!fxup^P}l_F;3-Ed3%@xWNEe=Rt3F3I>%_#
z_u0?-z&)7H!zK)MZLUP*N1UPR|B=y<(;d}~Da$|i`+r`AC5v}YN8MK2{u6k~BZ}KY
zD?!f0JM#zMYIsj1?3X#!a^bMQ`n*oz9f9g2Qn^&BlM&PDmzPwZ(D_G9QmXeT@)JAhzAc@R_)vI-*A#^SL%@z9kn(*uEE7VhSvb|$?Wc~V);f{8>
zBt6-bUa@lZ>Xj?#G_X^9Hw+DRc64+O4Q;5W?DCaSzNoU7m#D-fK?lyTv$ITEU7?6q
zy=-i9a&kbwd|p=c6C8{l0}oARLGHQqz6JBJoGQZQ(V
zqtDB4C>)K!18D26w(Y22P7dQbjL85yP;9mPqCnm;+J7N9N%+E*HQp@?W7`^ejm1RP
zjRq0RI66xPCEKovg8^Mr*le?_C-as#XngX5jVFQegv(aDvM1svEC{nwqYV
z2MR6Z9WKeUN(2y1*S{qckNU4z-HxWr=hjW_o|>AH=2UNUvy{!vy&f$LX7>_f4?V^{
z_x?anPjOu?FX#3^qTwFi3l?(lm4n}vo6*NHwvSRlr^Vl3x}f+7(!ioy*?hTtH~J*~
zJG?{vp`_a9)}1f#8vjb^qN;~KhuhkEHl&k&hsOh_n>UqkNeM{^51cnzztQJaH#(iX)o?nIU&4WR8%<4K43TP{
zLK%@%IH9g!`Ei$U?P<1zBFa4$x4ujD4C&F)9Xm!x$$7x4UsR`Y`9;3rMRfr;;E3;0K7`xl$F)st^FZpM$lU>-TsY~w
z|9+)$nqitD3HgU+qWW=pA(hSQB-Kwjo%E@^UO6~tY(}(c56W!u(^BfNJeha5$~%(u
zz2%MWXw?12x8Mz~X)_veUv`;05_R9_O307664n1LPm4$yseVpwPq_F#XrB>za`mYQ
zcKWqSPyDXZ6CpE|&(j1QV@^Iw|t4SRBHg?4a9}LGL!9Vx|c3(R!---?wdE$vk
zFdX~*=h^u_W2IHy=m(=NN~=)kZLy}B6wCLcV+9^z3p^5t#zPi!9c`x*E=heTP?A=y3YP+PMYRZx8{OFU(>&|#@r0Y~svk=CHfB)hu0
zXn3(0BjwguOv+fSRZiKdKkQF-yeA^V@9~+d0@06#WpzT%5M;PIg+`j;?bTWPb}5+Z
zPnN?H_sei*VtOwPiM_z>|0l*~fX@%Z2l@ug63nNe6zGlzCI*y}?b4CMG|}gV8LpLw
zcme#c04(z|dMi1ivd=NXgnp|nbDrgOvsl2MLitAyhvU<0r0mL!Y#a>v-5!^FaN|g(
zOUgB$b_Ao*U=Y}CYHG=*V6bOC{b_iPQ>koAQxkA(`d|tghVjU%lN_UcdA~pF4VrG3
zGwbvB^^H1CTJ;F@TzJ6}36IBRwzl*-209;kq;tU0+tO;fJf1`XJP&d*dGJd{KWy|q
z`Wc`trm3}or<>e^H)F)i@&yAKKtM^=D2cHkR-gBnJcH+Wy~J%kFEe3t^Y4dUPTvhJ
zC`RzT?+pe5M#~L8rz`yXo6YdZ$cQU=%XuD|yxQ--Dq(ofyCvuv!I)L>{V7K%8Oit2h;_n&JL?#zEQ3Hz^+CEX{~6)fgV5xt2L1&b{=lacG2fN0#{7FuMYxT
zZTCf^9Ua}>9UW2p?9>{lX&;l9c(Of%7Y$~6JVj%0X!}LmhXxI=Cs@5T=<&+(s-{$`
zX_ek)+uLK&Xso?GTTfN@7CSrPo9QgZ_#kt@b9kP0q@6zt`kYmI->I-o5{>HJ$S~?@
zSFlSfJj!W;)2v^P!;1JT!JZCW)ho0bjBW*vckSvL}r
zM?A=PQ*R;WYEPSoX9i{_!*Z6}YPqcVV~yce%Vc;Hjjk{F@8?*(BW$)cy4Ny|4`P%g
zTve~ZsGE$~Jhg$lmdlL~^Cg6d!xH7jY?nL(oI@Y5`pPQhEz)K!?Y0I;P2PVG)WR|rPEJ-J-AwEo=7F1u_+e&woF{Y@i7+sC))
z@zB2N4~sil?8_73w(gdoHz}K99xHhw`tX-%-x4Wo)dnsWIUZGPN7kxA*9b%F^rVb*p}P
zxE$_Eqlcm^q8mbhkfL07nSp?Lc83SsaTlm;syb!2dtC@ur^XwHT{WGUd%fOk`+dGi
zH>ss?h#XdAIomj@Yw^fey081#;zPcSI{~dSSvHA{|dtFqz9{Kv$zaDh8
zJAJ;;<+0$`lgU7;y**X^v2Ccx2vel=fInKTz}18OBxz?--i|DzQDhkkeO+fMqwCzR
ze?gX^=;a}w*U|0@ex3ORlF2+xnrg+)S7)KKlM(Fs3N-g2eD*wGX)s-+Ok5&1G`Edv
zGPDeCHmqqG;%SE)Ox8x8&2E80x=Plpp;x4ymeX}^
zT~8gEZ_Bd|&qL>TSiMhqt@TzTqIJzYRf31UUcGv%UcGwi&8*}Ns*>esbn3XPr=Gk)
zp{g7TO<8(DO=e!o*+0F*;
z>IL|kC8F)i+J(yp>>G5z4Y(k1sg#oLVj-Kth#M`%M6sa8Q%8>6-DIZN*Voyei2MEV
zM0;mnU-8i6FZUD*8#WXQJy{NJy8`KSw&9H5`dqm(>VZDvg06gM8ED0ZZ?$t6}vN
zSZzGk(c0dgPPezWcEsZH(BrP|G^1$J-CfyiTOiPu%`S81pP4^j>hX}f8yKL%!Ln*3
z>?Dkbd>$)*d_&2bQ!(QKvDV5Km6p<`unv6dKVmNroZ4tt$F(t9STX55`O}~pm`<2H
zuD8f2BIEFY)u6O-^0=M@mrtn17%b)E;XuR~=k}`hL;@}q8>vJ6E4wB?IC{F>2EJIV
zc{IfFz+!gww|4BcCJPbOmWi-AZy
zTrF_BqchXl+?bv~}k#D-630lC7;RD_hmTrR?bWXt`VthILzpgO7G~b-|@zwO)Hq
z`|6&0Mn=chfq?;U*MQT>Axt+{QLuEPA!wmnFtg`;F#!pAlXwTBo3&?KM-}tW5F!V1l1V7#^l0NCi)2HBTxO_tpYj3p)ieYp
zxb>Twz;D_<8kgbnI8IGNPZ*!(B})n8laT^qxDiAoqc2xeSLez
z#&!Xx_!Y*+X8QX2!jgk*{K;_N%orT2hT;>{jhZ+EimGAFwM#k#cf}TQ5kyqOKn{?{
z%k1{t7P5t`J6kArYef_HYD@dpL{nekQoIf?zLfi(pmca!LRUJBs0)U3<1nvEj}_wC
zG=h%YCY;B9rvv_7!?8Bx2|LylHf}7ead2Pk3nyDsLEj3ev!!=<%epmd+FI#vT(f4K
zPRBw~pWBeIu-zVqll+i6^PHsv>L?s?FzrpzV}en3BRh7qq`fe=@xQOJ%bC1hY#nqG)yJ+*c>!yJjOr8;h}_ZujTH}aH>o8_n&%d
zf4}5D5o07%QwvAwODkeo29$AcTInHONRq1U>Sz#
zcOXNjj7D2Mj#Z2VR6Z#=Ccgqd2ci*`wdJP91?@`tmw;X6g;Ej8ykdKyq6qzwGVnm7
zv&jn&OFOIp-nev<}-@#b_f&fn1MUwkEeKhI=HXj0V+|iYQQgt07@J=o|HeL%2*^iuF^u
zAa^`#L%T
zo>ama$>oPq9%ll|4fFMf!@2zG)dR_RBII;I6I}jmfjUgU*W83RtXh?bhg=><{nWbE
z)~P!K9UXlMrzbU(&qbVxlt=k}=ZrG=6XTT!F%#=F)v9_ZeYDh-R>Dttpo>Q_o~>bW
zWlRp~A#;^BzzM71LUNYOk*svCTCmxZoV8QcG6sqfiLEwM+)krL?2xn*`E2KFY`6K;nIYg(iYPN@I1fzlUm~-6B=APLiEdd*VD8=>o9K=CfH8rCC
z@kA+M>X&AVNfV_|Csir3a`uTQo-i`gtmPc~OOuMxN~I9Kl=g%|M2HNBWu`T`v+g
z!{~P6D%-l;bV^~3VXP^f(geKAa6!qna>Ot)Jj`IY97>RVzDbrn9yAGjb?h~W2rKk;
zbOxNR&Ui4Ijz-g3kKzM;edB04yn%1^c=}?#0B_rBiFKh;z(@}
zhir7C4T!4Il4W_a4K!9YX)#)pJV{5-v1PRFI)cFrwgE*eTa4{6q+)g%*J{PKDzQb<
zg}UJ2ONO*C&L`6PrBGgL5?YmFsQmxWCSV)DMxg&OjDBzfW^dok*~D@5a5@I;2%)r!
z$4IgjbRVGqYqUfePG^EUYsWY#ipEF~+kj(4yo+r>{sE=I0v_vjbKMu;1-WWT1E<9}D$vTjV;jOGaZDEa0?LXG&(-wzAYd@Cxp?bjxWC$N4
z#cT5gXXKP6pznc6f}|4cq0OPhJvJwwvJ>i30e6vHGnq+{wg}73&sZ|^nFJmqGqwyX
zWtG*9EGh%~iGue1qi!?k@mn#kTf{NqXUp?p1g%g4*5x+~Yg=2&1zM{HtE9)?$SJbu
zqfi=YG@whPq;%oCx*Mz|7Vkhy{Iu@VVAIT|3<#p}>FEsoff)`Es_jH|78AKtB^OOR
z-MVNaKB}k1cOf{6-hc|+f3bLv-fFQ9yH?eY<8QXEO=W~tIf^o7sUhm+EX2|`
zv-Bt&5MLk9T6TnjWx)7~rC&r5QL@p}p`gM}%fqP*CUHXCaL9#1^%brhZ9c+zxT
zrRL^RtiiZpHEB1^@ZcMn7540g6&a7OHN)ZhmS(@df2|$Rx3=H!lg%w0!_Tz(Ja6Ub
zp`8cOU!7t`kqGky-6;Qo{3>uWfE5bP0M%$sK{F7=CK<(~y^3_q!1G7^V3Yxb1G!G*
zaViAeN?M$e?(C@%b7-`&EEL`Tuon&hISoVK6NZzZCp!iTVdQRKbss_)5a0`!-c&r^
z(h`r$SzL<;`uo>6Z!K{W17Za)8>NeyKJbWWd&%i&Q=`KO{uo^bUoF0$7?(LYEK>e_4=LvlUlxA{s%Ar
zgO{bf`sftVh0EFwVl0n6Qi8p<9W}e4BHoyy0tn^03(8~E;vSeBq<)9)i{Ll*&ra$6
z(j(qupRrT+vAws?*YCBj*!v^j@teAGJa0by-rwe#ItC7Z>wo@QJ%@VgXz!`t_1`P?
zYNc8J`fEE&4T617_quv&w4`hmPE291)5?Z2YjG5%D}g2pZDDpxZmD+Yt=)rp$BP_z
z*~yI>N5RO#Z~oWn*=4_QC*QjF<()ebPo_M)V|r_(c2;-c>~E?thraDDBegdLH#d|(
zDwXA(P1FUOV0ndQLlVXo%Io2#I%n4bDi+Y^;bzKO7eK|-VL)nXUv;k<)k!oo29C7-TMJu7&}+V=0%z9M#I5qTYFz$ds{RZjz+sN`V@pU0Ba!#U-xW1$qUDfosB_+
zAz@%W7{(}LOuG|f7{Lr*uQSx!B~h_zoTc90XJSPzjJ>|LD6X_ykFn`6aOHf_w&`yM
zo1bR9RxsN=FwmV1xe`gQCz}T(>;&L0tHm?LO
zj~qvCIp^${Mn#Am-ozigop8XYu}-=(V8IVznFE;g%DcfsPS{shESa-BG488}|J76D
zhtQWrb{5*6BuAN6n`J2v>mxM;t2d@A$f$;$Of7}8yUA+^`nySBJcn+Us#+)jfnYAr9M*_$Xyb7>HGh#oI#4
z>QM&IIMsIDm{nK+&~?@3%UEk9gLX(IdqOd_ZUPQ3y);~a)bZTrk3{^pdVRj|DlWdT
zs-pwTr8%*rpfn-@EXRR>DgGQTZ>TjM!w36lF6_0<1^oiPMUK~L$jR~PnqIt}v$bEF
zmZT{v$L1k`p3sfWd`-C9aZe-1+o$EwefQl56g$UUL3l*q^2)3+c76*ES{_XS<{
zeoLb?N0&OJPn`g4STo5`8D;;JH1+7v8$EPq4JTnq0e9?nvhPGfJT
z-7@F%?DE0&zK@f4cX@n~I>%^`N_uQgTRUaXq(4lTd)PmTsc_4b`8fF1AbBqOj~ZQJ
z!wtPU8#uJc$G`+$z08<-$H
zalNDja^kq#1$fqT;4pT9`TIqfY@`gex*(VrC9^8K)I*F`@N9BP`8i-qC3Qw|T7kh%
z$k@kP;uokfT&;Pq{b|aKqefG>Z<<7cJ6fyXtC_h|_S{vwEE_Q2LZt2SK$}-8;U7Jm
z+hK+kDK(n~ooEjYSTCcf(iYc4F=cp07ot%gvWn93m~tj)U7D;!r&w~7m=&zfA%}g6
zTx;X&vbPo0z|tF*1e>rE3a@kEK_AE1-+Kh+;HqIevJZeYr?Adaa0VP+kNhExa^p|$
z)z*re#yO4#Q)18nVYI9rG0Rax+*SZm{=r&N1MGLM&og8&)R9){T^^UqjBtVt8q|Jf#zAn
z6#+q}Jja+)O$#bb3CeTat1V|PDJ@NL4AMHKZvqrVV2dCwkNF|htHWui&W*A;&`2Oq
zM793*aNJ%)F`Nc#(+w!bY|ht=1+L0nJ9uC%5Lc0b`_T
z(xswn?1}XhaHuekJ*ZN(fTqrTF|TJ=5|w;JZIKO{NN>?tyj5+jZ1jrIXeGUxkBUIH
z=BuF#KrXX+(42C&BM|VNy#q5N9kDkAT`qI&T99b)4Ka28Y~Nw$uRiIdlfsV3nXA0s
zRcA&V;gdq4LytopEiGIw4a@x1Q{^S`CyyWUD*ujUUe#E%3s$W<((CNkSF4A-QoVd~
z`M8yLqYp$>RE~~TtS?4cax2C|QnuV@=mu<%oC!2$yAi0$j-V!SmI^91iA)5FC}*Jn
z+}ZoO+ChdwvACg^SGT(52lzQ2+U&`+lxg5ivi%g+wF4s
z3GLlp6fElvj`#jHXzk#gIbHuF^1jHqfBr49F8k3dg6IWGF)@@FE>oEEyOIWRAU@sJH9q@
z3SwTa;+KCct&M>|Bs&rz0;Q4W=;zUF&R6yOn6G+=YNv5~7-F2Qc2MY66Smr+HG!Q7
zWaedTrjkFA0J
zb2hMeGrumx(P55Fv9D^rafu4*doDm@QaO**5hAj1^XV=9h9xi|;P(M~SL#-+jnZOkrZK^-1O(ZgI
z;YJ4?X825`Axw?h;s>U+`bHgc5)P{-zCk@+7>ABV-K(J5TenLXw@qQjUsz5|vH-Z3
z8`T2kGMEW{4LXiFjdg>tGHVGjjjZw#WfM3D6&J}{DA{yW66n{`H`6(-l>x6x=BYi|
zs}gB>H}{1G&9gz3*J}i!W-X7EsEHNaSQ)ChncD&=g|5hhVwsBUnT7=@oWieUFTP?>xEeHFtBPBr?vL>Hl~lc
zp8pyNgyU=1awcw}(3n2dhB%d9csza^0_sF9u3T{|9lK|(7pwI>YI`89hL*LvLPN7|
z(@u#2duiCsn=#vVAmG6-5IGjpVjo64m0^;x7meLDhG&rj=vVnUR2)8Sx6Sl(9DiN0
ze!ewSQS)Y`>4s9|tMO!EXpFP2TO2VacXg}T?{-noJYBa*M-?C@IswcU3}SB@U7AJ;5J^mN1+
zT4o~-O!^gZM(aTzL(8W4X597eOLWvxtohq|r4!Fqj7YW_V*$Y?Ut
z;U*L7k|QhY9t&kWKDsGtF*?QdG~3;Vk$*keo*cvD*~v-z5O|s+Hqa1x;2DaB;A3CT
z4d58%I2YKX@~e$mKe?uzjMb
zDUk?zv5ab62Cxs|dv?d~0kLKq*=8VgOlz2nzB9`#RBLCA9E-1H9K?nV>nG7W9bx}MA#3bp>SMC2WyRM`q
zpe+%Og-u*WpLrn^D$s-8**T$i_DPG{k51{;f96BT0NF&Not@o!XP;z!6_0>_U>5T&
z*F&2+AK#ASx+m0MN-C;84TFbiEw4EqMKhFjqvy!35j(zB+q|`g1Rh7vcYrgHS8@?g
z+|DD|qj(ss6uQ$WSOzSQJPgx!j&9#RI-Y86O^t6}YZz;>{!cU-*nH`wo3U(9C=^)k
zTsP(^1cT!PQVuYw9%eS*!2wU=VzzNzqju}$_|dbiziLUYvZww!zM)p&V&jTSh}}L2
z-?|~cjx{;@preYCTlq8@udf5JZOcEduqxBNu3k<|&bxrRQqd8KN+WiU&l?K+Hr^Tz
z1|4N15{XsI!}7f9C**nGmv5J^!>6O3Equuzk9*f$8S?r9rwzMI#NN*&{C@92(el-=
zesxg}-MwYYmj6?I2JH-4X!-j`Fb|=OuRWcPugYG>FNXHwrF0jdDLGey`g7LA7EP{*
zS2e7fUST!2YI+Aad$u?P6T?hnl@P4vnjgZPl@c;br7bpSIU_(a_|wjaG}a~|&45;v
z_#j>78|u=U<4aj
zuGesE$5N{_iC@MikG5lZM4YXU@6}US<-HJe#@&$0-{m^rgHI*p
z))br`({pxre4Wd+F5Z1M4xEKGIl1-$2;?6lU%ta|{1{#}m$Uk{#t?xe>8)OG)*l(8
zDmfPMXNAdeI^}x{BerJ?w6zoId>mv;_pts1m11h?E>0J=CrmUf
zY_rqZFjE4eV^h!XeLmdW+{t0P_>VugVZ(+$&6sO2vjR(LhGj>pK7FELv6?Lbf8)Ff
z$gg;~bkluJw7@WfVsO_ca9^$M)a54->g
zKe3gFM9jZ3F%ZSZIKpZAq}X%9vfqbQJO4^;*6TB%_?|^SFg72DZKg{
zwuC*eH?Srs!XaS(x@C2)@knBMX(JSLu-x*t+A9JMmig-C%e>5KyPQ}vZUM@Z5)!P>u4}uC20jxry{Q4Y+o>Or@Nq)V&!EdaG*eL0`
z;t*Qwpp6Oy9W`q3JgeO?bD-;SG8n|;&9Su<11mE{Pn+8w*r#$iu$?UzzUb?B$B)H9cz@2i?;POQ#V?P4NTmLe*#TYW};`J9Lk+o-U(^)>Q0T
zQOk<^&ZsL^G@-JOYd5Gw1Qx!klPWVS2f9UGGu9(WMv#SF|5~EK-W|paW2`>|%}cL3
zrF)fHLkNY`-d;w8u;A>m|<=S!9rxJ6CC-w4cDI^N%`kE@TmOcS@i
z)Y=`7cegf_e@JXnUv<@E{6xJTO4LoEIlMhPLk}jh@o4XL#i7)b_i(NUDmDTIiR?-@
zt=Y7+z{^)^4_5VSmvge?Ec`lpZYMExxdk=Q|m#n2%BfZ5XU$Z!q2bu3$pl@R$vCPkx-HZ3NPayD5|;T_#sE*pav
zLo(r|c$J--YE~(@G!ELu@D}Lh
z=Z6%_n!B##$YomQctAmv8H*9L`v4&cC8No0LheV&s{y
zi0*e=V{xMl6o)?=F%hs*qt*#ERVieIn$m6lXaH+U$h9HE6+ZW=kjn^tDFWf=`{Lt>
zTr>v1Ay?kzOtisb%sP5j+@@A#LNl=ANa;jzVn#S5-4Z?_CoHU5r$gA|ZEej)
zb1)Qy76Pr(6%HARl}0G+T(QO!;_S
zhPR~7%nW{8kJyn26}{oKv(F4F?(1)-Nv$5FN@bEJIQf=v*0T#Lu+K^MVu<1{ALfmONGBvGOl%nXyrW!H@
zDxAm|rWLaTNj4lI+fU#d*cet%dpd#$2yS5&CRLk
z5c*|vBzlGm-*p&u#*^M5GZb{5>WRgYCkFf;|3zz0!4uw|);8}LTe`)a2qMIy9iQlR
zcDfQV*E)Fv-^F)cv$ZvZRqODX41S^~)%s7v{D8v9DST1V;fw~~-I8wdnT}-0;R^j(
z!t07p1)XN-3r!ftA2fYI-zoiw(9QlJ>~w^7#ih?1crfggp+CYxztO*fTbye?s@VPY
zViK0Yd&LJVIYq{oI)bFKR3%v33gf`B&FW}jaZEJ{Y65z#Zb{Bdy5mqaY0#Et-KIWP
zA6BzCEi$lqZjL^>+97lx(p|)lRUfdA`dl7{ng#q$x2C1B4Q)-$k&{h-(3|JS&D5}c
zB;AxqL(ZLy<-GdjO?JmL-SLdi7wd*I4ulFNi&58D&^snPb(-KlBweBxt4reIb+>*Yc3u{n-=j_r!1cZHwp_)@ZTwXltP
zP$^<$%gJB2CK3)a>Px4)xO6slG0pE$t9l1fOxMAK$HVM;+mi_nuOzlybX=9Kn$L
zPwfWD#rhy1xfUJUl2!o`)<^E4)(yrms_!)WX89M)aYL~M9%e6I{a`vmgRfCy7c_)t
zoo)4-GZFYHZ2vXqNuCSa`!#yK92kYO`-J^LRgO)tE2$AnSR&d>p&S^;?g3MAts8p%
z$++3wZE!Iccr*)^TcLcsE-<+ZAGcH9*KIrEra!qoJ;0S=Fi#mXUC~fLn5E}~
zT}Db)J)G5GPe1}mwjQS3FTxHZy
z?_sQs^E%X%KRX2#W*HsCiQ=%x%|_nLIFW`gHeUM?Atbs#$vq)eK)ku*X>0maNko|4ZtGu{K+)
zgB(^;*w&@Nyr3N(PdzVPgC9ra4*jeIH0I;0_3N1v{kezdcPJh(`ubncwv0`nGNVN6
z@z?wm^hA5|)1@r;an|=LJa12gx|A4%C{q0FpNulT`nofk6-QH
zEyt#(3-~hTZuzh$P~97GmmS824NlV&kmKrL;|7;`MfE4Lt@@KyvVC>+N3w%ZsQ##c
z*+%NrPqdBl=gQa9j#bR;Z)7GZ1=YN(D42sik7Ed~LKI5T91KH@G63KKIm|DmyJxm&
z&d^nTY9MkqdA^$d5S}QwqWGFsJ$kObcWtA6^~hXU-s*%V;q)am-B}d0Q&1uf&0h4y(n8QrBHL{+@H0Vpk
zY^e64yabPYjWva*?l6zN8ZpqiLq?T)#c6A*U|kCGMx(Bl~Yxk7#WR10r%
zdYkK$_fsBl1cT--s1$P;x*X^?U8GBNsIH3;51$jr4VQk!J7){kX0`l^L
z$5W_2jOjw{>N@Ug2|Mud11yGSG`U@O-|ccY8E{vtFrDk-iEHgX{R+khrtk%n$Hb=v
zz6T}AduRngJES~i=zo#6HncnZQPo#7-*v|F7)r{5oPJxUXEEa|eL?u*9Ha3y1e1#^xsH!cf@lz2Q!9sqxMcHL`pe!{j
zw$4E*pK&}M<#LpcXa4ryd-Xz;_!9i2DfdMJ&wyh2+4pG`HJo||F<#U68?GSNqYS!?
z`+sut&0LD|v-lpb`B|Cp-XCp0Xa^V{Y>(dWMcGaIP!(U`_~|I(K+Zu_!f}nF#zCwg
zJP;tXEYq!wC%Q~59okxQSm}m%0A>X@lH
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
KISZn8x&9A2X%@x+
literal 0
HcmV?d00001
diff --git a/static/vendor/font-awesome/webfonts/fa-regular-400.woff2 b/static/vendor/font-awesome/webfonts/fa-regular-400.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..18400d7fad27fc52cfbabbda495b871bd912d045
GIT binary patch
literal 25392
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