From e3714e9b6aaea7119a0de37d6b8f473054e3ec98 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 21 Jun 2024 22:31:34 +0300 Subject: [PATCH 01/24] Fix search provider --- src/endpoints/search.js | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/endpoints/search.js b/src/endpoints/search.js index 7f08d89c8..457124228 100644 --- a/src/endpoints/search.js +++ b/src/endpoints/search.js @@ -147,25 +147,36 @@ router.post('/searxng', jsonParser, async (request, response) => { console.log('SearXNG query', baseUrl, query); - const url = new URL(baseUrl); - const params = new URLSearchParams(); - params.append('q', query); - params.append('format', 'html'); - url.pathname = '/search'; - url.search = params.toString(); + const mainPageUrl = new URL(baseUrl); + const mainPageRequest = await fetch(mainPageUrl, { headers: visitHeaders }); - const result = await fetch(url, { - method: 'POST', - headers: visitHeaders, - }); - - if (!result.ok) { - const text = await result.text(); - console.log('SearXNG request failed', result.statusText, text); + if (!mainPageRequest.ok) { + console.log('SearXNG request failed', mainPageRequest.statusText); return response.sendStatus(500); } - const data = await result.text(); + const mainPageText = await mainPageRequest.text(); + const clientHref = mainPageText.match(/href="(\/client.+\.css)"/)?.[1]; + + if (clientHref) { + const clientUrl = new URL(clientHref, baseUrl); + await fetch(clientUrl, { headers: visitHeaders }); + } + + const searchUrl = new URL('/search', baseUrl); + const searchParams = new URLSearchParams(); + searchParams.append('q', query); + searchUrl.search = searchParams.toString(); + + const searchResult = await fetch(searchUrl, { headers: visitHeaders }); + + if (!searchResult.ok) { + const text = await searchResult.text(); + console.log('SearXNG request failed', searchResult.statusText, text); + return response.sendStatus(500); + } + + const data = await searchResult.text(); return response.send(data); } catch (error) { console.log('SearXNG request failed', error); From a00560d2b379dddb7f7f4bd53bc0e842f6027f3e Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 22 Jun 2024 00:36:29 +0300 Subject: [PATCH 02/24] Ensure format supported before captioning --- public/scripts/extensions/caption/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/extensions/caption/index.js b/public/scripts/extensions/caption/index.js index d981c15a6..d7a167fd4 100644 --- a/public/scripts/extensions/caption/index.js +++ b/public/scripts/extensions/caption/index.js @@ -1,4 +1,4 @@ -import { getBase64Async, isTrueBoolean, saveBase64AsFile } from '../../utils.js'; +import { ensureImageFormatSupported, getBase64Async, isTrueBoolean, saveBase64AsFile } from '../../utils.js'; import { getContext, getApiUrl, doExtrasFetch, extension_settings, modules, renderExtensionTemplateAsync } from '../../extensions.js'; import { callPopup, getRequestHeaders, saveSettingsDebounced, substituteParamsExtended } from '../../../script.js'; import { getMessageTimeStamp } from '../../RossAscends-mods.js'; @@ -272,7 +272,7 @@ async function getCaptionForFile(file, prompt, quiet) { try { setSpinnerIcon(); const context = getContext(); - const fileData = await getBase64Async(file); + const fileData = await getBase64Async(await ensureImageFormatSupported(file)); const base64Format = fileData.split(',')[0].split(';')[0].split('/')[1]; const base64Data = fileData.split(',')[1]; const { caption } = await doCaptionRequest(base64Data, fileData, prompt); From 9c2de78ad3d73c55d777fbef13f4282da38c5fc3 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 22 Jun 2024 01:42:28 +0300 Subject: [PATCH 03/24] Fix OpenRouter caption headers --- src/endpoints/openai.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/endpoints/openai.js b/src/endpoints/openai.js index 9488d03e6..75de75b7b 100644 --- a/src/endpoints/openai.js +++ b/src/endpoints/openai.js @@ -6,6 +6,7 @@ const fs = require('fs'); const { jsonParser, urlencodedParser } = require('../express-common'); const { getConfigValue, mergeObjectWithYaml, excludeKeysByYaml, trimV1 } = require('../util'); const { setAdditionalHeaders } = require('../additional-headers'); +const { OPENROUTER_HEADERS } = require('../constants'); const router = express.Router(); @@ -80,7 +81,7 @@ router.post('/caption-image', jsonParser, async (request, response) => { if (request.body.api === 'openrouter') { apiUrl = 'https://openrouter.ai/api/v1/chat/completions'; - headers['HTTP-Referer'] = request.headers.referer; + Object.assign(headers, OPENROUTER_HEADERS); } if (request.body.api === 'openai') { From 473e11c773de10685022afc7a9ba17cc263407b0 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 22 Jun 2024 02:03:39 +0300 Subject: [PATCH 04/24] New OpenRouter providers --- public/scripts/textgen-models.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/scripts/textgen-models.js b/public/scripts/textgen-models.js index d8f36cf45..5f663c816 100644 --- a/public/scripts/textgen-models.js +++ b/public/scripts/textgen-models.js @@ -39,6 +39,8 @@ const OPENROUTER_PROVIDERS = [ 'Novita', 'Lynn', 'Lynn 2', + 'DeepSeek', + 'Infermatic', ]; export async function loadOllamaModels(data) { From 7c2b475e4653852a3b30902554bb7e4a6d3a2cd5 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sat, 22 Jun 2024 04:54:13 +0200 Subject: [PATCH 05/24] Improve popup class with close event handlers --- public/scripts/popup.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/public/scripts/popup.js b/public/scripts/popup.js index be460feb6..6e388839c 100644 --- a/public/scripts/popup.js +++ b/public/scripts/popup.js @@ -28,6 +28,8 @@ export const POPUP_RESULT = { * @property {boolean?} [allowVerticalScrolling] - Whether to allow vertical scrolling in the popup * @property {POPUP_RESULT|number?} [defaultResult] - The default result of this popup when Enter is pressed. Can be changed from `POPUP_RESULT.AFFIRMATIVE`. * @property {CustomPopupButton[]|string[]?} [customButtons] - Custom buttons to add to the popup. If only strings are provided, the buttons will be added with default options, and their result will be in order from `2` onward. + * @property {(popup: Popup) => boolean?} [onClosing] - Handler called before the popup closes, return `false` to cancel the close + * @property {(popup: Popup) => void?} [onClose] - Handler called after the popup closes, but before the DOM is cleaned up */ /** @@ -78,6 +80,9 @@ export class Popup { /** @type {POPUP_RESULT|number?} */ defaultResult; /** @type {CustomPopupButton[]|string[]?} */ customButtons; + /** @type {(popup: Popup) => boolean?} */ onClosing; + /** @type {(popup: Popup) => void?} */ onClose; + /** @type {POPUP_RESULT|number} */ result; /** @type {any} */ value; @@ -94,13 +99,17 @@ export class Popup { * @param {string} [inputValue=''] - The initial value of the input field * @param {PopupOptions} [options={}] - Additional options for the popup */ - constructor(content, type, inputValue = '', { okButton = null, cancelButton = null, rows = 1, wide = false, wider = false, large = false, allowHorizontalScrolling = false, allowVerticalScrolling = false, defaultResult = POPUP_RESULT.AFFIRMATIVE, customButtons = null } = {}) { + constructor(content, type, inputValue = '', { okButton = null, cancelButton = null, rows = 1, wide = false, wider = false, large = false, allowHorizontalScrolling = false, allowVerticalScrolling = false, defaultResult = POPUP_RESULT.AFFIRMATIVE, customButtons = null, onClosing = null, onClose = null } = {}) { Popup.util.popups.push(this); // Make this popup uniquely identifiable this.id = uuidv4(); this.type = type; + // Utilize event handlers being passed in + this.onClosing = onClosing; + this.onClose = onClose; + /**@type {HTMLTemplateElement}*/ const template = document.querySelector('#popup_template'); // @ts-ignore @@ -317,6 +326,12 @@ export class Popup { this.value = value; this.result = result; + + if (this.onClosing) { + const shouldClose = this.onClosing(this); + if (!shouldClose) return; + } + Popup.util.lastResult = { value, result }; this.hide(); } @@ -337,6 +352,11 @@ export class Popup { // Call the close on the dialog this.dlg.close(); + // Run a possible custom handler right before DOM removal + if (this.onClose) { + this.onClose(this); + } + // Remove it from the dom this.dlg.remove(); From d64b265a398073a7822aa718e001b44a2b0887e0 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sat, 22 Jun 2024 05:03:05 +0200 Subject: [PATCH 06/24] Tag import popup improvements - Save "remember" setting of tag import popup - Add user option to change the tag import setting - Improve tag import popup with adding drilled down bogus folders as auto-added tags - Extract tag import popup to template - Force-open popup no matter the setting on char dropdown button option --- public/index.html | 17 ++- public/script.js | 11 +- public/scripts/power-user.js | 19 ++- public/scripts/tags.js | 153 ++++++++++---------- public/scripts/templates/charTagImport.html | 35 +++++ 5 files changed, 143 insertions(+), 92 deletions(-) create mode 100644 public/scripts/templates/charTagImport.html diff --git a/public/index.html b/public/index.html index 97c2f3ec4..5096da04e 100644 --- a/public/index.html +++ b/public/index.html @@ -3923,13 +3923,22 @@

Character Handling

-
+
-
+
+ + +
-
-
+
@@ -5766,7 +5766,7 @@
-
+
@@ -5784,7 +5784,7 @@
-
+
diff --git a/public/script.js b/public/script.js index ca2765ef8..4b877c3d4 100644 --- a/public/script.js +++ b/public/script.js @@ -1368,7 +1368,7 @@ export async function printCharacters(fullRefresh = false) { nextText: '>', formatNavigator: PAGINATION_TEMPLATE, showNavigator: true, - callback: function (data) { + callback: function (/** @type {Entity[]} */ data) { $(listId).empty(); if (power_user.bogus_folders && isBogusFolderOpen()) { $(listId).append(getBackBlock()); @@ -1388,7 +1388,7 @@ export async function printCharacters(fullRefresh = false) { displayCount++; break; case 'tag': - $(listId).append(getTagBlock(i.item, i.entities, i.hidden)); + $(listId).append(getTagBlock(i.item, i.entities, i.hidden, i.isUseless)); break; } } @@ -1442,8 +1442,9 @@ function verifyCharactersSearchSortRule() { * @property {Character|Group|import('./scripts/tags.js').Tag|*} item - The item * @property {string|number} id - The id * @property {'character'|'group'|'tag'} type - The type of this entity (character, group, tag) - * @property {Entity[]} [entities] - An optional list of entities relevant for this item - * @property {number} [hidden] - An optional number representing how many hidden entities this entity contains + * @property {Entity[]?} [entities=null] - An optional list of entities relevant for this item + * @property {number?} [hidden=null] - An optional number representing how many hidden entities this entity contains + * @property {boolean?} [isUseless=null] - Specifies if the entity is useless (not relevant, but should still be displayed for consistency) and should be displayed greyed out */ /** @@ -1538,6 +1539,15 @@ export function getEntitiesList({ doFilter = false, doSort = true } = {}) { } } + // Final step, updating some properties after the last filter run + const nonTagEntitiesCount = entities.filter(entity => entity.type !== 'tag').length; + for (const entity of entities) { + if (entity.type === 'tag') { + if (entity.entities?.length == nonTagEntitiesCount) entity.isUseless = true; + } + } + + // Sort before returning if requested if (doSort) { sortEntitiesList(entities); } diff --git a/public/scripts/tags.js b/public/scripts/tags.js index 519fc47eb..9175ee284 100644 --- a/public/scripts/tags.js +++ b/public/scripts/tags.js @@ -283,11 +283,12 @@ function chooseBogusFolder(source, tagId, remove = false) { * Builds the tag block for the specified item. * * @param {Tag} tag The tag item - * @param {*} entities The list ob sub items for this tag - * @param {*} hidden A count of how many sub items are hidden + * @param {any[]} entities The list ob sub items for this tag + * @param {number} hidden A count of how many sub items are hidden + * @param {boolean} isUseless Whether the tag is useless (should be displayed greyed out) * @returns The html for the tag block */ -function getTagBlock(tag, entities, hidden = 0) { +function getTagBlock(tag, entities, hidden = 0, isUseless = false) { let count = entities.length; const tagFolder = TAG_FOLDER_TYPES[tag.folder_type]; @@ -300,6 +301,7 @@ function getTagBlock(tag, entities, hidden = 0) { template.find('.bogus_folder_hidden_counter').text(hidden > 0 ? `${hidden} hidden` : ''); template.find('.bogus_folder_counter').text(`${count} ${count != 1 ? 'characters' : 'character'}`); template.find('.bogus_folder_icon').addClass(tagFolder.fa_icon); + if (isUseless) template.addClass('useless'); // Fill inline character images buildAvatarList(template.find('.bogus_folder_avatars_block'), entities); diff --git a/public/style.css b/public/style.css index 75a77295d..6ca69bcf7 100644 --- a/public/style.css +++ b/public/style.css @@ -2363,6 +2363,10 @@ input[type="file"] { padding: 1px; } +#rm_print_characters_block .entity_block.useless { + opacity: 0.25; +} + #rm_ch_create_block { display: none; overflow-y: auto; From c79f1e4360019ce78efbed28face56b4de0a617c Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sat, 22 Jun 2024 08:52:13 +0200 Subject: [PATCH 08/24] Fix image enlarge popup image sizing --- public/style.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/style.css b/public/style.css index 75a77295d..896b9665f 100644 --- a/public/style.css +++ b/public/style.css @@ -368,6 +368,8 @@ input[type='checkbox']:focus-visible { .img_enlarged_container { padding: 10px; + height: 100%; + width: 100%; } .img_enlarged_container pre code, From 07da2461d00dee9b1acbe677963d6ce4c42e97c9 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sat, 22 Jun 2024 10:04:14 +0200 Subject: [PATCH 09/24] Fix vertical scaling of images in enlarge popup --- public/style.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public/style.css b/public/style.css index 896b9665f..ad905cf2f 100644 --- a/public/style.css +++ b/public/style.css @@ -367,6 +367,9 @@ input[type='checkbox']:focus-visible { } .img_enlarged_container { + display: flex; + flex-direction: column; + justify-content: flex-end; padding: 10px; height: 100%; width: 100%; @@ -4481,7 +4484,7 @@ a { max-height: 100%; border-radius: 2px; border: 1px solid transparent; - outline: 1px solid var(--SmartThemeBorderColor); + object-fit: contain; } .cropper-container { From aa16ac446d90712ad9b112f21ba04da6e784d554 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 22 Jun 2024 12:53:03 +0300 Subject: [PATCH 10/24] Migrate preference for existing users --- public/scripts/power-user.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index eb0438c15..bc43a8ca0 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -1564,7 +1564,10 @@ function loadPowerUserSettings(settings, data) { } // Clean up old/legacy settings - delete power_user.import_card_tags; + if (power_user.import_card_tags !== undefined) { + power_user.tag_import_setting = power_user.import_card_tags ? tag_import_setting.ASK : tag_import_setting.NONE; + delete power_user.import_card_tags; + } $('#single_line').prop('checked', power_user.single_line); $('#relaxed_api_urls').prop('checked', power_user.relaxed_api_urls); From 36ecf8a7174bd8c91a9f5c43a421e33d302adbf8 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 22 Jun 2024 12:56:57 +0300 Subject: [PATCH 11/24] Update UI when remembering tag import setting --- public/scripts/tags.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/scripts/tags.js b/public/scripts/tags.js index 176c46960..24a57a9c0 100644 --- a/public/scripts/tags.js +++ b/public/scripts/tags.js @@ -778,6 +778,7 @@ async function showTagImportPopup(character, existingTags, newTags, folderTags) const setting = buttonSettingsMap[popup.result]; if (!setting) return; power_user.tag_import_setting = setting; + $('#tag_import_setting').val(power_user.tag_import_setting); saveSettingsDebounced(); console.log('Remembered tag import setting:', Object.entries(tag_import_setting).find(x => x[1] === setting)[0], setting); } From b448568aa34d4f4d6d5b50877be8a88d92875626 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 22 Jun 2024 16:28:57 +0300 Subject: [PATCH 12/24] More ollama multimodal models --- public/scripts/extensions/caption/index.js | 6 ++++++ public/scripts/extensions/caption/settings.html | 10 ++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/public/scripts/extensions/caption/index.js b/public/scripts/extensions/caption/index.js index d7a167fd4..f5cc0845d 100644 --- a/public/scripts/extensions/caption/index.js +++ b/public/scripts/extensions/caption/index.js @@ -377,6 +377,12 @@ jQuery(async function () { } function switchMultimodalBlocks() { const isMultimodal = extension_settings.caption.source === 'multimodal'; + $('#caption_ollama_pull').on('click', (e) => { + const presetModel = extension_settings.caption.multimodal_model !== 'ollama_current' ? extension_settings.caption.multimodal_model : ''; + e.preventDefault(); + $('#ollama_download_model').trigger('click'); + $('#dialogue_popup_input').val(presetModel); + }); $('#caption_multimodal_block').toggle(isMultimodal); $('#caption_prompt_block').toggle(isMultimodal); $('#caption_multimodal_api').val(extension_settings.caption.multimodal_api); diff --git a/public/scripts/extensions/caption/settings.html b/public/scripts/extensions/caption/settings.html index 1cdfbaefb..3e23cfbd5 100644 --- a/public/scripts/extensions/caption/settings.html +++ b/public/scripts/extensions/caption/settings.html @@ -58,14 +58,20 @@ - - + + + + +
+
+ The model must be downloaded first! Do it with the ollama pull command or click here. +
- - Hint: Download models and set the URL in the API connection settings. +
+ The model must be downloaded first! Do it with the ollama pull command or click here. +
+ + Hint: Set the URL in the API connection settings.
From 8564d6faa8050e8454c8d8a1e909531456bfc393 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 22 Jun 2024 17:41:02 +0300 Subject: [PATCH 14/24] Debug function to purge all vectors --- public/scripts/extensions/vectors/index.js | 31 +++++++++++++++++++++- src/endpoints/vectors.js | 19 +++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/public/scripts/extensions/vectors/index.js b/public/scripts/extensions/vectors/index.js index 54c178b53..fc5fd8813 100644 --- a/public/scripts/extensions/vectors/index.js +++ b/public/scripts/extensions/vectors/index.js @@ -20,7 +20,7 @@ import { renderExtensionTemplateAsync, doExtrasFetch, getApiUrl, } from '../../extensions.js'; -import { collapseNewlines } from '../../power-user.js'; +import { collapseNewlines, registerDebugFunction } from '../../power-user.js'; import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js'; import { getDataBankAttachments, getDataBankAttachmentsForSource, getFileAttachment } from '../../chats.js'; import { debounce, getStringHash as calculateHash, waitUntilCondition, onlyUnique, splitRecursive, trimToStartSentence, trimToEndSentence } from '../../utils.js'; @@ -989,6 +989,28 @@ async function purgeVectorIndex(collectionId) { } } +/** + * Purges all vector indexes. + */ +async function purgeAllVectorIndexes() { + try { + const response = await fetch('/api/vector/purge-all', { + method: 'POST', + headers: getRequestHeaders(), + }); + + if (!response.ok) { + throw new Error('Failed to purge all vector indexes'); + } + + console.log('Vectors: Purged all vector indexes'); + toastr.success('All vector indexes purged', 'Purge successful'); + } catch (error) { + console.error('Vectors: Failed to purge all', error); + toastr.error('Failed to purge all vector indexes', 'Purge failed'); + } +} + function toggleSettings() { $('#vectors_files_settings').toggle(!!settings.enabled_files); $('#vectors_chats_settings').toggle(!!settings.enabled_chats); @@ -1578,4 +1600,11 @@ jQuery(async () => { ], returns: ARGUMENT_TYPE.LIST, })); + + registerDebugFunction('purge-everything', 'Purge all vector indices', 'Obliterate all stored vectors for all sources. No mercy.', async () => { + if (!confirm('Are you sure?')) { + return; + } + await purgeAllVectorIndexes(); + }); }); diff --git a/src/endpoints/vectors.js b/src/endpoints/vectors.js index 74666e584..38f74f7d8 100644 --- a/src/endpoints/vectors.js +++ b/src/endpoints/vectors.js @@ -1,5 +1,6 @@ const vectra = require('vectra'); const path = require('path'); +const fs = require('fs'); const express = require('express'); const sanitize = require('sanitize-filename'); const { jsonParser } = require('../express-common'); @@ -440,6 +441,24 @@ router.post('/delete', jsonParser, async (req, res) => { } }); +router.post('/purge-all', jsonParser, async (req, res) => { + try { + for (const source of SOURCES) { + const sourcePath = path.join(req.user.directories.vectors, sanitize(source)); + if (!fs.existsSync(sourcePath)) { + continue; + } + await fs.promises.rm(sourcePath, { recursive: true }); + console.log(`Deleted vector source store at ${sourcePath}`); + } + + return res.sendStatus(200); + } catch (error) { + console.error(error); + return res.sendStatus(500); + } +}); + router.post('/purge', jsonParser, async (req, res) => { try { if (!req.body.collectionId) { From d64647280a20da9507209d912e67122ad256a877 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 22 Jun 2024 17:41:40 +0300 Subject: [PATCH 15/24] Fix method deprecation warning --- src/endpoints/extensions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/endpoints/extensions.js b/src/endpoints/extensions.js index d14ddb8cf..87c06da9c 100644 --- a/src/endpoints/extensions.js +++ b/src/endpoints/extensions.js @@ -191,7 +191,7 @@ router.post('/delete', jsonParser, async (request, response) => { return response.status(400).send('Bad Request: extensionName is required in the request body.'); } - // Sanatize the extension name to prevent directory traversal + // Sanitize the extension name to prevent directory traversal const extensionName = sanitize(request.body.extensionName); try { @@ -201,7 +201,7 @@ router.post('/delete', jsonParser, async (request, response) => { return response.status(404).send(`Directory does not exist at ${extensionPath}`); } - await fs.promises.rmdir(extensionPath, { recursive: true }); + await fs.promises.rm(extensionPath, { recursive: true }); console.log(`Extension has been deleted at ${extensionPath}`); return response.send(`Extension has been deleted at ${extensionPath}`); From a39a1a7cec7020cac3114d9ad571c8e7b54ffce9 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 22 Jun 2024 17:44:08 +0300 Subject: [PATCH 16/24] Fix odd-named preset selection via command --- public/scripts/preset-manager.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public/scripts/preset-manager.js b/public/scripts/preset-manager.js index 42cc7585a..d751c5803 100644 --- a/public/scripts/preset-manager.js +++ b/public/scripts/preset-manager.js @@ -140,7 +140,10 @@ class PresetManager { * @param {string} value Preset option value */ selectPreset(value) { - $(this.select).find(`option[value=${value}]`).prop('selected', true); + const option = $(this.select).filter(function() { + return $(this).val() === value; + }); + option.prop('selected', true); $(this.select).val(value).trigger('change'); } From de1910268a21da3acd50fed3658905cee2c90c54 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 23 Jun 2024 01:26:25 +0300 Subject: [PATCH 17/24] Add missing macro reference --- public/scripts/templates/macros.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/scripts/templates/macros.html b/public/scripts/templates/macros.html index 353dcc12e..8d2e4ebdd 100644 --- a/public/scripts/templates/macros.html +++ b/public/scripts/templates/macros.html @@ -21,9 +21,11 @@
  • {{char_version}}the Character's version number
  • {{group}}a comma-separated list of group member names or the character name in solo chats. Alias: {{charIfNotGroup}}
  • {{model}}a text generation model name for the currently selected API. Can be inaccurate!
  • -
  • {{lastMessage}} - the text of the latest chat message.
  • +
  • {{lastMessage}}the text of the latest chat message.
  • +
  • {{lastUserMessage}}the text of the latest user chat message.
  • +
  • {{lastCharMessage}}the text of the latest character chat message.
  • {{lastMessageId}}index # of the latest chat message. Useful for slash command batching.
  • -
  • {{firstIncludedMessageId}} - the ID of the first message included in the context. Requires generation to be ran at least once in the current session.
  • +
  • {{firstIncludedMessageId}}the ID of the first message included in the context. Requires generation to be ran at least once in the current session.
  • {{currentSwipeId}}the 1-based ID of the current swipe in the last chat message. Empty string if the last message is user or prompt-hidden.
  • {{lastSwipeId}}the number of swipes in the last chat message. Empty string if the last message is user or prompt-hidden.
  • {{// (note)}}you can leave a note here, and the macro will be replaced with blank content. Not visible for the AI.
  • From 323f34f5d473cb76a90e38be03246dae85158f7a Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 23 Jun 2024 01:34:10 +0300 Subject: [PATCH 18/24] Fix QR breaking when no sets --- public/scripts/extensions/quick-reply/src/ui/SettingsUi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/extensions/quick-reply/src/ui/SettingsUi.js b/public/scripts/extensions/quick-reply/src/ui/SettingsUi.js index 48a5d059d..96d1208ce 100644 --- a/public/scripts/extensions/quick-reply/src/ui/SettingsUi.js +++ b/public/scripts/extensions/quick-reply/src/ui/SettingsUi.js @@ -148,7 +148,7 @@ export class SettingsUi { this.onQrSetChange(); } onQrSetChange() { - this.currentQrSet = QuickReplySet.get(this.currentSet.value); + this.currentQrSet = QuickReplySet.get(this.currentSet.value) ?? new QuickReplySet(); this.disableSend.checked = this.currentQrSet.disableSend; this.placeBeforeInput.checked = this.currentQrSet.placeBeforeInput; this.injectInput.checked = this.currentQrSet.injectInput; From 42766a715da043e1536e3d14e6a987ebd5ea5000 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sun, 23 Jun 2024 02:32:06 +0200 Subject: [PATCH 19/24] Popup type "DISPLAY" & image enlarge changes - New popup type "DISPLAY", for showing content with an X in the corner, without buttons - Rework popup result controls to automatically support click (or other) events to close complete the popup - Fix inline image icons/actions being keyboard interactable - Switch inline image enlarge popup to new DISPLAY type --- public/css/popup.css | 11 ++++++- public/index.html | 5 +-- public/script.js | 2 +- public/scripts/chats.js | 2 +- public/scripts/popup.js | 70 ++++++++++++++++++++++++++--------------- public/style.css | 20 +++++++++--- 6 files changed, 75 insertions(+), 35 deletions(-) diff --git a/public/css/popup.css b/public/css/popup.css index 62c29fcf9..0ce4af1ce 100644 --- a/public/css/popup.css +++ b/public/css/popup.css @@ -109,7 +109,6 @@ dialog { .menu_button.popup-button-ok { background-color: var(--crimson70a); - cursor: pointer; } .menu_button.popup-button-ok:hover { @@ -132,3 +131,13 @@ dialog { filter: brightness(1.3) saturate(1.3); } +.popup .popup-button-close { + position: absolute; + top: -8px; + right: -8px; + width: 20px; + height: 20px; + font-size: 19px; + filter: brightness(0.4); +} + diff --git a/public/index.html b/public/index.html index b2fc3783d..0fc7f5b36 100644 --- a/public/index.html +++ b/public/index.html @@ -4855,10 +4855,11 @@
    +
    diff --git a/public/script.js b/public/script.js index 29df8a38e..c1e199885 100644 --- a/public/script.js +++ b/public/script.js @@ -2122,7 +2122,7 @@ export function addCopyToCodeBlocks(messageElement) { hljs.highlightElement(codeBlocks.get(i)); if (navigator.clipboard !== undefined) { const copyButton = document.createElement('i'); - copyButton.classList.add('fa-solid', 'fa-copy', 'code-copy'); + copyButton.classList.add('fa-solid', 'fa-copy', 'code-copy', 'interactable'); copyButton.title = 'Copy code'; codeBlocks.get(i).appendChild(copyButton); copyButton.addEventListener('pointerup', function (event) { diff --git a/public/scripts/chats.js b/public/scripts/chats.js index 32a188c20..3362189fa 100644 --- a/public/scripts/chats.js +++ b/public/scripts/chats.js @@ -587,7 +587,7 @@ function enlargeMessageImage() { const titleEmpty = !title || title.trim().length === 0; imgContainer.find('pre').toggle(!titleEmpty); addCopyToCodeBlocks(imgContainer); - callGenericPopup(imgContainer, POPUP_TYPE.TEXT, '', { wide: true, large: true }); + callGenericPopup(imgContainer, POPUP_TYPE.DISPLAY, '', { large: true }); } async function deleteMessageImage() { diff --git a/public/scripts/popup.js b/public/scripts/popup.js index 6e388839c..7222a9a28 100644 --- a/public/scripts/popup.js +++ b/public/scripts/popup.js @@ -3,17 +3,22 @@ import { removeFromArray, runAfterAnimation, uuidv4 } from './utils.js'; /** @readonly */ /** @enum {Number} */ export const POPUP_TYPE = { - 'TEXT': 1, - 'CONFIRM': 2, - 'INPUT': 3, + /** Main popup type. Containing any content displayed, with buttons below. Can also contain additional input controls. */ + TEXT: 1, + /** Popup mainly made to confirm something, answering with a simple Yes/No or similar. Focus on the button controls. */ + CONFIRM: 2, + /** Popup who's main focus is the input text field, which is displayed here. Can contain additional content above. Return value for this is the input string. */ + INPUT: 3, + /** Popup without any button controls. Used to simply display content, with a small X in the corner. */ + DISPLAY: 4, }; /** @readonly */ /** @enum {number?} */ export const POPUP_RESULT = { - 'AFFIRMATIVE': 1, - 'NEGATIVE': 0, - 'CANCELLED': null, + AFFIRMATIVE: 1, + NEGATIVE: 0, + CANCELLED: null, }; /** @@ -75,8 +80,9 @@ export class Popup { /** @type {HTMLElement} */ content; /** @type {HTMLTextAreaElement} */ input; /** @type {HTMLElement} */ controls; - /** @type {HTMLElement} */ ok; - /** @type {HTMLElement} */ cancel; + /** @type {HTMLElement} */ okButton; + /** @type {HTMLElement} */ cancelButton; + /** @type {HTMLElement} */ closeButton; /** @type {POPUP_RESULT|number?} */ defaultResult; /** @type {CustomPopupButton[]|string[]?} */ customButtons; @@ -118,8 +124,9 @@ export class Popup { this.content = this.dlg.querySelector('.popup-content'); this.input = this.dlg.querySelector('.popup-input'); this.controls = this.dlg.querySelector('.popup-controls'); - this.ok = this.dlg.querySelector('.popup-button-ok'); - this.cancel = this.dlg.querySelector('.popup-button-cancel'); + this.okButton = this.dlg.querySelector('.popup-button-ok'); + this.cancelButton = this.dlg.querySelector('.popup-button-cancel'); + this.closeButton = this.dlg.querySelector('.popup-button-close'); this.dlg.setAttribute('data-id', this.id); if (wide) this.dlg.classList.add('wide_dialogue_popup'); @@ -129,8 +136,8 @@ export class Popup { if (allowVerticalScrolling) this.dlg.classList.add('vertical_scrolling_dialogue_popup'); // If custom button captions are provided, we set them beforehand - this.ok.textContent = typeof okButton === 'string' ? okButton : 'OK'; - this.cancel.textContent = typeof cancelButton === 'string' ? cancelButton : template.getAttribute('popup-button-cancel'); + this.okButton.textContent = typeof okButton === 'string' ? okButton : 'OK'; + this.cancelButton.textContent = typeof cancelButton === 'string' ? cancelButton : template.getAttribute('popup-button-cancel'); this.defaultResult = defaultResult; this.customButtons = customButtons; @@ -141,17 +148,14 @@ export class Popup { const buttonElement = document.createElement('div'); buttonElement.classList.add('menu_button', 'popup-button-custom', 'result-control'); buttonElement.classList.add(...(button.classes ?? [])); - buttonElement.setAttribute('data-result', String(button.result ?? undefined)); + buttonElement.dataset.result = String(button.result ?? undefined); buttonElement.textContent = button.text; buttonElement.tabIndex = 0; - if (button.action) buttonElement.addEventListener('click', button.action); - if (button.result) buttonElement.addEventListener('click', () => this.complete(button.result)); - if (button.appendAtEnd) { this.controls.appendChild(buttonElement); } else { - this.controls.insertBefore(buttonElement, this.ok); + this.controls.insertBefore(buttonElement, this.okButton); } }); @@ -159,23 +163,30 @@ export class Popup { const defaultButton = this.controls.querySelector(`[data-result="${this.defaultResult}"]`); if (defaultButton) defaultButton.classList.add('menu_button_default'); + // Styling differences depending on the popup type + // General styling for all types first, that might be overriden for specific types below + this.input.style.display = 'none'; + this.closeButton.style.display = 'none'; + switch (type) { case POPUP_TYPE.TEXT: { - this.input.style.display = 'none'; - if (!cancelButton) this.cancel.style.display = 'none'; + if (!cancelButton) this.cancelButton.style.display = 'none'; break; } case POPUP_TYPE.CONFIRM: { - this.input.style.display = 'none'; - if (!okButton) this.ok.textContent = template.getAttribute('popup-button-yes'); - if (!cancelButton) this.cancel.textContent = template.getAttribute('popup-button-no'); + if (!okButton) this.okButton.textContent = template.getAttribute('popup-button-yes'); + if (!cancelButton) this.cancelButton.textContent = template.getAttribute('popup-button-no'); break; } case POPUP_TYPE.INPUT: { this.input.style.display = 'block'; - if (!okButton) this.ok.textContent = template.getAttribute('popup-button-save'); + if (!okButton) this.okButton.textContent = template.getAttribute('popup-button-save'); break; } + case POPUP_TYPE.DISPLAY: { + this.controls.style.display = 'none'; + this.closeButton.style.display = 'block'; + } default: { console.warn('Unknown popup type.', type); break; @@ -202,8 +213,14 @@ export class Popup { // Set focus event that remembers the focused element this.dlg.addEventListener('focusin', (evt) => { if (evt.target instanceof HTMLElement && evt.target != this.dlg) this.lastFocus = evt.target; }); - this.ok.addEventListener('click', () => this.complete(POPUP_RESULT.AFFIRMATIVE)); - this.cancel.addEventListener('click', () => this.complete(POPUP_RESULT.NEGATIVE)); + // Bind event listeners for all result controls to their defined event type + this.dlg.querySelectorAll(`[data-result]`).forEach(resultControl => { + if (!(resultControl instanceof HTMLElement)) return; + const result = Number(resultControl.dataset.result); + if (isNaN(result)) throw new Error('Invalid result control. Result must be a number. ' + resultControl.dataset.result); + const type = resultControl.dataset.resultEvent || 'click'; + resultControl.addEventListener(type, () => this.complete(result)); + }); // Bind dialog listeners manually, so we can be sure context is preserved const cancelListener = (evt) => { @@ -296,6 +313,9 @@ export class Popup { if (applyAutoFocus) { control.setAttribute('autofocus', ''); + // Manually enable tabindex too, as this might only be applied by the interactable functionality in the background, but too late for HTML autofocus + // interactable only gets applied when inserted into the DOM + control.tabIndex = 0; } else { control.focus(); } diff --git a/public/style.css b/public/style.css index 96a1ef69e..3df89b441 100644 --- a/public/style.css +++ b/public/style.css @@ -4457,26 +4457,36 @@ a { .mes_img_controls { position: absolute; - top: 0.5em; + top: 0.1em; left: 0; width: 100%; - display: none; + display: flex; + opacity: 0; flex-direction: row; justify-content: space-between; padding: 1em; } .mes_img_controls .right_menu_button { - padding: 0; filter: brightness(80%); + padding: 1px; + height: 1.25em; + width: 1.25em; +} + +.mes_img_controls .right_menu_button::before { + /* Fix weird alignment with this font-awesome icons on focus */ + position: relative; + top: 0.6125em; } .mes_img_controls .right_menu_button:hover { filter: brightness(150%); } -.mes_img_container:hover .mes_img_controls { - display: flex; +.mes_img_container:hover .mes_img_controls, +.mes_img_container:focus-within .mes_img_controls { + opacity: 1; } .mes .mes_img_container.img_extra { From 48621f1d50becc9343851afd8627e0b89969171b Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sun, 23 Jun 2024 02:43:37 +0200 Subject: [PATCH 20/24] Fix scaling of enlarged popup image --- public/style.css | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/public/style.css b/public/style.css index 3df89b441..20dba9f6b 100644 --- a/public/style.css +++ b/public/style.css @@ -4494,11 +4494,9 @@ a { } .img_enlarged { - max-width: 100%; - max-height: 100%; - border-radius: 2px; - border: 1px solid transparent; object-fit: contain; + /* Scaling via flex-grow and object-fit only works if we have some kind of base-height set */ + min-height: 120px; } .cropper-container { From 7642b66a0e0e0253665ad9004c25cd86df0fed9b Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Sun, 23 Jun 2024 12:26:52 +0200 Subject: [PATCH 21/24] Improve enlarge inline image - Make enlarge inline image popup zoomable - Add optional popup class for transparent popups --- public/css/popup.css | 11 +++++--- public/scripts/chats.js | 22 +++++++++++++--- public/scripts/popup.js | 28 ++++++++++---------- public/style.css | 57 ++++++++++++++++++++++++++++++++--------- 4 files changed, 86 insertions(+), 32 deletions(-) diff --git a/public/css/popup.css b/public/css/popup.css index 0ce4af1ce..a35e7b96e 100644 --- a/public/css/popup.css +++ b/public/css/popup.css @@ -135,9 +135,14 @@ dialog { position: absolute; top: -8px; right: -8px; - width: 20px; - height: 20px; - font-size: 19px; + width: 21px; + height: 21px; + font-size: 18px; + padding: 2px 3px 3px 2px; + filter: brightness(0.4); + + /* Fix weird animation issue with font-scaling during popup open */ + backface-visibility: hidden; } diff --git a/public/scripts/chats.js b/public/scripts/chats.js index 3362189fa..83069aaab 100644 --- a/public/scripts/chats.js +++ b/public/scripts/chats.js @@ -35,7 +35,7 @@ import { extractTextFromOffice, } from './utils.js'; import { extension_settings, renderExtensionTemplateAsync, saveMetadataDebounced } from './extensions.js'; -import { POPUP_RESULT, POPUP_TYPE, callGenericPopup } from './popup.js'; +import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; import { ScraperManager } from './scrapers.js'; import { DragAndDropHandler } from './dragdrop.js'; @@ -566,7 +566,7 @@ export function isExternalMediaAllowed() { return !power_user.forbid_external_media; } -function enlargeMessageImage() { +async function enlargeMessageImage() { const mesBlock = $(this).closest('.mes'); const mesId = mesBlock.attr('mesid'); const message = chat[mesId]; @@ -580,14 +580,28 @@ function enlargeMessageImage() { const img = document.createElement('img'); img.classList.add('img_enlarged'); img.src = imgSrc; + const imgHolder = document.createElement('div'); + imgHolder.classList.add('img_enlarged_holder'); + imgHolder.append(img); const imgContainer = $('
    '); - imgContainer.prepend(img); + imgContainer.prepend(imgHolder); imgContainer.addClass('img_enlarged_container'); imgContainer.find('code').addClass('txt').text(title); const titleEmpty = !title || title.trim().length === 0; imgContainer.find('pre').toggle(!titleEmpty); addCopyToCodeBlocks(imgContainer); - callGenericPopup(imgContainer, POPUP_TYPE.DISPLAY, '', { large: true }); + + const popup = new Popup(imgContainer, POPUP_TYPE.DISPLAY, '', { large: true, transparent: true }); + + popup.dlg.style.width = 'unset'; + popup.dlg.style.height = 'unset'; + + img.addEventListener('click', () => { + const shouldZoom = !img.classList.contains('zoomed'); + img.classList.toggle('zoomed', shouldZoom); + }); + + await popup.show(); } async function deleteMessageImage() { diff --git a/public/scripts/popup.js b/public/scripts/popup.js index 7222a9a28..8e82994c5 100644 --- a/public/scripts/popup.js +++ b/public/scripts/popup.js @@ -23,18 +23,19 @@ export const POPUP_RESULT = { /** * @typedef {object} PopupOptions - * @property {string|boolean?} [okButton] - Custom text for the OK button, or `true` to use the default (If set, the button will always be displayed, no matter the type of popup) - * @property {string|boolean?} [cancelButton] - Custom text for the Cancel button, or `true` to use the default (If set, the button will always be displayed, no matter the type of popup) - * @property {number?} [rows] - The number of rows for the input field - * @property {boolean?} [wide] - Whether to display the popup in wide mode (wide screen, 1/1 aspect ratio) - * @property {boolean?} [wider] - Whether to display the popup in wider mode (just wider, no height scaling) - * @property {boolean?} [large] - Whether to display the popup in large mode (90% of screen) - * @property {boolean?} [allowHorizontalScrolling] - Whether to allow horizontal scrolling in the popup - * @property {boolean?} [allowVerticalScrolling] - Whether to allow vertical scrolling in the popup - * @property {POPUP_RESULT|number?} [defaultResult] - The default result of this popup when Enter is pressed. Can be changed from `POPUP_RESULT.AFFIRMATIVE`. - * @property {CustomPopupButton[]|string[]?} [customButtons] - Custom buttons to add to the popup. If only strings are provided, the buttons will be added with default options, and their result will be in order from `2` onward. - * @property {(popup: Popup) => boolean?} [onClosing] - Handler called before the popup closes, return `false` to cancel the close - * @property {(popup: Popup) => void?} [onClose] - Handler called after the popup closes, but before the DOM is cleaned up + * @property {string|boolean?} [okButton=null] - Custom text for the OK button, or `true` to use the default (If set, the button will always be displayed, no matter the type of popup) + * @property {string|boolean?} [cancelButton=null] - Custom text for the Cancel button, or `true` to use the default (If set, the button will always be displayed, no matter the type of popup) + * @property {number?} [rows=1] - The number of rows for the input field + * @property {boolean?} [wide=false] - Whether to display the popup in wide mode (wide screen, 1/1 aspect ratio) + * @property {boolean?} [wider=false] - Whether to display the popup in wider mode (just wider, no height scaling) + * @property {boolean?} [large=false] - Whether to display the popup in large mode (90% of screen) + * @property {boolean?} [transparent=false] - Whether to display the popup in transparent mode (no background, border, shadow or anything, only its content) + * @property {boolean?} [allowHorizontalScrolling=false] - Whether to allow horizontal scrolling in the popup + * @property {boolean?} [allowVerticalScrolling=false] - Whether to allow vertical scrolling in the popup + * @property {POPUP_RESULT|number?} [defaultResult=POPUP_RESULT.AFFIRMATIVE] - The default result of this popup when Enter is pressed. Can be changed from `POPUP_RESULT.AFFIRMATIVE`. + * @property {CustomPopupButton[]|string[]?} [customButtons=null] - Custom buttons to add to the popup. If only strings are provided, the buttons will be added with default options, and their result will be in order from `2` onward. + * @property {(popup: Popup) => boolean?} [onClosing=null] - Handler called before the popup closes, return `false` to cancel the close + * @property {(popup: Popup) => void?} [onClose=null] - Handler called after the popup closes, but before the DOM is cleaned up */ /** @@ -105,7 +106,7 @@ export class Popup { * @param {string} [inputValue=''] - The initial value of the input field * @param {PopupOptions} [options={}] - Additional options for the popup */ - constructor(content, type, inputValue = '', { okButton = null, cancelButton = null, rows = 1, wide = false, wider = false, large = false, allowHorizontalScrolling = false, allowVerticalScrolling = false, defaultResult = POPUP_RESULT.AFFIRMATIVE, customButtons = null, onClosing = null, onClose = null } = {}) { + constructor(content, type, inputValue = '', { okButton = null, cancelButton = null, rows = 1, wide = false, wider = false, large = false, transparent = false, allowHorizontalScrolling = false, allowVerticalScrolling = false, defaultResult = POPUP_RESULT.AFFIRMATIVE, customButtons = null, onClosing = null, onClose = null } = {}) { Popup.util.popups.push(this); // Make this popup uniquely identifiable @@ -132,6 +133,7 @@ export class Popup { if (wide) this.dlg.classList.add('wide_dialogue_popup'); if (wider) this.dlg.classList.add('wider_dialogue_popup'); if (large) this.dlg.classList.add('large_dialogue_popup'); + if (transparent) this.dlg.classList.add('transparent_dialogue_popup'); if (allowHorizontalScrolling) this.dlg.classList.add('horizontal_scrolling_dialogue_popup'); if (allowVerticalScrolling) this.dlg.classList.add('vertical_scrolling_dialogue_popup'); diff --git a/public/style.css b/public/style.css index 20dba9f6b..55aec739e 100644 --- a/public/style.css +++ b/public/style.css @@ -366,16 +366,6 @@ input[type='checkbox']:focus-visible { font-weight: bold; } -.img_enlarged_container { - display: flex; - flex-direction: column; - justify-content: flex-end; - padding: 10px; - height: 100%; - width: 100%; -} - -.img_enlarged_container pre code, .mes_text pre code { position: relative; display: block; @@ -3144,6 +3134,12 @@ grammarly-extension { min-width: 750px; } +.transparent_dialogue_popup { + background-color: transparent; + box-shadow: none; + border: none; +} + #dialogue_popup .horizontal_scrolling_dialogue_popup { overflow-x: unset !important; } @@ -4493,12 +4489,49 @@ a { display: flex; } -.img_enlarged { - object-fit: contain; +.img_enlarged_holder { /* Scaling via flex-grow and object-fit only works if we have some kind of base-height set */ min-height: 120px; } +.img_enlarged_holder:has(.zoomed) { + overflow: auto; +} + +.img_enlarged { + object-fit: contain; + width: 100%; + height: 100%; + cursor: zoom-in +} + +.img_enlarged.zoomed { + object-fit: cover; + width: auto; + height: auto; + cursor: zoom-out; +} + +.img_enlarged_container { + display: flex; + flex-direction: column; + justify-content: center; + padding: 10px; + height: 100%; + width: 100%; +} + +.img_enlarged_holder::-webkit-scrollbar-corner { + background-color: transparent; +} + +.img_enlarged_container pre code { + position: relative; + display: block; + overflow-x: auto; + padding: 1em; +} + .cropper-container { max-width: 100% !important; } From 58a85fa0c8a2719e06d0008c50dfc8baca161e72 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:11:00 +0300 Subject: [PATCH 22/24] Remove focus outline from transparent popups --- public/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/style.css b/public/style.css index 55aec739e..e6bc74ed3 100644 --- a/public/style.css +++ b/public/style.css @@ -3140,6 +3140,10 @@ grammarly-extension { border: none; } +.transparent_dialogue_popup:focus-visible { + outline: none; +} + #dialogue_popup .horizontal_scrolling_dialogue_popup { overflow-x: unset !important; } From a161ebfcaf1a345534e7f0af4bd15a17219ba349 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:53:01 +0300 Subject: [PATCH 23/24] Up visibility of close button --- public/css/popup.css | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/public/css/popup.css b/public/css/popup.css index a35e7b96e..bdd530a66 100644 --- a/public/css/popup.css +++ b/public/css/popup.css @@ -133,16 +133,15 @@ dialog { .popup .popup-button-close { position: absolute; - top: -8px; - right: -8px; - width: 21px; - height: 21px; - font-size: 18px; + top: -6px; + right: -6px; + width: 24px; + height: 24px; + font-size: 20px; padding: 2px 3px 3px 2px; - filter: brightness(0.4); + filter: brightness(0.8); /* Fix weird animation issue with font-scaling during popup open */ backface-visibility: hidden; } - From 03cfbca7cfeea2f6efca10c5c1edc8b21b5d2740 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:58:08 +0300 Subject: [PATCH 24/24] Distraction-free image zooming --- public/style.css | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/public/style.css b/public/style.css index e6bc74ed3..f00cb6c41 100644 --- a/public/style.css +++ b/public/style.css @@ -4536,6 +4536,29 @@ a { padding: 1em; } +.popup:has(.img_enlarged.zoomed).large_dialogue_popup { + height: 100vh !important; + height: 100svh !important; + max-height: 100vh !important; + max-height: 100svh !important; + max-width: 100vw !important; + max-width: 100svw !important; + padding: 0; +} + +.popup:has(.img_enlarged.zoomed).large_dialogue_popup .popup-content { + margin: 0; + padding: 0; +} + +.popup:has(.img_enlarged.zoomed).large_dialogue_popup .img_enlarged_container pre { + display: none; +} + +.popup:has(.img_enlarged.zoomed).large_dialogue_popup .popup-button-close { + display: none !important; +} + .cropper-container { max-width: 100% !important; }