🏠 Home 

ultra-hotkeys

hotkeys for various sites


Install this script?
  1. // ==UserScript==
  2. // @name ultra-hotkeys
  3. // @namespace Violentmonkey Scripts
  4. // @include https://www.messenger.com/*
  5. // @include https://app.standardnotes.com*
  6. // @include https://anilist.co/*
  7. // @include https://genius.com/*
  8. // @include https://web.whatsapp.com*
  9. // @include https://*pinterest.com*
  10. // @include https://hexdocs.pm/*
  11. // @include https://pipx.pypa.io/*
  12. // @version 3.3
  13. // @author KraXen72
  14. // @description hotkeys for various sites
  15. // @license MIT
  16. // ==/UserScript==
  17. // general hotkey system based on: https://github.com/KraXen72/fabric-moodboard/blob/master/src/ui-toolbar.ts
  18. // messenger hotkeys loosly based on: https://github.com/guoguo12/messenger-shortcuts by Allen Guo
  19. const hotkeyController = new AbortController()
  20. const { signal } = hotkeyController
  21. // message input auto-focuses after switching a chat, so it's ok
  22. const registeredHotkeys = []
  23. const debug = true
  24. /** Helper functions **/
  25. function click(targetNode) { fireMouseEvent(targetNode, 'click'); }
  26. // Modified from http://stackoverflow.com/a/2706236
  27. function fireMouseEvent(targetNode, event, opts = {}) {
  28. document.activeElement.blur()
  29. const eventObj = new Event(event, Object.assign({ bubbles: true, cancelable: false }, opts))
  30. targetNode.dispatchEvent(eventObj);
  31. }
  32. function fireKeybEvent(targetNode, event, opts = {}) {
  33. document.activeElement.blur()
  34. const eventObj = new KeyboardEvent(event, Object.assign({ bubbles: true, cancelable: false }, opts))
  35. if (debug) console.log(event, eventObj, targetNode)
  36. targetNode.dispatchEvent(eventObj);
  37. eventObj.preventDefault()
  38. }
  39. // sometimes you might need to pass custom code/which, check https://keycode.info
  40. function fireKeybEventH(targetNode, key, opts = {}) {
  41. const _key = opts?.shiftKey ?? false ? key.toUpperCase() : key.toLowerCase()
  42. const which = opts?.which ?? key.toUpperCase().charCodeAt(0)
  43. const newOpts = Object.assign({
  44. key: _key,
  45. keyCode: `Key${key.toUpperCase()}`,
  46. which,
  47. code: opts?.code ?? which
  48. }, opts)
  49. fireKeybEvent(targetNode, 'keydown', newOpts)
  50. }
  51. // fireKeybEvent(document.body, 'keydown', { altKey: true, key: 'k', code: 'KeyK', which: 75 })
  52. // click(qs(`#side ._1EUay > button[tabindex]`))
  53. document.addEventListener('keydown', (ev) => {
  54. for (let i = 0; i < registeredHotkeys.length; i++) {
  55. const hotkey = registeredHotkeys[i];
  56. const executeHotkey = () => {
  57. if (debug) console.log("running hotkey", hotkey.code, hotkey.constraints)
  58. if (hotkey?.constraints?.preventDefault === true ?? false) {
  59. ev.preventDefault()
  60. }
  61. hotkey.callback()
  62. }
  63. if (ev.code.toLowerCase() === hotkey.code.toLowerCase()) {
  64. if (typeof hotkey.constraints !== "undefined" && hotkey.constraints) {
  65. if (hotkey.constraints.exclusive) {
  66. if (![ev.ctrlKey, ev.shiftKey, ev.altKey].includes(true)) executeHotkey()
  67. } else {
  68. let valid = true
  69. if (hotkey.constraints.ctrlKey && hotkey.constraints.ctrlKey !== ev.ctrlKey) valid = false
  70. if (hotkey.constraints.altKey && hotkey.constraints.altKey !== ev.altKey) valid = false
  71. if (hotkey.constraints.shiftKey && hotkey.constraints.shiftKey !== ev.shiftKey) valid = false
  72. if (valid) executeHotkey()
  73. }
  74. } else {
  75. executeHotkey()
  76. }
  77. }
  78. }
  79. }, { signal })
  80. function registerHotkey(keycode, callback, constraints) {
  81. const htk = { code: keycode, callback }
  82. if (typeof constraints !== "undefined" && constraints) htk.constraints = constraints
  83. registeredHotkeys.push(htk)
  84. }
  85. // page actions
  86. switch (window.location.origin) {
  87. case "https://app.standardnotes.com":
  88. registerHotkey('keyk', standardnotesFocusSearch, { ctrlKey: true })
  89. registerHotkey('semicolon', () => document.getElementById("note-text-editor").focus(), { ctrlKey: true })
  90. registerHotkey('f1', standardnotesToggleFocusMode, { ctrlKey: true, preventDefault: true })
  91. break;
  92. case "https://www.messenger.com":
  93. registerHotkey('keyk', () => {document.querySelector(`input[type="search"]`).focus()}, { ctrlKey: true })
  94. registerHotkey('semicolon', () => click(document.querySelector(`div[contenteditable="true"][role="textbox"][tabindex="0"]`)), { ctrlKey: true })
  95. break;
  96. case "https://web.whatsapp.com":
  97. registerHotkey('keyk', () => fireKeybEventH(document.body, 'k', { altKey: true }), { ctrlKey: true })
  98. break;
  99. case "https://anilist.co":
  100. registerHotkey('keyk', () => click(document.querySelector('#nav div.search')), { ctrlKey: true })
  101. break;
  102. case "https://genius.com":
  103. registerHotkey('keyk', () => document.querySelector(`input[name="q"]`).focus(), { ctrlKey: true })
  104. break;
  105. case "https://www.pinterest.com":
  106. registerHotkey("keyk", () => document.querySelector("#searchBoxContainer input[type=text]").focus(), { ctrlKey: true })
  107. break;
  108. case "https://pipx.pypa.io":
  109. registerHotkey("keyk", () => document.querySelector("input.md-search__input").focus(), { ctrlKey: true })
  110. break;
  111. case "https://hexdocs.pm":
  112. registerHotkey("keyk", () => document.getElementById("search-input").focus(), { ctrlKey: true });
  113. registerHotkey("escape", () => {
  114. if (document.activeElement === document.getElementById("search-input")) gleamEscapeSearch()
  115. });
  116. break;
  117. default:
  118. console.log(`no hotkeys defined for ${window.location.origin}, but the script includes it's url`)
  119. break;
  120. }
  121. // site specific helpers
  122. function standardnotesToggleFocusMode() {
  123. fireKeybEventH(document.body, 'f', { ctrlKey: true, shiftKey: true })
  124. }
  125. function gleamEscapeSearch() {
  126. document.body.focus()
  127. document.documentElement.classList.remove("search-active");
  128. }
  129. function standardnotesFocusSearch() {
  130. if (document.body.classList.contains("focus-mode")) standardnotesToggleFocusMode()
  131. document.querySelector(`input#search-bar`).focus();
  132. }