From 815014c91913c34492a446848b253b67df96e91e Mon Sep 17 00:00:00 2001 From: Tony Atkins Date: Thu, 2 Nov 2023 15:21:23 +0100 Subject: [PATCH 1/4] NOGH: Added exception for prefered switch case style. --- .eslintrc.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index 6cbeee5..cec9abc 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,5 +2,12 @@ "env": { "browser": true }, - "extends": "eslint-config-fluid" + "extends": "eslint-config-fluid", + "rules": { + "indent": [ + "error", + 4, + { "SwitchCase": 1} + ] + } } From 5e314b8fd2c22037310928524bbea37507fd609f Mon Sep 17 00:00:00 2001 From: Tony Atkins Date: Thu, 2 Nov 2023 15:22:37 +0100 Subject: [PATCH 2/4] GH-101: Added action launcher. --- src/css/action-launcher.css | 21 ++ src/css/common.css | 12 + src/css/configuration-panel.css | 9 - src/css/modal.css | 46 ++++ src/html/configuration-panel.html | 2 +- src/js/background.js | 3 + .../configuration-panel.js | 3 +- src/js/content_scripts/action-launcher.js | 236 ++++++++++++++++++ src/js/content_scripts/input-mapper.js | 44 +++- src/js/content_scripts/modal.js | 174 +++++++++++++ src/js/shared/templateRenderer.js | 34 +++ src/manifest.json | 8 + 12 files changed, 579 insertions(+), 13 deletions(-) create mode 100644 src/css/action-launcher.css create mode 100644 src/css/common.css create mode 100644 src/css/modal.css create mode 100644 src/js/content_scripts/action-launcher.js create mode 100644 src/js/content_scripts/modal.js create mode 100644 src/js/shared/templateRenderer.js diff --git a/src/css/action-launcher.css b/src/css/action-launcher.css new file mode 100644 index 0000000..14beb20 --- /dev/null +++ b/src/css/action-launcher.css @@ -0,0 +1,21 @@ +.actionLauncher-actions { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin: 1rem; +} + +.actionLauncher-actions-action { + font-size: 1.25rem; + padding: 0.75rem; +} + +.actionLauncher-actions-action:hover { + outline: aquamarine; +} + +.actionLauncher-actions-action:focus { + background-color: aquamarine; + font-size: 1.4rem; + outline: none; +} diff --git a/src/css/common.css b/src/css/common.css new file mode 100644 index 0000000..a052358 --- /dev/null +++ b/src/css/common.css @@ -0,0 +1,12 @@ +@font-face { + font-family: ubuntu, sans-serif; + src: url("../fonts/Ubuntu-Medium.ttf"); +} + +* { + font-family: ubuntu, sans-serif; +} + +.hidden { + display: none; +} diff --git a/src/css/configuration-panel.css b/src/css/configuration-panel.css index fc1ab50..ea54c81 100644 --- a/src/css/configuration-panel.css +++ b/src/css/configuration-panel.css @@ -1,12 +1,3 @@ -@font-face { - font-family: ubuntu, sans-serif; - src: url("../fonts/Ubuntu-Medium.ttf"); -} - -* { - font-family: ubuntu, sans-serif; -} - :root { --width: 465px; } diff --git a/src/css/modal.css b/src/css/modal.css new file mode 100644 index 0000000..d516aeb --- /dev/null +++ b/src/css/modal.css @@ -0,0 +1,46 @@ +.modal-outer-container { + align-content: center; + background-color: #000c; + height: 100%; + left: 0; + position: fixed; + top: 0; + width: 100%; + z-index: 998; +} + +.modal-inner-container { + background-color: white; + border-radius: 1rem; + display: flex; + flex-direction: column; + height: 75%; + left: 12.5%; + position: fixed; + top: 12.5%; + width: 75%; + z-index: 997; +} + +.modal-header { + align-self: center; +} + +.modal-header h3 { + font-size: 2rem; + font-weight: bold; +} + +.modal-body { + border: 1px solid #ccc; + overflow-y: scroll; +} + +.modal-footer { + align-self: center; +} + +.modal-footer button { + font-size: 2rem; + margin: 1rem; +} diff --git a/src/html/configuration-panel.html b/src/html/configuration-panel.html index df5c9f1..4395d54 100644 --- a/src/html/configuration-panel.html +++ b/src/html/configuration-panel.html @@ -13,7 +13,7 @@ - + diff --git a/src/js/background.js b/src/js/background.js index 59bef88..91cf67a 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -296,6 +296,9 @@ https://github.com/fluid-lab/gamepad-navigator/blob/master/LICENSE }, reopenTabOrWindow: function () { chrome.sessions.restore(); + }, + openActionLauncher: function () { + gamepad.messageListenerUtils.openActionLauncher(); } }; diff --git a/src/js/configuration_panel/configuration-panel.js b/src/js/configuration_panel/configuration-panel.js index 590d845..8008e0d 100644 --- a/src/js/configuration_panel/configuration-panel.js +++ b/src/js/configuration_panel/configuration-panel.js @@ -98,7 +98,8 @@ https://github.com/fluid-lab/gamepad-navigator/blob/master/LICENSE sendArrowLeft: "Send left arrow to the focused element.", sendArrowRight: "Send right arrow to the focused element.", sendArrowUp: "Send up arrow to the focused element.", - sendArrowDown: "Send down arrow to the focused element." + sendArrowDown: "Send down arrow to the focused element.", + openActionLauncher: "Open Action Launcher" }, axes: { null: "None", diff --git a/src/js/content_scripts/action-launcher.js b/src/js/content_scripts/action-launcher.js new file mode 100644 index 0000000..438d8a9 --- /dev/null +++ b/src/js/content_scripts/action-launcher.js @@ -0,0 +1,236 @@ +/* +Copyright (c) 2023 The Gamepad Navigator Authors +See the AUTHORS.md file at the top-level directory of this distribution and at +https://github.com/fluid-lab/gamepad-navigator/raw/master/AUTHORS.md. + +Licensed under the BSD 3-Clause License. You may not use this file except in +compliance with this License. + +You may obtain a copy of the BSD 3-Clause License at +https://github.com/fluid-lab/gamepad-navigator/blob/master/LICENSE +*/ + +/* global gamepad */ + +(function (fluid) { + "use strict"; + + fluid.defaults("gamepad.actionLauncher", { + gradeNames: ["gamepad.modal"], + model: { + label: "Gamepad Navigator: Launch Action" + }, + components: { + actionsPanel: { + container: "{that}.dom.modalBody", + type: "gamepad.actionLauncher.actionsPanel" + } + } + }); + + fluid.defaults("gamepad.actionLauncher.action", { + gradeNames: ["gamepad.templateRenderer"], + + selectors: { + expanded: ".actionLauncher-actions-action-expanded" + }, + + model: { + value: 1, + oldValue: 0, + speedFactor: 1, + invert: false, + background: false, + homepageURL: "https://www.google.com/", + frequency: 100 + }, + + markup: { + container: "
%description
" + }, + + invokers: { + handleClick: { + funcName: "gamepad.actionLauncher.action.handleClick", + args: ["{that}", "{gamepad.inputMapper}", "{gamepad.actionLauncher}", "{arguments}.0"] // actionLauncher, inputMapper, modalComponent, event + }, + handleKeydown: { + funcName: "gamepad.actionLauncher.action.handleKeydown", + args: ["{that}", "{arguments}.0"] // event + } + }, + + listeners: { + "onCreate.bindClick": { + this: "{that}.container", + method: "click", + args: ["{that}.handleClick"] + }, + "onCreate.bindKeydown": { + this: "{that}.container", + method: "keydown", + args: ["{that}.handleKeydown"] + } + } + }); + + gamepad.actionLauncher.action.handleClick = function (actionComponent, inputMapperComponent, modalComponent, event) { + event.preventDefault(); + + // Close modal and restore previous focus. + modalComponent.closeModal(event); + + var actionFn = fluid.get(inputMapperComponent, actionComponent.model.actionKey); + if (actionFn) { + // Simulate a button press and release so that all actions are + // triggered appropriately. + + // All actions are called with: + // value, speedFactor, invert, background, oldValue, homepageURL + + // Simulate button down + actionFn( + 1, + actionComponent.model.speedFactor, + actionComponent.model.invert, + actionComponent.model.background, + 0, + actionComponent.model.homepageURL + ); + + // Simulate button up after a delay (100ms by default) + setTimeout(function () { + actionFn( + 0, + actionComponent.model.speedFactor, + actionComponent.model.invert, + actionComponent.model.background, + 1, + actionComponent.model.homepageURL + ); + }, actionComponent.model.frequency); + } + }; + + gamepad.actionLauncher.action.handleKeydown = function (that, event) { + if (event.key === " " || event.key === "Enter") { + that.handleClick(event); + } + }; + + fluid.defaults("gamepad.actionLauncher.actionsPanel", { + gradeNames: ["gamepad.templateRenderer"], + + markup: { + container: "
" + }, + + model: { + // TODO: Add support for controlling `backgroundOption` in the `openNewTab` and `openNewWindow` actions. + actionDefs: { + // All button-driven actions, except for the action launcher itself. + click: { + description: "Click" + }, + previousPageInHistory: { + description: "History back button" + }, + nextPageInHistory: { + description: "History next button" + }, + reverseTab: { + description: "Focus on the previous element" + }, + forwardTab: { + description: "Focus on the next element" + }, + scrollLeft: { + description: "Scroll left", + frequency: 250 + }, + scrollRight: { + description: "Scroll right", + frequency: 250 + }, + scrollUp: { + description: "Scroll up", + frequency: 250 + }, + scrollDown: { + description: "Scroll down", + frequency: 250 + }, + goToPreviousTab: { + description: "Switch to the previous browser tab" + }, + goToNextTab: { + description: "Switch to the next browser tab" + }, + closeCurrentTab: { + description: "Close current browser tab" + }, + openNewTab: { + description: "Open a new tab" + }, + closeCurrentWindow: { + description: "Close current browser window" + }, + openNewWindow: { + description: "Open a new browser window" + }, + goToPreviousWindow: { + description: "Switch to the previous browser window" + }, + goToNextWindow: { + description: "Switch to the next browser window" + }, + zoomIn: { + description: "Zoom-in on the active web page" + }, + zoomOut: { + description: "Zoom-out on the active web page" + }, + maximizeWindow: { + description: "Maximize the current browser window" + }, + restoreWindowSize: { + description: "Restore the size of current browser window" + }, + reopenTabOrWindow: { + description: "Re-open the last closed tab or window" + }, + sendArrowLeft: { + description: "Send left arrow to the focused element." + }, + sendArrowRight: { + description: "Send right arrow to the focused element." + }, + sendArrowUp: { + description: "Send up arrow to the focused element." + }, + sendArrowDown: { + description: "Send down arrow to the focused element." + } + // TODO: Add action to open configuration menu, when available. + } + }, + + dynamicComponents: { + action: { + container: "{that}.container", + type: "gamepad.actionLauncher.action", + sources: "{that}.model.actionDefs", + options: { + model: { + actionKey: "{sourcePath}", + description: "{source}.description", + speedFactor: "{source}.speedFactor", + invert: "{source}.invert", + background: "{source}.background", + frequency: "{source}.frequency" + } + } + } + } + }); +})(fluid); diff --git a/src/js/content_scripts/input-mapper.js b/src/js/content_scripts/input-mapper.js index 38f1e5f..397c348 100644 --- a/src/js/content_scripts/input-mapper.js +++ b/src/js/content_scripts/input-mapper.js @@ -18,13 +18,18 @@ https://github.com/fluid-lab/gamepad-navigator/blob/master/LICENSE var gamepad = fluid.registerNamespace("gamepad"); fluid.registerNamespace("gamepad.inputMapper"); + + // TODO: Focus trap in modal, look at Weavly and others. + // TODO: Close on click outside inner container. + // TODO: Close on escape. fluid.defaults("gamepad.inputMapper", { - gradeNames: ["gamepad.inputMapper.base"], + gradeNames: ["gamepad.inputMapper.base", "fluid.viewComponent"], listeners: { "onCreate.restoreFocus": "{that}.restoreFocus", "onCreate.updateControls": "{that}.updateControls" }, invokers: { + // Actions, these are called with: value, speedFactor, invert, background, oldValue, homepageURL restoreFocus: { funcName: "gamepad.inputMapper.restoreFocus", args: ["{that}.options.windowObject", "{that}.tabindexSortFilter"] @@ -92,10 +97,37 @@ https://github.com/fluid-lab/gamepad-navigator/blob/master/LICENSE reopenTabOrWindow: { funcName: "gamepad.inputMapperUtils.background.sendMessage", args: ["{that}", "reopenTabOrWindow", "{arguments}.0", "{arguments}.4"] + }, + openActionLauncher: { + funcName: "gamepad.inputMapper.openActionLauncher", + args: ["{that}", "{arguments}.0", "{arguments}.4"] // value, oldValue + } + }, + model: { + hideActionPanelLauncher: true + }, + components: { + actionLauncher: { + container: "{that}.container", + type: "gamepad.actionLauncher", + options: { + model: { + hidden: "{gamepad.inputMapper}.model.hideActionPanelLauncher", + lastExternalFocused: "{gamepad.inputMapper}.model.lastExternalFocused" + } + } } } }); + gamepad.inputMapper.openActionLauncher = function (that, value, oldValue) { + if (value && !oldValue) { + that.applier.change("lastExternalFocused", document.activeElement); + + that.applier.change("hideActionPanelLauncher", false); + } + }; + /** * * Restore the previously focused element on the web page after history navigation or @@ -177,6 +209,11 @@ https://github.com/fluid-lab/gamepad-navigator/blob/master/LICENSE }; })(window); + // TODO: Make this something the input mapper does itself to decide whether + // it should be active. When not visible, deactivate all modals and stop + // listening to gamepad input. When visible, start listening. See if there + // is some way to keep it alive if the window is visible but the dev tools + // are focused. /** * * Manages the inputMapper instance according to the visibility status of the @@ -204,7 +241,8 @@ https://github.com/fluid-lab/gamepad-navigator/blob/master/LICENSE */ // Pass the configuration options to the inputMapper component. - inputMapperInstance = gamepad.inputMapper(configurationOptions); + // TODO: Reactivate a "dormant" inputMapper instance here. + inputMapperInstance = gamepad.inputMapper("body", configurationOptions); inputMapperInstance.events.onGamepadConnected.fire(); } else if (visibilityStatus === "hidden" && inputMapperInstance !== null) { @@ -212,6 +250,8 @@ https://github.com/fluid-lab/gamepad-navigator/blob/master/LICENSE * Destroy the instance of the inputMapper in the current tab when another * window/tab is focused or opened. */ + // TODO: Manage this better, it makes inspecting HTML really hard. + // TODO: Make the input mapper "dormant". inputMapperInstance.destroy(); } }; diff --git a/src/js/content_scripts/modal.js b/src/js/content_scripts/modal.js new file mode 100644 index 0000000..9a0ec45 --- /dev/null +++ b/src/js/content_scripts/modal.js @@ -0,0 +1,174 @@ +/* +Copyright (c) 2023 The Gamepad Navigator Authors +See the AUTHORS.md file at the top-level directory of this distribution and at +https://github.com/fluid-lab/gamepad-navigator/raw/master/AUTHORS.md. + +Licensed under the BSD 3-Clause License. You may not use this file except in +compliance with this License. + +You may obtain a copy of the BSD 3-Clause License at +https://github.com/fluid-lab/gamepad-navigator/blob/master/LICENSE +*/ + +/* global ally */ +// TODO: Convert to using new-style modelListeners and model relays for +// "close" button. +// https://github.com/fluid-project/infusion/blob/main/tests/framework-tests/core/js/FluidIoCViewTests.js#L320-L423 + +(function (fluid) { + "use strict"; + + var gamepad = fluid.registerNamespace("gamepad"); + + fluid.defaults("gamepad.modal", { + gradeNames: ["gamepad.templateRenderer"], + model: { + hidden: true, + lastExternalFocused: false + }, + modelListeners: { + hidden: [ + { + this: "{that}.container", + method: "toggleClass", + args: ["hidden", "{change}.value"] + }, + { + funcName: "gamepad.modal.wrapFocusOnOpen", + args: ["{that}", "{change}.value"] + } + ] + }, + selectors: { + icon: ".modal-icon", + innerContainer: ".modal-inner-container", + modalBody: ".modal-body", + modalCloseButton: ".modal-close-button", + leadingFocusTrap: ".modal-focus-trap-leading", + trailingFocusTrap: ".modal-focus-trap-trailing" + }, + markup: { + // TODO: Add the ability to retrieve our icon URL and display the icon onscreen. + container: "" + }, + invokers: { + closeModal: { + funcName: "gamepad.modal.closeModal", + args: ["{that}", "{arguments}.0"] // event + }, + handleKeydown: { + funcName: "gamepad.modal.handleKeydown", + args: ["{that}", "{arguments}.0"] // event + + }, + handleOuterContainerClick: { + funcName: "gamepad.modal.handleOuterContainerClick", + args: ["{that}", "{arguments}.0"] // event + }, + focusFirst: { + funcName: "gamepad.modal.wrapFocus", + args: ["{that}"] + }, + focusLast: { + funcName: "gamepad.modal.wrapFocus", + args: ["{that}", true] + } + }, + // TODO: Relay hidden classname to control visibility. + // TODO: clicking outside the inner container should close the modal. + // We can't use this form because we also need to restore focus. + // modelRelay: { + // source: "{that}.model.dom.outerContainer.click", + // target: "{that}.model.hidden", + // singleTransform: "fluid.transforms.toggle" + // }, + listeners: { + "onCreate.bindOuterContainerClick": { + this: "{that}.container", + method: "click", + args: ["{that}.handleOuterContainerClick"] + + }, + "onCreate.bindKeydownFocusTrap": { + this: "{that}.container", + method: "keydown", + args: ["{that}.handleKeydown"] + }, + "onCreate.bindFocusTrapForwardWrap": { + this: "{that}.dom.trailingFocusTrap", + method: "focus", + args: ["{that}.focusFirst"] + }, + "onCreate.bindFocusTrapBackwardWrap": { + this: "{that}.dom.leadingFocusTrap", + method: "focus", + args: ["{that}.focusLast"] + }, + "onCreate.bindCloseButtonClick": { + this: "{that}.dom.modalCloseButton", + method: "click", + args: "{that}.closeModal" + } + } + }); + + gamepad.modal.handleOuterContainerClick = function (that, event) { + var innerContainer = that.locate("innerContainer"); + var targetInsideContainer = innerContainer[0].contains(event.target); + + if (!targetInsideContainer) { + that.closeModal(event); + } + }; + + gamepad.modal.handleKeydown = function (that, event) { + if (event.key === "Escape") { + that.closeModal(event); + } + else { + var innerContainer = that.locate("innerContainer"); + var targetInsideContainer = innerContainer[0].contains(event.target); + if (!targetInsideContainer) { + event.preventDefault(); + } + } + }; + + gamepad.modal.closeModal = function (that, event) { + event.preventDefault(); + that.applier.change("hidden", true); + if (that.model.lastExternalFocused && that.model.lastExternalFocused.focus) { + that.model.lastExternalFocused.focus(); + } + }; + + /** + * + * While our modal is open, tab navigation is limited to elements within + * the modal. This function is used to wrap around when we hit the end of + * "our" elements. + * + * @param {Object} that - The modal component. + * @param {Boolean} reverse - Whether to navigate backwards. + * + */ + gamepad.modal.wrapFocus = function (that, reverse) { + var innerContainer = that.locate("innerContainer"); + + // Search for tabbables, focus on first or last element depending. + // TODO: Repurpose sorting from other areas where we use ally? + var tabbableElements = ally.query.tabbable({ context: innerContainer, strategy: "strict" }); + if (tabbableElements.length) { + var elementIndex = reverse ? tabbableElements.length - 1 : 0; + var elementToFocus = tabbableElements[elementIndex]; + elementToFocus.focus(); + } + }; + + // Focus on the first element on initial open. + gamepad.modal.wrapFocusOnOpen = function (that, hidden) { + if (!hidden) { + that.focusFirst(); + } + }; +})(fluid); diff --git a/src/js/shared/templateRenderer.js b/src/js/shared/templateRenderer.js new file mode 100644 index 0000000..227eaab --- /dev/null +++ b/src/js/shared/templateRenderer.js @@ -0,0 +1,34 @@ +/* +Copyright (c) 2023 The Gamepad Navigator Authors +See the AUTHORS.md file at the top-level directory of this distribution and at +https://github.com/fluid-lab/gamepad-navigator/raw/master/AUTHORS.md. + +Licensed under the BSD 3-Clause License. You may not use this file except in +compliance with this License. + +You may obtain a copy of the BSD 3-Clause License at +https://github.com/fluid-lab/gamepad-navigator/blob/master/LICENSE +*/ +(function (fluid) { + "use strict"; + var gamepad = gamepad || fluid.registerNamespace("gamepad"); + + fluid.defaults("gamepad.templateRenderer", { + gradeNames: ["fluid.containerRenderingView"], + markup: { + container: "
" + }, + model: {}, + invokers: { + renderMarkup: { + funcName: "gamepad.templateRenderer.render", + args: ["{that}", "{that}.options.markup.container", "{that}.model"] + } + } + }); + + gamepad.templateRenderer.render = function (that, markupTemplate, model) { + var renderedContent = fluid.stringTemplate(markupTemplate, model); + return renderedContent; + }; +})(fluid); diff --git a/src/manifest.json b/src/manifest.json index a8853b5..23ee38a 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -14,11 +14,19 @@ "matches": [ "" ], + "css": [ + "css/common.css", + "css/modal.css", + "css/action-launcher.css" + ], "js": [ "js/lib/infusion/infusion-all.js", "js/lib/ally/ally.min.js", "js/content_scripts/gamepad-navigator.js", "js/shared/configuration-maps.js", + "js/shared/templateRenderer.js", + "js/content_scripts/modal.js", + "js/content_scripts/action-launcher.js", "js/content_scripts/input-mapper-content-utils.js", "js/content_scripts/input-mapper-background-utils.js", "js/content_scripts/input-mapper-base.js", From 22f46f7ebb43cd2e6ecd3d683491b983a30cb417 Mon Sep 17 00:00:00 2001 From: Tony Atkins Date: Thu, 2 Nov 2023 15:28:03 +0100 Subject: [PATCH 3/4] Ignored security issues with dev dependencies. --- audit-resolve.json | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/audit-resolve.json b/audit-resolve.json index c0549f6..4b1e95e 100644 --- a/audit-resolve.json +++ b/audit-resolve.json @@ -370,23 +370,51 @@ }, "1094304|fluid-lint-all>eslint-plugin-markdown>remark-parse>trim>markdownlint-config-fluid>markdownlint>markdown-it>stylelint>autoprefixer>postcss": { "decision": "ignore", - "madeAt": 1697531759856 + "madeAt": 1698935247034 }, "1094304|stylelint-config-fluid>stylelint>autoprefixer>postcss": { "decision": "ignore", - "madeAt": 1697531759856 + "madeAt": 1698935247034 }, "1094304|stylelint-order>postcss": { "decision": "ignore", - "madeAt": 1697531759856 + "madeAt": 1698935247034 }, "1094304|stylelint-scss>stylelint>autoprefixer>postcss": { "decision": "ignore", - "madeAt": 1697531759856 + "madeAt": 1698935247034 }, "1094304|sugarss>postcss": { "decision": "ignore", "madeAt": 1697531759856 + }, + "1094446|@babel/traverse": { + "decision": "ignore", + "madeAt": 1698935245343 + }, + "1094304|postcss-less>postcss": { + "decision": "ignore", + "madeAt": 1698935247034 + }, + "1094450|grunt-prompt>lodash": { + "decision": "ignore", + "madeAt": 1698935249521 + }, + "1094493|grunt-prompt>lodash": { + "decision": "ignore", + "madeAt": 1698935251197 + }, + "1094498|grunt-prompt>lodash": { + "decision": "ignore", + "madeAt": 1698935253548 + }, + "1094499|grunt-prompt>lodash": { + "decision": "ignore", + "madeAt": 1698935255621 + }, + "1094500|grunt-prompt>lodash": { + "decision": "ignore", + "madeAt": 1698935257541 } }, "rules": {}, From 47bf55ce6b146fa2a34f0dc653851d745462bf64 Mon Sep 17 00:00:00 2001 From: Tony Atkins Date: Thu, 2 Nov 2023 18:59:37 +0100 Subject: [PATCH 4/4] GH-101: Add action launcher to default config. --- src/js/shared/configuration-maps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/shared/configuration-maps.js b/src/js/shared/configuration-maps.js index 440eeaa..4b8ba8e 100644 --- a/src/js/shared/configuration-maps.js +++ b/src/js/shared/configuration-maps.js @@ -32,7 +32,7 @@ https://github.com/fluid-lab/gamepad-navigator/blob/master/LICENSE // Face Button. // Circle on PlayStation controller & B on Xbox controller. "1": { - defaultAction: null, + defaultAction: "openActionLauncher", currentAction: null, speedFactor: 1, background: false