{"id":16711778,"url":"https://github.com/bburdette/windowkeys","last_synced_at":"2025-04-05T04:42:40.440Z","repository":{"id":57674831,"uuid":"412519379","full_name":"bburdette/windowkeys","owner":"bburdette","description":"subscribe to window keypress events, specifying preventDefault as needed.","archived":false,"fork":false,"pushed_at":"2023-04-15T19:08:45.000Z","size":26,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-02-10T12:43:08.621Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Elm","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bburdette.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2021-10-01T15:23:50.000Z","updated_at":"2022-06-02T08:37:02.000Z","dependencies_parsed_at":"2023-02-19T01:01:51.869Z","dependency_job_id":null,"html_url":"https://github.com/bburdette/windowkeys","commit_stats":{"total_commits":19,"total_committers":3,"mean_commits":6.333333333333333,"dds":0.368421052631579,"last_synced_commit":"113e50f6ae9b3aff9f729cf7f4e87aeae920a060"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bburdette%2Fwindowkeys","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bburdette%2Fwindowkeys/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bburdette%2Fwindowkeys/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bburdette%2Fwindowkeys/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bburdette","download_url":"https://codeload.github.com/bburdette/windowkeys/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247289394,"owners_count":20914464,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-12T20:26:28.814Z","updated_at":"2025-04-05T04:42:40.398Z","avatar_url":"https://github.com/bburdette.png","language":"Elm","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WindowKeys\n\nThis [elm package](https://package.elm-lang.org/packages/bburdette/windowkeys/latest/) provides a way to subscribe to keydown events for certain keys, at the window level of the DOM.\nI wrote it to have a way to get reliable keydown events for shortcut key combinations, like \"ctrl-s\".\n\nIts pretty similar to what you get with Browser.onKeyPress, except that you only get events for keys you've\nspecified.  Also - key point - you can tell if to 'preventDefault' on those keydown events too,\nsomething that Browser.onKeyPress does not provide.\n\nYou can do something similar to this by adding a key event listener to your topmost html div element.  But sometimes\nthat element loses focus, and then you don't get your shortcut events anymore.\n\nBetter to subscribe to key events at the 'window' level.\n\n### Ellie\n\nHere's an [ellie](https://ellie-app.com/mqMNrkBNnVBa1) demonstrating the lib.  Same code as in /example in the repo.\n\n### Code example:\n\nI want to get a keydown event for the Enter key, and for ctrl-s.  For Enter, I want to allow normal Enter key events\ntoo; but for ctrl-s I want to prevent the normal browser event, which prompts the user to save the current page to a file.\n\nTo do that I issue this Cmd from my `init` function:\n\n\n    skcommand \u003c|\n        WindowKeys.SetWindowKeys\n            [ { key = \"s\"\n              , ctrl = True\n              , alt = False\n              , shift = False\n              , preventDefault = True }\n            , { key = \"Enter\"\n              , ctrl = False\n              , alt = False\n              , shift = False\n              , preventDefault = False }\n            ]\n\nSo one key has preventDefault specified, and the other not.\n\nThis - along with some port wrangling - will cause me to get `WkMsg WindowKeys.Key` messages delivered\nto my `update` function, and from there I can route them to the current page/dialog/whatever.\n\n### Elm Setup\n\nI declare this port and make a 'keyreceive' Sub to go in my subscriptions.\n\n    port receiveKeyMsg : (JD.Value -\u003e msg) -\u003e Sub msg\n\n    keyreceive =\n        receiveKeyMsg \u003c| WindowKeys.receive WkMsg\n\nI'll also need this port for sending WindowKeys commands - pretty much just SetWindowKeys for now.  `skcommand`\nis just a convenience function, see it in action in the usage example above.\n\n    port sendKeyCommand : JE.Value -\u003e Cmd msg\n\n    skcommand =\n        WindowKeys.send sendKeyCommand\n\nAnd finally in my Msg type I add the WkMsg:\n\n    type Msg\n        = WkMsg (Result JD.Error WindowKeys.Key)\n        | \u003cmore message declarations\u003e\n\n### Javascript Setup\n\nThere are two js functions needed - one recieves the list of keys from elm and stores them in the `windowkeys` var.\n\nThe other function is a keyboard event handler you attach to `window`.  It checks `windowkeys` and sends\nevents to Elm.\n\nBoth functions are defined in the following script, which is in file form [here](https://github.com/bburdette/windowkeys/blob/f54ea442a148956311fc44bfeeb8ba0a97e223d6/windowkey.js).\n\n\n    \u003cscript\u003e\n        // this will contain the keys we're monitoring.\n        let windowkeys = {};\n\n        // wire up your sendKeyCommand port in elm to this function:\n        //    app.ports.sendKeyCommand.subscribe(sendKeyCommand);\n        function sendKeyCommand( kc ) {\n          // console.log(\"sendKeyCommand\", kc);\n\n          if (kc.cmd == \"SetWindowKeys\") {\n            windowkeys = {};\n            for (let i = 0; i \u003c kc.keys.length; i++) {\n              k = kc.keys[i];\n              if (!windowkeys[k.key]) {\n                windowkeys[k.key] = {};\n              }\n              if (!windowkeys[k.key][k.ctrl]) {\n                windowkeys[k.key][k.ctrl] = {};\n              }\n              if (!windowkeys[k.key][k.ctrl][k.alt]) {\n                windowkeys[k.key][k.ctrl][k.alt] = {};\n              }\n              if (!windowkeys[k.key][k.ctrl][k.alt][k.shift]) {\n                windowkeys[k.key][k.ctrl][k.alt][k.shift] = {};\n              }\n              windowkeys[k.key] [k.ctrl] [k.alt] [k.shift] = k.preventDefault\n            }   \n          }\n        }\n\n        // add this line after your elm app init.\n        // window.addEventListener( \"keydown\", keycheck, false );\n        function keycheck(e) {\n          try {\n            let pd = windowkeys[e.key][e.ctrlKey][e.altKey][e.shiftKey];\n            if (pd) {\n              e.preventDefault();\n            }\n            else {\n              // make false for cases like pd == null, undefined, etc.\n              pd = false;\n            }\n            // console.log(\"key found: \", e.key, \" preventdefault: \", pd);\n            app.ports.receiveKeyMsg.send({ key : e.key\n                                         , ctrl : e.ctrlKey\n                                         , alt : e.altKey\n                                         , shift : e.shiftKey\n                                         , preventDefault : pd});\n          } catch (error)\n          {\n           // console.log(\"key not found: \", e.key);\n          }\n        }\n    \u003c/script\u003e\n\nPut the above in your index.html or in a js file for import if you like (without the \u003cscript\u003e tags).\n\nThen after your elm app initialization, you'll need to add a port subscription and an event listener, like so:\n\n    \u003cscript\u003e\n      var app = Elm.Main.init( { node: document.getElementById(\"elm\") });\n      // Add these two lines!\n      app.ports.sendKeyCommand.subscribe(sendKeyCommand);\n      window.addEventListener( \"keydown\", keycheck, false );\n    \u003c/script\u003e\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbburdette%2Fwindowkeys","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbburdette%2Fwindowkeys","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbburdette%2Fwindowkeys/lists"}