Dozens new keyboard interactables

- Made dozens of existing controls keyboard interactable
- Tweaked styling so the keyboard focus looks more pleasant
This commit is contained in:
Wolfsblvt 2024-06-06 02:48:06 +02:00
parent 55a95c910f
commit e2089b1e44
8 changed files with 128 additions and 68 deletions

View File

@ -119,6 +119,16 @@
opacity: 0.6; opacity: 0.6;
} }
#tagList .tag:has(.tag_remove:hover),
#tagList .tag:has(.tag_remove:focus) {
opacity: 1;
}
#tagList .tag:has(.tag_remove:hover) .tag_name,
#tagList .tag:has(.tag_remove:focus) .tag_name {
opacity: 0.6;
}
.tags.tags_inline { .tags.tags_inline {
opacity: 0.6; opacity: 0.6;
column-gap: 0.2rem; column-gap: 0.2rem;
@ -176,6 +186,7 @@
.tag.selected { .tag.selected {
opacity: 1 !important; opacity: 1 !important;
filter: none !important; filter: none !important;
border: 1px solid lightgreen;
} }
.tag.excluded { .tag.excluded {

View File

@ -5611,21 +5611,21 @@
</div> </div>
</div> </div>
<div class="mes_buttons"> <div class="mes_buttons">
<div title="Message Actions" class="extraMesButtonsHint fa-solid fa-ellipsis" data-i18n="[title]Message Actions"></div> <div title="Message Actions" class="mes_button extraMesButtonsHint fa-solid fa-ellipsis" data-i18n="[title]Message Actions"></div>
<div class="extraMesButtons"> <div class="extraMesButtons">
<div title="Translate message" class="mes_translate fa-solid fa-language" data-i18n="[title]Translate message"></div> <div title="Translate message" class="mes_button mes_translate fa-solid fa-language" data-i18n="[title]Translate message"></div>
<div title="Generate Image" class="sd_message_gen fa-solid fa-paintbrush" data-i18n="[title]Generate Image"></div> <div title="Generate Image" class="mes_button sd_message_gen fa-solid fa-paintbrush" data-i18n="[title]Generate Image"></div>
<div title="Narrate" class="mes_narrate fa-solid fa-bullhorn" data-i18n="[title]Narrate"></div> <div title="Narrate" class="mes_button mes_narrate fa-solid fa-bullhorn" data-i18n="[title]Narrate"></div>
<div title="Prompt" class="mes_prompt fa-solid fa-square-poll-horizontal " data-i18n="[title]Prompt" style="display: none;"></div> <div title="Prompt" class="mes_button mes_prompt fa-solid fa-square-poll-horizontal " data-i18n="[title]Prompt" style="display: none;"></div>
<div title="Exclude message from prompts" class="mes_hide fa-solid fa-eye" data-i18n="[title]Exclude message from prompts"></div> <div title="Exclude message from prompts" class="mes_button mes_hide fa-solid fa-eye" data-i18n="[title]Exclude message from prompts"></div>
<div title="Include message in prompts" class="mes_unhide fa-solid fa-eye-slash" data-i18n="[title]Include message in prompts"></div> <div title="Include message in prompts" class="mes_button mes_unhide fa-solid fa-eye-slash" data-i18n="[title]Include message in prompts"></div>
<div title="Embed file or image" class="mes_embed fa-solid fa-paperclip" data-i18n="[title]Embed file or image"></div> <div title="Embed file or image" class="mes_button mes_embed fa-solid fa-paperclip" data-i18n="[title]Embed file or image"></div>
<div title="Create checkpoint" class="mes_create_bookmark fa-regular fa-solid fa-flag-checkered" data-i18n="[title]Create checkpoint"></div> <div title="Create checkpoint" class="mes_button mes_create_bookmark fa-regular fa-solid fa-flag-checkered" data-i18n="[title]Create checkpoint"></div>
<div title="Create branch" class="mes_create_branch fa-regular fa-code-branch" data-i18n="[title]Create Branch"></div> <div title="Create branch" class="mes_button mes_create_branch fa-regular fa-code-branch" data-i18n="[title]Create Branch"></div>
<div title="Copy" class="mes_copy fa-solid fa-copy " data-i18n="[title]Copy"></div> <div title="Copy" class="mes_button mes_copy fa-solid fa-copy " data-i18n="[title]Copy"></div>
</div> </div>
<div title="Open checkpoint chat" class="mes_bookmark fa-solid fa-flag" data-i18n="[title]Open checkpoint chat"></div> <div title="Open checkpoint chat" class="mes_button mes_bookmark fa-solid fa-flag" data-i18n="[title]Open checkpoint chat"></div>
<div title="Edit" class="mes_edit fa-solid fa-pencil " data-i18n="[title]Edit"></div> <div title="Edit" class="mes_button mes_edit fa-solid fa-pencil " data-i18n="[title]Edit"></div>
</div> </div>
<div class="mes_edit_buttons"> <div class="mes_edit_buttons">
<div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div> <div class="mes_edit_done menu_button fa-solid fa-check" title="Confirm" data-i18n="[title]Confirm"></div>
@ -6245,7 +6245,7 @@
</form> </form>
<div id="nonQRFormItems"> <div id="nonQRFormItems">
<div id="leftSendForm" class="alignContentCenter"> <div id="leftSendForm" class="alignContentCenter">
<div id="options_button" class="fa-solid fa-bars"></div> <div id="options_button" class="fa-solid fa-bars interactable"></div>
</div> </div>
<textarea id="send_textarea" name="text" data-i18n="[no_connection_text]Not connected to API!;[connected_text]Type a message, or /? for help" placeholder="Not connected to API!" no_connection_text="Not connected to API!" connected_text="Type a message, or /? for help"></textarea> <textarea id="send_textarea" name="text" data-i18n="[no_connection_text]Not connected to API!;[connected_text]Type a message, or /? for help" placeholder="Not connected to API!" no_connection_text="Not connected to API!" connected_text="Type a message, or /? for help"></textarea>
<div id="rightSendForm" class="alignContentCenter"> <div id="rightSendForm" class="alignContentCenter">
@ -6261,8 +6261,8 @@
<div id="mes_stop" title="Abort request" class="mes_stop" data-i18n="[title]Abort request"> <div id="mes_stop" title="Abort request" class="mes_stop" data-i18n="[title]Abort request">
<i class="fa-solid fa-circle-stop"></i> <i class="fa-solid fa-circle-stop"></i>
</div> </div>
<div id="mes_continue" class="fa-fw fa-solid fa-arrow-right displayNone" title="Continue the last message" data-i18n="[title]Continue the last message"></div> <div id="mes_continue" class="fa-fw fa-solid fa-arrow-right interactable displayNone" title="Continue the last message" data-i18n="[title]Continue the last message"></div>
<div id="send_but" class="fa-solid fa-paper-plane displayNone" title="Send a message" data-i18n="[title]Send a message"></div> <div id="send_but" class="fa-solid fa-paper-plane interactable displayNone" title="Send a message" data-i18n="[title]Send a message"></div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -236,7 +236,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument } from './scripts/slash-commands/Sl
import { SlashCommandBrowser } from './scripts/slash-commands/SlashCommandBrowser.js'; import { SlashCommandBrowser } from './scripts/slash-commands/SlashCommandBrowser.js';
import { initCustomSelectedSamplers, validateDisabledSamplers } from './scripts/samplerSelect.js'; import { initCustomSelectedSamplers, validateDisabledSamplers } from './scripts/samplerSelect.js';
import { DragAndDropHandler } from './scripts/dragdrop.js'; import { DragAndDropHandler } from './scripts/dragdrop.js';
import { initKeyboard } from './scripts/keyboard.js'; import { INTERACTABLE_CONTROL_CLASS, initKeyboard } from './scripts/keyboard.js';
//exporting functions and vars for mods //exporting functions and vars for mods
export { export {
@ -524,10 +524,13 @@ let scrollLock = false;
export let abortStatusCheck = new AbortController(); export let abortStatusCheck = new AbortController();
let charDragDropHandler = null; let charDragDropHandler = null;
/** @type {number} The debounce timeout used for chat/settings save. debounce_timeout.long: 1.000 ms */ /** @type {debounce_timeout} The debounce timeout used for chat/settings save. debounce_timeout.long: 1.000 ms */
const durationSaveEdit = debounce_timeout.relaxed; export const DEFAULT_SAVE_EDIT_TIMEOUT = debounce_timeout.relaxed;
export const saveSettingsDebounced = debounce(() => saveSettings(), durationSaveEdit); /** @type {debounce_timeout} The debounce timeout used for printing. debounce_timeout.quick: 100 ms */
export const saveCharacterDebounced = debounce(() => $('#create_button').trigger('click'), durationSaveEdit); export const DEFAULT_PRINT_TIMEOUT = debounce_timeout.quick;
export const saveSettingsDebounced = debounce(() => saveSettings(), DEFAULT_SAVE_EDIT_TIMEOUT);
export const saveCharacterDebounced = debounce(() => $('#create_button').trigger('click'), DEFAULT_SAVE_EDIT_TIMEOUT);
/** /**
* Prints the character list in a debounced fashion without blocking, with a delay of 100 milliseconds. * Prints the character list in a debounced fashion without blocking, with a delay of 100 milliseconds.
@ -535,7 +538,7 @@ export const saveCharacterDebounced = debounce(() => $('#create_button').trigger
* *
* The printing will also always reprint all filter options of the global list, to keep them up to date. * The printing will also always reprint all filter options of the global list, to keep them up to date.
*/ */
export const printCharactersDebounced = debounce(() => { printCharacters(false); }, debounce_timeout.quick); export const printCharactersDebounced = debounce(() => { printCharacters(false); }, DEFAULT_PRINT_TIMEOUT);
/** /**
* @enum {string} System message types * @enum {string} System message types
@ -5684,7 +5687,7 @@ async function read_avatar_load(input) {
} }
await createOrEditCharacter(); await createOrEditCharacter();
await delay(durationSaveEdit); await delay(DEFAULT_SAVE_EDIT_TIMEOUT);
const formData = new FormData($('#form_create').get(0)); const formData = new FormData($('#form_create').get(0));
await fetch(getThumbnailUrl('avatar', formData.get('avatar_url')), { await fetch(getThumbnailUrl('avatar', formData.get('avatar_url')), {
@ -5728,7 +5731,7 @@ export function getThumbnailUrl(type, file) {
return `/thumbnail?type=${type}&file=${encodeURIComponent(file)}`; return `/thumbnail?type=${type}&file=${encodeURIComponent(file)}`;
} }
export function buildAvatarList(block, entities, { templateId = 'inline_avatar_template', empty = true, selectable = false, highlightFavs = true } = {}) { export function buildAvatarList(block, entities, { templateId = 'inline_avatar_template', empty = true, interactable = false, highlightFavs = true } = {}) {
if (empty) { if (empty) {
block.empty(); block.empty();
} }
@ -5763,8 +5766,8 @@ export function buildAvatarList(block, entities, { templateId = 'inline_avatar_t
avatarTemplate.attr('title', `[Group] ${entity.item.name}`); avatarTemplate.attr('title', `[Group] ${entity.item.name}`);
} }
if (selectable) { if (interactable) {
avatarTemplate.addClass('selectable'); avatarTemplate.addClass(INTERACTABLE_CONTROL_CLASS);
avatarTemplate.toggleClass('character_select', entity.type === 'character'); avatarTemplate.toggleClass('character_select', entity.type === 'character');
avatarTemplate.toggleClass('group_select', entity.type === 'group'); avatarTemplate.toggleClass('group_select', entity.type === 'group');
} }
@ -7170,7 +7173,7 @@ export async function saveMetadata() {
export async function saveChatConditional() { export async function saveChatConditional() {
try { try {
await waitUntilCondition(() => !isChatSaving, durationSaveEdit, 100); await waitUntilCondition(() => !isChatSaving, DEFAULT_SAVE_EDIT_TIMEOUT, 100);
} catch { } catch {
console.warn('Timeout waiting for chat to save'); console.warn('Timeout waiting for chat to save');
return; return;
@ -8850,7 +8853,7 @@ jQuery(async function () {
$('#send_textarea').on('focusin focus click', () => { $('#send_textarea').on('focusin focus click', () => {
S_TAPreviouslyFocused = true; S_TAPreviouslyFocused = true;
}); });
$('#options_button, #send_but, #option_regenerate, #option_continue, #mes_continue').on('click', () => { $('#send_but, #option_regenerate, #option_continue, #mes_continue').on('click', () => {
if (S_TAPreviouslyFocused) { if (S_TAPreviouslyFocused) {
$('#send_textarea').focus(); $('#send_textarea').focus();
} }
@ -9360,7 +9363,7 @@ jQuery(async function () {
} }
function isMouseOverButtonOrMenu() { function isMouseOverButtonOrMenu() {
return menu.is(':hover') || button.is(':hover'); return menu.is(':hover, :focus-within') || button.is(':hover, :focus');
} }
button.on('click', function () { button.on('click', function () {

View File

@ -303,7 +303,7 @@ export async function favsToHotswap() {
return; return;
} }
buildAvatarList(container, favs, { selectable: true, highlightFavs: false }); buildAvatarList(container, favs, { interactable: true, highlightFavs: false });
} }
//changes input bar and send button display depending on connection status //changes input bar and send button display depending on connection status

View File

@ -349,12 +349,12 @@ function autoConnectInputHandler() {
function addExtensionsButtonAndMenu() { function addExtensionsButtonAndMenu() {
const buttonHTML = const buttonHTML =
'<div id="extensionsMenuButton" style="display: none;" class="fa-solid fa-magic-wand-sparkles" title="Extras Extensions" /></div>'; '<div id="extensionsMenuButton" style="display: none;" class="fa-solid fa-magic-wand-sparkles interactable" title="Extras Extensions" /></div>';
const extensionsMenuHTML = '<div id="extensionsMenu" class="options-content" style="display: none;"></div>'; const extensionsMenuHTML = '<div id="extensionsMenu" class="options-content" style="display: none;"></div>';
$(document.body).append(extensionsMenuHTML); $(document.body).append(extensionsMenuHTML);
$('#leftSendForm').prepend(buttonHTML); $('#leftSendForm').append(buttonHTML);
const button = $('#extensionsMenuButton'); const button = $('#extensionsMenuButton');
const dropdown = $('#extensionsMenu'); const dropdown = $('#extensionsMenu');

View File

@ -1,5 +1,25 @@
/* All selectors that should act as interactables / keyboard buttons by default */ /* All selectors that should act as interactables / keyboard buttons by default */
const interactableSelectors = ['.menu_button', '.right_menu_button', '.custom_interactable', '.interactable']; const interactableSelectors = [
'.custom_interactable',
'.interactable',
'.menu_button',
'.right_menu_button',
'.drawer-icon',
'.inline-drawer-icon',
'.paginationjs-pages li a',
'.group_select',
'.character_select',
'.bogus_folder_select',
'.avatar-container',
'.tag .tag_remove',
'.bg_example',
'.bg_example .bg_button',
'#options a',
'#extensionsMenu div:has(.extensionsMenuExtensionButton)',
'.mes_buttons .mes_button',
'.extraMesButtons>div:not(.mes_button)',
'.stscript_btn'
];
export const INTERACTABLE_CONTROL_CLASS = 'interactable'; export const INTERACTABLE_CONTROL_CLASS = 'interactable';
export const CUSTOM_INTERACTABLE_CONTROL_CLASS = 'custom_interactable'; export const CUSTOM_INTERACTABLE_CONTROL_CLASS = 'custom_interactable';

View File

@ -9,6 +9,7 @@ import {
buildAvatarList, buildAvatarList,
eventSource, eventSource,
event_types, event_types,
DEFAULT_PRINT_TIMEOUT,
} from '../script.js'; } from '../script.js';
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
import { FILTER_TYPES, FILTER_STATES, DEFAULT_FILTER_STATE, isFilterState, FilterHelper } from './filters.js'; import { FILTER_TYPES, FILTER_STATES, DEFAULT_FILTER_STATE, isFilterState, FilterHelper } from './filters.js';
@ -22,7 +23,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '
import { isMobile } from './RossAscends-mods.js'; import { isMobile } from './RossAscends-mods.js';
import { POPUP_RESULT, POPUP_TYPE, callGenericPopup } from './popup.js'; import { POPUP_RESULT, POPUP_TYPE, callGenericPopup } from './popup.js';
import { debounce_timeout } from './constants.js'; import { debounce_timeout } from './constants.js';
import { registerInteractableType } from './keyboard.js'; import { INTERACTABLE_CONTROL_CLASS, registerInteractableType } from './keyboard.js';
export { export {
TAG_FOLDER_TYPES, TAG_FOLDER_TYPES,
@ -851,7 +852,7 @@ function newTag(tagName) {
/** /**
* @typedef {object} TagOptions - Options for tag behavior. (Same object will be passed into "appendTagToList") * @typedef {object} TagOptions - Options for tag behavior. (Same object will be passed into "appendTagToList")
* @property {boolean} [removable=false] - Whether tags can be removed. * @property {boolean} [removable=false] - Whether tags can be removed.
* @property {boolean} [selectable=false] - Whether tags can be selected. * @property {boolean} [isFilter=false] - Whether tags can be selected as a filter.
* @property {function} [action=undefined] - Action to perform on tag interaction. * @property {function} [action=undefined] - Action to perform on tag interaction.
* @property {(tag: Tag)=>boolean} [removeAction=undefined] - Action to perform on tag removal instead of the default remove action. If the action returns false, the tag will not be removed. * @property {(tag: Tag)=>boolean} [removeAction=undefined] - Action to perform on tag removal instead of the default remove action. If the action returns false, the tag will not be removed.
* @property {boolean} [isGeneralList=false] - If true, indicates that this is the general list of tags. * @property {boolean} [isGeneralList=false] - If true, indicates that this is the general list of tags.
@ -971,7 +972,7 @@ function printTagList(element, { tags = undefined, addTag = undefined, forEntity
* @param {TagOptions} [options={}] - Options for tag behavior * @param {TagOptions} [options={}] - Options for tag behavior
* @returns {void} * @returns {void}
*/ */
function appendTagToList(listElement, tag, { removable = false, selectable = false, action = undefined, removeAction = undefined, isGeneralList = false, skipExistsCheck = false } = {}) { function appendTagToList(listElement, tag, { removable = false, isFilter = false, action = undefined, removeAction = undefined, isGeneralList = false, skipExistsCheck = false } = {}) {
if (!listElement) { if (!listElement) {
return; return;
} }
@ -1011,19 +1012,20 @@ function appendTagToList(listElement, tag, { removable = false, selectable = fal
// We could have multiple ways of actions passed in. The manual arguments have precendence in front of a specified tag action // We could have multiple ways of actions passed in. The manual arguments have precendence in front of a specified tag action
const clickableAction = action ?? tag.action; const clickableAction = action ?? tag.action;
// If this is a tag for a general list and its either selectable or actionable, lets mark its current state // If this is a tag for a general list and its either a filter or actionable, lets mark its current state
if ((selectable || clickableAction) && isGeneralList) { if ((isFilter || clickableAction) && isGeneralList) {
toggleTagThreeState(tagElement, { stateOverride: tag.filter_state ?? DEFAULT_FILTER_STATE }); toggleTagThreeState(tagElement, { stateOverride: tag.filter_state ?? DEFAULT_FILTER_STATE });
} }
if (selectable) { if (isFilter) {
tagElement.on('click', () => onTagFilterClick.bind(tagElement)(listElement)); tagElement.on('click', () => onTagFilterClick.bind(tagElement)(listElement));
tagElement.addClass(INTERACTABLE_CONTROL_CLASS);
} }
if (clickableAction) { if (clickableAction) {
const filter = getFilterHelper($(listElement)); const filter = getFilterHelper($(listElement));
tagElement.on('click', (e) => clickableAction.bind(tagElement)(filter, e)); tagElement.on('click', (e) => clickableAction.bind(tagElement)(filter, e));
tagElement.addClass('clickable-action'); tagElement.addClass('clickable-action').addClass(INTERACTABLE_CONTROL_CLASS);
} }
$(listElement).append(tagElement); $(listElement).append(tagElement);
@ -1032,6 +1034,7 @@ function appendTagToList(listElement, tag, { removable = false, selectable = fal
function onTagFilterClick(listElement) { function onTagFilterClick(listElement) {
const tagId = $(this).attr('id'); const tagId = $(this).attr('id');
const existingTag = tags.find((tag) => tag.id === tagId); const existingTag = tags.find((tag) => tag.id === tagId);
const parent = $(this).parents('.tags');
let state = toggleTagThreeState($(this)); let state = toggleTagThreeState($(this));
@ -1042,6 +1045,9 @@ function onTagFilterClick(listElement) {
// We don't print anything manually, updating the filter will automatically trigger a redraw of all relevant stuff // We don't print anything manually, updating the filter will automatically trigger a redraw of all relevant stuff
runTagFilters(listElement); runTagFilters(listElement);
// Focus the tag again we were at, if possible. To improve keyboard navigation
setTimeout(() => parent.find(`.tag[id="${tagId}"]`).trigger('focus'), DEFAULT_PRINT_TIMEOUT + 1);
} }
/** /**
@ -1119,7 +1125,7 @@ function printTagFilters(type = tag_filter_types.character) {
const characterTagIds = Object.values(tag_map).flat(); const characterTagIds = Object.values(tag_map).flat();
const tagsToDisplay = tags.filter(x => characterTagIds.includes(x.id)).sort(compareTagsForSort); const tagsToDisplay = tags.filter(x => characterTagIds.includes(x.id)).sort(compareTagsForSort);
printTagList($(FILTER_SELECTOR), { empty: false, tags: tagsToDisplay, tagOptions: { selectable: true, isGeneralList: true } }); printTagList($(FILTER_SELECTOR), { empty: false, tags: tagsToDisplay, tagOptions: { isFilter: true, isGeneralList: true } });
// Print bogus folder navigation // Print bogus folder navigation
const bogusDrilldown = $(FILTER_SELECTOR).siblings('.rm_tag_bogus_drilldown'); const bogusDrilldown = $(FILTER_SELECTOR).siblings('.rm_tag_bogus_drilldown');
@ -1910,8 +1916,6 @@ export function initTags() {
eventSource.on(event_types.CHARACTER_DUPLICATED, copyTags); eventSource.on(event_types.CHARACTER_DUPLICATED, copyTags);
eventSource.makeFirst(event_types.CHAT_CHANGED, () => selected_group ? applyTagsOnGroupSelect() : applyTagsOnCharacterSelect()); eventSource.makeFirst(event_types.CHAT_CHANGED, () => selected_group ? applyTagsOnGroupSelect() : applyTagsOnCharacterSelect());
registerInteractableType('.tag.actionable');
$(document).on('input', '#tag_view_list input[name="auto_sort_tags"]', (evt) => { $(document).on('input', '#tag_view_list input[name="auto_sort_tags"]', (evt) => {
const toggle = $(evt.target).is(':checked'); const toggle = $(evt.target).is(':checked');
toggleAutoSortTags(evt.originalEvent, toggle); toggleAutoSortTags(evt.originalEvent, toggle);

View File

@ -688,11 +688,18 @@ body .panelControlBar {
} }
#rightSendForm>div:hover, #rightSendForm>div:hover,
#leftSendForm>div:hover { #rightSendForm>div:focus,
#leftSendForm>div:hover,
#leftSendForm>div:focus {
opacity: 1; opacity: 1;
filter: brightness(1.2); filter: brightness(1.2);
} }
#rightSendForm>div:focus,
#leftSendForm>div:focus {
outline: 1px solid var(--white100);
}
#send_but { #send_but {
order: 2; order: 2;
} }
@ -825,6 +832,12 @@ body .panelControlBar {
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
flex-flow: column; flex-flow: column;
border-radius: 10px; border-radius: 10px;
padding: 2px;
}
#extensionsMenu,
.options-content {
padding: 2px;
} }
.options-content, .options-content,
@ -860,11 +873,6 @@ body .panelControlBar {
padding: 1px; padding: 1px;
} }
#extensionsMenuButton:hover {
opacity: 1;
filter: brightness(1.2);
}
.options-content a, .options-content a,
#extensionsMenu>div, #extensionsMenu>div,
.list-group>div, .list-group>div,
@ -1033,11 +1041,11 @@ body .panelControlBar {
justify-content: space-evenly; justify-content: space-evenly;
} }
.avatar.selectable { .avatar.interactable {
opacity: 0.6; opacity: 0.6;
} }
.avatar.selectable:hover { .avatar.interactable:hover {
opacity: 1; opacity: 1;
background-color: transparent !important; background-color: transparent !important;
cursor: pointer; cursor: pointer;
@ -2250,6 +2258,7 @@ input[type="file"] {
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
opacity: 0.5; opacity: 0.5;
padding: 1px;
} }
#rm_button_selected_ch:hover { #rm_button_selected_ch:hover {
@ -2315,6 +2324,7 @@ input[type="file"] {
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;
height: 100%; height: 100%;
padding: 1px;
} }
#rm_ch_create_block { #rm_ch_create_block {
@ -2688,8 +2698,11 @@ input[type=search]:focus::-webkit-search-cancel-button {
} }
.bogus_folder_select:hover, .bogus_folder_select:hover,
.bogus_folder_select:focus-within,
.character_select:hover, .character_select:hover,
.avatar-container:hover { .character_select:focus-within,
.avatar-container:hover,
.avatar-container:focus-within {
background-color: var(--white30a); background-color: var(--white30a);
} }
@ -2920,6 +2933,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
flex-direction: row; flex-direction: row;
align-items: flex-start; align-items: flex-start;
gap: 5px; gap: 5px;
margin: 1px;
padding: 5px; padding: 5px;
border-radius: 10px; border-radius: 10px;
cursor: pointer; cursor: pointer;
@ -3019,7 +3033,7 @@ grammarly-extension {
font-size: calc(var(--mainFontSize) * 0.9); font-size: calc(var(--mainFontSize) * 0.9);
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 6px;
padding: 1px 3px; padding: 1px 3px;
} }
@ -3030,8 +3044,8 @@ grammarly-extension {
text-align: right; text-align: right;
} }
.rm_stats_button { #result_info .right_menu_button {
cursor: pointer; padding: 4px;
} }
/* Focus */ /* Focus */
@ -3406,6 +3420,7 @@ input[type='checkbox'].del_checkbox {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-around; justify-content: space-around;
padding: 1px;
} }
.avatar-container .avatar { .avatar-container .avatar {
@ -3523,6 +3538,18 @@ input[type='checkbox'].del_checkbox {
z-index: 1; z-index: 1;
} }
.neo-range-slider:hover,
.neo-range-slider:focus,
input[type="range"]:hover,
input[type="range"]:focus {
filter: brightness(1.25);
}
.neo-range-slider:focus,
input[type="range"]:focus {
outline: 1px solid var(--white100);
}
.range-block-range { .range-block-range {
margin: 0; margin: 0;
flex: 5; flex: 5;
@ -3722,35 +3749,29 @@ input[type="range"]::-webkit-slider-thumb {
/* height: 20px; */ /* height: 20px; */
position: relative; position: relative;
display: flex; display: flex;
gap: 10px; gap: 4px;
flex-wrap: nowrap; flex-wrap: nowrap;
justify-content: flex-end; justify-content: flex-end;
transition: all 200ms; transition: all 200ms;
overflow-x: hidden; overflow-x: hidden;
padding: 1px;
} }
.extraMesButtons { .extraMesButtons {
display: none; display: none;
} }
.mes_buttons .mes_edit, .mes_button,
.mes_buttons .mes_bookmark, .extraMesButtons>div {
.mes_buttons .mes_create_bookmark,
.extraMesButtonsHint,
.tagListHint,
.extraMesButtons div {
cursor: pointer; cursor: pointer;
transition: 0.3s ease-in-out; transition: 0.3s ease-in-out;
filter: drop-shadow(0px 0px 2px black); filter: drop-shadow(0px 0px 2px black);
opacity: 0.3; opacity: 0.3;
padding: 1px 3px;
} }
.mes_buttons .mes_edit:hover, .mes_button:hover,
.mes_buttons .mes_bookmark:hover, .extraMesButtons>div:hover {
.mes_buttons .mes_create_bookmark:hover,
.extraMesButtonsHint:hover,
.tagListHint:hover,
.extraMesButtons div:hover {
opacity: 1; opacity: 1;
} }
@ -4523,6 +4544,7 @@ body:has(#character_popup.open) #top-settings-holder:has(.drawer-content.openDra
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
font-size: var(--topBarIconSize); font-size: var(--topBarIconSize);
padding: 1px 3px;
} }
.drawer-icon.openIcon { .drawer-icon.openIcon {