From 54bba07420225e4c27f0b2fb5aaf1d13655703ed Mon Sep 17 00:00:00 2001
From: Lucas Scala <123923688+Vibecoder9000@users.noreply.github.com>
Date: Tue, 24 Feb 2026 13:27:03 -0600
Subject: [PATCH] Background Folders (#5187)
* backend, frontend, bugfixes
* Mobile button and sizing
* lint
* clear folder thumbnailFile on delete, rename thumbnailFile on rename
* use filteredImages when changing sort option
* Address all the review comments
* Fix friendly title generation to handle empty strings
* Move add folder button to the header
* instead of search filtering the backgrounds in a folder and showing the folder if the results > 0, search the folder names.
* Trade button places
* Adjust button text
* feat: restrict folder creation to the Global tab
---------
Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
---
public/css/backgrounds.css | 169 ++++++++++-
public/css/mobile-styles.css | 53 +++-
public/index.html | 36 +++
public/scripts/backgrounds.js | 499 +++++++++++++++++++++++++++++++-
src/endpoints/backgrounds.js | 31 +-
src/endpoints/image-metadata.js | 307 +++++++++++++++++++-
6 files changed, 1079 insertions(+), 16 deletions(-)
diff --git a/public/css/backgrounds.css b/public/css/backgrounds.css
index 6d58f0ea1..e08ea31ca 100644
--- a/public/css/backgrounds.css
+++ b/public/css/backgrounds.css
@@ -107,7 +107,8 @@
opacity: 1;
}
-.bg_example .mobile-only-menu-toggle {
+.bg_example .mobile-only-menu-toggle,
+.bg_folder_tile .mobile-only-menu-toggle {
display: none;
}
@@ -376,3 +377,169 @@
#bg_custom_content:not(:empty)~#bg_chat_hint {
display: none;
}
+
+.bg_folder_grid.bg_list {
+ display: grid;
+ gap: 5px;
+ width: 100%;
+ grid-template-columns: repeat(calc(var(--bg-thumb-columns, 5) + 2), 1fr);
+ margin-bottom: 10px;
+}
+
+.bg_folder_grid:empty {
+ display: none;
+}
+
+.bg_folder_tile {
+ cursor: pointer;
+ position: relative;
+ overflow: hidden;
+ border-radius: 8px;
+ height: auto;
+ aspect-ratio: 1 / 1;
+ outline: 2px solid var(--SmartThemeBorderColor);
+ outline-offset: -1px;
+ box-shadow: 0 0 7px var(--black50a);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ background-color: var(--SmartThemeBlurTintColor);
+ transition: filter var(--animation-duration) ease;
+}
+
+.bg_folder_tile:hover {
+ filter: brightness(1.1);
+}
+
+.bg_folder_tile_cover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-size: cover;
+ background-position: center;
+ border-radius: inherit;
+}
+
+.bg_folder_tile_overlay {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background: linear-gradient(transparent, rgba(0, 0, 0, 0.85));
+ color: var(--SmartThemeBodyColor);
+ padding: 6px 8px 4px;
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ font-size: 0.85em;
+ font-weight: 600;
+ pointer-events: none;
+ border-radius: 0 0 8px 8px;
+}
+
+.bg_folder_tile_overlay i {
+ font-size: 0.9em;
+ opacity: 0.8;
+}
+
+.bg_folder_tile_name {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+/* Folder tile action menu */
+.bg_folder_tile .bg_folder_tile_menu {
+ display: flex;
+ position: absolute;
+ top: 2px;
+ right: 2px;
+ background-color: rgba(0, 0, 0, 0.5);
+ border-radius: 5px;
+ padding: 3px;
+ z-index: 3;
+ backdrop-filter: blur(4px);
+ border: 1px solid var(--SmartThemeBorderColor);
+ align-items: center;
+
+ opacity: 0;
+ visibility: hidden;
+ transform: scale(0.9);
+ transform-origin: center;
+ transition: opacity var(--animation-duration) ease-out, visibility var(--animation-duration) ease-out, transform var(--animation-duration) ease-out;
+}
+
+.bg_folder_tile:hover .bg_folder_tile_menu,
+.bg_folder_tile:focus-within .bg_folder_tile_menu {
+ opacity: 1;
+ visibility: visible;
+ transform: scale(1);
+}
+
+.bg_folder_tile .jg-button {
+ display: flex;
+ width: 24px;
+ height: 24px;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ padding: 5px;
+ font-size: 1.1em;
+ border-radius: 5px;
+ transition: background-color var(--animation-duration) ease;
+}
+
+.bg_folder_tile .jg-button:hover {
+ background-color: rgba(255, 255, 255, 0.2);
+}
+
+/* New Folder placeholder tile */
+.bg_new_folder_tile {
+ border: 2px dashed var(--SmartThemeBorderColor);
+ outline: none;
+ color: var(--SmartThemeBodyColor);
+ opacity: 0.6;
+ gap: 6px;
+ font-size: 0.85em;
+ font-weight: 600;
+}
+
+.bg_new_folder_tile:hover {
+ opacity: 1;
+ transform: scale(1.03);
+}
+
+/* Breadcrumb bar */
+.bg_folder_breadcrumb {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 5px 0;
+ margin-bottom: 5px;
+}
+
+.bg_current_folder_name {
+ font-weight: 600;
+ font-size: 1em;
+ color: var(--SmartThemeBodyColor);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+#Backgrounds:not(.in-folder-view) .jg-set-cover {
+ display: none !important;
+}
+
+#Backgrounds.in-folder-view .jg-set-cover {
+ display: flex !important;
+}
+
+/* Hide folder actions on custom (chat-specific) backgrounds */
+.bg_example[custom="true"] .jg-folder,
+.bg_example[custom="true"] .jg-set-cover {
+ display: none !important;
+}
diff --git a/public/css/mobile-styles.css b/public/css/mobile-styles.css
index 01abb6ab5..7e6a6da7b 100644
--- a/public/css/mobile-styles.css
+++ b/public/css/mobile-styles.css
@@ -38,6 +38,56 @@
grid-template-columns: repeat(var(--bg-thumb-columns, 3), 1fr);
}
+ .bg_folder_grid {
+ grid-template-columns: repeat(var(--bg-thumb-columns, 3), 1fr);
+ }
+
+ .bg_folder_tile .bg_folder_tile_menu {
+ opacity: 0;
+ visibility: hidden;
+ transform: scale(0.9);
+ }
+
+ .bg_folder_tile:hover .bg_folder_tile_menu,
+ .bg_folder_tile:focus-within .bg_folder_tile_menu {
+ display: none;
+ }
+
+ .bg_folder_tile.mobile-menu-open .bg_folder_tile_menu {
+ display: flex;
+ opacity: 1;
+ visibility: visible;
+ transform: scale(1);
+ z-index: 4;
+ }
+
+ .bg_folder_tile.mobile-menu-open .mobile-only-menu-toggle {
+ opacity: 0;
+ pointer-events: none;
+ }
+
+ .bg_folder_tile .mobile-only-menu-toggle {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ width: 30px;
+ height: 30px;
+ background-color: rgba(0, 0, 0, 0.4);
+ color: white;
+ border-radius: 6px;
+ z-index: 3;
+ cursor: pointer;
+ backdrop-filter: blur(2px);
+ }
+
+ .bg_folder_tile .jg-button {
+ width: 30px;
+ height: 30px;
+ }
+
.bg_list {
width: unset;
}
@@ -102,7 +152,8 @@
}
#add_background_button_top>span,
- #auto_background>span {
+ #auto_background>span,
+ #bg_add_folder_button>span {
display: none;
}
diff --git a/public/index.html b/public/index.html
index fa4aeb690..bd8aab5e5 100644
--- a/public/index.html
+++ b/public/index.html
@@ -5597,6 +5597,10 @@
Auto-select
+