a11y: Setup observers for element roles (#4469)

* a11y: Setup observers for element roles

* Reuse a function

* Add error handling
This commit is contained in:
Cohee
2025-09-01 17:24:13 +03:00
committed by GitHub
parent fe8ebedeb2
commit ff8d7d64fc
4 changed files with 111 additions and 1 deletions
+1 -1
View File
@@ -84,7 +84,7 @@
</div> </div>
<script src="lib/jquery-3.5.1.min.js"></script> <script src="lib/jquery-3.5.1.min.js"></script>
<script src="scripts/login.js"></script> <script src="scripts/login.js" type="module"></script>
</body> </body>
</html> </html>
+2
View File
@@ -266,6 +266,7 @@ import { initDataMaid } from './scripts/data-maid.js';
import { clearItemizedPrompts, deleteItemizedPrompts, findItemizedPromptSet, initItemizedPrompts, itemizedParams, itemizedPrompts, loadItemizedPrompts, promptItemize, replaceItemizedPromptText, saveItemizedPrompts } from './scripts/itemized-prompts.js'; import { clearItemizedPrompts, deleteItemizedPrompts, findItemizedPromptSet, initItemizedPrompts, itemizedParams, itemizedPrompts, loadItemizedPrompts, promptItemize, replaceItemizedPromptText, saveItemizedPrompts } from './scripts/itemized-prompts.js';
import { getSystemMessageByType, initSystemMessages, SAFETY_CHAT, sendSystemMessage, system_message_types, system_messages } from './scripts/system-messages.js'; import { getSystemMessageByType, initSystemMessages, SAFETY_CHAT, sendSystemMessage, system_message_types, system_messages } from './scripts/system-messages.js';
import { event_types, eventSource } from './scripts/events.js'; import { event_types, eventSource } from './scripts/events.js';
import { initAccessibility } from './scripts/a11y.js';
// API OBJECT FOR EXTERNAL WIRING // API OBJECT FOR EXTERNAL WIRING
globalThis.SillyTavern = { globalThis.SillyTavern = {
@@ -687,6 +688,7 @@ async function firstLoadInit() {
initCustomSelectedSamplers(); initCustomSelectedSamplers();
initDataMaid(); initDataMaid();
initItemizedPrompts(); initItemizedPrompts();
initAccessibility();
addDebugFunctions(); addDebugFunctions();
doDailyExtensionUpdatesCheck(); doDailyExtensionUpdatesCheck();
await hideLoader(); await hideLoader();
+104
View File
@@ -0,0 +1,104 @@
/**
* Shared module between login and main app.
* Be careful what you import!
*/
const buttonSelectors = [
'.menu_button',
'.right_menu_button',
'.mes_button',
'.drawer-icon',
'.inline-drawer-icon',
'.swipe_left',
'.swipe_right',
'.character_select',
'.tags .tag',
].join(', ');
const listSelectors = [
'.options-content',
'.list-group',
'#rm_print_characters_block',
'#rm_group_members',
'#rm_group_add_members',
'.tag_view_list_tags',
'.secretKeyManagerList',
'.recentChatList',
'.dataMaidCategoryContent',
'#userList',
].join(', ');
const listItemSelectors = [
'.options-content .interactable',
'.list-group .list-group-item',
'#rm_print_characters_block .entity_block',
'#rm_group_members .group_member',
'#rm_group_add_members .group_member',
'.tag_view_list_tags .tag_view_item',
'.secretKeyManagerList .secretKeyManagerItem',
'.recentChatList .recentChat',
'.dataMaidCategoryContent .dataMaidItem',
'#userList .userSelect',
].join(', ');
/** @type {Record<string, (element: Element) => void>} */
const a11yRules = {
[buttonSelectors]: (element) => {
element.setAttribute('role', 'button');
},
[listSelectors]: (element) => {
element.setAttribute('role', 'list');
},
[listItemSelectors]: (element) => {
element.setAttribute('role', 'listitem');
},
'#toast-container .toast-message': (element) => {
element.setAttribute('role', 'alert');
},
};
/**
* Apply accessibility rules to an element.
* @param {Element} element Element to process.
*/
function applyA11yRules(element) {
try {
for (const [selector, rule] of Object.entries(a11yRules)) {
// Apply if the element directly matches the selector
if (element.matches(selector)) {
rule(element);
}
// Apply the rule to descendants
element.querySelectorAll(selector).forEach(rule);
}
} catch (error) {
console.error('Error applying accessibility rules to element:', element, error);
}
}
function setAccessibilityObserver() {
// Apply for existing elements
applyA11yRules(document.body);
// Setup observer for dynamic content
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
for (const addedNode of mutation.addedNodes) {
if (addedNode instanceof Element && addedNode.nodeType === Node.ELEMENT_NODE) {
applyA11yRules(addedNode);
}
}
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
}
export function initAccessibility() {
setAccessibilityObserver();
}
+4
View File
@@ -1,3 +1,5 @@
import { initAccessibility } from './a11y.js';
/** /**
* CRSF token for requests. * CRSF token for requests.
*/ */
@@ -265,6 +267,8 @@ function configureDiscreetLogin() {
} }
(async function () { (async function () {
initAccessibility();
csrfToken = await getCsrfToken(); csrfToken = await getCsrfToken();
const userList = await getUserList(); const userList = await getUserList();