-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add chord/sequence support to mapper tool #91
Changes from all commits
0977134
02822d7
45ded83
7b36f39
9373215
76ae61b
0ec75e4
b2aa588
6db787b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
name: Publish Pages site | ||
|
||
on: | ||
push: | ||
branches: | ||
- master | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- uses: actions/setup-node@v3 | ||
with: | ||
node-version: 12.x | ||
|
||
- run: npm install | ||
|
||
- run: npm run build | ||
|
||
- name: Copy files | ||
run: | | ||
mkdir _site | ||
cp -r pages _site | ||
cp -r dist _site | ||
|
||
- name: Fix permissions | ||
run: | | ||
chmod -c -R +rX "_site/" | while read line; do | ||
echo "::warning title=Invalid file permissions automatically fixed::$line" | ||
done | ||
|
||
- uses: actions/upload-pages-artifact@v2 | ||
|
||
deploy: | ||
needs: build | ||
|
||
permissions: | ||
pages: write | ||
id-token: write | ||
|
||
environment: | ||
name: github-pages | ||
url: ${{ steps.deployment.outputs.page_url }} | ||
|
||
runs-on: ubuntu-latest | ||
steps: | ||
- id: deployment | ||
uses: actions/deploy-pages@v2 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
node_modules/ | ||
dist/ | ||
_site/ |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
|
||
<head> | ||
<meta charset="utf-8"> | ||
<meta name="viewport" content="width=device-width"> | ||
<title>hotkey | Mapper Tool</title> | ||
<link href="https://unpkg.com/@primer/css@^21.0.8/dist/primer.css" rel="stylesheet" /> | ||
<script type="module" src="https://unpkg.com/@github/clipboard-copy-element@latest?module"></script> | ||
</head> | ||
|
||
<body> | ||
<div class="mx-auto my-3 col-12 col-md-8 col-lg-6"> | ||
<h1 id="app-name">Hotkey Code</h1> | ||
<p id="hint">Press a key combination to see the corresponding hotkey string. Quickly press another combination to build a sequence.</p> | ||
<div class="position-relative"> | ||
<input | ||
readonly | ||
role="application" | ||
aria-roledescription="Input Capture" | ||
autofocus | ||
aria-labelledby="app-name" | ||
aria-describedby="hint sequence-hint" | ||
aria-live="assertive" | ||
aria-atomic="true" | ||
id="hotkey-code" | ||
class="border rounded-2 mt-2 p-6 f1 text-mono" | ||
style="width: 100%" | ||
/> | ||
|
||
<div class="position-absolute bottom-2 left-3 right-3 d-flex" style="align-items: center; gap: 8px"> | ||
<!-- This indicates that the input is listening for a sequence press. Ideally we'd have a way to tell screen | ||
readers this too, but if we make this live and add more text it will get annoying because it will conflict | ||
with the already-live input above. --> | ||
<p id="sequence-status" class="color-fg-subtle" style="margin: 0" aria-hidden hidden>→</p> | ||
|
||
<span style="flex: 1"></span> | ||
|
||
<button id="reset-button" class="btn">Reset</button> | ||
|
||
<clipboard-copy for="hotkey-code" class="btn"> | ||
Copy to clipboard | ||
</clipboard-copy> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<script type="module"> | ||
import {eventToHotkeyString} from '../dist/index.js' | ||
import sequenceTracker from '../dist/sequence.js' | ||
|
||
const hotkeyCodeElement = document.getElementById('hotkey-code') | ||
const sequenceStatusElement = document.getElementById('sequence-status') | ||
const resetButtonElement = document.getElementById('reset-button') | ||
|
||
const sequenceTracker = new sequenceTracker({ | ||
onReset() { | ||
sequenceStatusElement.hidden = true | ||
} | ||
}) | ||
|
||
let currentsequence = null | ||
|
||
hotkeyCodeElement.addEventListener('keydown', event => { | ||
if (event.key === "Tab") | ||
return; | ||
|
||
event.preventDefault(); | ||
event.stopPropagation(); | ||
|
||
currentsequence = eventToHotkeyString(event) | ||
event.currentTarget.value = [...sequenceTracker.path, currentsequence].join(' '); | ||
}) | ||
|
||
hotkeyCodeElement.addEventListener('keyup', () => { | ||
// we don't just build the sequence from the keyup event because keyups don't necessarily map to keydowns - for | ||
// example, the keyup event for meta+b is just meta. | ||
if (currentsequence) { | ||
sequenceTracker.registerKeypress(currentsequence) | ||
sequenceStatusElement.hidden = false | ||
currentsequence = null | ||
} | ||
}) | ||
|
||
resetButtonElement.addEventListener('click', () => { | ||
sequenceTracker.reset() | ||
hotkeyCodeElement.value = '' | ||
}) | ||
</script> | ||
</body> | ||
|
||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
interface SequenceTrackerOptions { | ||
onReset?: () => void | ||
} | ||
|
||
export default class SequenceTracker { | ||
static readonly CHORD_TIMEOUT = 1500 | ||
|
||
private _path: readonly string[] = [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A bit nit, but the underscore seems superfluous and inconsistent with the other private fields. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I needed some way to distinguish this from the public I did try using real private class fields ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah didn't realize that we didn't have proper |
||
private timer: number | null = null | ||
private onReset | ||
|
||
constructor({onReset}: SequenceTrackerOptions = {}) { | ||
this.onReset = onReset | ||
} | ||
|
||
get path(): readonly string[] { | ||
return this._path | ||
} | ||
|
||
registerKeypress(hotkey: string): void { | ||
this._path = [...this._path, hotkey] | ||
this.startTimer() | ||
} | ||
|
||
reset(): void { | ||
this.killTimer() | ||
this._path = [] | ||
this.onReset?.() | ||
} | ||
|
||
private killTimer(): void { | ||
if (this.timer != null) { | ||
window.clearTimeout(this.timer) | ||
} | ||
this.timer = null | ||
} | ||
|
||
private startTimer(): void { | ||
this.killTimer() | ||
this.timer = window.setTimeout(() => this.reset(), SequenceTracker.CHORD_TIMEOUT) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(kinda tangential but ...)
it would be nice if the action and local dev were in sync... but I guess we don't need to run locally from a
_site
directory. Maybe we just need instructions on running locally.I've been running locally via
npx serve .
... not sure about you? It might be nice to addserve
as a dev dependency and add instructions in the README about running locally.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yeah that would be useful. I've been running
npm run build
and then serving with the VSCode live preview, which is a bit cumbersome, especially sincebuild
doesn't support a watch mode. Maybe in a future PR?