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
Signed| @@ -84,14 +84,6 @@ | ||
| 84 | 84 | gap: 5px; |
| 85 | 85 | } |
| 86 | 86 | |
| 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 | - | |
| 95 | 87 | #bg_menu_content, |
| 96 | 88 | #bg_custom_content { |
| 97 | 89 | display: grid; |
| @@ -301,3 +293,80 @@ | ||
| 301 | 293 | .bg_example:focus-within .BGSampleTitle { |
| 302 | 294 | opacity: 1; |
| 303 | 295 | } |
| 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 | +} | |
| @@ -97,6 +97,10 @@ | ||
| 97 | 97 | max-width: 100dvw; |
| 98 | 98 | } |
| 99 | 99 | |
| 100 | + #bg_tabs .bg_tabs_list .bg_tab_button { | |
| 101 | + min-width: 6em; | |
| 102 | + } | |
| 103 | + | |
| 100 | 104 | #add_background_button_top>span, |
| 101 | 105 | #auto_background>span { |
| 102 | 106 | display: none; |
| @@ -5418,11 +5418,14 @@ | ||
| 5418 | 5418 | <input id="bg-filter" class="text_pole" type="search" data-i18n="[placeholder]Search..." placeholder="Search..." /> |
| 5419 | 5419 | </div> |
| 5420 | 5420 | </div> |
| 5421 | 5421 | <div id="bgbg_tabs" class="heading-scrollablecontainer-contentwith-controls"> |
| 5422 | 5422 | <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> | |
| 5425 | 5425 | </h3li> |
| 5426 | + <li class="bg_tab_button"> | |
| 5427 | + <a href="#bg_chat_tab" data-i18n="Chat">Chat</a> | |
| 5428 | + </li> | |
| 5426 | 5429 | <div class="heading-controls"> |
| 5427 | 5430 | <button id="bg_thumb_zoom_out" class="menu_button menu_button_icon" title="Make thumbnails smaller" data-i18n="[title]Make thumbnails smaller"> |
| 5428 | 5431 | <i class="fa-solid fa-minus"></i> |
| @@ -5431,20 +5434,19 @@ | ||
| 5431 | 5434 | <i class="fa-solid fa-plus"></i> |
| 5432 | 5435 | </button> |
| 5433 | 5436 | </div> |
| 5437 | + </ul> | |
| 5438 | + <div id="bg_global_tab" class="bg_tab_panel"> | |
| 5439 | + <div id="bg_menu_content" class="bg_list"></div> | |
| 5434 | 5440 | </div> |
| 5435 | 5441 | <div id="bg_menu_contentbg_chat_tab" class="bg_listbg_tab_panel"> |
| 5436 | - </div> | |
| 5442 | + <div id="bg_custom_content" class="bg_list"></div> | |
| 5437 | - <h3 data-i18n="Chat Backgrounds" class="wide100p textAlignCenter"> | |
| 5438 | - Chat Backgrounds | |
| 5439 | - </h3> | |
| 5440 | 5443 | <div id="bg_chat_hint" class="wide100p textAlignCenter"> |
| 5441 | 5444 | <span data-i18n="bg_chat_hint_1">Chat backgrounds generated with the</span> <code><i class="fa-solid fa-paintbrush"></i> Image Generation</code> <span data-i18n="bg_chat_hint_2">extension will appear here.</span> |
| 5442 | 5445 | </div> |
| 5443 | - <div id="bg_custom_content" class="bg_list"> | |
| 5444 | 5446 | </div> |
| 5445 | 5447 | </div> |
| 5446 | 5448 | <form id="form_bg_upload" style="display: none;"hidden> |
| 5447 | 5449 | <input type="file" id="add_bg_button" name="avatar" accept="image/jpeg,image/png,image/gif,image/bmp,image/svg+xml*,video/*" multiple hidden> |
| 5448 | 5450 | </form> |
| 5449 | 5451 | <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"> |
| 5450 | 5452 | <i class="fa-solid fa-chevron-up" aria-hidden="true"></i> |
| @@ -6393,7 +6395,7 @@ | ||
| 6393 | 6395 | <div class="jg-menu"> |
| 6394 | 6396 | <div data-action="lock" class="jg-button jg-lock fa-solid fa-lock fa-fw pointer" data-i18n="[title]Lock" title="Lock"></div> |
| 6395 | 6397 | <div data-action="unlock" class="jg-button jg-unlock fa-solid fa-lock-open fa-fw pointer" data-i18n="[title]Unlock" title="Unlock"></div> |
| 6396 | 6398 | <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> |
| 6397 | 6399 | <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> |
| 6398 | 6400 | <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> |
| 6399 | 6401 | </div> |
| @@ -739,8 +739,8 @@ | ||
| 739 | 739 | "Filter": "تصفية", |
| 740 | 740 | "Automatically select a background based on the chat context": "تحديد خلفية تلقائيًا استنادًا إلى سياق الدردشة", |
| 741 | 741 | "Auto-select": "التحديد التلقائي", |
| 742 | 742 | "System BackgroundsGlobal": "خلفيات النظامعالمي", |
| 743 | 743 | "Chat Backgrounds": "خلفيات الدردشة", |
| 744 | 744 | "bg_chat_hint_1": "خلفيات الدردشة التي تم إنشاؤها باستخدام", |
| 745 | 745 | "bg_chat_hint_2": "سوف يظهر الامتداد هنا", |
| 746 | 746 | "Extensions": "امتدادات", |
| @@ -913,7 +913,7 @@ | ||
| 913 | 913 | "Save": "حفظ", |
| 914 | 914 | "Chat History": "تاريخ الدردشة", |
| 915 | 915 | "Import Chat": "استيراد الدردشة", |
| 916 | 916 | "Copy to systemglobal backgrounds": "نسخ إلى خلفياتالخلفيات النظامالعالمية", |
| 917 | 917 | "Rename background": "إعادة تسمية الخلفية", |
| 918 | 918 | "Lock": "قفل", |
| 919 | 919 | "Unlock": "الغاء القفل", |
| @@ -739,8 +739,8 @@ | ||
| 739 | 739 | "Filter": "Filter", |
| 740 | 740 | "Automatically select a background based on the chat context": "Automatisch einen Hintergrund basierend auf dem Chatkontext auswählen", |
| 741 | 741 | "Auto-select": "Automatische Auswahl", |
| 742 | 742 | "System BackgroundsGlobal": "SystemhintergründeGlobal", |
| 743 | 743 | "Chat Backgrounds": "Chat-Hintergründe", |
| 744 | 744 | "bg_chat_hint_1": "Chat-Hintergründe generiert mit dem", |
| 745 | 745 | "bg_chat_hint_2": "Die Erweiterung wird hier angezeigt.", |
| 746 | 746 | "Extensions": "Erweiterungen", |
| @@ -913,7 +913,7 @@ | ||
| 913 | 913 | "Save": "Speichern", |
| 914 | 914 | "Chat History": "Chat-Verlauf", |
| 915 | 915 | "Import Chat": "Chat importieren", |
| 916 | 916 | "Copy to systemglobal backgrounds": "In Systemhintergründeglobale Hintergründe kopieren", |
| 917 | 917 | "Rename background": "Hintergrund umbenennen", |
| 918 | 918 | "Lock": "Sperren", |
| 919 | 919 | "Unlock": "Freischalten", |
| @@ -739,8 +739,8 @@ | ||
| 739 | 739 | "Filter": "Filtro", |
| 740 | 740 | "Automatically select a background based on the chat context": "Seleccionar automáticamente un fondo basado en el contexto del chat", |
| 741 | 741 | "Auto-select": "Auto-seleccionar", |
| 742 | 742 | "System BackgroundsGlobal": "Fondos del SistemaGlobal", |
| 743 | 743 | "Chat Backgrounds": "Fondos de Chat", |
| 744 | 744 | "bg_chat_hint_1": "Fondos de chat generados con el", |
| 745 | 745 | "bg_chat_hint_2": "La extensión aparecerá aquí.", |
| 746 | 746 | "Extensions": "Extensiones", |
| @@ -913,7 +913,7 @@ | ||
| 913 | 913 | "Save": "Guardar", |
| 914 | 914 | "Chat History": "Historial de chat", |
| 915 | 915 | "Import Chat": "Importar chat", |
| 916 | 916 | "Copy to systemglobal backgrounds": "Copiar a fondos del sistemaglobales", |
| 917 | 917 | "Rename background": "Cambiar nombre de fondo", |
| 918 | 918 | "Lock": "Cerrar", |
| 919 | 919 | "Unlock": "desbloquear", |
| @@ -684,8 +684,8 @@ | ||
| 684 | 684 | "Filter": "Filtre", |
| 685 | 685 | "Automatically select a background based on the chat context": "Sélectionner automatiquement un arrière-plan en fonction du contexte de la discussion", |
| 686 | 686 | "Auto-select": "Sélection automatique", |
| 687 | 687 | "System BackgroundsGlobal": "Arrière-plans du systèmeGlobal", |
| 688 | 688 | "Chat Backgrounds": "Arrière-plans de la discussionDiscussion", |
| 689 | 689 | "bg_chat_hint_1": "Les arrière-plans générés avec l'extension ", |
| 690 | 690 | "bg_chat_hint_2": " apparaîtrons ici.", |
| 691 | 691 | "Extensions": "Extensions", |
| @@ -856,7 +856,7 @@ | ||
| 856 | 856 | "Save": "Enregistrer", |
| 857 | 857 | "Chat History": "Historique de la conversation", |
| 858 | 858 | "Import Chat": "Importer une discussion", |
| 859 | 859 | "Copy to systemglobal backgrounds": "Copier vers les arrière-plans du systèmeglobaux", |
| 860 | 860 | "Rename background": "Renommer l'arrière-plan", |
| 861 | 861 | "Lock": "Verrouiller", |
| 862 | 862 | "Unlock": "Déverrouiller", |
| @@ -737,8 +737,8 @@ | ||
| 737 | 737 | "Filter": "Sía", |
| 738 | 738 | "Automatically select a background based on the chat context": "Velja sjálfkrafa bakgrunn út frá samhengi spjallsins", |
| 739 | 739 | "Auto-select": "Sjálfval", |
| 740 | 740 | "System BackgroundsGlobal": "Kerfis bakgrunnsmyndirAlmennt", |
| 741 | 741 | "Chat Backgrounds": "Bakgrunnsmyndir spjallsSpjall", |
| 742 | 742 | "bg_chat_hint_1": "Spjallbakgrunnur myndaður með", |
| 743 | 743 | "bg_chat_hint_2": "viðbót mun birtast hér.", |
| 744 | 744 | "Extensions": "Viðbætur", |
| @@ -911,7 +911,7 @@ | ||
| 911 | 911 | "Save": "Vista", |
| 912 | 912 | "Chat History": "Spjall saga", |
| 913 | 913 | "Import Chat": "Flytja inn spjall", |
| 914 | 914 | "Copy to systemglobal backgrounds": "Afritaðu í kerfisbakgrunnalþjóðlegan bakgrunn", |
| 915 | 915 | "Rename background": "Endurnefna bakgrunn", |
| 916 | 916 | "Lock": "Læsa", |
| 917 | 917 | "Unlock": "Opnaðu", |
| @@ -739,8 +739,8 @@ | ||
| 739 | 739 | "Filter": "Filtro", |
| 740 | 740 | "Automatically select a background based on the chat context": "Seleziona automaticamente uno sfondo in base al contesto della chat", |
| 741 | 741 | "Auto-select": "Selezione automatica", |
| 742 | 742 | "System BackgroundsGlobal": "Sfondi di sistemaGlobale", |
| 743 | 743 | "Chat Backgrounds": "Sfondi Chat", |
| 744 | 744 | "bg_chat_hint_1": "Sfondi chat generati con", |
| 745 | 745 | "bg_chat_hint_2": "l'estensione apparirà qui.", |
| 746 | 746 | "Extensions": "Estensioni", |
| @@ -913,7 +913,7 @@ | ||
| 913 | 913 | "Save": "Salva", |
| 914 | 914 | "Chat History": "Cronologia Chat", |
| 915 | 915 | "Import Chat": "Importa chat", |
| 916 | 916 | "Copy to systemglobal backgrounds": "Copia negli sfondi di sistemaglobali", |
| 917 | 917 | "Rename background": "Rinominare lo sfondo", |
| 918 | 918 | "Lock": "Serratura", |
| 919 | 919 | "Unlock": "Sbloccare", |
| @@ -739,8 +739,8 @@ | ||
| 739 | 739 | "Filter": "フィルター", |
| 740 | 740 | "Automatically select a background based on the chat context": "チャットのコンテキストに基づいて背景を自動選択", |
| 741 | 741 | "Auto-select": "自動選択", |
| 742 | 742 | "System BackgroundsGlobal": "システムの背景グローバル", |
| 743 | 743 | "Chat Backgrounds": "チャットの背景チャット", |
| 744 | 744 | "bg_chat_hint_1": "", |
| 745 | 745 | "bg_chat_hint_2": "拡張機能で生成したチャットの背景はここに表示されます。", |
| 746 | 746 | "Extensions": "拡張機能", |
| @@ -913,7 +913,7 @@ | ||
| 913 | 913 | "Save": "保存", |
| 914 | 914 | "Chat History": "チャット履歴", |
| 915 | 915 | "Import Chat": "チャットをインポート", |
| 916 | 916 | "Copy to systemglobal backgrounds": "システム背景にコピーグローバル背景にコピー", |
| 917 | 917 | "Rename background": "背景の名前を変更", |
| 918 | 918 | "Lock": "ロック", |
| 919 | 919 | "Unlock": "ロック解除", |
| @@ -752,8 +752,8 @@ | ||
| 752 | 752 | "Filter": "필터", |
| 753 | 753 | "Automatically select a background based on the chat context": "채팅 컨텍스트를 기반으로 자동으로 배경 선택", |
| 754 | 754 | "Auto-select": "자동 선택", |
| 755 | 755 | "System BackgroundsGlobal": "시스템 배경글로벌", |
| 756 | 756 | "Chat Backgrounds": "채팅 배경", |
| 757 | 757 | "bg_chat_hint_1": "다음으로 생성된 채팅 배경", |
| 758 | 758 | "bg_chat_hint_2": "확장 프로그램이 여기에 표시됩니다.", |
| 759 | 759 | "Extensions": "확장", |
| @@ -927,7 +927,7 @@ | ||
| 927 | 927 | "Save": "저장", |
| 928 | 928 | "Chat History": "채팅 기록", |
| 929 | 929 | "Import Chat": "채팅 가져오기", |
| 930 | 930 | "Copy to systemglobal backgrounds": "시스템글로벌 배경에 복사", |
| 931 | 931 | "Rename background": "배경 이름 바꾸기", |
| 932 | 932 | "Lock": "잠금", |
| 933 | 933 | "Unlock": "잠금 해제", |
| @@ -735,8 +735,8 @@ | ||
| 735 | 735 | "Filter": "Filter", |
| 736 | 736 | "Automatically select a background based on the chat context": "Automatisch een achtergrond selecteren op basis van de chatcontext", |
| 737 | 737 | "Auto-select": "Automatisch selecteren", |
| 738 | 738 | "System BackgroundsGlobal": "SysteemachtergrondenGlobaal", |
| 739 | 739 | "Chat Backgrounds": "Achtergronden chatChat", |
| 740 | 740 | "bg_chat_hint_1": "Chatachtergronden gegenereerd met de", |
| 741 | 741 | "bg_chat_hint_2": "extensie zal hier verschijnen.", |
| 742 | 742 | "Extensions": "Uitbreidingen", |
| @@ -909,7 +909,7 @@ | ||
| 909 | 909 | "Save": "Opslaan", |
| 910 | 910 | "Chat History": "Chatgeschiedenis", |
| 911 | 911 | "Import Chat": "Chat importeren", |
| 912 | 912 | "Copy to systemglobal backgrounds": "Kopiëren naar systeemachtergrondenglobale achtergronden", |
| 913 | 913 | "Rename background": "Achtergrond hernoemen", |
| 914 | 914 | "Lock": "Slot", |
| 915 | 915 | "Unlock": "Ontgrendelen", |
| @@ -737,8 +737,8 @@ | ||
| 737 | 737 | "Filter": "Filtro", |
| 738 | 738 | "Automatically select a background based on the chat context": "Selecionar automaticamente um plano de fundo com base no contexto do chat", |
| 739 | 739 | "Auto-select": "Seleção automática", |
| 740 | 740 | "System BackgroundsGlobal": "Fundos do SistemaGlobal", |
| 741 | 741 | "Chat Backgrounds": "Fundos de Chat", |
| 742 | 742 | "bg_chat_hint_1": "Planos de fundo de bate-papo gerados com o", |
| 743 | 743 | "bg_chat_hint_2": "a extensão aparecerá aqui.", |
| 744 | 744 | "Extensions": "Extensões", |
| @@ -911,7 +911,7 @@ | ||
| 911 | 911 | "Save": "Salvar", |
| 912 | 912 | "Chat History": "Histórico de Chat", |
| 913 | 913 | "Import Chat": "Importar bate-papo", |
| 914 | 914 | "Copy to systemglobal backgrounds": "Copiar para planos de fundo do sistemaglobais", |
| 915 | 915 | "Rename background": "Renomear plano de fundo", |
| 916 | 916 | "Lock": "Trancar", |
| 917 | 917 | "Unlock": "Desbloquear", |
| @@ -244,7 +244,6 @@ | ||
| 244 | 244 | "Always include examples": "Всегда применять примеры", |
| 245 | 245 | "Never include examples": "Никогда не применять примеры", |
| 246 | 246 | "Forbid External Media": "Запрет внешних медиа", |
| 247 | - "System Backgrounds": "Системные фоны", | |
| 248 | 247 | "Name": "Имя", |
| 249 | 248 | "Auto-connect": "Подключаться автоматически", |
| 250 | 249 | "First message": "Первое сообщение", |
| @@ -350,7 +349,7 @@ | ||
| 350 | 349 | "Minimum number of blacklisted words detected to trigger an auto-swipe": "Минимальное количество обнаруженных запрещённых слов, при котором срабатывает авто-свайп.", |
| 351 | 350 | "User Message Blur Tint": "Ваши сообщения", |
| 352 | 351 | "AI Message Blur Tint": "Сообщения ИИ", |
| 353 | 352 | "Chat Backgrounds": "Фоны чатаЧат", |
| 354 | 353 | "Chat Background": "Фон чата", |
| 355 | 354 | "UI Background": "Фон UI", |
| 356 | 355 | "Mad Lab Mode": "Режим безумца", |
| @@ -1326,7 +1325,7 @@ | ||
| 1326 | 1325 | "Auto Mode delay": "Auto Mode delay", |
| 1327 | 1326 | "Bulk_edit_characters": "Bulk edit characters\r\rClick to toggle characters\rShift + Click to select/deselect a range of characters\rRight-click for actions", |
| 1328 | 1327 | "Bulk select all characters": "Bulk select all characters", |
| 1329 | 1328 | "Copy to systemglobal backgrounds": "CopyСкопировать toв systemглобальные backgroundsфоны", |
| 1330 | 1329 | "Chat Scenario Override": "Chat Scenario Override", |
| 1331 | 1330 | "Chat Lorebook": "Chat Lorebook for", |
| 1332 | 1331 | "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.", |
| @@ -745,8 +745,8 @@ | ||
| 745 | 745 | "Filter": "ตัวกรอง", |
| 746 | 746 | "Automatically select a background based on the chat context": "เลือกพื้นหลังโดยอัตโนมัติตามบริบทของแชท", |
| 747 | 747 | "Auto-select": "เลือกอัตโนมัติ", |
| 748 | 748 | "System BackgroundsGlobal": "พื้นหลังของระบบทั่วโลก", |
| 749 | 749 | "Chat Backgrounds": "พื้นหลังแชทแชท", |
| 750 | 750 | "bg_chat_hint_1": "พื้นหลังของแชทที่ถูกสร้างด้วย", |
| 751 | 751 | "bg_chat_hint_2": "จะปรากฏที่นี่", |
| 752 | 752 | "Extensions": "โปรแกรมส่วนขยาย", |
| @@ -921,7 +921,7 @@ | ||
| 921 | 921 | "Save": "บันทึก", |
| 922 | 922 | "Chat History": "ประวัติการแชท", |
| 923 | 923 | "Import Chat": "นำเข้าไฟล์แชท", |
| 924 | 924 | "Copy to systemglobal backgrounds": "คัดลอกไปยังพื้นหลังนหลังทั่วโลก", |
| 925 | 925 | "Rename background": "เปลี่ยนชื่อพื้นหลัง", |
| 926 | 926 | "Lock": "ล็อค", |
| 927 | 927 | "Unlock": "ปลดล็อค", |
| @@ -737,8 +737,8 @@ | ||
| 737 | 737 | "Filter": "Фільтр", |
| 738 | 738 | "Automatically select a background based on the chat context": "Автоматичний вибір фону на основі контексту чату", |
| 739 | 739 | "Auto-select": "Автовибір", |
| 740 | 740 | "System BackgroundsGlobal": "Системні фониГлобальні", |
| 741 | 741 | "Chat Backgrounds": "Фони чатуЧат", |
| 742 | 742 | "bg_chat_hint_1": "Фонове зображення чату, створене за допомогою", |
| 743 | 743 | "bg_chat_hint_2": "тут з’явиться розширення.", |
| 744 | 744 | "Extensions": "Розширення", |
| @@ -911,7 +911,7 @@ | ||
| 911 | 911 | "Save": "Зберегти", |
| 912 | 912 | "Chat History": "Історія чату", |
| 913 | 913 | "Import Chat": "Імпорт чату", |
| 914 | 914 | "Copy to systemglobal backgrounds": "Скопіювати в системніглобальні фони", |
| 915 | 915 | "Rename background": "Перейменувати фон", |
| 916 | 916 | "Lock": "Замок", |
| 917 | 917 | "Unlock": "Розблокувати", |
| @@ -737,8 +737,8 @@ | ||
| 737 | 737 | "Filter": "Bộ lọc", |
| 738 | 738 | "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", |
| 739 | 739 | "Auto-select": "Tự động chọn", |
| 740 | 740 | "System BackgroundsGlobal": "Nền HệToàn thốngcầu", |
| 741 | 741 | "Chat Backgrounds": "Hình nềnTrò Chatchuyện", |
| 742 | 742 | "bg_chat_hint_1": "Hình nền trò chuyện được tạo bằng", |
| 743 | 743 | "bg_chat_hint_2": "tiện ích sẽ xuất hiện ở đây.", |
| 744 | 744 | "Extensions": "Tiện ích", |
| @@ -911,7 +911,7 @@ | ||
| 911 | 911 | "Save": "Lưu", |
| 912 | 912 | "Chat History": "Lịch sử Trò chuyện", |
| 913 | 913 | "Import Chat": "Nhập trò chuyện", |
| 914 | 914 | "Copy to systemglobal backgrounds": "Sao chép vào nền hệtoàn thốngcầu", |
| 915 | 915 | "Rename background": "Đổi tên nền background", |
| 916 | 916 | "Lock": "Khóa", |
| 917 | 917 | "Unlock": "Mở khóa", |
| @@ -961,8 +961,7 @@ | ||
| 961 | 961 | "Automatically select a background based on the chat context": "根据聊天上下文自动选择背景", |
| 962 | 962 | "Auto-select": "自动选择", |
| 963 | 963 | "Add Background": "添加背景", |
| 964 | 964 | "System BackgroundsGlobal": "系统背景全局", |
| 965 | - "Chat Backgrounds": "聊天背景", | |
| 966 | 965 | "bg_chat_hint_1": "使用生成的聊天背景", |
| 967 | 966 | "bg_chat_hint_2": "扩展名将出现在这里。", |
| 968 | 967 | "Extensions": "扩展", |
| @@ -1157,7 +1156,7 @@ | ||
| 1157 | 1156 | "Save": "保存", |
| 1158 | 1157 | "Chat History": "聊天记录", |
| 1159 | 1158 | "Import Chat": "导入聊天", |
| 1160 | 1159 | "Copy to systemglobal backgrounds": "复制到系统背景复制到全局背景", |
| 1161 | 1160 | "Lock": "锁定", |
| 1162 | 1161 | "Unlock": "解锁", |
| 1163 | 1162 | "Rename Background": "重命名背景", |
| @@ -736,8 +736,7 @@ | ||
| 736 | 736 | "Filter": "篩選", |
| 737 | 737 | "Automatically select a background based on the chat context": "根據聊天上下文自動選擇背景", |
| 738 | 738 | "Auto-select": "自動選擇", |
| 739 | 739 | "System BackgroundsGlobal": "系統背景圖片全域", |
| 740 | - "Chat Backgrounds": "聊天背景圖片", | |
| 741 | 740 | "bg_chat_hint_1": "使用擴充功能", |
| 742 | 741 | "bg_chat_hint_2": "所產生的背景圖片將在此顯示。", |
| 743 | 742 | "Extensions": "擴充功能", |
| @@ -909,7 +908,7 @@ | ||
| 909 | 908 | "(Examples of chat dialog. Begin each example with START on a new line.)": "(聊天對話範例。每個範例以新的行並以「START」開始。)", |
| 910 | 909 | "Chat History": "聊天記錄", |
| 911 | 910 | "Import Chat": "匯入聊天", |
| 912 | 911 | "Copy to systemglobal backgrounds": "複製到系統背景圖片複製到全局背景圖片", |
| 913 | 912 | "Rename background": "重新命名背景圖片", |
| 914 | 913 | "Lock": "上鎖", |
| 915 | 914 | "Unlock": "解鎖", |
| @@ -50,6 +50,14 @@ const toolbarSelectors = [ | ||
| 50 | 50 | '.jg-menu', |
| 51 | 51 | ].join(', '); |
| 52 | 52 | |
| 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 | + | |
| 53 | 61 | /** @type {Record<string, (element: Element) => void>} */ |
| 54 | 62 | const a11yRules = { |
| 55 | 63 | [buttonSelectors]: (element) => { |
| @@ -64,6 +72,12 @@ const a11yRules = { | ||
| 64 | 72 | [toolbarSelectors]: (element) => { |
| 65 | 73 | element.setAttribute('role', 'toolbar'); |
| 66 | 74 | }, |
| 75 | + [tabListSelectors]: (element) => { | |
| 76 | + element.setAttribute('role', 'tablist'); | |
| 77 | + }, | |
| 78 | + [tabItemSelectors]: (element) => { | |
| 79 | + element.setAttribute('role', 'tab'); | |
| 80 | + }, | |
| 67 | 81 | '#toast-container .toast': (element) => { |
| 68 | 82 | element.setAttribute('role', 'status'); |
| 69 | 83 | }, |
| @@ -1,12 +1,15 @@ | ||
| 1 | 1 | import { Fuse, localforage } from '../lib.js'; |
| 2 | 2 | import { characters, chat_metadata, eventSource, event_types, generateQuietPrompt, getCurrentChatId, getRequestHeaders, getThumbnailUrl, saveMetadata, saveSettingsDebounced, this_chid } from '../script.js'; |
| 3 | 3 | import { openThirdPartyExtensionMenu, saveMetadataDebounced } from './extensions.js'; |
| 4 | 4 | import { SlashCommand } from './slash-commands/SlashCommand.js'; |
| 5 | 5 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 6 | 6 | import { createThumbnail, flashHighlight, getBase64Async, stringFormat, debounce, setupScrollToTop, saveBase64AsFile, getFileExtension } from './utils.js'; |
| 7 | 7 | import { debounce_timeout } from './constants.js'; |
| 8 | 8 | import { t } from './i18n.js'; |
| 9 | 9 | import { 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'; | |
| 10 | 13 | |
| 11 | 14 | const BG_METADATA_KEY = 'custom_background'; |
| 12 | 15 | const LIST_METADATA_KEY = 'chat_backgrounds'; |
| @@ -39,6 +42,26 @@ const THUMBNAIL_CONFIG = { | ||
| 39 | 42 | }; |
| 40 | 43 | |
| 41 | 44 | /** |
| 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 | +/** | |
| 42 | 65 | * Global IntersectionObserver instance for lazy loading backgrounds |
| 43 | 66 | * @type {IntersectionObserver|null} |
| 44 | 67 | */ |
| @@ -187,6 +210,7 @@ function onLockBackgroundClick(event = null) { | ||
| 187 | 210 | |
| 188 | 211 | // Update UI states to reflect the new lock. |
| 189 | 212 | highlightLockedBackground(); |
| 213 | + highlightSelectedBackground(); | |
| 190 | 214 | } |
| 191 | 215 | |
| 192 | 216 | /** |
| @@ -219,21 +243,27 @@ function removeBackgroundMetadata() { | ||
| 219 | 243 | saveMetadataDebounced(); |
| 220 | 244 | } |
| 221 | 245 | |
| 222 | -function onSelectBackgroundClick() { | |
| 246 | +/** | |
| 247 | + * Handles the click event for selecting a background. | |
| 248 | + * @param {JQuery.Event} e Event | |
| 249 | + */ | |
| 250 | +function onSelectBackgroundClick(e) { | |
| 223 | 251 | const bgFile = $(this).attr('bgfile'); |
| 252 | + const isCustom = $(this).attr('custom') === 'true'; | |
| 224 | 253 | const backgroundCssUrl = getUrlParameter(this); |
| 254 | + const bypassGlobalLock = !isCustom && e.shiftKey; | |
| 225 | 255 | |
| 226 | 256 | if ((isChatBackgroundLocked() || isCustom) && !bypassGlobalLock) { |
| 227 | 257 | // If a background is locked, update the locked background directly |
| 228 | 258 | saveBackgroundMetadata(backgroundCssUrl); |
| 229 | 259 | $('#bg1').css('background-image', backgroundCssUrl); |
| 230 | - highlightLockedBackground(); | |
| 231 | 260 | } else { |
| 232 | 261 | // Otherwise, update the global background setting |
| 233 | 262 | setBackground(bgFile, backgroundCssUrl); |
| 234 | 263 | } |
| 235 | 264 | |
| 236 | 265 | // Update UI highlights to reflect the changes. |
| 266 | + highlightLockedBackground(); | |
| 237 | 267 | highlightSelectedBackground(); |
| 238 | 268 | } |
| 239 | 269 | |
| @@ -375,20 +405,37 @@ async function onDeleteBackgroundClick(e) { | ||
| 375 | 405 | const bgToDelete = $(this).closest('.bg_example'); |
| 376 | 406 | const url = bgToDelete.data('url'); |
| 377 | 407 | 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 | + }); | |
| 379 | 426 | const bg = bgToDelete.attr('bgfile'); |
| 380 | 427 | |
| 381 | 428 | if (confirm) { |
| 382 | 429 | // If it's not custom, it's a built-in background. Delete it from the server |
| 383 | 430 | if (!isCustom) { |
| 384 | 431 | await delBackground(bg); |
| 385 | 432 | } else { |
| 386 | 433 | const list = chat_metadata[LIST_METADATA_KEY] || []; |
| 387 | 434 | const index = list.indexOf(bg); |
| 388 | 435 | list.splice(index, 1); |
| 389 | 436 | } |
| 390 | 437 | |
| 391 | 438 | if (bg === background_settings.name || url === chat_metadata[BG_METADATA_KEY]) { |
| 392 | 439 | const siblingSelector = '.bg_example'; |
| 393 | 440 | const nextBg = bgToDelete.next(siblingSelector); |
| 394 | 441 | const prevBg = bgToDelete.prev(siblingSelector); |
| @@ -409,13 +456,18 @@ async function onDeleteBackgroundClick(e) { | ||
| 409 | 456 | |
| 410 | 457 | if (url === chat_metadata[BG_METADATA_KEY]) { |
| 411 | 458 | removeBackgroundMetadata(); |
| 412 | - highlightLockedBackground(); | |
| 413 | 459 | } |
| 414 | 460 | |
| 415 | 461 | if (isCustom) { |
| 462 | + if (deleteFromServer) { | |
| 463 | + await deleteMediaFromServer(bg); | |
| 464 | + } | |
| 416 | 465 | renderChatBackgrounds(); |
| 417 | 466 | saveMetadataDebouncedawait saveMetadata(); |
| 418 | 467 | } |
| 468 | + | |
| 469 | + highlightLockedBackground(); | |
| 470 | + highlightSelectedBackground(); | |
| 419 | 471 | } |
| 420 | 472 | } |
| 421 | 473 | |
| @@ -607,25 +659,43 @@ async function delBackground(bg) { | ||
| 607 | 659 | } |
| 608 | 660 | } |
| 609 | 661 | |
| 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; | |
| 612 | 669 | |
| 613 | 670 | if (!(forminput instanceof HTMLFormElementHTMLInputElement)) { |
| 614 | 671 | console.error('form_bg_uploadInvalid isinput notelement afor formbackground upload'); |
| 615 | 672 | return; |
| 616 | 673 | } |
| 617 | 674 | |
| 618 | - const formData = new FormData(form); | |
| 675 | + for (const file of input.files) { | |
| 619 | - | |
| 676 | + if (file.size === 0) { | |
| 620 | - const file = formData.get('avatar'); | |
| 677 | + continue; | |
| 621 | - if (!(file instanceof File) || file.size === 0) { | |
| 622 | - form.reset(); | |
| 623 | - return; | |
| 624 | 678 | } |
| 625 | 679 | |
| 680 | + const formData = new FormData(); | |
| 681 | + formData.append('avatar', file); | |
| 682 | + | |
| 626 | 683 | await convertFileIfVideo(formData); |
| 684 | + switch (getActiveBackgroundTab()) { | |
| 685 | + case BG_SOURCES.GLOBAL: | |
| 627 | 686 | await uploadBackground(formData); |
| 628 | - form.reset(); | |
| 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 | + } | |
| 695 | + } | |
| 696 | + | |
| 697 | + // Allow re-uploading the same file again by clearing the input value | |
| 698 | + input.value = ''; | |
| 629 | 699 | } |
| 630 | 700 | |
| 631 | 701 | /** |
| @@ -699,6 +769,50 @@ async function uploadBackground(formData) { | ||
| 699 | 769 | } |
| 700 | 770 | |
| 701 | 771 | /** |
| 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 | +/** | |
| 702 | 816 | * @param {string} bg |
| 703 | 817 | */ |
| 704 | 818 | function highlightNewBackground(bg) { |
| @@ -746,6 +860,14 @@ function onBackgroundFilterInput() { | ||
| 746 | 860 | |
| 747 | 861 | const debouncedOnBackgroundFilterInput = debounce(onBackgroundFilterInput, debounce_timeout.standard); |
| 748 | 862 | |
| 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 | + | |
| 749 | 871 | export function initBackgrounds() { |
| 750 | 872 | eventSource.on(event_types.CHAT_CHANGED, onChatChanged); |
| 751 | 873 | eventSource.on(event_types.FORCE_SET_BACKGROUND, forceSetBackground); |
| @@ -797,7 +919,7 @@ export function initBackgrounds() { | ||
| 797 | 919 | applyThumbnailColumns(background_settings.thumbnailColumns + 1); |
| 798 | 920 | }); |
| 799 | 921 | $('#auto_background').on('click', autoBackgroundCommand); |
| 800 | 922 | $('#add_bg_button').on('change', (e) => onBackgroundUploadSelected(e.originalEvent)); |
| 801 | 923 | $('#bg-filter').on('input', () => debouncedOnBackgroundFilterInput()); |
| 802 | 924 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 803 | 925 | name: 'lockbg', |
| @@ -839,9 +961,13 @@ export function initBackgrounds() { | ||
| 839 | 961 | await onChatChanged(); |
| 840 | 962 | }); |
| 841 | 963 | |
| 964 | + Object.values(BG_TABS).forEach(tabId => { | |
| 842 | 965 | setupScrollToTop({ |
| 843 | - scrollContainerId: 'bg-scrollable-content', | |
| 966 | + scrollContainerId: tabId, | |
| 844 | 967 | buttonId: 'bg-scroll-top', |
| 845 | 968 | drawerId: 'Backgrounds', |
| 846 | 969 | }); |
| 970 | + }); | |
| 971 | + | |
| 972 | + $('#bg_tabs').tabs(); | |
| 847 | 973 | } |
| @@ -19,6 +19,7 @@ const interactableSelectors = [ | ||
| 19 | 19 | '.stscript_btn', // STscript buttons in the chat bar |
| 20 | 20 | '.select2_choice_clickable+span.select2-container .select2-selection__choice__display', // select2 control elements if they are meant to be clickable |
| 21 | 21 | '.avatar_load_preview', // Char display avatar selection |
| 22 | + '.bg_tabs_list .bg_tab_button', // Background tabs | |
| 22 | 23 | ]; |
| 23 | 24 | |
| 24 | 25 | if (CSS.supports('selector(:has(*))')) { |
| @@ -2775,6 +2775,17 @@ export function setupScrollToTop({ scrollContainerId, buttonId, drawerId, visibi | ||
| 2775 | 2775 | }; |
| 2776 | 2776 | btn.addEventListener('click', onActivate); |
| 2777 | 2777 | |
| 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 | + | |
| 2778 | 2789 | // Initial state check |
| 2779 | 2790 | updateButtonVisibility(); |
| 2780 | 2791 | |
| @@ -2782,6 +2793,7 @@ export function setupScrollToTop({ scrollContainerId, buttonId, drawerId, visibi | ||
| 2782 | 2793 | return () => { |
| 2783 | 2794 | scrollContainer.removeEventListener('scroll', onScroll); |
| 2784 | 2795 | btn.removeEventListener('click', onActivate); |
| 2796 | + resizeObserver.disconnect(); | |
| 2785 | 2797 | }; |
| 2786 | 2798 | } |
| 2787 | 2799 | |