Compare commits

...

2 Commits

Author SHA1 Message Date
Will Miao 121d8d5cea fix(ui): lighten toolbar shortcut keycaps and fix contrast on active buttons 2026-09-05 00:21:11 +08:00
Will Miao ec147bd677 feat(ui): add setting to keep the action bar visible while scrolling (#1095)
Adds a 'Keep Action Bar Visible' toggle (default off) under
Settings > Interface > Layout Settings. When enabled, the controls bar
(Refresh, Download, etc.) and the breadcrumb nav are wrapped in a shared
sticky container (.sticky-topbar) so both stay pinned as one unit; when
disabled, the wrapper is display: contents and the original behavior
(only the breadcrumb stays visible) is preserved.
2026-09-04 23:53:27 +08:00
19 changed files with 77 additions and 9 deletions
+2
View File
@@ -484,6 +484,8 @@
"layoutSettings": { "layoutSettings": {
"groupByModel": "Nach Modell gruppieren", "groupByModel": "Nach Modell gruppieren",
"groupByModelHelp": "Wenn aktiviert, wird nur die neueste Version jedes CivitAI-Modells als einzelne Karte angezeigt. Ältere Versionen werden ausgeblendet.", "groupByModelHelp": "Wenn aktiviert, wird nur die neueste Version jedes CivitAI-Modells als einzelne Karte angezeigt. Ältere Versionen werden ausgeblendet.",
"stickyControls": "Aktionsleiste sichtbar halten",
"stickyControlsHelp": "Wenn aktiviert, bleibt die Aktionsleiste (Aktualisieren, Herunterladen usw.) beim Scrollen zusammen mit der Breadcrumb-Navigation oben angeheftet.",
"displayDensity": "Anzeige-Dichte", "displayDensity": "Anzeige-Dichte",
"displayDensityOptions": { "displayDensityOptions": {
"default": "Standard", "default": "Standard",
+2
View File
@@ -484,6 +484,8 @@
"layoutSettings": { "layoutSettings": {
"groupByModel": "Group by Model", "groupByModel": "Group by Model",
"groupByModelHelp": "When enabled, only the latest version of each CivitAI model is shown as a single card. Older versions are hidden.", "groupByModelHelp": "When enabled, only the latest version of each CivitAI model is shown as a single card. Older versions are hidden.",
"stickyControls": "Keep Action Bar Visible",
"stickyControlsHelp": "When enabled, the action bar (Refresh, Download, etc.) stays pinned at the top while scrolling, together with the breadcrumb navigation.",
"displayDensity": "Display Density", "displayDensity": "Display Density",
"displayDensityOptions": { "displayDensityOptions": {
"default": "Default", "default": "Default",
+2
View File
@@ -484,6 +484,8 @@
"layoutSettings": { "layoutSettings": {
"groupByModel": "Agrupar por modelo", "groupByModel": "Agrupar por modelo",
"groupByModelHelp": "Cuando está activado, solo se muestra la versión más reciente de cada modelo de CivitAI como una tarjeta única. Las versiones anteriores están ocultas.", "groupByModelHelp": "Cuando está activado, solo se muestra la versión más reciente de cada modelo de CivitAI como una tarjeta única. Las versiones anteriores están ocultas.",
"stickyControls": "Mantener visible la barra de acciones",
"stickyControlsHelp": "Cuando está activado, la barra de acciones (Actualizar, Descargar, etc.) permanece fijada en la parte superior al desplazarse, junto con la navegación por rutas.",
"displayDensity": "Densidad de visualización", "displayDensity": "Densidad de visualización",
"displayDensityOptions": { "displayDensityOptions": {
"default": "Predeterminado", "default": "Predeterminado",
+2
View File
@@ -484,6 +484,8 @@
"layoutSettings": { "layoutSettings": {
"groupByModel": "Grouper par modèle", "groupByModel": "Grouper par modèle",
"groupByModelHelp": "Lorsque activé, seule la version la plus récente de chaque modèle CivitAI s'affiche sous forme de carte unique. Les versions plus anciennes sont masquées.", "groupByModelHelp": "Lorsque activé, seule la version la plus récente de chaque modèle CivitAI s'affiche sous forme de carte unique. Les versions plus anciennes sont masquées.",
"stickyControls": "Garder la barre d'actions visible",
"stickyControlsHelp": "Lorsque activé, la barre d'actions (Actualiser, Télécharger, etc.) reste épinglée en haut lors du défilement, avec la navigation par fil d'Ariane.",
"displayDensity": "Densité d'affichage", "displayDensity": "Densité d'affichage",
"displayDensityOptions": { "displayDensityOptions": {
"default": "Par défaut", "default": "Par défaut",
+2
View File
@@ -484,6 +484,8 @@
"layoutSettings": { "layoutSettings": {
"groupByModel": "קיבוץ לפי מודל", "groupByModel": "קיבוץ לפי מודל",
"groupByModelHelp": "כאשר מופעל, רק הגרסה העדכנית ביותר של כל מודל CivitAI מוצגת ככרטיס בודד. גרסאות ישנות יותר מוסתרות.", "groupByModelHelp": "כאשר מופעל, רק הגרסה העדכנית ביותר של כל מודל CivitAI מוצגת ככרטיס בודד. גרסאות ישנות יותר מוסתרות.",
"stickyControls": "השארת סרגל הפעולות גלוי",
"stickyControlsHelp": "כאשר מופעל, סרגל הפעולות (רענון, הורדה וכו') נשאר מוצמד לחלק העליון בעת גלילה, יחד עם ניווט פירורי הלחם.",
"displayDensity": "צפיפות תצוגה", "displayDensity": "צפיפות תצוגה",
"displayDensityOptions": { "displayDensityOptions": {
"default": "ברירת מחדל", "default": "ברירת מחדל",
+2
View File
@@ -484,6 +484,8 @@
"layoutSettings": { "layoutSettings": {
"groupByModel": "モデルでグループ化", "groupByModel": "モデルでグループ化",
"groupByModelHelp": "有効にすると、各CivitAIモデルの最新バージョンのみが1枚のカードとして表示され、古いバージョンは非表示になります。", "groupByModelHelp": "有効にすると、各CivitAIモデルの最新バージョンのみが1枚のカードとして表示され、古いバージョンは非表示になります。",
"stickyControls": "アクションバーを常に表示",
"stickyControlsHelp": "有効にすると、アクションバー(更新、ダウンロードなど)がスクロール時にパンくずナビゲーションと一緒に画面上部に固定されます。",
"displayDensity": "表示密度", "displayDensity": "表示密度",
"displayDensityOptions": { "displayDensityOptions": {
"default": "デフォルト", "default": "デフォルト",
+2
View File
@@ -484,6 +484,8 @@
"layoutSettings": { "layoutSettings": {
"groupByModel": "모델별 그룹화", "groupByModel": "모델별 그룹화",
"groupByModelHelp": "활성화하면 각 CivitAI 모델의 최신 버전만 단일 카드로 표시되며, 이전 버전은 숨겨집니다.", "groupByModelHelp": "활성화하면 각 CivitAI 모델의 최신 버전만 단일 카드로 표시되며, 이전 버전은 숨겨집니다.",
"stickyControls": "작업 표시줄 항상 표시",
"stickyControlsHelp": "활성화하면 작업 표시줄(새로고침, 다운로드 등)이 스크롤 시 브레드크럼 내비게이션과 함께 상단에 고정됩니다.",
"displayDensity": "표시 밀도", "displayDensity": "표시 밀도",
"displayDensityOptions": { "displayDensityOptions": {
"default": "기본", "default": "기본",
+2
View File
@@ -484,6 +484,8 @@
"layoutSettings": { "layoutSettings": {
"groupByModel": "Группировать по модели", "groupByModel": "Группировать по модели",
"groupByModelHelp": "При включении отображается только последняя версия каждой модели CivitAI в виде одной карточки. Старые версии скрыты.", "groupByModelHelp": "При включении отображается только последняя версия каждой модели CivitAI в виде одной карточки. Старые версии скрыты.",
"stickyControls": "Держать панель действий видимой",
"stickyControlsHelp": "При включении панель действий (Обновить, Загрузить и т. д.) остаётся закреплённой вверху при прокрутке вместе с навигацией по папкам.",
"displayDensity": "Плотность отображения", "displayDensity": "Плотность отображения",
"displayDensityOptions": { "displayDensityOptions": {
"default": "По умолчанию", "default": "По умолчанию",
+2
View File
@@ -484,6 +484,8 @@
"layoutSettings": { "layoutSettings": {
"groupByModel": "按模型分组", "groupByModel": "按模型分组",
"groupByModelHelp": "开启后,每个 CivitAI 模型仅显示最新版本的单张卡片,旧版本将被隐藏。", "groupByModelHelp": "开启后,每个 CivitAI 模型仅显示最新版本的单张卡片,旧版本将被隐藏。",
"stickyControls": "保持操作栏可见",
"stickyControlsHelp": "开启后,操作栏(刷新、下载等)会在滚动时与路径导航一起固定在页面顶部。",
"displayDensity": "显示密度", "displayDensity": "显示密度",
"displayDensityOptions": { "displayDensityOptions": {
"default": "默认", "default": "默认",
+2
View File
@@ -484,6 +484,8 @@
"layoutSettings": { "layoutSettings": {
"groupByModel": "按模型分組", "groupByModel": "按模型分組",
"groupByModelHelp": "啟用後,每個 CivitAI 模型僅顯示最新版本的單張卡片,舊版本將被隱藏。", "groupByModelHelp": "啟用後,每個 CivitAI 模型僅顯示最新版本的單張卡片,舊版本將被隱藏。",
"stickyControls": "保持操作列可見",
"stickyControlsHelp": "啟用後,操作列(重新整理、下載等)會在捲動時與麵包屑導覽一起固定在頁面頂端。",
"displayDensity": "顯示密度", "displayDensity": "顯示密度",
"displayDensityOptions": { "displayDensityOptions": {
"default": "預設", "default": "預設",
+1
View File
@@ -116,6 +116,7 @@ DEFAULT_SETTINGS: Dict[str, Any] = {
"backup_retention_count": 5, "backup_retention_count": 5,
"use_new_license_icons": True, "use_new_license_icons": True,
"group_by_model": False, "group_by_model": False,
"sticky_controls": False,
# AI / LLM provider configuration (BYOK) # AI / LLM provider configuration (BYOK)
"llm_provider": "openai", # "openai" | "ollama" | "custom" "llm_provider": "openai", # "openai" | "ollama" | "custom"
"llm_api_key": "", "llm_api_key": "",
+33 -8
View File
@@ -25,6 +25,23 @@
box-shadow: var(--shadow-xs); box-shadow: var(--shadow-xs);
} }
/* Wrapper around the controls bar and breadcrumb nav. With the sticky-controls
setting off it is transparent to layout (display: contents), preserving the
original behavior (only the breadcrumb stays visible). When enabled, the whole
wrapper sticks as one unit so the two bars can never drift apart. */
.sticky-topbar {
display: contents;
}
body.sticky-controls .sticky-topbar {
display: block;
position: sticky;
top: 0;
z-index: calc(var(--z-header) - 1);
background: var(--bg-color);
box-shadow: var(--shadow-xs);
}
/* Responsive container for larger screens */ /* Responsive container for larger screens */
@media (min-width: 2150px) { @media (min-width: 2150px) {
.container { .container {
@@ -201,20 +218,17 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-left: 6px; min-width: 16px;
min-width: 18px; height: 16px;
height: 18px; padding: 0 4px;
padding: 0 5px; font-size: 10px;
font-size: 11px; font-weight: 500;
font-weight: 600;
line-height: 1; line-height: 1;
text-transform: uppercase; text-transform: uppercase;
border-radius: var(--border-radius-xs); border-radius: var(--border-radius-xs);
background-color: var(--shortcut-bg); background-color: var(--shortcut-bg);
border: 1px solid var(--shortcut-border); border: 1px solid var(--shortcut-border);
box-shadow: var(--shortcut-shadow);
color: var(--shortcut-text); color: var(--shortcut-text);
vertical-align: middle;
opacity: 0.8; opacity: 0.8;
transition: var(--transition-base); transition: var(--transition-base);
} }
@@ -225,10 +239,21 @@
border-color: var(--shortcut-border-hover); border-color: var(--shortcut-border-hover);
} }
/* Invert the keycap on active (accent-filled) buttons for contrast.
Must come after the hover rule above so it wins on active+hover. */
.control-group button.active .shortcut-key,
.control-group button.active:hover .shortcut-key {
color: var(--lora-accent);
background: rgba(255, 255, 255, 0.92);
border-color: transparent;
opacity: 1;
}
/* Ensure correct vertical alignment for text+shortcut */ /* Ensure correct vertical alignment for text+shortcut */
.control-group button span { .control-group button span {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 6px;
} }
/* Select dropdown styling */ /* Select dropdown styling */
+10
View File
@@ -1047,6 +1047,12 @@ export class SettingsManager {
groupByModelCheckbox.checked = !!state.global.settings.group_by_model; groupByModelCheckbox.checked = !!state.global.settings.group_by_model;
} }
// Set sticky controls
const stickyControlsCheckbox = document.getElementById('stickyControls');
if (stickyControlsCheckbox) {
stickyControlsCheckbox.checked = !!state.global.settings.sticky_controls;
}
// Set model name display setting // Set model name display setting
const modelNameDisplaySelect = document.getElementById('modelNameDisplay'); const modelNameDisplaySelect = document.getElementById('modelNameDisplay');
if (modelNameDisplaySelect) { if (modelNameDisplaySelect) {
@@ -3396,6 +3402,10 @@ export class SettingsManager {
const groupByModel = !!state.global.settings.group_by_model; const groupByModel = !!state.global.settings.group_by_model;
document.body.classList.toggle('group-by-model', groupByModel); document.body.classList.toggle('group-by-model', groupByModel);
// Apply sticky controls mode (keeps the action bar visible while scrolling)
const stickyControls = !!state.global.settings.sticky_controls;
document.body.classList.toggle('sticky-controls', stickyControls);
} }
} }
+1
View File
@@ -59,6 +59,7 @@ const DEFAULT_SETTINGS_BASE = Object.freeze({
strip_lora_on_copy: false, strip_lora_on_copy: false,
use_new_license_icons: true, use_new_license_icons: true,
group_by_model: false, group_by_model: false,
sticky_controls: false,
llm_provider: 'openai', llm_provider: 'openai',
llm_api_key: '', llm_api_key: '',
llm_api_base: '', llm_api_base: '',
+2
View File
@@ -54,8 +54,10 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="sticky-topbar">
{% include 'components/controls.html' %} {% include 'components/controls.html' %}
{% include 'components/breadcrumb.html' %} {% include 'components/breadcrumb.html' %}
</div>
{% include 'components/duplicates_banner.html' %} {% include 'components/duplicates_banner.html' %}
{% include 'components/folder_sidebar.html' %} {% include 'components/folder_sidebar.html' %}
@@ -79,6 +79,9 @@
<!-- Group by model toggle --> <!-- Group by model toggle -->
{{ sm.setting_toggle('groupByModel', 'group_by_model', 'settings.layoutSettings.groupByModel', 'settings.layoutSettings.groupByModelHelp') }} {{ sm.setting_toggle('groupByModel', 'group_by_model', 'settings.layoutSettings.groupByModel', 'settings.layoutSettings.groupByModelHelp') }}
<!-- Sticky controls toggle -->
{{ sm.setting_toggle('stickyControls', 'sticky_controls', 'settings.layoutSettings.stickyControls', 'settings.layoutSettings.stickyControlsHelp') }}
{{ sm.setting_select('cardInfoDisplay', 'card_info_display', 'settings.layoutSettings.cardInfoDisplay', [ {{ sm.setting_select('cardInfoDisplay', 'card_info_display', 'settings.layoutSettings.cardInfoDisplay', [
('always', 'settings.layoutSettings.cardInfoDisplayOptions.always'), ('always', 'settings.layoutSettings.cardInfoDisplayOptions.always'),
('hover', 'settings.layoutSettings.cardInfoDisplayOptions.hover'), ('hover', 'settings.layoutSettings.cardInfoDisplayOptions.hover'),
+2
View File
@@ -53,8 +53,10 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="sticky-topbar">
{% include 'components/controls.html' %} {% include 'components/controls.html' %}
{% include 'components/breadcrumb.html' %} {% include 'components/breadcrumb.html' %}
</div>
{% include 'components/duplicates_banner.html' %} {% include 'components/duplicates_banner.html' %}
{% include 'components/folder_sidebar.html' %} {% include 'components/folder_sidebar.html' %}
+2
View File
@@ -8,8 +8,10 @@
{% block init_check_url %}/api/loras/list?page=1&page_size=1{% endblock %} {% block init_check_url %}/api/loras/list?page=1&page_size=1{% endblock %}
{% block content %} {% block content %}
<div class="sticky-topbar">
{% include 'components/controls.html' %} {% include 'components/controls.html' %}
{% include 'components/breadcrumb.html' %} {% include 'components/breadcrumb.html' %}
</div>
{% include 'components/duplicates_banner.html' %} {% include 'components/duplicates_banner.html' %}
{% include 'components/folder_sidebar.html' %} {% include 'components/folder_sidebar.html' %}
+3 -1
View File
@@ -64,10 +64,12 @@
{% block init_check_url %}/api/recipes?page=1&page_size=1{% endblock %} {% block init_check_url %}/api/recipes?page=1&page_size=1{% endblock %}
{% block content %} {% block content %}
<!-- Recipe controls --> <!-- Sticky topbar: controls + breadcrumb -->
<div class="sticky-topbar">
{% include 'components/controls.html' %} {% include 'components/controls.html' %}
<!-- Breadcrumb Navigation --> <!-- Breadcrumb Navigation -->
{% include 'components/breadcrumb.html' %} {% include 'components/breadcrumb.html' %}
</div>
<!-- Duplicates banner (hidden by default) --> <!-- Duplicates banner (hidden by default) -->
<div id="duplicatesBanner" class="duplicates-banner" style="display: none;"> <div id="duplicatesBanner" class="duplicates-banner" style="display: none;">