From e2179e5ca42c7e648074b763d06abe26c247f321 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 20 Feb 2026 13:16:08 -0600 Subject: [PATCH 1/2] Add `mode=plain` to `kbd` shortcode (#13489) Allow rendering keyboard shortcuts as literal text without OS-specific symbol translation, for teaching/slideshow contexts where shortcuts for multiple OSes need to be shown simultaneously. Co-Authored-By: Claude Opus 4.6 --- news/changelog-1.9.md | 1 + src/resources/extensions/quarto/kbd/kbd.lua | 28 ++++++++++++++++++- .../extensions/quarto/kbd/resources/kbd.js | 3 ++ tests/docs/smoke-all/shortcodes/kbd-plain.qmd | 15 ++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tests/docs/smoke-all/shortcodes/kbd-plain.qmd diff --git a/news/changelog-1.9.md b/news/changelog-1.9.md index 0f61d281a9c..791dcd4606e 100644 --- a/news/changelog-1.9.md +++ b/news/changelog-1.9.md @@ -3,6 +3,7 @@ All changes included in 1.9: ## Shortcodes - ([#13342](https://github.com/quarto-dev/quarto-cli/issues/13342)): Ensure that the `contents` shortcode works inside metadata. +- ([#13489](https://github.com/quarto-dev/quarto-cli/issues/13489)): Add `mode=plain` option to the `kbd` shortcode to render keyboard shortcuts exactly as written, without OS-specific symbol translation. ## Regression fixes diff --git a/src/resources/extensions/quarto/kbd/kbd.lua b/src/resources/extensions/quarto/kbd/kbd.lua index edac0a8547a..3062d987d7d 100644 --- a/src/resources/extensions/quarto/kbd/kbd.lua +++ b/src/resources/extensions/quarto/kbd/kbd.lua @@ -4,14 +4,37 @@ return { local function get_osname(v) if v == "win" then return "windows" end if v == "mac" then return "mac" end - if v == "linux" then return "linux" end + if v == "linux" then return "linux" end end + + -- Extract and validate mode kwarg + local mode = kwargs["mode"] + if mode ~= nil then + mode = pandoc.utils.stringify(mode) + kwargs["mode"] = nil + if mode ~= "plain" then + return quarto.shortcode.error_output("kbd", "unknown mode: " .. mode .. ", supported modes are: plain", "inline") + end + -- plain mode requires a positional argument + if #args == 0 then + return quarto.shortcode.error_output("kbd", "plain mode requires a positional argument", "inline") + end + -- plain mode doesn't accept OS kwargs + for k, _ in pairs(kwargs) do + return quarto.shortcode.error_output("kbd", "plain mode does not accept OS-specific arguments", "inline") + end + end + if quarto.doc.is_format("html:js") then quarto.doc.add_html_dependency({ name = 'kbd', scripts = { 'resources/kbd.js' }, stylesheets = { 'resources/kbd.css' } }) + if mode == "plain" then + local text = pandoc.utils.stringify(args[1]) + return pandoc.RawInline('html', '' .. text .. '') + end local kwargs_strs = {} local title_strs = {} for k, v in pairs(kwargs) do @@ -73,6 +96,9 @@ return { -- {{< kbd Shift-Ctrl-P >}} -- {{< kbd Shift-Ctrl-P mac=Shift-Command-P >}} -- {{< kbd mac=Shift-Command-P win=Shift-Control-S linux=Shift-Ctrl-S >}} + if mode == "plain" then + return pandoc.Code(pandoc.utils.stringify(args[1])) + end local result = {}; local n_kwargs = 0 for k, v in pairs(kwargs) do diff --git a/src/resources/extensions/quarto/kbd/resources/kbd.js b/src/resources/extensions/quarto/kbd/resources/kbd.js index 4f0fb5609e3..f033a3bc189 100644 --- a/src/resources/extensions/quarto/kbd/resources/kbd.js +++ b/src/resources/extensions/quarto/kbd/resources/kbd.js @@ -45,6 +45,9 @@ window.addEventListener("DOMContentLoaded", (_) => { for (const el of Array.from(document.querySelectorAll("kbd"))) { el.classList.add("kbd"); + if (el.dataset.mode === "plain") { + continue; + } if (el.dataset[os.name] !== undefined) { el.innerText = el.dataset[os.name]; } diff --git a/tests/docs/smoke-all/shortcodes/kbd-plain.qmd b/tests/docs/smoke-all/shortcodes/kbd-plain.qmd new file mode 100644 index 00000000000..df8911dc5e8 --- /dev/null +++ b/tests/docs/smoke-all/shortcodes/kbd-plain.qmd @@ -0,0 +1,15 @@ +--- +title: kbd plain mode test +format: html +_quarto: + tests: + html: + ensureFileRegexMatches: + - ['data-mode="plain"'] + - ['Shift-Ctrl-K'] + - ['Command-Shift-K'] +--- + +{{< kbd Shift-Ctrl-K mode=plain >}} + +{{< kbd Command-Shift-K mode=plain >}} From 8ae92cb3c65db7397e23d890bd8444cc8fc85932 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 20 Feb 2026 13:38:56 -0600 Subject: [PATCH 2/2] Fix kbd shortcode handling empty mode kwarg and test format Quarto's shortcode system populates kwargs with empty values for unspecified keys, so check for empty string before validating mode. Also fix test to use correct ensureFileRegexMatches format (single array of match patterns). Co-Authored-By: Claude Opus 4.6 --- src/resources/extensions/quarto/kbd/kbd.lua | 21 +++++++++++-------- tests/docs/smoke-all/shortcodes/kbd-plain.qmd | 5 ++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/resources/extensions/quarto/kbd/kbd.lua b/src/resources/extensions/quarto/kbd/kbd.lua index 3062d987d7d..22b2e903d53 100644 --- a/src/resources/extensions/quarto/kbd/kbd.lua +++ b/src/resources/extensions/quarto/kbd/kbd.lua @@ -12,16 +12,19 @@ return { if mode ~= nil then mode = pandoc.utils.stringify(mode) kwargs["mode"] = nil - if mode ~= "plain" then + if mode == "" then + mode = nil + elseif mode ~= "plain" then return quarto.shortcode.error_output("kbd", "unknown mode: " .. mode .. ", supported modes are: plain", "inline") - end - -- plain mode requires a positional argument - if #args == 0 then - return quarto.shortcode.error_output("kbd", "plain mode requires a positional argument", "inline") - end - -- plain mode doesn't accept OS kwargs - for k, _ in pairs(kwargs) do - return quarto.shortcode.error_output("kbd", "plain mode does not accept OS-specific arguments", "inline") + else + -- plain mode requires a positional argument + if #args == 0 then + return quarto.shortcode.error_output("kbd", "plain mode requires a positional argument", "inline") + end + -- plain mode doesn't accept OS kwargs + for k, _ in pairs(kwargs) do + return quarto.shortcode.error_output("kbd", "plain mode does not accept OS-specific arguments", "inline") + end end end diff --git a/tests/docs/smoke-all/shortcodes/kbd-plain.qmd b/tests/docs/smoke-all/shortcodes/kbd-plain.qmd index df8911dc5e8..182643a7104 100644 --- a/tests/docs/smoke-all/shortcodes/kbd-plain.qmd +++ b/tests/docs/smoke-all/shortcodes/kbd-plain.qmd @@ -5,9 +5,8 @@ _quarto: tests: html: ensureFileRegexMatches: - - ['data-mode="plain"'] - - ['Shift-Ctrl-K'] - - ['Command-Shift-K'] + - ['data-mode="plain"', 'Shift-Ctrl-K', 'Command-Shift-K'] + - [] --- {{< kbd Shift-Ctrl-K mode=plain >}}