Backgrounds menu tabs (#4845) * Add tabs for backgrounds menu * Improve styling and layout * Use resize observer * Add upload to chat bg logic * Custom BG should always lock * Fix auto-select when deleting active chat bg * Use frame event cancellation on rapid resize * Allow multiple uploads * Fix mobile tab selector * Bypass global lock with shift key * Update copy tooltip * Update localization files

1da6e401297a13f3a490feb7c0f289a819baf66a

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
23 files changed, +325 -100Ignore whitespace
public/css/backgrounds.css+77 -8
@@ -84,14 +84,6 @@
8484 gap: 5px;
8585}
8686
87-#bg-scrollable-content {
88- flex-grow: 1;
89- overflow-y: auto;
90- overflow-x: hidden;
91- padding: 0 5px 15px;
92- position: relative;
93-}
94-
9587#bg_menu_content,
9688#bg_custom_content {
9789 display: grid;
@@ -301,3 +293,80 @@
301293.bg_example:focus-within .BGSampleTitle {
302294 opacity: 1;
303295}
296+
297+#bg_tabs {
298+ display: flex;
299+ flex-direction: column;
300+ flex-grow: 1;
301+ height: 100%;
302+ overflow-y: hidden;
303+ gap: 5px;
304+}
305+
306+#bg_tabs .bg_tabs_list {
307+ display: flex;
308+ flex-direction: row;
309+ gap: 5px;
310+ background: none;
311+ border: none;
312+ margin: 5px 0;
313+ padding: 0;
314+ padding-right: 4em;
315+ position: relative;
316+}
317+
318+#bg_tabs .bg_tabs_list .bg_tab_button {
319+ display: flex;
320+ align-items: center;
321+ justify-content: center;
322+ text-align: center;
323+ min-width: 8em;
324+ list-style: none;
325+ color: var(--SmartThemeBodyColor);
326+ background-color: var(--SmartThemeBlurTintColor);
327+ border: 1px solid var(--SmartThemeBorderColor);
328+ border-radius: 5px;
329+ padding: 0;
330+ cursor: pointer;
331+ font-weight: 600;
332+ margin: 0 !important;
333+ filter: none;
334+ opacity: 0.8;
335+ transition: opacity var(--animation-duration-2x);
336+}
337+
338+#bg_tabs .bg_tabs_list .bg_tab_button:hover {
339+ opacity: 1;
340+}
341+
342+#bg_tabs .bg_tabs_list .bg_tab_button.ui-tabs-active {
343+ background-color: color-mix(in srgb, var(--SmartThemeQuoteColor) 33%, var(--SmartThemeBlurTintColor) 66%);
344+}
345+
346+#bg_tabs.ui-widget-content {
347+ border: none !important;
348+}
349+
350+#bg_tabs .bg_tabs_list::before,
351+#bg_tabs .bg_tabs_list::after {
352+ display: none;
353+}
354+
355+#bg_tabs .bg_tabs_list .bg_tab_button a {
356+ width: 100%;
357+ height: 100%;
358+ padding: 3px 5px;
359+ cursor: pointer;
360+ color: var(--SmartThemeBodyColor);
361+}
362+
363+#bg_tabs .bg_tab_panel {
364+ flex-grow: 1;
365+ height: 100%;
366+ overflow-y: auto;
367+ padding: 10px;
368+}
369+
370+#bg_custom_content:not(:empty)~#bg_chat_hint {
371+ display: none;
372+}
public/css/mobile-styles.css+4 -0
@@ -97,6 +97,10 @@
9797 max-width: 100dvw;
9898 }
9999
100+ #bg_tabs .bg_tabs_list .bg_tab_button {
101+ min-width: 6em;
102+ }
103+
100104 #add_background_button_top>span,
101105 #auto_background>span {
102106 display: none;
public/index.html+19 -17
@@ -5418,11 +5418,14 @@
54185418 <input id="bg-filter" class="text_pole" type="search" data-i18n="[placeholder]Search..." placeholder="Search..." />
54195419 </div>
54205420 </div>
54215421 <div id="bgbg_tabs" class="heading-scrollablecontainer-contentwith-controls">
54225422 <divul class="heading-container-with-controlsbg_tabs_list">
5423- <h3 data-i18n="System Backgrounds" class="wide100p textAlignCenter heading-text">
5423+ <li class="bg_tab_button">
5424- System Backgrounds
5424+ <a href="#bg_global_tab" data-i18n="Global">Global</a>
54255425 </h3li>
5426+ <li class="bg_tab_button">
5427+ <a href="#bg_chat_tab" data-i18n="Chat">Chat</a>
5428+ </li>
54265429 <div class="heading-controls">
54275430 <button id="bg_thumb_zoom_out" class="menu_button menu_button_icon" title="Make thumbnails smaller" data-i18n="[title]Make thumbnails smaller">
54285431 <i class="fa-solid fa-minus"></i>
@@ -5431,20 +5434,19 @@
54315434 <i class="fa-solid fa-plus"></i>
54325435 </button>
54335436 </div>
5437+ </ul>
5438+ <div id="bg_global_tab" class="bg_tab_panel">
5439+ <div id="bg_menu_content" class="bg_list"></div>
54345440 </div>
54355441 <div id="bg_menu_contentbg_chat_tab" class="bg_listbg_tab_panel">
5436- </div>
5442+ <div id="bg_custom_content" class="bg_list"></div>
54375443 <h3div data-i18nid="Chat Backgroundsbg_chat_hint" class="wide100p textAlignCenter">
5438- Chat Backgrounds
5444+ <span data-i18n="bg_chat_hint_1">Chat backgrounds generated with the</span> <code><i class="fa-solid fa-paintbrush"></i>&nbsp;Image Generation</code> <span data-i18n="bg_chat_hint_2">extension will appear here.</span>
54395445 </h3div>
5440- <div id="bg_chat_hint" class="wide100p textAlignCenter">
5441- <span data-i18n="bg_chat_hint_1">Chat backgrounds generated with the</span> <code><i class="fa-solid fa-paintbrush"></i>&nbsp;Image Generation</code> <span data-i18n="bg_chat_hint_2">extension will appear here.</span>
5442- </div>
5443- <div id="bg_custom_content" class="bg_list">
54445446 </div>
54455447 </div>
54465448 <form id="form_bg_upload" style="display: none;"hidden>
54475449 <input type="file" id="add_bg_button" name="avatar" accept="image/jpeg,image/png,image/gif,image/bmp,image/svg+xml*,video/*" multiple hidden>
54485450 </form>
54495451 <button id="bg-scroll-top" type="button" class="menu_button menu_button_icon" title="Scroll backgrounds to top" data-i18n="[title]Scroll backgrounds to top">
54505452 <i class="fa-solid fa-chevron-up" aria-hidden="true"></i>
@@ -6393,7 +6395,7 @@
63936395 <div class="jg-menu">
63946396 <div data-action="lock" class="jg-button jg-lock fa-solid fa-lock fa-fw pointer" data-i18n="[title]Lock" title="Lock"></div>
63956397 <div data-action="unlock" class="jg-button jg-unlock fa-solid fa-lock-open fa-fw pointer" data-i18n="[title]Unlock" title="Unlock"></div>
63966398 <div data-action="copy" class="jg-button jg-copy fa-solid fa-file-arrow-up" data-i18n="[title]Copy to systemglobal backgrounds" title="Copy to systemglobal backgrounds"></div>
63976399 <div data-action="edit" class="jg-button jg-edit fa-solid fa-pen-to-square fa-fw pointer" data-i18n="[title]Rename Background" title="Rename Background"></div>
63986400 <div data-action="delete" class="jg-button jg-delete fa-solid fa-trash-can fa-fw pointer" data-i18n="[title]Delete Background" title="Delete Background"></div>
63996401 </div>
public/locales/ar-sa.json+3 -3
@@ -739,8 +739,8 @@
739739 "Filter": "تصفية",
740740 "Automatically select a background based on the chat context": "تحديد خلفية تلقائيًا استنادًا إلى سياق الدردشة",
741741 "Auto-select": "التحديد التلقائي",
742742 "System BackgroundsGlobal": "خلفيات النظامعالمي",
743743 "Chat Backgrounds": "خلفيات الدردشة",
744744 "bg_chat_hint_1": "خلفيات الدردشة التي تم إنشاؤها باستخدام",
745745 "bg_chat_hint_2": "سوف يظهر الامتداد هنا",
746746 "Extensions": "امتدادات",
@@ -913,7 +913,7 @@
913913 "Save": "حفظ",
914914 "Chat History": "تاريخ الدردشة",
915915 "Import Chat": "استيراد الدردشة",
916916 "Copy to systemglobal backgrounds": "نسخ إلى خلفياتالخلفيات النظامالعالمية",
917917 "Rename background": "إعادة تسمية الخلفية",
918918 "Lock": "قفل",
919919 "Unlock": "الغاء القفل",
public/locales/de-de.json+3 -3
@@ -739,8 +739,8 @@
739739 "Filter": "Filter",
740740 "Automatically select a background based on the chat context": "Automatisch einen Hintergrund basierend auf dem Chatkontext auswählen",
741741 "Auto-select": "Automatische Auswahl",
742742 "System BackgroundsGlobal": "SystemhintergründeGlobal",
743743 "Chat Backgrounds": "Chat-Hintergründe",
744744 "bg_chat_hint_1": "Chat-Hintergründe generiert mit dem",
745745 "bg_chat_hint_2": "Die Erweiterung wird hier angezeigt.",
746746 "Extensions": "Erweiterungen",
@@ -913,7 +913,7 @@
913913 "Save": "Speichern",
914914 "Chat History": "Chat-Verlauf",
915915 "Import Chat": "Chat importieren",
916916 "Copy to systemglobal backgrounds": "In Systemhintergründeglobale Hintergründe kopieren",
917917 "Rename background": "Hintergrund umbenennen",
918918 "Lock": "Sperren",
919919 "Unlock": "Freischalten",
public/locales/es-es.json+3 -3
@@ -739,8 +739,8 @@
739739 "Filter": "Filtro",
740740 "Automatically select a background based on the chat context": "Seleccionar automáticamente un fondo basado en el contexto del chat",
741741 "Auto-select": "Auto-seleccionar",
742742 "System BackgroundsGlobal": "Fondos del SistemaGlobal",
743743 "Chat Backgrounds": "Fondos de Chat",
744744 "bg_chat_hint_1": "Fondos de chat generados con el",
745745 "bg_chat_hint_2": "La extensión aparecerá aquí.",
746746 "Extensions": "Extensiones",
@@ -913,7 +913,7 @@
913913 "Save": "Guardar",
914914 "Chat History": "Historial de chat",
915915 "Import Chat": "Importar chat",
916916 "Copy to systemglobal backgrounds": "Copiar a fondos del sistemaglobales",
917917 "Rename background": "Cambiar nombre de fondo",
918918 "Lock": "Cerrar",
919919 "Unlock": "desbloquear",
public/locales/fr-fr.json+3 -3
@@ -684,8 +684,8 @@
684684 "Filter": "Filtre",
685685 "Automatically select a background based on the chat context": "Sélectionner automatiquement un arrière-plan en fonction du contexte de la discussion",
686686 "Auto-select": "Sélection automatique",
687687 "System BackgroundsGlobal": "Arrière-plans du systèmeGlobal",
688688 "Chat Backgrounds": "Arrière-plans de la discussionDiscussion",
689689 "bg_chat_hint_1": "Les arrière-plans générés avec l'extension ",
690690 "bg_chat_hint_2": " apparaîtrons ici.",
691691 "Extensions": "Extensions",
@@ -856,7 +856,7 @@
856856 "Save": "Enregistrer",
857857 "Chat History": "Historique de la conversation",
858858 "Import Chat": "Importer une discussion",
859859 "Copy to systemglobal backgrounds": "Copier vers les arrière-plans du systèmeglobaux",
860860 "Rename background": "Renommer l'arrière-plan",
861861 "Lock": "Verrouiller",
862862 "Unlock": "Déverrouiller",
public/locales/is-is.json+3 -3
@@ -737,8 +737,8 @@
737737 "Filter": "Sía",
738738 "Automatically select a background based on the chat context": "Velja sjálfkrafa bakgrunn út frá samhengi spjallsins",
739739 "Auto-select": "Sjálfval",
740740 "System BackgroundsGlobal": "Kerfis bakgrunnsmyndirAlmennt",
741741 "Chat Backgrounds": "Bakgrunnsmyndir spjallsSpjall",
742742 "bg_chat_hint_1": "Spjallbakgrunnur myndaður með",
743743 "bg_chat_hint_2": "viðbót mun birtast hér.",
744744 "Extensions": "Viðbætur",
@@ -911,7 +911,7 @@
911911 "Save": "Vista",
912912 "Chat History": "Spjall saga",
913913 "Import Chat": "Flytja inn spjall",
914914 "Copy to systemglobal backgrounds": "Afritaðu í kerfisbakgrunnalþjóðlegan bakgrunn",
915915 "Rename background": "Endurnefna bakgrunn",
916916 "Lock": "Læsa",
917917 "Unlock": "Opnaðu",
public/locales/it-it.json+3 -3
@@ -739,8 +739,8 @@
739739 "Filter": "Filtro",
740740 "Automatically select a background based on the chat context": "Seleziona automaticamente uno sfondo in base al contesto della chat",
741741 "Auto-select": "Selezione automatica",
742742 "System BackgroundsGlobal": "Sfondi di sistemaGlobale",
743743 "Chat Backgrounds": "Sfondi Chat",
744744 "bg_chat_hint_1": "Sfondi chat generati con",
745745 "bg_chat_hint_2": "l'estensione apparirà qui.",
746746 "Extensions": "Estensioni",
@@ -913,7 +913,7 @@
913913 "Save": "Salva",
914914 "Chat History": "Cronologia Chat",
915915 "Import Chat": "Importa chat",
916916 "Copy to systemglobal backgrounds": "Copia negli sfondi di sistemaglobali",
917917 "Rename background": "Rinominare lo sfondo",
918918 "Lock": "Serratura",
919919 "Unlock": "Sbloccare",
public/locales/ja-jp.json+3 -3
@@ -739,8 +739,8 @@
739739 "Filter": "フィルター",
740740 "Automatically select a background based on the chat context": "チャットのコンテキストに基づいて背景を自動選択",
741741 "Auto-select": "自動選択",
742742 "System BackgroundsGlobal": "システムの背景グローバル",
743743 "Chat Backgrounds": "チャットの背景チャット",
744744 "bg_chat_hint_1": "",
745745 "bg_chat_hint_2": "拡張機能で生成したチャットの背景はここに表示されます。",
746746 "Extensions": "拡張機能",
@@ -913,7 +913,7 @@
913913 "Save": "保存",
914914 "Chat History": "チャット履歴",
915915 "Import Chat": "チャットをインポート",
916916 "Copy to systemglobal backgrounds": "システム背景にコピーグローバル背景にコピー",
917917 "Rename background": "背景の名前を変更",
918918 "Lock": "ロック",
919919 "Unlock": "ロック解除",
public/locales/ko-kr.json+3 -3
@@ -752,8 +752,8 @@
752752 "Filter": "필터",
753753 "Automatically select a background based on the chat context": "채팅 컨텍스트를 기반으로 자동으로 배경 선택",
754754 "Auto-select": "자동 선택",
755755 "System BackgroundsGlobal": "시스템 배경글로벌",
756756 "Chat Backgrounds": "채팅 배경",
757757 "bg_chat_hint_1": "다음으로 생성된 채팅 배경",
758758 "bg_chat_hint_2": "확장 프로그램이 여기에 표시됩니다.",
759759 "Extensions": "확장",
@@ -927,7 +927,7 @@
927927 "Save": "저장",
928928 "Chat History": "채팅 기록",
929929 "Import Chat": "채팅 가져오기",
930930 "Copy to systemglobal backgrounds": "시스템글로벌 배경에 복사",
931931 "Rename background": "배경 이름 바꾸기",
932932 "Lock": "잠금",
933933 "Unlock": "잠금 해제",
public/locales/nl-nl.json+3 -3
@@ -735,8 +735,8 @@
735735 "Filter": "Filter",
736736 "Automatically select a background based on the chat context": "Automatisch een achtergrond selecteren op basis van de chatcontext",
737737 "Auto-select": "Automatisch selecteren",
738738 "System BackgroundsGlobal": "SysteemachtergrondenGlobaal",
739739 "Chat Backgrounds": "Achtergronden chatChat",
740740 "bg_chat_hint_1": "Chatachtergronden gegenereerd met de",
741741 "bg_chat_hint_2": "extensie zal hier verschijnen.",
742742 "Extensions": "Uitbreidingen",
@@ -909,7 +909,7 @@
909909 "Save": "Opslaan",
910910 "Chat History": "Chatgeschiedenis",
911911 "Import Chat": "Chat importeren",
912912 "Copy to systemglobal backgrounds": "Kopiëren naar systeemachtergrondenglobale achtergronden",
913913 "Rename background": "Achtergrond hernoemen",
914914 "Lock": "Slot",
915915 "Unlock": "Ontgrendelen",
public/locales/pt-pt.json+3 -3
@@ -737,8 +737,8 @@
737737 "Filter": "Filtro",
738738 "Automatically select a background based on the chat context": "Selecionar automaticamente um plano de fundo com base no contexto do chat",
739739 "Auto-select": "Seleção automática",
740740 "System BackgroundsGlobal": "Fundos do SistemaGlobal",
741741 "Chat Backgrounds": "Fundos de Chat",
742742 "bg_chat_hint_1": "Planos de fundo de bate-papo gerados com o",
743743 "bg_chat_hint_2": "a extensão aparecerá aqui.",
744744 "Extensions": "Extensões",
@@ -911,7 +911,7 @@
911911 "Save": "Salvar",
912912 "Chat History": "Histórico de Chat",
913913 "Import Chat": "Importar bate-papo",
914914 "Copy to systemglobal backgrounds": "Copiar para planos de fundo do sistemaglobais",
915915 "Rename background": "Renomear plano de fundo",
916916 "Lock": "Trancar",
917917 "Unlock": "Desbloquear",
public/locales/ru-ru.json+2 -3
@@ -244,7 +244,6 @@
244244 "Always include examples": "Всегда применять примеры",
245245 "Never include examples": "Никогда не применять примеры",
246246 "Forbid External Media": "Запрет внешних медиа",
247- "System Backgrounds": "Системные фоны",
248247 "Name": "Имя",
249248 "Auto-connect": "Подключаться автоматически",
250249 "First message": "Первое сообщение",
@@ -350,7 +349,7 @@
350349 "Minimum number of blacklisted words detected to trigger an auto-swipe": "Минимальное количество обнаруженных запрещённых слов, при котором срабатывает авто-свайп.",
351350 "User Message Blur Tint": "Ваши сообщения",
352351 "AI Message Blur Tint": "Сообщения ИИ",
353352 "Chat Backgrounds": "Фоны чатаЧат",
354353 "Chat Background": "Фон чата",
355354 "UI Background": "Фон UI",
356355 "Mad Lab Mode": "Режим безумца",
@@ -1326,7 +1325,7 @@
13261325 "Auto Mode delay": "Auto Mode delay",
13271326 "Bulk_edit_characters": "Bulk edit characters\r\rClick to toggle characters\rShift + Click to select/deselect a range of characters\rRight-click for actions",
13281327 "Bulk select all characters": "Bulk select all characters",
13291328 "Copy to systemglobal backgrounds": "CopyСкопировать toв systemглобальные backgroundsфоны",
13301329 "Chat Scenario Override": "Chat Scenario Override",
13311330 "Chat Lorebook": "Chat Lorebook for",
13321331 "chat_world_template_txt": "A selected World Info will be bound to this chat. When generating an AI reply,\n it will be combined with the entries from global and character lorebooks.",
public/locales/th-th.json+3 -3
@@ -745,8 +745,8 @@
745745 "Filter": "ตัวกรอง",
746746 "Automatically select a background based on the chat context": "เลือกพื้นหลังโดยอัตโนมัติตามบริบทของแชท",
747747 "Auto-select": "เลือกอัตโนมัติ",
748748 "System BackgroundsGlobal": "พื้นหลังของระบบทั่วโลก",
749749 "Chat Backgrounds": "พื้นหลังแชทแชท",
750750 "bg_chat_hint_1": "พื้นหลังของแชทที่ถูกสร้างด้วย",
751751 "bg_chat_hint_2": "จะปรากฏที่นี่",
752752 "Extensions": "โปรแกรมส่วนขยาย",
@@ -921,7 +921,7 @@
921921 "Save": "บันทึก",
922922 "Chat History": "ประวัติการแชท",
923923 "Import Chat": "นำเข้าไฟล์แชท",
924924 "Copy to systemglobal backgrounds": "คัดลอกไปยังพื้นหลังนหลังทั่วโลก",
925925 "Rename background": "เปลี่ยนชื่อพื้นหลัง",
926926 "Lock": "ล็อค",
927927 "Unlock": "ปลดล็อค",
public/locales/uk-ua.json+3 -3
@@ -737,8 +737,8 @@
737737 "Filter": "Фільтр",
738738 "Automatically select a background based on the chat context": "Автоматичний вибір фону на основі контексту чату",
739739 "Auto-select": "Автовибір",
740740 "System BackgroundsGlobal": "Системні фониГлобальні",
741741 "Chat Backgrounds": "Фони чатуЧат",
742742 "bg_chat_hint_1": "Фонове зображення чату, створене за допомогою",
743743 "bg_chat_hint_2": "тут з’явиться розширення.",
744744 "Extensions": "Розширення",
@@ -911,7 +911,7 @@
911911 "Save": "Зберегти",
912912 "Chat History": "Історія чату",
913913 "Import Chat": "Імпорт чату",
914914 "Copy to systemglobal backgrounds": "Скопіювати в системніглобальні фони",
915915 "Rename background": "Перейменувати фон",
916916 "Lock": "Замок",
917917 "Unlock": "Розблокувати",
public/locales/vi-vn.json+3 -3
@@ -737,8 +737,8 @@
737737 "Filter": "Bộ lọc",
738738 "Automatically select a background based on the chat context": "Tự động chọn một nền dựa trên context trò chuyện",
739739 "Auto-select": "Tự động chọn",
740740 "System BackgroundsGlobal": "Nền HệToàn thốngcầu",
741741 "Chat Backgrounds": "Hình nềnTrò Chatchuyện",
742742 "bg_chat_hint_1": "Hình nền trò chuyện được tạo bằng",
743743 "bg_chat_hint_2": "tiện ích sẽ xuất hiện ở đây.",
744744 "Extensions": "Tiện ích",
@@ -911,7 +911,7 @@
911911 "Save": "Lưu",
912912 "Chat History": "Lịch sử Trò chuyện",
913913 "Import Chat": "Nhập trò chuyện",
914914 "Copy to systemglobal backgrounds": "Sao chép vào nền hệtoàn thốngcầu",
915915 "Rename background": "Đổi tên nền background",
916916 "Lock": "Khóa",
917917 "Unlock": "Mở khóa",
public/locales/zh-cn.json+2 -3
@@ -961,8 +961,7 @@
961961 "Automatically select a background based on the chat context": "根据聊天上下文自动选择背景",
962962 "Auto-select": "自动选择",
963963 "Add Background": "添加背景",
964964 "System BackgroundsGlobal": "系统背景全局",
965- "Chat Backgrounds": "聊天背景",
966965 "bg_chat_hint_1": "使用生成的聊天背景",
967966 "bg_chat_hint_2": "扩展名将出现在这里。",
968967 "Extensions": "扩展",
@@ -1157,7 +1156,7 @@
11571156 "Save": "保存",
11581157 "Chat History": "聊天记录",
11591158 "Import Chat": "导入聊天",
11601159 "Copy to systemglobal backgrounds": "复制到系统背景复制到全局背景",
11611160 "Lock": "锁定",
11621161 "Unlock": "解锁",
11631162 "Rename Background": "重命名背景",
public/locales/zh-tw.json+2 -3
@@ -736,8 +736,7 @@
736736 "Filter": "篩選",
737737 "Automatically select a background based on the chat context": "根據聊天上下文自動選擇背景",
738738 "Auto-select": "自動選擇",
739739 "System BackgroundsGlobal": "系統背景圖片全域",
740- "Chat Backgrounds": "聊天背景圖片",
741740 "bg_chat_hint_1": "使用擴充功能",
742741 "bg_chat_hint_2": "所產生的背景圖片將在此顯示。",
743742 "Extensions": "擴充功能",
@@ -909,7 +908,7 @@
909908 "(Examples of chat dialog. Begin each example with START on a new line.)": "(聊天對話範例。每個範例以新的行並以「START」開始。)",
910909 "Chat History": "聊天記錄",
911910 "Import Chat": "匯入聊天",
912911 "Copy to systemglobal backgrounds": "複製到系統背景圖片複製到全局背景圖片",
913912 "Rename background": "重新命名背景圖片",
914913 "Lock": "上鎖",
915914 "Unlock": "解鎖",
public/scripts/a11y.js+14 -0
@@ -50,6 +50,14 @@ const toolbarSelectors = [
5050 '.jg-menu',
5151].join(', ');
5252
53+const tabListSelectors = [
54+ '#bg_tabs .bg_tabs_list',
55+].join(', ');
56+
57+const tabItemSelectors = [
58+ '#bg_tabs .bg_tabs_list .bg_tab_button',
59+].join(', ');
60+
5361/** @type {Record<string, (element: Element) => void>} */
5462const a11yRules = {
5563 [buttonSelectors]: (element) => {
@@ -64,6 +72,12 @@ const a11yRules = {
6472 [toolbarSelectors]: (element) => {
6573 element.setAttribute('role', 'toolbar');
6674 },
75+ [tabListSelectors]: (element) => {
76+ element.setAttribute('role', 'tablist');
77+ },
78+ [tabItemSelectors]: (element) => {
79+ element.setAttribute('role', 'tab');
80+ },
6781 '#toast-container .toast': (element) => {
6882 element.setAttribute('role', 'status');
6983 },
public/scripts/backgrounds.js+153 -27
@@ -1,12 +1,15 @@
11import { Fuse, localforage } from '../lib.js';
22import { characters, chat_metadata, eventSource, event_types, generateQuietPrompt, getCurrentChatId, getRequestHeaders, getThumbnailUrl, saveMetadata, saveSettingsDebounced, this_chid } from '../script.js';
33import { openThirdPartyExtensionMenu, saveMetadataDebounced } from './extensions.js';
44import { SlashCommand } from './slash-commands/SlashCommand.js';
55import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
66import { createThumbnail, flashHighlight, getBase64Async, stringFormat, debounce, setupScrollToTop, saveBase64AsFile, getFileExtension } from './utils.js';
77import { debounce_timeout } from './constants.js';
88import { t } from './i18n.js';
99import { Popup } from './popup.js';
10+import { groups, selected_group } from './group-chats.js';
11+import { humanizedDateTime } from './RossAscends-mods.js';
12+import { deleteMediaFromServer } from './chats.js';
1013
1114const BG_METADATA_KEY = 'custom_background';
1215const LIST_METADATA_KEY = 'chat_backgrounds';
@@ -39,6 +42,26 @@ const THUMBNAIL_CONFIG = {
3942};
4043
4144/**
45+ * Background source types.
46+ * @readonly
47+ * @enum {number}
48+ */
49+const BG_SOURCES = {
50+ GLOBAL: 0,
51+ CHAT: 1,
52+};
53+
54+/**
55+ * Mapping of background sources to their corresponding tab IDs.
56+ * @readonly
57+ * @type {Record<string, string>}
58+ */
59+const BG_TABS = Object.freeze({
60+ [BG_SOURCES.GLOBAL]: 'bg_global_tab',
61+ [BG_SOURCES.CHAT]: 'bg_chat_tab',
62+});
63+
64+/**
4265 * Global IntersectionObserver instance for lazy loading backgrounds
4366 * @type {IntersectionObserver|null}
4467 */
@@ -187,6 +210,7 @@ function onLockBackgroundClick(event = null) {
187210
188211 // Update UI states to reflect the new lock.
189212 highlightLockedBackground();
213+ highlightSelectedBackground();
190214}
191215
192216/**
@@ -219,21 +243,27 @@ function removeBackgroundMetadata() {
219243 saveMetadataDebounced();
220244}
221245
222-function onSelectBackgroundClick() {
246+/**
247+ * Handles the click event for selecting a background.
248+ * @param {JQuery.Event} e Event
249+ */
250+function onSelectBackgroundClick(e) {
223251 const bgFile = $(this).attr('bgfile');
252+ const isCustom = $(this).attr('custom') === 'true';
224253 const backgroundCssUrl = getUrlParameter(this);
254+ const bypassGlobalLock = !isCustom && e.shiftKey;
225255
226256 if ((isChatBackgroundLocked() || isCustom) && !bypassGlobalLock) {
227257 // If a background is locked, update the locked background directly
228258 saveBackgroundMetadata(backgroundCssUrl);
229259 $('#bg1').css('background-image', backgroundCssUrl);
230- highlightLockedBackground();
231260 } else {
232261 // Otherwise, update the global background setting
233262 setBackground(bgFile, backgroundCssUrl);
234263 }
235264
236265 // Update UI highlights to reflect the changes.
266+ highlightLockedBackground();
237267 highlightSelectedBackground();
238268}
239269
@@ -375,20 +405,37 @@ async function onDeleteBackgroundClick(e) {
375405 const bgToDelete = $(this).closest('.bg_example');
376406 const url = bgToDelete.data('url');
377407 const isCustom = bgToDelete.attr('custom') === 'true';
378- const confirm = await Popup.show.confirm(t`Delete the background?`, null);
408+ const deleteFromServerId = 'delete_bg_from_server';
409+ const customInputs = [
410+ {
411+ type: 'checkbox',
412+ label: t`Also delete file from server`,
413+ id: deleteFromServerId,
414+ defaultState: true,
415+ },
416+ ];
417+ let deleteFromServer = false;
418+ const confirm = await Popup.show.confirm(t`Delete the background?`, null, {
419+ customInputs: isCustom ? customInputs : [],
420+ onClose: (popup) => {
421+ if (isCustom) {
422+ deleteFromServer = Boolean(popup?.inputResults?.get(deleteFromServerId) ?? false);
423+ }
424+ },
425+ });
379426 const bg = bgToDelete.attr('bgfile');
380427
381428 if (confirm) {
382429 // If it's not custom, it's a built-in background. Delete it from the server
383430 if (!isCustom) {
384431 await delBackground(bg);
385432 } else {
386433 const list = chat_metadata[LIST_METADATA_KEY] || [];
387434 const index = list.indexOf(bg);
388435 list.splice(index, 1);
389436 }
390437
391438 if (bg === background_settings.name || url === chat_metadata[BG_METADATA_KEY]) {
392439 const siblingSelector = '.bg_example';
393440 const nextBg = bgToDelete.next(siblingSelector);
394441 const prevBg = bgToDelete.prev(siblingSelector);
@@ -409,13 +456,18 @@ async function onDeleteBackgroundClick(e) {
409456
410457 if (url === chat_metadata[BG_METADATA_KEY]) {
411458 removeBackgroundMetadata();
412- highlightLockedBackground();
413459 }
414460
415461 if (isCustom) {
462+ if (deleteFromServer) {
463+ await deleteMediaFromServer(bg);
464+ }
416465 renderChatBackgrounds();
417466 saveMetadataDebouncedawait saveMetadata();
418467 }
468+
469+ highlightLockedBackground();
470+ highlightSelectedBackground();
419471 }
420472}
421473
@@ -607,25 +659,43 @@ async function delBackground(bg) {
607659 }
608660}
609661
610-async function onBackgroundUploadSelected() {
662+/**
611- const form = $('#form_bg_upload').get(0);
663+ * Background upload handler.
664+ * @param {Event} e Event
665+ * @returns {Promise<void>}
666+ */
667+async function onBackgroundUploadSelected(e) {
668+ const input = e.currentTarget;
612669
613670 if (!(forminput instanceof HTMLFormElementHTMLInputElement)) {
614671 console.error('form_bg_uploadInvalid isinput notelement afor formbackground upload');
615672 return;
616673 }
617674
618- const formData = new FormData(form);
675+ for (const file of input.files) {
676+ if (file.size === 0) {
677+ continue;
678+ }
619679
620680 const fileformData = formData.getnew FormData('avatar');
621- if (!(file instanceof File) || file.size === 0) {
681+ formData.append('avatar', file);
622- form.reset();
682+
623- return;
683+ await convertFileIfVideo(formData);
684+ switch (getActiveBackgroundTab()) {
685+ case BG_SOURCES.GLOBAL:
686+ await uploadBackground(formData);
687+ break;
688+ case BG_SOURCES.CHAT:
689+ await uploadChatBackground(formData);
690+ break;
691+ default:
692+ console.error('Unknown background source type');
693+ continue;
694+ }
624695 }
625696
626- await convertFileIfVideo(formData);
697+ // Allow re-uploading the same file again by clearing the input value
627- await uploadBackground(formData);
698+ input.value = '';
628- form.reset();
629699}
630700
631701/**
@@ -699,6 +769,50 @@ async function uploadBackground(formData) {
699769}
700770
701771/**
772+ * Upload a chat background using a FormData object.
773+ * @param {FormData} formData FormData containing the background file
774+ * @returns {Promise<void>}
775+ */
776+async function uploadChatBackground(formData) {
777+ try {
778+ if (!getCurrentChatId()) {
779+ toastr.warning(t`Select a chat to upload a background for it`);
780+ return;
781+ }
782+ if (!formData.has('avatar')) {
783+ console.log('No file provided. Chat background upload cancelled.');
784+ return;
785+ }
786+
787+ const file = formData.get('avatar');
788+ if (!(file instanceof File)) {
789+ console.error('Invalid file type for chat background upload');
790+ return;
791+ }
792+
793+ const imageDataUri = await getBase64Async(file);
794+ const base64Data = imageDataUri.split(',')[1];
795+ const extension = getFileExtension(file);
796+ const characterName = selected_group
797+ ? groups.find(g => g.id === selected_group)?.id?.toString()
798+ : characters[this_chid]?.name;
799+ const filename = `${characterName}_${humanizedDateTime()}`;
800+ const imagePath = await saveBase64AsFile(base64Data, characterName, filename, extension);
801+
802+ const list = chat_metadata[LIST_METADATA_KEY] || [];
803+ list.push(imagePath);
804+ chat_metadata[LIST_METADATA_KEY] = list;
805+ await saveMetadata();
806+ renderChatBackgrounds();
807+ highlightNewBackground(imagePath);
808+ highlightLockedBackground();
809+ highlightSelectedBackground();
810+ } catch (error) {
811+ console.error('Error uploading chat background:', error);
812+ }
813+}
814+
815+/**
702816 * @param {string} bg
703817 */
704818function highlightNewBackground(bg) {
@@ -746,6 +860,14 @@ function onBackgroundFilterInput() {
746860
747861const debouncedOnBackgroundFilterInput = debounce(onBackgroundFilterInput, debounce_timeout.standard);
748862
863+/**
864+ * Gets the active background tab source.
865+ * @returns {BG_SOURCES} Active background tab source
866+ */
867+export function getActiveBackgroundTab() {
868+ return $('#bg_tabs').tabs('option', 'active');
869+}
870+
749871export function initBackgrounds() {
750872 eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
751873 eventSource.on(event_types.FORCE_SET_BACKGROUND, forceSetBackground);
@@ -797,7 +919,7 @@ export function initBackgrounds() {
797919 applyThumbnailColumns(background_settings.thumbnailColumns + 1);
798920 });
799921 $('#auto_background').on('click', autoBackgroundCommand);
800922 $('#add_bg_button').on('change', (e) => onBackgroundUploadSelected(e.originalEvent));
801923 $('#bg-filter').on('input', () => debouncedOnBackgroundFilterInput());
802924 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
803925 name: 'lockbg',
@@ -839,9 +961,13 @@ export function initBackgrounds() {
839961 await onChatChanged();
840962 });
841963
842- setupScrollToTop({
964+ Object.values(BG_TABS).forEach(tabId => {
843- scrollContainerId: 'bg-scrollable-content',
965+ setupScrollToTop({
844- buttonId: 'bg-scroll-top',
966+ scrollContainerId: tabId,
845967 drawerId buttonId: 'Backgroundsbg-scroll-top',
968+ drawerId: 'Backgrounds',
969+ });
846970 });
971+
972+ $('#bg_tabs').tabs();
847973}
public/scripts/keyboard.js+1 -0
@@ -19,6 +19,7 @@ const interactableSelectors = [
1919 '.stscript_btn', // STscript buttons in the chat bar
2020 '.select2_choice_clickable+span.select2-container .select2-selection__choice__display', // select2 control elements if they are meant to be clickable
2121 '.avatar_load_preview', // Char display avatar selection
22+ '.bg_tabs_list .bg_tab_button', // Background tabs
2223];
2324
2425if (CSS.supports('selector(:has(*))')) {
public/scripts/utils.js+12 -0
@@ -2775,6 +2775,17 @@ export function setupScrollToTop({ scrollContainerId, buttonId, drawerId, visibi
27752775 };
27762776 btn.addEventListener('click', onActivate);
27772777
2778+ let frameHandle = null;
2779+ const resizeObserver = new ResizeObserver(() => {
2780+ if (frameHandle !== null) {
2781+ cancelAnimationFrame(frameHandle);
2782+ }
2783+ frameHandle = requestAnimationFrame(() => {
2784+ updateButtonVisibilityThrottled();
2785+ });
2786+ });
2787+ resizeObserver.observe(drawer);
2788+
27782789 // Initial state check
27792790 updateButtonVisibility();
27802791
@@ -2782,6 +2793,7 @@ export function setupScrollToTop({ scrollContainerId, buttonId, drawerId, visibi
27822793 return () => {
27832794 scrollContainer.removeEventListener('scroll', onScroll);
27842795 btn.removeEventListener('click', onActivate);
2796+ resizeObserver.disconnect();
27852797 };
27862798}
27872799