diff options
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/css/settings.css | 10 | ||||
| -rw-r--r-- | ext/data/schemas/options-schema.json | 37 | ||||
| -rw-r--r-- | ext/js/data/options-util.js | 24 | ||||
| -rw-r--r-- | ext/js/display/display.js | 13 | ||||
| -rw-r--r-- | ext/js/dom/dom-data-binder.js | 2 | ||||
| -rw-r--r-- | ext/js/input/hotkey-handler.js | 8 | ||||
| -rw-r--r-- | ext/js/pages/settings/keyboard-shortcuts-controller.js | 174 | ||||
| -rw-r--r-- | ext/settings.html | 45 | 
8 files changed, 233 insertions, 80 deletions
| diff --git a/ext/css/settings.css b/ext/css/settings.css index 6bc06bf7..9701aa56 100644 --- a/ext/css/settings.css +++ b/ext/css/settings.css @@ -2071,10 +2071,16 @@ button.hotkey-list-item-enabled-button[data-scope-count='0'] {  .hotkey-list-item-enabled-button-label>.checkbox {      margin-right: 0.5em;  } -.hotkey-list-item-action-argument { +.hotkey-list-item-action-argument-container {      margin-left: 0.375em;      flex: 1 1 auto; -    visibility: hidden; +    display: flex; +    flex-flow: row nowrap; +    align-items: stretch; +    width: var(--input-width-large); +} +.hotkey-argument-label { +    margin-right: 0.25em;  }  .hotkey-list-item-flex-row {      display: flex; diff --git a/ext/data/schemas/options-schema.json b/ext/data/schemas/options-schema.json index c9d9be6d..89e2d361 100644 --- a/ext/data/schemas/options-schema.json +++ b/ext/data/schemas/options-schema.json @@ -988,6 +988,7 @@                                              "type": "object",                                              "required": [                                                  "action", +                                                "argument",                                                  "key",                                                  "modifiers",                                                  "scopes", @@ -998,6 +999,10 @@                                                      "type": "string",                                                      "default": ""                                                  }, +                                                "argument": { +                                                    "type": "string", +                                                    "default": "" +                                                },                                                  "key": {                                                      "type": ["string", "null"],                                                      "default": null @@ -1026,22 +1031,22 @@                                              }                                          },                                          "default": [ -                                            {"action": "close",             "key": "Escape",    "modifiers": [],       "scopes": ["popup"], "enabled": true}, -                                            {"action": "focusSearchBox",    "key": "Escape",    "modifiers": [],       "scopes": ["search"], "enabled": true}, -                                            {"action": "previousEntry3",    "key": "PageUp",    "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "nextEntry3",        "key": "PageDown",  "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "lastEntry",         "key": "End",       "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "firstEntry",        "key": "Home",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "previousEntry",     "key": "ArrowUp",   "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "nextEntry",         "key": "ArrowDown", "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "historyBackward",   "key": "KeyB",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "historyForward",    "key": "KeyF",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "addNoteKanji",      "key": "KeyK",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "addNoteTermKanji",  "key": "KeyE",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "addNoteTermKana",   "key": "KeyR",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "playAudio",         "key": "KeyP",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "viewNote",          "key": "KeyV",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, -                                            {"action": "copyHostSelection", "key": "KeyC",      "modifiers": ["ctrl"], "scopes": ["popup"], "enabled": true} +                                            {"action": "close",             "argument": "",  "key": "Escape",    "modifiers": [],       "scopes": ["popup"], "enabled": true}, +                                            {"action": "focusSearchBox",    "argument": "",  "key": "Escape",    "modifiers": [],       "scopes": ["search"], "enabled": true}, +                                            {"action": "previousEntry",     "argument": "3", "key": "PageUp",    "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "nextEntry",         "argument": "3", "key": "PageDown",  "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "lastEntry",         "argument": "",  "key": "End",       "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "firstEntry",        "argument": "",  "key": "Home",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "previousEntry",     "argument": "1", "key": "ArrowUp",   "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "nextEntry",         "argument": "1", "key": "ArrowDown", "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "historyBackward",   "argument": "",  "key": "KeyB",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "historyForward",    "argument": "",  "key": "KeyF",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "addNoteKanji",      "argument": "",  "key": "KeyK",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "addNoteTermKanji",  "argument": "",  "key": "KeyE",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "addNoteTermKana",   "argument": "",  "key": "KeyR",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "playAudio",         "argument": "",  "key": "KeyP",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "viewNote",          "argument": "",  "key": "KeyV",      "modifiers": ["alt"],  "scopes": ["popup", "search"], "enabled": true}, +                                            {"action": "copyHostSelection", "argument": "",  "key": "KeyC",      "modifiers": ["ctrl"], "scopes": ["popup"], "enabled": true}                                          ]                                      }                                  } diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index fd62a558..5b2e2bd3 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -754,8 +754,32 @@ class OptionsUtil {          // Version 10 changes:          //  Removed global option useSettingsV2.          //  Added part-of-speech field template. +        //  Added an argument to hotkey inputs.          await this._applyAnkiFieldTemplatesPatch(options, '/data/templates/anki-field-templates-upgrade-v10.handlebars');          delete options.global.useSettingsV2; +        for (const profile of options.profiles) { +            for (const hotkey of profile.options.inputs.hotkeys) { +                switch (hotkey.action) { +                    case 'previousEntry': +                        hotkey.argument = '1'; +                        break; +                    case 'previousEntry3': +                        hotkey.action = 'previousEntry'; +                        hotkey.argument = '3'; +                        break; +                    case 'nextEntry': +                        hotkey.argument = '1'; +                        break; +                    case 'nextEntry3': +                        hotkey.action = 'nextEntry'; +                        hotkey.argument = '3'; +                        break; +                    default: +                        hotkey.argument = ''; +                        break; +                } +            } +        }          return options;      }  } diff --git a/ext/js/display/display.js b/ext/js/display/display.js index 7cc3b437..b0805aca 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -115,10 +115,8 @@ class Display extends EventDispatcher {          this._hotkeyHandler.registerActions([              ['close',             () => { this._onHotkeyClose(); }], -            ['nextEntry',         () => { this._focusEntry(this._index + 1, true); }], -            ['nextEntry3',        () => { this._focusEntry(this._index + 3, true); }], -            ['previousEntry',     () => { this._focusEntry(this._index - 1, true); }], -            ['previousEntry3',    () => { this._focusEntry(this._index - 3, true); }], +            ['nextEntry',         this._onHotkeyActionMoveRelative.bind(this, 1)], +            ['previousEntry',     this._onHotkeyActionMoveRelative.bind(this, -1)],              ['lastEntry',         () => { this._focusEntry(this._definitions.length - 1, true); }],              ['firstEntry',        () => { this._focusEntry(0, true); }],              ['historyBackward',   () => { this._sourceTermView(); }], @@ -1825,6 +1823,13 @@ class Display extends EventDispatcher {          this.close();      } +    _onHotkeyActionMoveRelative(sign, argument) { +        let count = Number.parseInt(argument, 10); +        if (!Number.isFinite(count)) { count = 1; } +        count = Math.max(0, Math.floor(count)); +        this._focusEntry(this._index + count * sign, true); +    } +      _closeAllPopupMenus() {          for (const popupMenu of PopupMenu.openMenus) {              popupMenu.close(); diff --git a/ext/js/dom/dom-data-binder.js b/ext/js/dom/dom-data-binder.js index 292b2f67..65e91233 100644 --- a/ext/js/dom/dom-data-binder.js +++ b/ext/js/dom/dom-data-binder.js @@ -210,7 +210,7 @@ class DOMDataBinder {      static convertToNumber(value, constraints) {          value = parseFloat(value); -        if (!Number.isFinite(value)) { return 0; } +        if (!Number.isFinite(value)) { value = 0; }          let {min, max, step} = constraints;          min = DOMDataBinder.convertToNumberOrNull(min); diff --git a/ext/js/input/hotkey-handler.js b/ext/js/input/hotkey-handler.js index d3b82d48..8388302d 100644 --- a/ext/js/input/hotkey-handler.js +++ b/ext/js/input/hotkey-handler.js @@ -166,12 +166,12 @@ class HotkeyHandler extends EventDispatcher {      }      _invokeHandlers(modifiers, hotkeyInfo) { -        for (const {modifiers: handlerModifiers, action} of hotkeyInfo.handlers) { +        for (const {modifiers: handlerModifiers, action, argument} of hotkeyInfo.handlers) {              if (!this._areSame(handlerModifiers, modifiers)) { continue; }              const actionHandler = this._actions.get(action);              if (typeof actionHandler !== 'undefined') { -                const result = actionHandler(); +                const result = actionHandler(argument);                  if (result !== false) {                      return true;                  } @@ -196,7 +196,7 @@ class HotkeyHandler extends EventDispatcher {          this._hotkeys.clear();          for (const [scope, registrations] of this._hotkeyRegistrations.entries()) { -            for (const {action, key, modifiers, scopes, enabled} of registrations) { +            for (const {action, argument, key, modifiers, scopes, enabled} of registrations) {                  if (!(enabled && key !== null && action !== '' && scopes.includes(scope))) { continue; }                  let hotkeyInfo = this._hotkeys.get(key); @@ -205,7 +205,7 @@ class HotkeyHandler extends EventDispatcher {                      this._hotkeys.set(key, hotkeyInfo);                  } -                hotkeyInfo.handlers.push({modifiers: new Set(modifiers), action}); +                hotkeyInfo.handlers.push({modifiers: new Set(modifiers), action, argument});              }          }          this._updateEventHandlers(); diff --git a/ext/js/pages/settings/keyboard-shortcuts-controller.js b/ext/js/pages/settings/keyboard-shortcuts-controller.js index 514928d7..2f636541 100644 --- a/ext/js/pages/settings/keyboard-shortcuts-controller.js +++ b/ext/js/pages/settings/keyboard-shortcuts-controller.js @@ -16,6 +16,7 @@   */  /* global + * DOMDataBinder   * KeyboardMouseInputField   */ @@ -30,6 +31,26 @@ class KeyboardShortcutController {          this._emptyIndicator = null;          this._stringComparer = new Intl.Collator('en-US'); // Invariant locale          this._scrollContainer = null; +        this._actionDetails = new Map([ +            ['',                                 {scopes: new Set()}], +            ['close',                            {scopes: new Set(['popup', 'search'])}], +            ['focusSearchBox',                   {scopes: new Set(['search'])}], +            ['nextEntry',                        {scopes: new Set(['popup', 'search']), argument: {template: 'hotkey-argument-move-offset', default: '1'}}], +            ['previousEntry',                    {scopes: new Set(['popup', 'search']), argument: {template: 'hotkey-argument-move-offset', default: '1'}}], +            ['lastEntry',                        {scopes: new Set(['popup', 'search'])}], +            ['firstEntry',                       {scopes: new Set(['popup', 'search'])}], +            ['nextEntryDifferentDictionary',     {scopes: new Set(['popup', 'search'])}], +            ['previousEntryDifferentDictionary', {scopes: new Set(['popup', 'search'])}], +            ['historyBackward',                  {scopes: new Set(['popup', 'search'])}], +            ['historyForward',                   {scopes: new Set(['popup', 'search'])}], +            ['addNoteKanji',                     {scopes: new Set(['popup', 'search'])}], +            ['addNoteTermKanji',                 {scopes: new Set(['popup', 'search'])}], +            ['addNoteTermKana',                  {scopes: new Set(['popup', 'search'])}], +            ['viewNote',                         {scopes: new Set(['popup', 'search'])}], +            ['playAudio',                        {scopes: new Set(['popup', 'search'])}], +            ['copyHostSelection',                {scopes: new Set(['popup'])}], +            ['scanSelectedText',                 {scopes: new Set(['web'])}] +        ]);      }      get settingsController() { @@ -96,6 +117,10 @@ class KeyboardShortcutController {          return defaultOptions.profiles[0].options.inputs.hotkeys;      } +    getActionDetails(action) { +        return this._actionDetails.get(action); +    } +      // Private      _onOptionsChanged({options}) { @@ -134,6 +159,7 @@ class KeyboardShortcutController {      async _addNewEntry() {          const newEntry = {              action: '', +            argument: '',              key: null,              modifiers: [],              scopes: ['popup', 'search'], @@ -169,6 +195,9 @@ class KeyboardShortcutHotkeyEntry {          this._enabledButton = null;          this._scopeMenu = null;          this._scopeMenuEventListeners = new EventListenerCollection(); +        this._argumentContainer = null; +        this._argumentInput = null; +        this._argumentEventListeners = new EventListenerCollection();      }      prepare() { @@ -183,6 +212,7 @@ class KeyboardShortcutHotkeyEntry {          this._actionSelect = action;          this._enabledButton = enabledButton; +        this._argumentContainer = node.querySelector('.hotkey-list-item-action-argument-container');          this._inputField = new KeyboardMouseInputField(input, null, this._os);          this._inputField.prepare(this._data.key, this._data.modifiers, false, true); @@ -193,6 +223,7 @@ class KeyboardShortcutHotkeyEntry {          enabledToggle.dataset.setting = `${this._basePath}.enabled`;          this._updateScopesButton(); +        this._updateActionArgument();          this._eventListeners.addEventListener(scopesButton, 'menuOpen', this._onScopesMenuOpen.bind(this));          this._eventListeners.addEventListener(scopesButton, 'menuClose', this._onScopesMenuClose.bind(this)); @@ -205,6 +236,7 @@ class KeyboardShortcutHotkeyEntry {          this._eventListeners.removeAllEventListeners();          this._inputField.cleanup();          this._clearScopeMenu(); +        this._clearArgumentEventListeners();          if (this._node.parentNode !== null) {              this._node.parentNode.removeChild(this._node);          } @@ -228,6 +260,11 @@ class KeyboardShortcutHotkeyEntry {      _onScopesMenuOpen(e) {          const {menu} = e.detail; +        const validScopes = this._getValidScopesForAction(this._data.action); +        if (validScopes.size === 0) { +            menu.close(); +            return; +        }          this._scopeMenu = menu;          this._updateScopeMenuItems(menu);          this._updateDisplay(menu.containerNode); // Fix a animation issue due to changing checkbox values @@ -260,6 +297,21 @@ class KeyboardShortcutHotkeyEntry {          this._setAction(value);      } +    _onArgumentValueChange(template, e) { +        const node = e.currentTarget; +        const value = this._getArgumentInputValue(node); +        let newValue = value; +        switch (template) { +            case 'hotkey-argument-move-offset': +                newValue = `${DOMDataBinder.convertToNumber(value, node)}`; +                break; +        } +        if (value !== newValue) { +            this._setArgumentInputValue(node, newValue); +        } +        this._setArgument(newValue); +    } +      async _delete() {          this._parent.deleteEntry(this._index);      } @@ -294,13 +346,13 @@ class KeyboardShortcutHotkeyEntry {              scopes.splice(index, 1);          } -        this._updateScopesButton(); -          await this._modifyProfileSettings([{              action: 'set',              path: `${this._basePath}.scopes`,              value: scopes          }]); + +        this._updateScopesButton();      }      async _modifyProfileSettings(targets) { @@ -326,58 +378,81 @@ class KeyboardShortcutHotkeyEntry {      }      async _setAction(value) { -        const targets = [{ -            action: 'set', -            path: `${this._basePath}.action`, -            value -        }]; +        const validScopesOld = this._getValidScopesForAction(this._data.action); + +        const scopes = this._data.scopes; + +        let details = this._parent.getActionDetails(value); +        if (typeof details === 'undefined') { details = {}; } + +        let validScopes = details.scopes; +        if (typeof validScopes === 'undefined') { validScopes = new Set(); } + +        const {argument: argumentDetails} = details; +        let defaultArgument = typeof argumentDetails !== 'undefined' ? argumentDetails.default : ''; +        if (typeof defaultArgument !== 'string') { defaultArgument = ''; }          this._data.action = value; +        this._data.argument = defaultArgument; -        const scopes = this._data.scopes; -        const validScopes = this._getValidScopesForAction(value); -        if (validScopes !== null) { -            let changed = false; +        let scopesChanged = false; +        if ((validScopesOld !== null ? validScopesOld.size : 0) === scopes.length) { +            scopes.length = 0; +            scopesChanged = true; +        } else {              for (let i = 0, ii = scopes.length; i < ii; ++i) {                  if (!validScopes.has(scopes[i])) {                      scopes.splice(i, 1);                      --i;                      --ii; -                    changed = true; +                    scopesChanged = true;                  }              } -            if (changed) { -                if (scopes.length === 0) { -                    scopes.push(...validScopes); -                } -                targets.push({ -                    action: 'set', -                    path: `${this._basePath}.scopes`, -                    value: scopes -                }); -                this._updateCheckboxStates(); -            } +        } +        if (scopesChanged && scopes.length === 0) { +            scopes.push(...validScopes);          } -        await this._modifyProfileSettings(targets); +        await this._modifyProfileSettings([ +            { +                action: 'set', +                path: `${this._basePath}.action`, +                value: this._data.action +            }, +            { +                action: 'set', +                path: `${this._basePath}.argument`, +                value: this._data.argument +            }, +            { +                action: 'set', +                path: `${this._basePath}.scopes`, +                value: this._data.scopes +            } +        ]); -        this._updateCheckboxVisibility(); +        this._updateScopesButton(); +        this._updateScopesMenu(); +        this._updateActionArgument();      } -    _updateCheckboxStates() { -        if (this._scopeMenu === null) { return; } -        this._updateScopeMenuItems(this._scopeMenu); +    async _setArgument(value) { +        this._data.argument = value; +        await this._modifyProfileSettings([{ +            action: 'set', +            path: `${this._basePath}.argument`, +            value +        }]);      } -    _updateCheckboxVisibility() { +    _updateScopesMenu() {          if (this._scopeMenu === null) { return; }          this._updateScopeMenuItems(this._scopeMenu);      }      _getValidScopesForAction(action) { -        const optionNode = this._actionSelect.querySelector(`option[value="${action}"]`); -        const scopesString = (optionNode !== null ? optionNode.dataset.scopes : void 0); -        return (typeof scopesString === 'string' ? new Set(scopesString.split(' ')) : null); +        const details = this._parent.getActionDetails(action); +        return typeof details !== 'undefined' ? details.scopes : null;      }      _updateScopeMenuItems(menu) { @@ -419,4 +494,39 @@ class KeyboardShortcutHotkeyEntry {          getComputedStyle(node).getPropertyValue('display');          style.display = display;      } + +    _updateActionArgument() { +        this._clearArgumentEventListeners(); + +        const {action, argument} = this._data; +        const details = this._parent.getActionDetails(action); +        const {argument: argumentDetails} = typeof details !== 'undefined' ? details : {}; + +        this._argumentContainer.textContent = ''; +        if (typeof argumentDetails !== 'undefined') { +            const {template} = argumentDetails; +            const node = this._parent.settingsController.instantiateTemplate(template); +            const inputSelector = '.hotkey-argument-input'; +            const inputNode = node.matches(inputSelector) ? node : node.querySelector(inputSelector); +            if (inputNode !== null) { +                this._setArgumentInputValue(inputNode, argument); +                this._argumentInput = inputNode; +                this._argumentEventListeners.addEventListener(inputNode, 'change', this._onArgumentValueChange.bind(this, template), false); +            } +            this._argumentContainer.appendChild(node); +        } +    } + +    _clearArgumentEventListeners() { +        this._argumentEventListeners.removeAllEventListeners(); +        this._argumentInput = null; +    } + +    _getArgumentInputValue(node) { +        return node.value; +    } + +    _setArgumentInputValue(node, value) { +        node.value = value; +    }  } diff --git a/ext/settings.html b/ext/settings.html index b99cf0c3..12526442 100644 --- a/ext/settings.html +++ b/ext/settings.html @@ -3142,28 +3142,26 @@      <div class="hotkey-list-item-action-label-cell">Action:</div>      <div class="hotkey-list-item-action-cell">          <select class="hotkey-list-item-action"> -            <option value="" data-scopes="popup search">None</option> -            <option value="close" data-scopes="popup search">Close</option> -            <option value="focusSearchBox" data-scopes="search">Focus search box</option> -            <option value="nextEntry" data-scopes="popup search">Go to next entry</option> -            <option value="nextEntry3" data-scopes="popup search">Go to next entry (x3)</option> -            <option value="previousEntry" data-scopes="popup search">Go to previous entry</option> -            <option value="previousEntry3" data-scopes="popup search">Go to previous entry (x3)</option> -            <option value="lastEntry" data-scopes="popup search">Go to last entry</option> -            <option value="firstEntry" data-scopes="popup search">Go to first entry</option> -            <option value="nextEntryDifferentDictionary" data-scopes="popup search">Go to next dictionary</option> -            <option value="previousEntryDifferentDictionary" data-scopes="popup search">Go to previous dictionary</option> -            <option value="historyBackward" data-scopes="popup search">Navigate backward in history</option> -            <option value="historyForward" data-scopes="popup search">Navigate forward in history</option> -            <option value="addNoteKanji" data-scopes="popup search">Add kanji note</option> -            <option value="addNoteTermKanji" data-scopes="popup search">Add term note</option> -            <option value="addNoteTermKana" data-scopes="popup search">Add term note (reading)</option> -            <option value="viewNote" data-scopes="popup search">View note</option> -            <option value="playAudio" data-scopes="popup search">Play audio</option> -            <option value="copyHostSelection" data-scopes="popup">Copy host window selection</option> -            <option value="scanSelectedText" data-scopes="web">Scan selected text</option> +            <option value="">None</option> +            <option value="close">Close</option> +            <option value="focusSearchBox">Focus search box</option> +            <option value="nextEntry">Go to next entry</option> +            <option value="previousEntry">Go to previous entry</option> +            <option value="lastEntry">Go to last entry</option> +            <option value="firstEntry">Go to first entry</option> +            <option value="nextEntryDifferentDictionary">Go to next dictionary</option> +            <option value="previousEntryDifferentDictionary">Go to previous dictionary</option> +            <option value="historyBackward">Navigate backward in history</option> +            <option value="historyForward">Navigate forward in history</option> +            <option value="addNoteKanji">Add kanji note</option> +            <option value="addNoteTermKanji">Add term note</option> +            <option value="addNoteTermKana">Add term note (reading)</option> +            <option value="viewNote">View note</option> +            <option value="playAudio">Play audio</option> +            <option value="copyHostSelection">Copy host window selection</option> +            <option value="scanSelectedText">Scan selected text</option>          </select> -        <input type="text" class="hotkey-list-item-action-argument"> +        <div class="hotkey-list-item-action-argument-container"></div>      </div>  </div></div></template> @@ -3205,6 +3203,11 @@      <button class="popup-menu-item" data-menu-action="resetInput">Reset input</button>  </div></div></div></template> +<template id="hotkey-argument-move-offset-template"><div class="flex-row-nowrap"> +    <span class="hotkey-argument-label">Count:</span> +    <input type="number" step="1" min="1" class="hotkey-argument-input"> +</div></template> +  <!-- Scripts -->  <script src="/lib/jszip.min.js"></script> |