From 89f3228f045fda70d3905d5e6cb2ee11aa32a594 Mon Sep 17 00:00:00 2001 From: Martin Brennan Date: Tue, 5 Aug 2025 15:31:52 +1000 Subject: [PATCH 1/7] UX: Consistency and accessibility improvements for keyboard shortcuts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Apply these rules to the display of all keyboard shortcuts in the app: * No shortcuts show a + between keys * On Windows and Linux, show Ctrl and Alt words, but show ⇧ for Shift * On macOS, show ⌃ for Control, ⌘ for Command, ⌥ for Option, and ⇧ for Shift * Use capital letters for the shortcut keys, e.g. Ctrl+S instead of Ctrl+s * Add a filter to the keyboard shortcut help modal to allow searching * Add aria-keyshortcuts attributes to the keyboard shortcuts in the composer toolbar and popup menu options --- .../components/composer/toolbar-buttons.gjs | 1 + .../modal/keyboard-shortcuts-help.gjs | 104 +++++++++++------- .../components/toolbar-popup-menu-options.gjs | 3 +- .../discourse/app/lib/composer/toolbar.js | 23 +++- .../discourse/app/lib/keyboard-shortcuts.js | 4 +- .../discourse/app/lib/utilities.js | 18 ++- .../common/components/keyboard_shortcuts.scss | 5 + config/locales/client.en.yml | 14 ++- plugins/chat/config/locales/client.en.yml | 2 +- .../config/locales/client.en.yml | 2 +- .../spec/system/details_spec.rb | 2 +- plugins/poll/config/locales/client.en.yml | 2 +- .../config/locales/client.en.yml | 2 +- 13 files changed, 119 insertions(+), 63 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/composer/toolbar-buttons.gjs b/app/assets/javascripts/discourse/app/components/composer/toolbar-buttons.gjs index 689aacb4f7398..d034586badcb4 100644 --- a/app/assets/javascripts/discourse/app/components/composer/toolbar-buttons.gjs +++ b/app/assets/javascripts/discourse/app/components/composer/toolbar-buttons.gjs @@ -67,6 +67,7 @@ export default class ComposerToolbarButtons extends Component { @icon={{button.icon}} @preventFocus={{button.preventFocus}} @onKeyDown={{this.rovingButtonBar}} + aria-keyshortcuts={{button.ariaKeyshortcuts}} tabindex={{this.tabIndex button}} class={{concatClass "toolbar__button" diff --git a/app/assets/javascripts/discourse/app/components/modal/keyboard-shortcuts-help.gjs b/app/assets/javascripts/discourse/app/components/modal/keyboard-shortcuts-help.gjs index 731345ca294f0..191696b2a197c 100644 --- a/app/assets/javascripts/discourse/app/components/modal/keyboard-shortcuts-help.gjs +++ b/app/assets/javascripts/discourse/app/components/modal/keyboard-shortcuts-help.gjs @@ -1,8 +1,13 @@ import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; import { concat } from "@ember/helper"; +import { on } from "@ember/modifier"; +import { action } from "@ember/object"; import { service } from "@ember/service"; import { htmlSafe } from "@ember/template"; import DModal from "discourse/components/d-modal"; +import FilterInput from "discourse/components/filter-input"; +import withEventValue from "discourse/helpers/with-event-value"; import { extraKeyboardShortcutsHelp } from "discourse/lib/keyboard-shortcuts"; import { translateModKey } from "discourse/lib/utilities"; import { i18n } from "discourse-i18n"; @@ -13,9 +18,8 @@ const ALT = translateModKey("Alt"); const META = translateModKey("Meta"); const CTRL = i18n("shortcut_modifier_key.ctrl"); const ENTER = i18n("shortcut_modifier_key.enter"); - +const ESC = i18n("shortcut_modifier_key.esc"); const COMMA = i18n(`${KEY}.shortcut_key_delimiter_comma`); -const PLUS = i18n(`${KEY}.shortcut_key_delimiter_plus`); const translationForExtraShortcuts = { shift: SHIFT, @@ -24,13 +28,16 @@ const translationForExtraShortcuts = { ctrl: CTRL, enter: ENTER, comma: COMMA, - plus: PLUS, }; -function buildHTML(keys1, keys2, keysDelimiter, shortcutsDelimiter) { +function buildHTML(keys1, keys2, shortcutsDelimiter) { const allKeys = [keys1, keys2] .reject((keys) => keys.length === 0) - .map((keys) => keys.map((k) => `${k}`).join(keysDelimiter)) + .map((keys) => + keys + .map((k) => `${k.length === 1 ? k.toUpperCase() : k}`) + .join(" ") + ) .map((keys) => shortcutsDelimiter !== "space" && shortcutsDelimiter !== "newline" ? wrapInSpan(keys, shortcutsDelimiter) @@ -67,10 +74,10 @@ function wrapInSpan(shortcut, delimiter) { function buildShortcut( key, - { keys1 = [], keys2 = [], keysDelimiter = COMMA, shortcutsDelimiter = "or" } + { keys1 = [], keys2 = [], shortcutsDelimiter = "or" } ) { const context = { - shortcut: buildHTML(keys1, keys2, keysDelimiter, shortcutsDelimiter), + shortcut: buildHTML(keys1, keys2, shortcutsDelimiter), }; return i18n(`${KEY}.${key}`, context); } @@ -78,6 +85,8 @@ function buildShortcut( export default class KeyboardShortcutsHelp extends Component { @service currentUser; + @tracked searchTerm = ""; + get shortcuts() { let shortcuts = { jump_to: { shortcuts: this._buildJumpToSection() }, @@ -97,20 +106,16 @@ export default class KeyboardShortcutsHelp extends Component { search: buildShortcut("application.search", { keys1: ["/"], keys2: [CTRL, ALT, "f"], - keysDelimiter: PLUS, }), filter_sidebar: buildShortcut("application.filter_sidebar", { keys1: [META, "/"], - keysDelimiter: PLUS, }), help: buildShortcut("application.help", { keys1: ["?"] }), bulk_select: buildShortcut("application.toggle_bulk_select", { keys1: [SHIFT, "b"], - keysDelimiter: PLUS, }), dismiss: buildShortcut("application.dismiss", { keys1: [SHIFT, "d"], - keysDelimiter: PLUS, }), x: buildShortcut("application.x", { keys1: ["x"], @@ -118,7 +123,6 @@ export default class KeyboardShortcutsHelp extends Component { log_out: buildShortcut("application.log_out", { keys1: [SHIFT, "z"], keys2: [SHIFT, "z"], - keysDelimiter: PLUS, shortcutsDelimiter: "space", }), }, @@ -133,17 +137,14 @@ export default class KeyboardShortcutsHelp extends Component { }), reply_topic: buildShortcut("actions.reply_topic", { keys1: [SHIFT, "r"], - keysDelimiter: PLUS, }), reply_post: buildShortcut("actions.reply_post", { keys1: ["r"] }), quote_post: buildShortcut("actions.quote_post", { keys1: ["q"] }), pin_unpin_topic: buildShortcut("actions.pin_unpin_topic", { keys1: [SHIFT, "p"], - keysDelimiter: PLUS, }), share_topic: buildShortcut("actions.share_topic", { keys1: [SHIFT, "s"], - keysDelimiter: PLUS, }), share_post: buildShortcut("actions.share_post", { keys1: ["s"] }), like: buildShortcut("actions.like", { keys1: ["l"] }), @@ -165,15 +166,12 @@ export default class KeyboardShortcutsHelp extends Component { }), print: buildShortcut("actions.print", { keys1: [META, "p"], - keysDelimiter: PLUS, }), defer: buildShortcut("actions.defer", { keys1: [SHIFT, "u"], - keysDelimiter: PLUS, }), topic_admin_actions: buildShortcut("actions.topic_admin_actions", { keys1: [SHIFT, "a"], - keysDelimiter: PLUS, }), archive_private_message: buildShortcut( "actions.archive_private_message", @@ -199,12 +197,10 @@ export default class KeyboardShortcutsHelp extends Component { next_prev: buildShortcut("navigation.next_prev", { keys1: [SHIFT, "j"], keys2: [SHIFT, "k"], - keysDelimiter: PLUS, shortcutsDelimiter: "slash", }), go_to_unread_post: buildShortcut("navigation.go_to_unread_post", { keys1: [SHIFT, "l"], - keysDelimiter: PLUS, }), }, }, @@ -212,67 +208,57 @@ export default class KeyboardShortcutsHelp extends Component { shortcuts: { return: buildShortcut("composing.return", { keys1: [SHIFT, "c"], - keysDelimiter: PLUS, }), fullscreen: buildShortcut("composing.fullscreen", { keys1: [SHIFT, "F11"], - keysDelimiter: PLUS, + }), + minimize: buildShortcut("composing.minimize", { + keys1: [ESC], + }), + create_or_reply: buildShortcut("composing.create_or_reply", { + keys1: [META, ENTER], }), insertCurrentTime: buildShortcut("composing.insert_current_time", { keys1: [META, SHIFT, "."], - keysDelimiter: PLUS, }), bold: buildShortcut("composing.bold", { keys1: [META, "b"], - keysDelimiter: PLUS, }), italic: buildShortcut("composing.italic", { keys1: [META, "i"], - keysDelimiter: PLUS, }), link: buildShortcut("composing.link", { keys1: [META, "k"], - keysDelimiter: PLUS, }), preformatted: buildShortcut("composing.preformatted", { keys1: [META, "e"], - keysDelimiter: PLUS, }), paragraph: buildShortcut("composing.paragraph", { keys1: [META, ALT, "0"], - keysDelimiter: PLUS, }), heading1: buildShortcut("composing.heading_1", { keys1: [META, ALT, "1"], - keysDelimiter: PLUS, }), heading2: buildShortcut("composing.heading_2", { keys1: [META, ALT, "2"], - keysDelimiter: PLUS, }), heading3: buildShortcut("composing.heading_3", { keys1: [META, ALT, "3"], - keysDelimiter: PLUS, }), heading4: buildShortcut("composing.heading_4", { keys1: [META, ALT, "4"], - keysDelimiter: PLUS, }), toggleDirection: buildShortcut("composing.toggle_direction", { keys1: [META, SHIFT, "6"], - keysDelimiter: PLUS, }), orderedList: buildShortcut("composing.ordered_list", { keys1: [META, SHIFT, "7"], - keysDelimiter: PLUS, }), unorderedList: buildShortcut("composing.unordered_list", { keys1: [META, SHIFT, "8"], - keysDelimiter: PLUS, }), blockquote: buildShortcut("composing.blockquote", { keys1: [META, SHIFT, "9"], - keysDelimiter: PLUS, }), }, }, @@ -317,7 +303,6 @@ export default class KeyboardShortcutsHelp extends Component { }), full_page_search: buildShortcut("search_menu.full_page_search", { keys1: [META, "Enter"], - keysDelimiter: PLUS, }), }, }, @@ -327,7 +312,6 @@ export default class KeyboardShortcutsHelp extends Component { shortcuts: { admin_search_open: buildShortcut("admin.search_open", { keys1: [META, "/"], - keysDelimiter: PLUS, }), admin_search_prev_next: buildShortcut("admin.search_prev_next", { keys1: ["↑"], @@ -345,6 +329,39 @@ export default class KeyboardShortcutsHelp extends Component { return shortcuts; } + @action + filterShortcuts(event) { + this.searchTerm = event.target.value.toLowerCase().trim(); + } + + get filteredShortcuts() { + return Object.entries(this.shortcuts).reduce( + (acc, [category, shortcutCategory]) => { + const filteredShortcuts = Object.entries( + shortcutCategory.shortcuts + ).reduce((shortcutsAcc, [name, shortcut]) => { + if ( + this.searchTerm === "" || + name.toLowerCase().includes(this.searchTerm) || + shortcut.toLowerCase().includes(this.searchTerm) + ) { + shortcutsAcc[name] = shortcut; + } + return shortcutsAcc; + }, {}); + + if (Object.keys(filteredShortcuts).length > 0) { + acc[category] = { + ...shortcutCategory, + shortcuts: filteredShortcuts, + }; + } + return acc; + }, + {} + ); + } + _buildExtraShortcuts(shortcuts) { for (const [category, helps] of Object.entries( extraKeyboardShortcutsHelp @@ -437,10 +454,19 @@ export default class KeyboardShortcutsHelp extends Component { > <:body>
+ + +
+ {{! A11Y, allows keyboard users to scroll modal body }} - {{#each-in this.shortcuts as |category shortcutCategory|}} + {{#each-in this.filteredShortcuts as |category shortcutCategory|}}
${translateModKey( - PLATFORM_KEY_MODIFIER + "+" + content.shortcut + PLATFORM_KEY_MODIFIER + " " + content.shortcut.replace(/\+/g, " ") )}`; } @@ -154,6 +154,7 @@ export default class ToolbarPopupmenuOptions extends Component { @action={{fn this.onSelect option}} data-name={{option.name}} class={{concatClass (if (this.getActive option) "--active")}} + aria-keyshortcuts={{option.ariaKeyshortcuts}} /> {{/each}} diff --git a/app/assets/javascripts/discourse/app/lib/composer/toolbar.js b/app/assets/javascripts/discourse/app/lib/composer/toolbar.js index 102821665b9eb..c6e4fcdc8697a 100644 --- a/app/assets/javascripts/discourse/app/lib/composer/toolbar.js +++ b/app/assets/javascripts/discourse/app/lib/composer/toolbar.js @@ -101,9 +101,14 @@ export class ToolbarBase { // Main button shortcut bindings and title text. const title = i18n(buttonAttrs.title || `composer.${buttonAttrs.id}_title`); if (buttonAttrs.shortcut) { + const shortcutKeyTranslated = translateModKey( + buttonAttrs.shortcut.length === 1 + ? buttonAttrs.shortcut.toUpperCase() + : buttonAttrs.shortcut + ); const shortcutTitle = `${translateModKey( - PLATFORM_KEY_MODIFIER + "+" - )}${translateModKey(buttonAttrs.shortcut)}`; + PLATFORM_KEY_MODIFIER + " " + )}${shortcutKeyTranslated.replace(/\+/g, " ")}`; if (buttonAttrs.hideShortcutInTitle) { createdButton.title = title; @@ -116,6 +121,8 @@ export class ToolbarBase { this.shortcuts[ `${PLATFORM_KEY_MODIFIER}+${buttonAttrs.shortcut}`.toLowerCase() ] = createdButton; + + createdButton.ariaKeyshortcuts = shortcutTitle.replace(/\s/g, "+"); } else { createdButton.title = title; } @@ -124,11 +131,23 @@ export class ToolbarBase { if (buttonAttrs.popupMenu) { buttonAttrs.popupMenu.options()?.forEach((option) => { if (option.shortcut) { + const shortcutKeyTranslated = translateModKey( + option.shortcut.length === 1 + ? option.shortcut.toUpperCase() + : option.shortcut + ); + const shortcutTitle = `${translateModKey( + PLATFORM_KEY_MODIFIER + " " + )}${shortcutKeyTranslated.replace(/\+/g, " ")}`; + // These shortcuts are actually bound in the keymap inside // components/d-editor.gjs this.shortcuts[ `${PLATFORM_KEY_MODIFIER}+${option.shortcut}`.toLowerCase() ] = option; + + // TODO (martin) Actually this shouldn't have the macOS/symbols i think? + option.ariaKeyshortcuts = shortcutTitle.replace(/\s/g, "+"); } }); } diff --git a/app/assets/javascripts/discourse/app/lib/keyboard-shortcuts.js b/app/assets/javascripts/discourse/app/lib/keyboard-shortcuts.js index cafdc2ccb20dd..613d531740cdd 100644 --- a/app/assets/javascripts/discourse/app/lib/keyboard-shortcuts.js +++ b/app/assets/javascripts/discourse/app/lib/keyboard-shortcuts.js @@ -522,7 +522,9 @@ export default { this.appEvents.trigger("header:keyboard-trigger", { type: "user", event }); }, - showHelpModal() { + showHelpModal(event) { + event.preventDefault(); + getOwner(this) .lookup("controller:application") .send("showKeyboardShortcutsHelp"); diff --git a/app/assets/javascripts/discourse/app/lib/utilities.js b/app/assets/javascripts/discourse/app/lib/utilities.js index 2e30ad2dc1ad1..678d785b48d10 100644 --- a/app/assets/javascripts/discourse/app/lib/utilities.js +++ b/app/assets/javascripts/discourse/app/lib/utilities.js @@ -480,19 +480,17 @@ export function translateModKey(string) { // Apple device users are used to glyphs for shortcut keys if (isApple) { string = string - .toLowerCase() - .replace("shift", "\u21E7") - .replace("meta", "\u2318") - .replace("alt", "\u2325") - .replace("ctrl", "\u2303") + .replace(/shift/i, "\u21E7") + .replace(/meta/i, "\u2318") + .replace(/alt/i, "\u2325") + .replace(/ctrl/i, "\u2303") .replace(/\+/g, ""); } else { string = string - .toLowerCase() - .replace("shift", i18n("shortcut_modifier_key.shift")) - .replace("ctrl", i18n("shortcut_modifier_key.ctrl")) - .replace("meta", i18n("shortcut_modifier_key.ctrl")) - .replace("alt", i18n("shortcut_modifier_key.alt")); + .replace(/shift/i, "\u21E7") + .replace(/ctrl/i, i18n("shortcut_modifier_key.ctrl")) + .replace(/meta/i, i18n("shortcut_modifier_key.ctrl")) + .replace(/alt/i, i18n("shortcut_modifier_key.alt")); } return string; diff --git a/app/assets/stylesheets/common/components/keyboard_shortcuts.scss b/app/assets/stylesheets/common/components/keyboard_shortcuts.scss index 850856d177aca..0e4b0a8d7ac25 100644 --- a/app/assets/stylesheets/common/components/keyboard_shortcuts.scss +++ b/app/assets/stylesheets/common/components/keyboard_shortcuts.scss @@ -49,6 +49,11 @@ #keyboard-shortcuts-help { box-sizing: border-box; + .filter-input-container { + grid-column: 1 / -1; + margin-bottom: 1em; + } + .keyboard-shortcuts-help__container { columns: 2 20em; // Set maximum of 2 columns diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index eedb37cb4d9ed..2f50dba004951 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2790,6 +2790,7 @@ en: rich: "Rich text" shortcut_modifier_key: + esc: "Esc" shift: "Shift" ctrl: "Ctrl" alt: "Alt" @@ -2880,8 +2881,8 @@ en: unlist: "unlisted" add_warning: "This is an official warning." - toggle_whisper: "Toggle Whisper" - insert_table: "Insert Table" + toggle_whisper: "Toggle whisper" + insert_table: "Insert table" posting_not_on_topic: "Which topic do you want to reply to?" saved_local_draft_tip: "saved locally" similar_topics: "Your topic is similar to…" @@ -3006,10 +3007,10 @@ en: paste_code_text: "type or paste code here" upload_title: "Upload" upload_description: "enter upload description here" - olist_title: "Numbered List" - ulist_title: "Bulleted List" + olist_title: "Numbered list" + ulist_title: "Bulleted list" list_item: "List item" - toggle_direction: "Toggle Direction" + toggle_direction: "Toggle direction" help: "Markdown Editing Help" collapse: "minimize the composer panel" open: "open the composer panel" @@ -4835,6 +4836,7 @@ en: invalid_video_url: This video cannot be played because the URL is invalid or unavailable. keyboard_shortcuts_help: + search_placeholder: "Search for a shortcut by description or key combination" shortcut_key_delimiter_comma: ", " shortcut_key_delimiter_plus: "+" shortcut_delimiter_or: "%{shortcut1} or %{shortcut2}" @@ -4883,6 +4885,8 @@ en: composing: title: "Composing" return: "%{shortcut} Return to composer" + minimize: "%{shortcut} Minimize composer" + create_or_reply: "%{shortcut} Create topic or post reply" fullscreen: "%{shortcut} Fullscreen composer" insert_current_time: "%{shortcut} Insert current time" bold: "%{shortcut} Bold" diff --git a/plugins/chat/config/locales/client.en.yml b/plugins/chat/config/locales/client.en.yml index ca5534460e6b6..de6ac3b3ce2ba 100644 --- a/plugins/chat/config/locales/client.en.yml +++ b/plugins/chat/config/locales/client.en.yml @@ -741,7 +741,7 @@ en: switch_channel_arrows: "%{shortcut} Switch channel" switch__unread_channel_arrows: "%{shortcut} Switch unread channel" open_quick_channel_selector: "%{shortcut} Open quick channel selector" - open_insert_link_modal: "%{shortcut} Insert hyperlink (composer only)" + open_insert_link_modal: "%{shortcut} Insert link (composer only)" composer_bold: "%{shortcut} Bold (composer only)" composer_italic: "%{shortcut} Italic (composer only)" composer_code: "%{shortcut} Code (composer only)" diff --git a/plugins/discourse-details/config/locales/client.en.yml b/plugins/discourse-details/config/locales/client.en.yml index 66788556380a3..e2ccfa1063526 100644 --- a/plugins/discourse-details/config/locales/client.en.yml +++ b/plugins/discourse-details/config/locales/client.en.yml @@ -6,7 +6,7 @@ en: discourse_details: "Discourse Details" js: details: - title: Hide Details + title: Hide details composer: details_title: Summary details_text: "This text will be hidden" diff --git a/plugins/discourse-details/spec/system/details_spec.rb b/plugins/discourse-details/spec/system/details_spec.rb index a68d27f4cb6c9..9fd27ef9e80b4 100644 --- a/plugins/discourse-details/spec/system/details_spec.rb +++ b/plugins/discourse-details/spec/system/details_spec.rb @@ -16,7 +16,7 @@ visit("/new-topic") composer.fill_content("test :+1:").toggle_rich_editor.select_all find(".toolbar-menu__options-trigger").click - find("button[title='Hide Details']").click + find("button[title='Hide details']").click rich.click(x: 22, y: 30) # hack for pseudo element expect(rich).to have_css( diff --git a/plugins/poll/config/locales/client.en.yml b/plugins/poll/config/locales/client.en.yml index b86d865549d21..79121dd77cb82 100644 --- a/plugins/poll/config/locales/client.en.yml +++ b/plugins/poll/config/locales/client.en.yml @@ -128,7 +128,7 @@ en: error_while_exporting_results: "Sorry, there was an error exporting poll results." ui_builder: - title: Build Poll + title: Build poll insert: Insert Poll help: options_min_count: Enter at least 1 option. diff --git a/plugins/spoiler-alert/config/locales/client.en.yml b/plugins/spoiler-alert/config/locales/client.en.yml index 8d615f509f5f9..69b9f1d5fccf0 100644 --- a/plugins/spoiler-alert/config/locales/client.en.yml +++ b/plugins/spoiler-alert/config/locales/client.en.yml @@ -6,7 +6,7 @@ en: spoiler_alert: "Discourse Spoiler Alert" js: spoiler: - title: Blur Spoiler + title: Blur spoiler label: show: "Show hidden content" composer: From dd2a76464f2d401f48436f4861f681250ae8af64 Mon Sep 17 00:00:00 2001 From: Martin Brennan Date: Tue, 5 Aug 2025 16:20:50 +1000 Subject: [PATCH 2/7] DEV: Lint --- app/assets/javascripts/discourse/app/lib/composer/toolbar.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/discourse/app/lib/composer/toolbar.js b/app/assets/javascripts/discourse/app/lib/composer/toolbar.js index c6e4fcdc8697a..4f1d4e27f26f1 100644 --- a/app/assets/javascripts/discourse/app/lib/composer/toolbar.js +++ b/app/assets/javascripts/discourse/app/lib/composer/toolbar.js @@ -22,6 +22,7 @@ import { i18n } from "discourse-i18n"; * @property {boolean} [hideShortcutInTitle] * @property {string} title * @property {string} [shortcut] + * @property {string} [ariaKeyshortcuts] * @property {boolean} [unshift] * @property {Function} [active] */ From 61afacf593e2e02e4f3bab3132db33230c0a2d30 Mon Sep 17 00:00:00 2001 From: Martin Brennan Date: Tue, 5 Aug 2025 17:24:10 +1000 Subject: [PATCH 3/7] DEV: More UX tweaks --- .../addon/controllers/admin-search-index.js | 2 +- .../app/components/composer-save-button.gjs | 1 + .../app/components/composer/toggle-switch.gjs | 18 +++++++++++++++--- .../app/components/sidebar/search.gjs | 2 +- .../discourse/app/lib/composer/toolbar.js | 5 ++--- .../javascripts/discourse/app/lib/utilities.js | 7 ++++--- .../tests/acceptance/composer-test.js | 4 ++-- .../stylesheets/common/base/sidebar.scss | 1 + .../components/ai-helper-options-list.gjs | 2 +- 9 files changed, 28 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/admin/addon/controllers/admin-search-index.js b/app/assets/javascripts/admin/addon/controllers/admin-search-index.js index 384229ef80d55..60940274c4321 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-search-index.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-search-index.js @@ -6,6 +6,6 @@ export default class AdminSearchIndexController extends Controller { queryParams = ["filter"]; get shortcutHTML() { - return `${translateModKey(PLATFORM_KEY_MODIFIER)} + /`; + return `${translateModKey(PLATFORM_KEY_MODIFIER)} /`; } } diff --git a/app/assets/javascripts/discourse/app/components/composer-save-button.gjs b/app/assets/javascripts/discourse/app/components/composer-save-button.gjs index a5373d3ee1f0d..72a22492c103b 100644 --- a/app/assets/javascripts/discourse/app/components/composer-save-button.gjs +++ b/app/assets/javascripts/discourse/app/components/composer-save-button.gjs @@ -22,6 +22,7 @@ export default class ComposerSaveButton extends Component { @translatedTitle={{this.translatedTitle}} @forwardEvent={{@forwardEvent}} class={{concatClass "btn-primary create" (if @disableSubmit "disabled")}} + aria-keyshortcuts={{translateModKey "Meta+Enter" "+"}} ...attributes /> diff --git a/app/assets/javascripts/discourse/app/components/composer/toggle-switch.gjs b/app/assets/javascripts/discourse/app/components/composer/toggle-switch.gjs index a3d45e9d117c8..0a43ef6b0e0c3 100644 --- a/app/assets/javascripts/discourse/app/components/composer/toggle-switch.gjs +++ b/app/assets/javascripts/discourse/app/components/composer/toggle-switch.gjs @@ -15,14 +15,25 @@ export default class ComposerToggleSwitch extends Component { } get label() { - const keyboardShortcut = `${translateModKey("ctrl")}M`; if (this.args.state) { - return i18n("composer.switch_to_markdown", { keyboardShortcut }); + return i18n("composer.switch_to_markdown", { + keyboardShortcut: this.keyboardShortcut, + }); } else { - return i18n("composer.switch_to_rich_text", { keyboardShortcut }); + return i18n("composer.switch_to_rich_text", { + keyboardShortcut: this.keyboardShortcut, + }); } } + get keyboardShortcut() { + return `${translateModKey("ctrl")} M`; + } + + get ariaKeyshortcuts() { + return this.keyboardShortcut.replace(/ /g, "+"); + } +